diff --git a/go.mod b/go.mod index 7ac418ebd5..b776cc445d 100644 --- a/go.mod +++ b/go.mod @@ -77,7 +77,7 @@ require ( gopkg.in/inf.v0 v0.9.1 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.6.0 - tags.cncf.io/container-device-interface v1.0.2-0.20251120202831-139ffec09210 + tags.cncf.io/container-device-interface v1.1.0 ) require ( @@ -190,5 +190,5 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - tags.cncf.io/container-device-interface/specs-go v1.0.1-0.20251120202831-139ffec09210 // indirect + tags.cncf.io/container-device-interface/specs-go v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index 319a11ed22..2631245e71 100644 --- a/go.sum +++ b/go.sum @@ -600,7 +600,7 @@ sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= src.elv.sh v0.16.0-rc1.0.20220116211855-fda62502ad7f h1:pjVeIo9Ba6K1Wy+rlwX91zT7A+xGEmxiNRBdN04gDTQ= src.elv.sh v0.16.0-rc1.0.20220116211855-fda62502ad7f/go.mod h1:kPbhv5+fBeUh85nET3wWhHGUaUQ64nZMJ8FwA5v5Olg= -tags.cncf.io/container-device-interface v1.0.2-0.20251120202831-139ffec09210 h1:ucIvxFr8UEFjsROkGrjxb3BKqZZpfifkRT9nLgeMD9U= -tags.cncf.io/container-device-interface v1.0.2-0.20251120202831-139ffec09210/go.mod h1:kIlIMADdgOVbyLj4ZvEtCvHXqFXqxfbVKKKgBZt8NgQ= -tags.cncf.io/container-device-interface/specs-go v1.0.1-0.20251120202831-139ffec09210 h1:SDIHrIFfJP54QHSdPS0VfwcVYodmkp6y/OPL/ceoejs= -tags.cncf.io/container-device-interface/specs-go v1.0.1-0.20251120202831-139ffec09210/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ= +tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY= +tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q= +tags.cncf.io/container-device-interface/specs-go v1.1.0 h1:QRZVeAceQM+zTZe12eyfuJuuzp524EKYwhmvLd+h+yQ= +tags.cncf.io/container-device-interface/specs-go v1.1.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ= diff --git a/vendor/modules.txt b/vendor/modules.txt index 57654c700e..576956a8c6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1208,12 +1208,12 @@ gopkg.in/yaml.v3 # sigs.k8s.io/yaml v1.6.0 ## explicit; go 1.22 sigs.k8s.io/yaml -# tags.cncf.io/container-device-interface v1.0.2-0.20251120202831-139ffec09210 +# tags.cncf.io/container-device-interface v1.1.0 ## explicit; go 1.21 tags.cncf.io/container-device-interface/internal/validation tags.cncf.io/container-device-interface/internal/validation/k8s tags.cncf.io/container-device-interface/pkg/cdi tags.cncf.io/container-device-interface/pkg/parser -# tags.cncf.io/container-device-interface/specs-go v1.0.1-0.20251120202831-139ffec09210 +# tags.cncf.io/container-device-interface/specs-go v1.1.0 ## explicit; go 1.19 tags.cncf.io/container-device-interface/specs-go diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go index 450a84f695..f498049c6e 100644 --- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go +++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go @@ -113,6 +113,14 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { } } + if len(e.NetDevices) > 0 { + // specgen is currently missing functionality to set Linux NetDevices, + // so we use a locally rolled function for now. + for _, dev := range e.NetDevices { + specgenAddLinuxNetDevice(&specgen, dev.HostInterfaceName, (&LinuxNetDevice{dev}).toOCI()) + } + } + if len(e.Mounts) > 0 { for _, m := range e.Mounts { specgen.RemoveMount(m.ContainerPath) @@ -162,6 +170,24 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { return nil } +func specgenAddLinuxNetDevice(specgen *ocigen.Generator, hostIf string, netDev *oci.LinuxNetDevice) { + if specgen == nil || netDev == nil { + return + } + ensureLinuxNetDevices(specgen.Config) + specgen.Config.Linux.NetDevices[hostIf] = *netDev +} + +// Ensure OCI Spec Linux NetDevices map is not nil. +func ensureLinuxNetDevices(spec *oci.Spec) { + if spec.Linux == nil { + spec.Linux = &oci.Linux{} + } + if spec.Linux.NetDevices == nil { + spec.Linux.NetDevices = map[string]oci.LinuxNetDevice{} + } +} + // Validate container edits. func (e *ContainerEdits) Validate() error { if e == nil || e.ContainerEdits == nil { @@ -191,6 +217,9 @@ func (e *ContainerEdits) Validate() error { return err } } + if err := ValidateNetDevices(e.NetDevices); err != nil { + return err + } return nil } @@ -210,6 +239,7 @@ func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits { e.Env = append(e.Env, o.Env...) e.DeviceNodes = append(e.DeviceNodes, o.DeviceNodes...) + e.NetDevices = append(e.NetDevices, o.NetDevices...) e.Hooks = append(e.Hooks, o.Hooks...) e.Mounts = append(e.Mounts, o.Mounts...) if o.IntelRdt != nil { @@ -244,6 +274,9 @@ func (e *ContainerEdits) isEmpty() bool { if e.IntelRdt != nil { return false } + if len(e.NetDevices) > 0 { + return false + } return true } @@ -257,6 +290,49 @@ func ValidateEnv(env []string) error { return nil } +// ValidateNetDevices validates the given net devices. +func ValidateNetDevices(devices []*cdi.LinuxNetDevice) error { + var ( + hostSeen = map[string]string{} + nameSeen = map[string]string{} + ) + + for _, dev := range devices { + if err := (&LinuxNetDevice{dev}).Validate(); err != nil { + return err + } + if other, ok := hostSeen[dev.HostInterfaceName]; ok { + return fmt.Errorf("invalid linux net device, duplicate HostInterfaceName %q with names %q and %q", + dev.HostInterfaceName, dev.Name, other) + } + hostSeen[dev.HostInterfaceName] = dev.Name + + if other, ok := nameSeen[dev.Name]; ok { + return fmt.Errorf("invalid linux net device, duplicate Name %q with HostInterfaceName %q and %q", + dev.Name, dev.HostInterfaceName, other) + } + nameSeen[dev.Name] = dev.HostInterfaceName + } + + return nil +} + +// LinuxNetDevice is a CDI Spec LinuxNetDevice wrapper, used for OCI conversion and validating. +type LinuxNetDevice struct { + *cdi.LinuxNetDevice +} + +// Validate LinuxNetDevice. +func (d *LinuxNetDevice) Validate() error { + if d.HostInterfaceName == "" { + return errors.New("invalid linux net device, empty HostInterfaceName") + } + if d.Name == "" { + return errors.New("invalid linux net device, empty Name") + } + return nil +} + // DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes. type DeviceNode struct { *cdi.DeviceNode diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits_unix.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits_unix.go index e0d41a6815..a840978099 100644 --- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits_unix.go +++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits_unix.go @@ -21,6 +21,7 @@ package cdi import ( "errors" "fmt" + "os" "golang.org/x/sys/unix" ) @@ -31,16 +32,28 @@ const ( fifoDevice = "p" ) +type deviceInfo struct { + // cgroup properties + deviceType string + major int64 + minor int64 + + // device node properties + fileMode os.FileMode +} + // deviceInfoFromPath takes the path to a device and returns its type, // major and minor device numbers. // // It was adapted from https://github.com/opencontainers/runc/blob/v1.1.9/libcontainer/devices/device_unix.go#L30-L69 -func deviceInfoFromPath(path string) (devType string, major, minor int64, _ error) { +func deviceInfoFromPath(path string) (*deviceInfo, error) { var stat unix.Stat_t err := unix.Lstat(path, &stat) if err != nil { - return "", 0, 0, err + return nil, err } + + var devType string switch stat.Mode & unix.S_IFMT { case unix.S_IFBLK: devType = blockDevice @@ -49,38 +62,71 @@ func deviceInfoFromPath(path string) (devType string, major, minor int64, _ erro case unix.S_IFIFO: devType = fifoDevice default: - return "", 0, 0, errors.New("not a device node") + return nil, errors.New("not a device node") } devNumber := uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS. - return devType, int64(unix.Major(devNumber)), int64(unix.Minor(devNumber)), nil + + di := deviceInfo{ + deviceType: devType, + major: int64(unix.Major(devNumber)), + minor: int64(unix.Minor(devNumber)), + fileMode: os.FileMode(stat.Mode &^ unix.S_IFMT), + } + + return &di, nil } // fillMissingInfo fills in missing mandatory attributes from the host device. func (d *DeviceNode) fillMissingInfo() error { + hasMinimalSpecification := d.Type != "" && (d.Major != 0 || d.Type == fifoDevice) + + // Ensure that the host path and the container path match. if d.HostPath == "" { d.HostPath = d.Path } - if d.Type != "" && (d.Major != 0 || d.Type == "p") { + // Try to extract the device info from the host path. + di, err := deviceInfoFromPath(d.HostPath) + if err != nil { + // The error is only considered fatal if the device is not already + // minimally specified since it is allowed for a device vendor to fully + // specify a device node specification. + if !hasMinimalSpecification { + return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err) + } return nil } - deviceType, major, minor, err := deviceInfoFromPath(d.HostPath) - if err != nil { - return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err) + // Even for minimally-specified device nodes, we update the file mode if + // required. This is useful for rootless containers where device node + // requests may be treated as bind mounts. + if d.FileMode == nil { + d.FileMode = &di.fileMode + } + + // If the device is minimally specified, we make no further updates and + // don't perform additional checks. + if hasMinimalSpecification { + return nil } if d.Type == "" { - d.Type = deviceType - } else { - if d.Type != deviceType { - return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)", - d.Path, d.HostPath, d.Type, deviceType) - } + d.Type = di.deviceType } - if d.Major == 0 && d.Type != "p" { - d.Major = major - d.Minor = minor + if d.Type != di.deviceType { + return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)", + d.Path, d.HostPath, d.Type, di.deviceType) + } + + // For a fifoDevice, we do not update the major and minor number. + if d.Type == fifoDevice { + return nil + } + + // Update the major and minor number for the device node if required. + if d.Major == 0 { + d.Major = di.major + d.Minor = di.minor } return nil diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/oci.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/oci.go index d8fa14a2f1..f37499fcf2 100644 --- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/oci.go +++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/oci.go @@ -63,3 +63,10 @@ func (i *IntelRdt) toOCI() *spec.LinuxIntelRdt { EnableMonitoring: i.EnableMonitoring, } } + +// toOCI returns the opencontainers runtime Spec LinuxNetDevice for this LinuxNetDevice. +func (d *LinuxNetDevice) toOCI() *spec.LinuxNetDevice { + return &spec.LinuxNetDevice{ + Name: d.Name, + } +} diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go index 577331f05b..ccda9d860e 100644 --- a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go +++ b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go @@ -24,12 +24,13 @@ type Device struct { // ContainerEdits are edits a container runtime must make to the OCI spec to expose the device. type ContainerEdits struct { - Env []string `json:"env,omitempty" yaml:"env,omitempty"` - DeviceNodes []*DeviceNode `json:"deviceNodes,omitempty" yaml:"deviceNodes,omitempty"` - Hooks []*Hook `json:"hooks,omitempty" yaml:"hooks,omitempty"` - Mounts []*Mount `json:"mounts,omitempty" yaml:"mounts,omitempty"` - IntelRdt *IntelRdt `json:"intelRdt,omitempty" yaml:"intelRdt,omitempty"` // Added in v0.7.0 - AdditionalGIDs []uint32 `json:"additionalGids,omitempty" yaml:"additionalGids,omitempty"` // Added in v0.7.0 + Env []string `json:"env,omitempty" yaml:"env,omitempty"` + DeviceNodes []*DeviceNode `json:"deviceNodes,omitempty" yaml:"deviceNodes,omitempty"` + NetDevices []*LinuxNetDevice `json:"netDevices,omitempty" yaml:"netDevices,omitempty"` // Added in v1.1.0 + Hooks []*Hook `json:"hooks,omitempty" yaml:"hooks,omitempty"` + Mounts []*Mount `json:"mounts,omitempty" yaml:"mounts,omitempty"` + IntelRdt *IntelRdt `json:"intelRdt,omitempty" yaml:"intelRdt,omitempty"` // Added in v0.7.0 + AdditionalGIDs []uint32 `json:"additionalGids,omitempty" yaml:"additionalGids,omitempty"` // Added in v0.7.0 } // DeviceNode represents a device node that needs to be added to the OCI spec. @@ -67,6 +68,12 @@ type IntelRdt struct { ClosID string `json:"closID,omitempty" yaml:"closID,omitempty"` L3CacheSchema string `json:"l3CacheSchema,omitempty" yaml:"l3CacheSchema,omitempty"` MemBwSchema string `json:"memBwSchema,omitempty" yaml:"memBwSchema,omitempty"` - Schemata []string `json:"schemata,omitempty" yaml:"schemata,omitempty"` - EnableMonitoring bool `json:"enableMonitoring,omitempty" yaml:"enableMonitoring,omitempty"` + Schemata []string `json:"schemata,omitempty" yaml:"schemata,omitempty"` // Added in v1.1.0. + EnableMonitoring bool `json:"enableMonitoring,omitempty" yaml:"enableMonitoring,omitempty"` // Added in v1.1.0. +} + +// LinuxNetDevice represents an OCI LinuxNetDevice to be added to the OCI Spec. +type LinuxNetDevice struct { + HostInterfaceName string `json:"hostInterfaceName" yaml:"hostInterfaceName"` + Name string `json:"name" yaml:"name"` } diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/version.go b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go index 4e5d736625..c3f5a8d10d 100644 --- a/vendor/tags.cncf.io/container-device-interface/specs-go/version.go +++ b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go @@ -25,7 +25,7 @@ import ( const ( // CurrentVersion is the current version of the Spec. - CurrentVersion = "1.0.0" + CurrentVersion = "1.1.0" // vCurrent is the current version as a semver-comparable type vCurrent version = "v" + CurrentVersion @@ -150,12 +150,20 @@ func requiresV110(spec *Spec) bool { } } + if len(spec.ContainerEdits.NetDevices) > 0 { + return true + } + for _, dev := range spec.Devices { if i := dev.ContainerEdits.IntelRdt; i != nil { if i.Schemata != nil || i.EnableMonitoring { return true } } + + if len(dev.ContainerEdits.NetDevices) > 0 { + return true + } } return false