diff --git a/cmd/cdi/cmd/validate.go b/cmd/cdi/cmd/validate.go index 9dc8faf..5a08843 100644 --- a/cmd/cdi/cmd/validate.go +++ b/cmd/cdi/cmd/validate.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "tags.cncf.io/container-device-interface/pkg/cdi" + "tags.cncf.io/container-device-interface/specs-go" ) // validateCmd is our CDI command for validating CDI Spec files in the cache. @@ -49,6 +50,15 @@ were reported by the cache.`, fmt.Printf(" %2d: %v\n", idx, strings.TrimSpace(err.Error())) } } + + for _, v := range cache.ListVendors() { + for _, s := range cache.GetVendorSpecs(v) { + if err := specs.ValidateVersion(s.Spec); err != nil { + fmt.Printf("Spec file %s failed version validation: %v\n", s.GetPath(), err) + } + } + } + os.Exit(1) }, } diff --git a/cmd/cdi/go.mod b/cmd/cdi/go.mod index 6893285..e9647a0 100644 --- a/cmd/cdi/go.mod +++ b/cmd/cdi/go.mod @@ -4,13 +4,14 @@ go 1.20 require ( github.com/fsnotify/fsnotify v1.5.1 - github.com/opencontainers/runtime-spec v1.1.0 + github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 github.com/spf13/cobra v1.6.0 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.4.0 tags.cncf.io/container-device-interface v1.0.1 tags.cncf.io/container-device-interface/schema v0.0.0 + tags.cncf.io/container-device-interface/specs-go v1.0.0 ) require ( @@ -22,7 +23,6 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/mod v0.19.0 // indirect golang.org/x/sys v0.19.0 // indirect - tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect ) replace ( diff --git a/cmd/cdi/go.sum b/cmd/cdi/go.sum index b1265d5..f67322a 100644 --- a/cmd/cdi/go.sum +++ b/cmd/cdi/go.sum @@ -18,8 +18,8 @@ github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= -github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= diff --git a/cmd/validate/go.sum b/cmd/validate/go.sum index 5089ed5..607c1db 100644 --- a/cmd/validate/go.sum +++ b/cmd/validate/go.sum @@ -3,7 +3,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/go.mod b/go.mod index 3ec778a..49e9316 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/fsnotify/fsnotify v1.5.1 - github.com/opencontainers/runtime-spec v1.1.0 + github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.19.0 diff --git a/go.sum b/go.sum index 719eb14..df6c298 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= -github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= diff --git a/pkg/cdi/container-edits.go b/pkg/cdi/container-edits.go index 4744eff..dc561b2 100644 --- a/pkg/cdi/container-edits.go +++ b/pkg/cdi/container-edits.go @@ -113,6 +113,14 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { } } + if e.NetDevices != nil { + // 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.HostIf, (&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 e.NetDevices != nil { + return false + } return true } @@ -257,6 +290,46 @@ 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 other, ok := hostSeen[dev.HostIf]; ok { + return fmt.Errorf("invalid linux net device, duplicate HostIf %q with names %q and %q", + dev.HostIf, dev.Name, other) + } + hostSeen[dev.HostIf] = dev.Name + + if other, ok := nameSeen[dev.Name]; ok { + return fmt.Errorf("invalid linux net device, duplicate Name %q with HostIf %q and %q", + dev.Name, dev.HostIf, other) + } + nameSeen[dev.Name] = dev.HostIf + } + + 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.HostIf == "" { + return errors.New("invalid linux net device, empty HostIf") + } + 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/pkg/cdi/oci.go b/pkg/cdi/oci.go index 4d62c41..4500b9a 100644 --- a/pkg/cdi/oci.go +++ b/pkg/cdi/oci.go @@ -63,3 +63,10 @@ func (i *IntelRdt) toOCI() *spec.LinuxIntelRdt { EnableMBM: i.EnableMBM, } } + +// 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/schema/defs.json b/schema/defs.json index f549423..e27e7e0 100644 --- a/schema/defs.json +++ b/schema/defs.json @@ -26,6 +26,9 @@ "Env": { "$ref": "#/definitions/ArrayOfStrings" }, + "InterfaceName": { + "type": "string" + }, "mapStringString": { "type": "object", "patternProperties": { @@ -111,6 +114,17 @@ "path" ] }, + "LinuxNetDevice": { + "type": "object", + "properties": { + "hostIf": { + "$ref": "#/definitions/InterfaceName" + }, + "name": { + "$ref": "#/definitions/InterfaceName" + } + } + }, "containerEdits": { "type": "object", "properties": { @@ -126,6 +140,12 @@ "$ref": "#/definitions/DeviceNode" } }, + "netDevices": { + "type": "array", + "items": { + "$ref": "#/definitions/LinuxNetDevice" + } + }, "mounts": { "type": "array", "items": { diff --git a/schema/go.mod b/schema/go.mod index 87074a0..240237f 100644 --- a/schema/go.mod +++ b/schema/go.mod @@ -13,7 +13,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/opencontainers/runtime-spec v1.1.0 // indirect + github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 // indirect github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect diff --git a/schema/go.sum b/schema/go.sum index 6ebffba..a730346 100644 --- a/schema/go.sum +++ b/schema/go.sum @@ -15,8 +15,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= -github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA= +github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= diff --git a/specs-go/config.go b/specs-go/config.go index f28657b..dbcfe80 100644 --- a/specs-go/config.go +++ b/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. @@ -70,3 +71,9 @@ type IntelRdt struct { EnableCMT bool `json:"enableCMT,omitempty" yaml:"enableCMT,omitempty"` EnableMBM bool `json:"enableMBM,omitempty" yaml:"enableMBM,omitempty"` } + +// LinuxNetDevice represents an OCI LinuxNetDevice to be added to the OCI Spec. +type LinuxNetDevice struct { + HostIf string `json:"hostIf" yaml:"hostIf"` + Name string `json:"name" yaml:"name"` +} diff --git a/specs-go/version.go b/specs-go/version.go index 002e035..f924385 100644 --- a/specs-go/version.go +++ b/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 @@ -40,6 +40,7 @@ const ( v070 version = "v0.7.0" v080 version = "v0.8.0" v100 version = "v1.0.0" + v110 version = "v1.1.0" // vEarliest is the earliest supported version of the CDI specification vEarliest version = v030 @@ -58,6 +59,7 @@ var validSpecVersions = requiredVersionMap{ v070: requiresV070, v080: requiresV080, v100: requiresV100, + v110: requiresV110, } // ValidateVersion checks whether the specified spec version is valid. @@ -140,6 +142,20 @@ func (r requiredVersionMap) requiredVersion(spec *Spec) version { return minVersion } +// requiresV110 returns true if the spec uses v1.1.0 features. +// NetDevices was added after v1.0.0 so it presence requires v1.1.0. +func requiresV110(spec *Spec) bool { + if len(spec.ContainerEdits.NetDevices) != 0 { + return true + } + for _, dev := range spec.Devices { + if len(dev.ContainerEdits.NetDevices) != 0 { + return true + } + } + return false +} + // requiresV100 returns true if the spec uses v1.0.0 features. // Since the v1.0.0 spec bump was due to moving the minimum version checks to // the spec package, there are no explicit spec changes.