vendor: tags.cncf.io/container-device-interface v1.1.0

adds compatibility with runtime-spec v1.3.0

full diff: https://github.com/cncf-tags/container-device-interface/compare/v1.0.1...v1.1.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-12-12 14:59:26 +01:00
parent 2c533f9327
commit 6766ce7be2
16 changed files with 218 additions and 126 deletions

4
go.mod
View File

@@ -118,7 +118,7 @@ require (
gotest.tools/v3 v3.5.2
pgregory.net/rapid v1.2.0
resenje.org/singleflight v0.4.3
tags.cncf.io/container-device-interface v1.0.1
tags.cncf.io/container-device-interface v1.1.0
)
require (
@@ -254,7 +254,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect
tags.cncf.io/container-device-interface/specs-go v1.1.0 // indirect
)
replace github.com/moby/moby/api => ./api

8
go.sum
View File

@@ -874,7 +874,7 @@ resenje.org/singleflight v0.4.3/go.mod h1:lAgQK7VfjG6/pgredbQfmV0RvG/uVhKo6vSuZ0
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8=
tags.cncf.io/container-device-interface/specs-go v1.0.0/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=

6
vendor/modules.txt vendored
View File

@@ -1902,13 +1902,13 @@ resenje.org/singleflight
# sigs.k8s.io/yaml v1.6.0
## explicit; go 1.22
sigs.k8s.io/yaml
# tags.cncf.io/container-device-interface v1.0.1
## explicit; go 1.20
# 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.0
# tags.cncf.io/container-device-interface/specs-go v1.1.0
## explicit; go 1.19
tags.cncf.io/container-device-interface/specs-go
# github.com/moby/moby/api => ./api

View File

@@ -520,7 +520,7 @@ func (w *watch) stop() {
return
}
w.watcher.Close()
_ = w.watcher.Close()
w.tracked = nil
}

View File

@@ -1,26 +0,0 @@
//go:build darwin
// +build darwin
/*
Copyright © 2021 The CDI Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cdi
import "syscall"
func osSync() {
_ = syscall.Sync()
}

View File

@@ -1,26 +0,0 @@
//go:build !windows && !darwin
// +build !windows,!darwin
/*
Copyright © 2021 The CDI Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cdi
import "syscall"
func osSync() {
syscall.Sync()
}

View File

@@ -1,22 +0,0 @@
//go:build windows
// +build windows
/*
Copyright © 2021 The CDI Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cdi
func osSync() {}

View File

@@ -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
@@ -337,8 +413,10 @@ func ValidateIntelRdt(i *cdi.IntelRdt) error {
// Validate validates the IntelRdt configuration.
func (i *IntelRdt) Validate() error {
// ClosID must be a valid Linux filename
if len(i.ClosID) >= 4096 || i.ClosID == "." || i.ClosID == ".." || strings.ContainsAny(i.ClosID, "/\n") {
// ClosID must be a valid Linux filename. Exception: "/" refers to the root CLOS.
switch c := i.ClosID; {
case c == "/":
case len(c) >= 4096, c == ".", c == "..", strings.ContainsAny(c, "/\n"):
return errors.New("invalid ClosID")
}
return nil

View File

@@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
/*
Copyright © 2021 The CDI Authors
@@ -22,6 +21,7 @@ package cdi
import (
"errors"
"fmt"
"os"
"golang.org/x/sys/unix"
)
@@ -32,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
@@ -50,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

View File

@@ -1,5 +1,4 @@
//go:build windows
// +build windows
/*
Copyright © 2021 The CDI Authors

View File

@@ -56,10 +56,17 @@ func (d *DeviceNode) toOCI() spec.LinuxDevice {
// toOCI returns the opencontainers runtime Spec LinuxIntelRdt for this IntelRdt config.
func (i *IntelRdt) toOCI() *spec.LinuxIntelRdt {
return &spec.LinuxIntelRdt{
ClosID: i.ClosID,
L3CacheSchema: i.L3CacheSchema,
MemBwSchema: i.MemBwSchema,
EnableCMT: i.EnableCMT,
EnableMBM: i.EnableMBM,
ClosID: i.ClosID,
L3CacheSchema: i.L3CacheSchema,
MemBwSchema: i.MemBwSchema,
Schemata: i.Schemata,
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,
}
}

View File

@@ -156,7 +156,7 @@ func (s *Spec) write(overwrite bool) error {
return fmt.Errorf("failed to create Spec file: %w", err)
}
_, err = tmp.Write(data)
tmp.Close()
_ = tmp.Close()
if err != nil {
return fmt.Errorf("failed to write Spec file: %w", err)
}
@@ -164,7 +164,7 @@ func (s *Spec) write(overwrite bool) error {
err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(s.path), overwrite)
if err != nil {
os.Remove(tmp.Name())
_ = os.Remove(tmp.Name())
err = fmt.Errorf("failed to write Spec file: %w", err)
}

View File

@@ -32,7 +32,9 @@ func renameIn(dir, src, dst string, overwrite bool) error {
if err != nil {
return fmt.Errorf("rename failed: %w", err)
}
defer dirf.Close()
defer func() {
_ = dirf.Close()
}()
if !overwrite {
flags = unix.RENAME_NOREPLACE

View File

@@ -1,5 +1,4 @@
//go:build !linux
// +build !linux
/*
Copyright © 2022 The CDI Authors

View File

@@ -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.
@@ -64,9 +65,15 @@ type Hook struct {
// IntelRdt describes the Linux IntelRdt parameters to set in the OCI spec.
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"`
EnableCMT bool `json:"enableCMT,omitempty" yaml:"enableCMT,omitempty"`
EnableMBM bool `json:"enableMBM,omitempty" yaml:"enableMBM,omitempty"`
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"` // 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"`
}

View File

@@ -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,33 @@ func (r requiredVersionMap) requiredVersion(spec *Spec) version {
return minVersion
}
// requiresV110 returns true if the spec uses v1.1.0 features.
func requiresV110(spec *Spec) bool {
if i := spec.ContainerEdits.IntelRdt; i != nil {
if i.Schemata != nil || i.EnableMonitoring {
return true
}
}
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
}
// 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.