build(deps): bump github.com/container-orchestrated-devices/container-device-interface

Bumps [github.com/container-orchestrated-devices/container-device-interface](https://github.com/container-orchestrated-devices/container-device-interface) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/container-orchestrated-devices/container-device-interface/releases)
- [Commits](https://github.com/container-orchestrated-devices/container-device-interface/compare/v0.5.3...v0.5.4)

---
updated-dependencies:
- dependency-name: github.com/container-orchestrated-devices/container-device-interface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

[NO NEW TESTS NEEDED]

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
dependabot[bot]
2023-02-06 20:09:41 +00:00
committed by Valentin Rothberg
parent a9ec6492e8
commit e9942c61dd
68 changed files with 364 additions and 8752 deletions

View File

@@ -17,9 +17,9 @@
package cdi
import (
"errors"
"fmt"
"strings"
"github.com/pkg/errors"
)
const (
@@ -34,14 +34,14 @@ const (
func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error) {
key, err := AnnotationKey(plugin, deviceID)
if err != nil {
return annotations, errors.Wrap(err, "CDI annotation failed")
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
}
if _, ok := annotations[key]; ok {
return annotations, errors.Errorf("CDI annotation failed, key %q used", key)
return annotations, fmt.Errorf("CDI annotation failed, key %q used", key)
}
value, err := AnnotationValue(devices)
if err != nil {
return annotations, errors.Wrap(err, "CDI annotation failed")
return annotations, fmt.Errorf("CDI annotation failed: %w", err)
}
if annotations == nil {
@@ -70,7 +70,7 @@ func ParseAnnotations(annotations map[string]string) ([]string, []string, error)
}
for _, d := range strings.Split(value, ",") {
if !IsQualifiedName(d) {
return nil, nil, errors.Errorf("invalid CDI device name %q", d)
return nil, nil, fmt.Errorf("invalid CDI device name %q", d)
}
devices = append(devices, d)
}
@@ -98,11 +98,11 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_")
if len(name) > maxNameLen {
return "", errors.Errorf("invalid plugin+deviceID %q, too long", name)
return "", fmt.Errorf("invalid plugin+deviceID %q, too long", name)
}
if c := rune(name[0]); !isAlphaNumeric(c) {
return "", errors.Errorf("invalid name %q, first '%c' should be alphanumeric",
return "", fmt.Errorf("invalid name %q, first '%c' should be alphanumeric",
name, c)
}
if len(name) > 2 {
@@ -111,13 +111,13 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
case isAlphaNumeric(c):
case c == '_' || c == '-' || c == '.':
default:
return "", errors.Errorf("invalid name %q, invalid charcter '%c'",
return "", fmt.Errorf("invalid name %q, invalid charcter '%c'",
name, c)
}
}
}
if c := rune(name[len(name)-1]); !isAlphaNumeric(c) {
return "", errors.Errorf("invalid name %q, last '%c' should be alphanumeric",
return "", fmt.Errorf("invalid name %q, last '%c' should be alphanumeric",
name, c)
}

View File

@@ -17,6 +17,8 @@
package cdi
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
@@ -24,13 +26,10 @@ import (
"strings"
"sync"
stderr "errors"
"github.com/container-orchestrated-devices/container-device-interface/internal/multierror"
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
"github.com/fsnotify/fsnotify"
oci "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// Option is an option to change some aspect of default CDI behavior.
@@ -97,7 +96,7 @@ func (c *Cache) configure(options ...Option) error {
for _, o := range options {
if err = o(c); err != nil {
return errors.Wrapf(err, "failed to apply cache options")
return fmt.Errorf("failed to apply cache options: %w", err)
}
}
@@ -159,7 +158,7 @@ func (c *Cache) refresh() error {
return false
case devPrio == oldPrio:
devPath, oldPath := devSpec.GetPath(), oldSpec.GetPath()
collectError(errors.Errorf("conflicting device %q (specs %q, %q)",
collectError(fmt.Errorf("conflicting device %q (specs %q, %q)",
name, devPath, oldPath), devPath, oldPath)
conflicts[name] = struct{}{}
}
@@ -169,7 +168,7 @@ func (c *Cache) refresh() error {
_ = scanSpecDirs(c.specDirs, func(path string, priority int, spec *Spec, err error) error {
path = filepath.Clean(path)
if err != nil {
collectError(errors.Wrapf(err, "failed to load CDI Spec"), path)
collectError(fmt.Errorf("failed to load CDI Spec %w", err), path)
return nil
}
@@ -219,7 +218,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
var unresolved []string
if ociSpec == nil {
return devices, errors.Errorf("can't inject devices, nil OCI Spec")
return devices, fmt.Errorf("can't inject devices, nil OCI Spec")
}
c.Lock()
@@ -244,12 +243,12 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
}
if unresolved != nil {
return unresolved, errors.Errorf("unresolvable CDI devices %s",
return unresolved, fmt.Errorf("unresolvable CDI devices %s",
strings.Join(devices, ", "))
}
if err := edits.Apply(ociSpec); err != nil {
return nil, errors.Wrap(err, "failed to inject devices")
return nil, fmt.Errorf("failed to inject devices: %w", err)
}
return nil, nil
@@ -320,7 +319,7 @@ func (c *Cache) RemoveSpec(name string) error {
}
err = os.Remove(path)
if err != nil && stderr.Is(err, fs.ErrNotExist) {
if err != nil && errors.Is(err, fs.ErrNotExist) {
err = nil
}
@@ -409,7 +408,17 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
// GetSpecErrors returns all errors encountered for the spec during the
// last cache refresh.
func (c *Cache) GetSpecErrors(spec *Spec) []error {
return c.errors[spec.GetPath()]
var errors []error
c.Lock()
defer c.Unlock()
if errs, ok := c.errors[spec.GetPath()]; ok {
errors = make([]error, len(errs))
copy(errors, errs)
}
return errors
}
// GetErrors returns all errors encountered during the last
@@ -475,7 +484,7 @@ func (w *watch) setup(dirs []string, dirErrors map[string]error) {
w.watcher, err = fsnotify.NewWatcher()
if err != nil {
for _, dir := range dirs {
dirErrors[dir] = errors.Wrap(err, "failed to create watcher")
dirErrors[dir] = fmt.Errorf("failed to create watcher: %w", err)
}
return
}
@@ -558,7 +567,7 @@ func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
update = true
} else {
w.tracked[dir] = false
dirErrors[dir] = errors.Wrap(err, "failed to monitor for changes")
dirErrors[dir] = fmt.Errorf("failed to monitor for changes: %w", err)
}
}

View File

@@ -17,13 +17,13 @@
package cdi
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/pkg/errors"
"github.com/container-orchestrated-devices/container-device-interface/specs-go"
oci "github.com/opencontainers/runtime-spec/specs-go"
ocigen "github.com/opencontainers/runtime-tools/generate"
@@ -140,7 +140,7 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
ensureOCIHooks(spec)
spec.Hooks.StartContainer = append(spec.Hooks.StartContainer, h.ToOCI())
default:
return errors.Errorf("unknown hook name %q", h.HookName)
return fmt.Errorf("unknown hook name %q", h.HookName)
}
}
@@ -154,7 +154,7 @@ func (e *ContainerEdits) Validate() error {
}
if err := ValidateEnv(e.Env); err != nil {
return errors.Wrap(err, "invalid container edits")
return fmt.Errorf("invalid container edits: %w", err)
}
for _, d := range e.DeviceNodes {
if err := (&DeviceNode{d}).Validate(); err != nil {
@@ -209,7 +209,7 @@ func (e *ContainerEdits) isEmpty() bool {
func ValidateEnv(env []string) error {
for _, v := range env {
if strings.IndexByte(v, byte('=')) <= 0 {
return errors.Errorf("invalid environment variable %q", v)
return fmt.Errorf("invalid environment variable %q", v)
}
}
return nil
@@ -234,11 +234,11 @@ func (d *DeviceNode) Validate() error {
return errors.New("invalid (empty) device path")
}
if _, ok := validTypes[d.Type]; !ok {
return errors.Errorf("device %q: invalid type %q", d.Path, d.Type)
return fmt.Errorf("device %q: invalid type %q", d.Path, d.Type)
}
for _, bit := range d.Permissions {
if bit != 'r' && bit != 'w' && bit != 'm' {
return errors.Errorf("device %q: invalid persmissions %q",
return fmt.Errorf("device %q: invalid persmissions %q",
d.Path, d.Permissions)
}
}
@@ -253,13 +253,13 @@ type Hook struct {
// Validate a hook.
func (h *Hook) Validate() error {
if _, ok := validHookNames[h.HookName]; !ok {
return errors.Errorf("invalid hook name %q", h.HookName)
return fmt.Errorf("invalid hook name %q", h.HookName)
}
if h.Path == "" {
return errors.Errorf("invalid hook %q with empty path", h.HookName)
return fmt.Errorf("invalid hook %q with empty path", h.HookName)
}
if err := ValidateEnv(h.Env); err != nil {
return errors.Wrapf(err, "invalid hook %q", h.HookName)
return fmt.Errorf("invalid hook %q: %w", h.HookName, err)
}
return nil
}
@@ -298,7 +298,8 @@ func sortMounts(specgen *ocigen.Generator) {
// orderedMounts defines how to sort an OCI Spec Mount slice.
// This is the almost the same implementation sa used by CRI-O and Docker,
// with a minor tweak for stable sorting order (easier to test):
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
//
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
type orderedMounts []oci.Mount
// Len returns the number of mounts. Used in sorting.

View File

@@ -20,8 +20,9 @@
package cdi
import (
"fmt"
runc "github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors"
)
// fillMissingInfo fills in missing mandatory attributes from the host device.
@@ -36,14 +37,14 @@ func (d *DeviceNode) fillMissingInfo() error {
hostDev, err := runc.DeviceFromPath(d.HostPath, "rwm")
if err != nil {
return errors.Wrapf(err, "failed to stat CDI host device %q", d.HostPath)
return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err)
}
if d.Type == "" {
d.Type = string(hostDev.Type)
} else {
if d.Type != string(hostDev.Type) {
return errors.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
d.Path, d.HostPath, d.Type, string(hostDev.Type))
}
}

View File

@@ -17,9 +17,10 @@
package cdi
import (
"fmt"
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
oci "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// Device represents a CDI device of a Spec.
@@ -69,10 +70,10 @@ func (d *Device) validate() error {
}
edits := d.edits()
if edits.isEmpty() {
return errors.Errorf("invalid device, empty device edits")
return fmt.Errorf("invalid device, empty device edits")
}
if err := edits.Validate(); err != nil {
return errors.Wrapf(err, "invalid device %q", d.Name)
return fmt.Errorf("invalid device %q: %w", d.Name, err)
}
return nil
}

View File

@@ -46,7 +46,6 @@
// "fmt"
// "strings"
//
// "github.com/pkg/errors"
// log "github.com/sirupsen/logrus"
//
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
@@ -58,7 +57,7 @@
//
// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
// if err != nil {
// return errors.Wrap(err, "CDI device injection failed")
// return fmt.Errorf("CDI device injection failed: %w", err)
// }
//
// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
@@ -90,7 +89,6 @@
// "fmt"
// "strings"
//
// "github.com/pkg/errors"
// log "github.com/sirupsen/logrus"
//
// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
@@ -115,7 +113,7 @@
//
// unresolved, err := registry.InjectDevices(spec, devices)
// if err != nil {
// return errors.Wrap(err, "CDI device injection failed")
// return fmt.Errorf("CDI device injection failed: %w", err)
// }
//
// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
@@ -196,7 +194,7 @@
// return fmt.Errorf("failed to generate Spec name: %w", err)
// }
//
// return registry.WriteSpec(spec, specName)
// return registry.SpecDB().WriteSpec(spec, specName)
// }
//
// Similary, generating and later cleaning up transient Spec files can be
@@ -241,7 +239,7 @@
// return fmt.Errorf("failed to generate Spec name: %w", err)
// }
//
// return registry.WriteSpec(spec, specName)
// return registry.SpecDB().WriteSpec(spec, specName)
// }
//
// func removeTransientSpec(ctr Container) error {
@@ -249,7 +247,7 @@
// transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
//
// return registry.RemoveSpec(specName)
// return registry.SpecDB().RemoveSpec(specName)
// }
//
// CDI Spec Validation

View File

@@ -17,9 +17,8 @@
package cdi
import (
"fmt"
"strings"
"github.com/pkg/errors"
)
// QualifiedName returns the qualified name for a device.
@@ -50,23 +49,23 @@ func ParseQualifiedName(device string) (string, string, string, error) {
vendor, class, name := ParseDevice(device)
if vendor == "" {
return "", "", device, errors.Errorf("unqualified device %q, missing vendor", device)
return "", "", device, fmt.Errorf("unqualified device %q, missing vendor", device)
}
if class == "" {
return "", "", device, errors.Errorf("unqualified device %q, missing class", device)
return "", "", device, fmt.Errorf("unqualified device %q, missing class", device)
}
if name == "" {
return "", "", device, errors.Errorf("unqualified device %q, missing device name", device)
return "", "", device, fmt.Errorf("unqualified device %q, missing device name", device)
}
if err := ValidateVendorName(vendor); err != nil {
return "", "", device, errors.Wrapf(err, "invalid device %q", device)
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
}
if err := ValidateClassName(class); err != nil {
return "", "", device, errors.Wrapf(err, "invalid device %q", device)
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
}
if err := ValidateDeviceName(name); err != nil {
return "", "", device, errors.Wrapf(err, "invalid device %q", device)
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
}
return vendor, class, name, nil
@@ -115,22 +114,22 @@ func ParseQualifier(kind string) (string, string) {
// - underscore, dash, and dot ('_', '-', and '.')
func ValidateVendorName(vendor string) error {
if vendor == "" {
return errors.Errorf("invalid (empty) vendor name")
return fmt.Errorf("invalid (empty) vendor name")
}
if !isLetter(rune(vendor[0])) {
return errors.Errorf("invalid vendor %q, should start with letter", vendor)
return fmt.Errorf("invalid vendor %q, should start with letter", vendor)
}
for _, c := range string(vendor[1 : len(vendor)-1]) {
switch {
case isAlphaNumeric(c):
case c == '_' || c == '-' || c == '.':
default:
return errors.Errorf("invalid character '%c' in vendor name %q",
return fmt.Errorf("invalid character '%c' in vendor name %q",
c, vendor)
}
}
if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
return errors.Errorf("invalid vendor %q, should end with a letter or digit", vendor)
return fmt.Errorf("invalid vendor %q, should end with a letter or digit", vendor)
}
return nil
@@ -143,22 +142,22 @@ func ValidateVendorName(vendor string) error {
// - underscore and dash ('_', '-')
func ValidateClassName(class string) error {
if class == "" {
return errors.Errorf("invalid (empty) device class")
return fmt.Errorf("invalid (empty) device class")
}
if !isLetter(rune(class[0])) {
return errors.Errorf("invalid class %q, should start with letter", class)
return fmt.Errorf("invalid class %q, should start with letter", class)
}
for _, c := range string(class[1 : len(class)-1]) {
switch {
case isAlphaNumeric(c):
case c == '_' || c == '-':
default:
return errors.Errorf("invalid character '%c' in device class %q",
return fmt.Errorf("invalid character '%c' in device class %q",
c, class)
}
}
if !isAlphaNumeric(rune(class[len(class)-1])) {
return errors.Errorf("invalid class %q, should end with a letter or digit", class)
return fmt.Errorf("invalid class %q, should end with a letter or digit", class)
}
return nil
}
@@ -170,10 +169,10 @@ func ValidateClassName(class string) error {
// - underscore, dash, dot, colon ('_', '-', '.', ':')
func ValidateDeviceName(name string) error {
if name == "" {
return errors.Errorf("invalid (empty) device name")
return fmt.Errorf("invalid (empty) device name")
}
if !isAlphaNumeric(rune(name[0])) {
return errors.Errorf("invalid class %q, should start with a letter or digit", name)
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
}
if len(name) == 1 {
return nil
@@ -183,12 +182,12 @@ func ValidateDeviceName(name string) error {
case isAlphaNumeric(c):
case c == '_' || c == '-' || c == '.' || c == ':':
default:
return errors.Errorf("invalid character '%c' in device name %q",
return fmt.Errorf("invalid character '%c' in device name %q",
c, name)
}
}
if !isAlphaNumeric(rune(name[len(name)-1])) {
return errors.Errorf("invalid name %q, should end with a letter or digit", name)
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
}
return nil
}

View File

@@ -18,6 +18,7 @@ package cdi
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -25,30 +26,17 @@ import (
"sync"
oci "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
)
const (
// CurrentVersion is the current vesion of the CDI Spec.
CurrentVersion = cdi.CurrentVersion
// defaultSpecExt is the file extension for the default encoding.
defaultSpecExt = ".yaml"
)
var (
// Valid CDI Spec versions.
validSpecVersions = map[string]struct{}{
"0.1.0": {},
"0.2.0": {},
"0.3.0": {},
"0.4.0": {},
"0.5.0": {},
}
// Externally set CDI Spec validation function.
specValidator func(*cdi.Spec) error
validatorLock sync.RWMutex
@@ -78,15 +66,15 @@ func ReadSpec(path string, priority int) (*Spec, error) {
case os.IsNotExist(err):
return nil, err
case err != nil:
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
return nil, fmt.Errorf("failed to read CDI Spec %q: %w", path, err)
}
raw, err := ParseSpec(data)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
return nil, fmt.Errorf("failed to parse CDI Spec %q: %w", path, err)
}
if raw == nil {
return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path)
return nil, fmt.Errorf("failed to parse CDI Spec %q, no Spec data", path)
}
spec, err := newSpec(raw, path, priority)
@@ -120,7 +108,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
spec.vendor, spec.class = ParseQualifier(spec.Kind)
if spec.devices, err = spec.validate(); err != nil {
return nil, errors.Wrap(err, "invalid CDI Spec")
return nil, fmt.Errorf("invalid CDI Spec: %w", err)
}
return spec, nil
@@ -147,30 +135,30 @@ func (s *Spec) write(overwrite bool) error {
data, err = json.Marshal(s.Spec)
}
if err != nil {
return errors.Wrap(err, "failed to marshal Spec file")
return fmt.Errorf("failed to marshal Spec file: %w", err)
}
dir = filepath.Dir(s.path)
err = os.MkdirAll(dir, 0o755)
if err != nil {
return errors.Wrap(err, "failed to create Spec dir")
return fmt.Errorf("failed to create Spec dir: %w", err)
}
tmp, err = os.CreateTemp(dir, "spec.*.tmp")
if err != nil {
return errors.Wrap(err, "failed to create Spec file")
return fmt.Errorf("failed to create Spec file: %w", err)
}
_, err = tmp.Write(data)
tmp.Close()
if err != nil {
return errors.Wrap(err, "failed to write Spec file")
return fmt.Errorf("failed to write Spec file: %w", err)
}
err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(s.path), overwrite)
if err != nil {
os.Remove(tmp.Name())
err = errors.Wrap(err, "failed to write Spec file")
err = fmt.Errorf("failed to write Spec file: %w", err)
}
return err
@@ -216,6 +204,15 @@ func (s *Spec) validate() (map[string]*Device, error) {
if err := validateVersion(s.Version); err != nil {
return nil, err
}
minVersion, err := MinimumRequiredVersion(s.Spec)
if err != nil {
return nil, fmt.Errorf("could not determine minumum required version: %v", err)
}
if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
}
if err := ValidateVendorName(s.vendor); err != nil {
return nil, err
}
@@ -230,10 +227,10 @@ func (s *Spec) validate() (map[string]*Device, error) {
for _, d := range s.Devices {
dev, err := newDevice(s, d)
if err != nil {
return nil, errors.Wrapf(err, "failed add device %q", d.Name)
return nil, fmt.Errorf("failed add device %q: %w", d.Name, err)
}
if _, conflict := devices[d.Name]; conflict {
return nil, errors.Errorf("invalid spec, multiple device %q", d.Name)
return nil, fmt.Errorf("invalid spec, multiple device %q", d.Name)
}
devices[d.Name] = dev
}
@@ -243,8 +240,8 @@ func (s *Spec) validate() (map[string]*Device, error) {
// validateVersion checks whether the specified spec version is supported.
func validateVersion(version string) error {
if _, ok := validSpecVersions[version]; !ok {
return errors.Errorf("invalid version %q", version)
if !validSpecVersions.isValidVersion(version) {
return fmt.Errorf("invalid version %q", version)
}
return nil
@@ -255,7 +252,7 @@ func ParseSpec(data []byte) (*cdi.Spec, error) {
var raw *cdi.Spec
err := yaml.UnmarshalStrict(data, &raw)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal CDI Spec")
return nil, fmt.Errorf("failed to unmarshal CDI Spec: %w", err)
}
return raw, nil
}
@@ -279,7 +276,7 @@ func validateSpec(raw *cdi.Spec) error {
}
err := specValidator(raw)
if err != nil {
return errors.Wrap(err, "Spec validation failed")
return fmt.Errorf("Spec validation failed: %w", err)
}
return nil
}
@@ -329,7 +326,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string {
func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
vendor, class := ParseQualifier(raw.Kind)
if vendor == "" {
return "", errors.Errorf("invalid vendor/class %q in Spec", raw.Kind)
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
}
return GenerateSpecName(vendor, class), nil
@@ -343,7 +340,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) {
vendor, class := ParseQualifier(raw.Kind)
if vendor == "" {
return "", errors.Errorf("invalid vendor/class %q in Spec", raw.Kind)
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
}
return GenerateTransientSpecName(vendor, class, transientID), nil

View File

@@ -17,9 +17,9 @@
package cdi
import (
"fmt"
"os"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -30,7 +30,7 @@ func renameIn(dir, src, dst string, overwrite bool) error {
dirf, err := os.Open(dir)
if err != nil {
return errors.Wrap(err, "rename failed")
return fmt.Errorf("rename failed: %w", err)
}
defer dirf.Close()
@@ -41,7 +41,7 @@ func renameIn(dir, src, dst string, overwrite bool) error {
dirFd := int(dirf.Fd())
err = unix.Renameat2(dirFd, src, dirFd, dst, flags)
if err != nil {
return errors.Wrap(err, "rename failed")
return fmt.Errorf("rename failed: %w", err)
}
return nil

View File

@@ -0,0 +1,160 @@
/*
Copyright © 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 (
"strings"
"golang.org/x/mod/semver"
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
)
const (
// CurrentVersion is the current version of the CDI Spec.
CurrentVersion = cdi.CurrentVersion
// vCurrent is the current version as a semver-comparable type
vCurrent version = "v" + CurrentVersion
// These represent the released versions of the CDI specification
v010 version = "v0.1.0"
v020 version = "v0.2.0"
v030 version = "v0.3.0"
v040 version = "v0.4.0"
v050 version = "v0.5.0"
// vEarliest is the earliest supported version of the CDI specification
vEarliest version = v030
)
// validSpecVersions stores a map of spec versions to functions to check the required versions.
// Adding new fields / spec versions requires that a `requiredFunc` be implemented and
// this map be updated.
var validSpecVersions = requiredVersionMap{
v010: nil,
v020: nil,
v030: nil,
v040: requiresV040,
v050: requiresV050,
}
// MinimumRequiredVersion determines the minumum spec version for the input spec.
func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
minVersion := validSpecVersions.requiredVersion(spec)
return minVersion.String(), nil
}
// version represents a semantic version string
type version string
// newVersion creates a version that can be used for semantic version comparisons.
func newVersion(v string) version {
return version("v" + strings.TrimPrefix(v, "v"))
}
// String returns the string representation of the version.
// This trims a leading v if present.
func (v version) String() string {
return strings.TrimPrefix(string(v), "v")
}
// IsGreaterThan checks with a version is greater than the specified version.
func (v version) IsGreaterThan(o version) bool {
return semver.Compare(string(v), string(o)) > 0
}
// IsLatest checks whether the version is the latest supported version
func (v version) IsLatest() bool {
return v == vCurrent
}
type requiredFunc func(*cdi.Spec) bool
type requiredVersionMap map[version]requiredFunc
// isValidVersion checks whether the specified version is valid.
// A version is valid if it is contained in the required version map.
func (r requiredVersionMap) isValidVersion(specVersion string) bool {
_, ok := validSpecVersions[newVersion(specVersion)]
return ok
}
// requiredVersion returns the minimum version required for the given spec
func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
minVersion := vEarliest
for v, isRequired := range validSpecVersions {
if isRequired == nil {
continue
}
if isRequired(spec) && v.IsGreaterThan(minVersion) {
minVersion = v
}
// If we have already detected the latest version then no later version could be detected
if minVersion.IsLatest() {
break
}
}
return minVersion
}
// requiresV050 returns true if the spec uses v0.5.0 features
func requiresV050(spec *cdi.Spec) bool {
var edits []*cdi.ContainerEdits
for _, d := range spec.Devices {
// The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter
if len(d.Name) > 0 && !isLetter(rune(d.Name[0])) {
return true
}
edits = append(edits, &d.ContainerEdits)
}
edits = append(edits, &spec.ContainerEdits)
for _, e := range edits {
for _, dn := range e.DeviceNodes {
// The HostPath field was added in v0.5.0
if dn.HostPath != "" {
return true
}
}
}
return false
}
// requiresV040 returns true if the spec uses v0.4.0 features
func requiresV040(spec *cdi.Spec) bool {
var edits []*cdi.ContainerEdits
for _, d := range spec.Devices {
edits = append(edits, &d.ContainerEdits)
}
edits = append(edits, &spec.ContainerEdits)
for _, e := range edits {
for _, m := range e.Mounts {
// The Type field was added in v0.4.0
if m.Type != "" {
return true
}
}
}
return false
}