summaryrefslogtreecommitdiff
path: root/vendor/github.com/container-orchestrated-devices/container-device-interface
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/container-orchestrated-devices/container-device-interface')
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go292
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go37
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go17
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go13
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go12
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go5
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go58
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go48
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go39
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go3
10 files changed, 478 insertions, 46 deletions
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
index 5c3f803f4..30aa1057b 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
@@ -22,6 +22,8 @@ import (
"strings"
"sync"
+ cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
+ "github.com/fsnotify/fsnotify"
"github.com/hashicorp/go-multierror"
oci "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -33,30 +35,46 @@ type Option func(*Cache) error
// Cache stores CDI Specs loaded from Spec directories.
type Cache struct {
sync.Mutex
- specDirs []string
- specs map[string][]*Spec
- devices map[string]*Device
- errors map[string][]error
+ specDirs []string
+ specs map[string][]*Spec
+ devices map[string]*Device
+ errors map[string][]error
+ dirErrors map[string]error
+
+ autoRefresh bool
+ watch *watch
+}
+
+// WithAutoRefresh returns an option to control automatic Cache refresh.
+// By default auto-refresh is enabled, the list of Spec directories are
+// monitored and the Cache is automatically refreshed whenever a change
+// is detected. This option can be used to disable this behavior when a
+// manually refreshed mode is preferable.
+func WithAutoRefresh(autoRefresh bool) Option {
+ return func(c *Cache) error {
+ c.autoRefresh = autoRefresh
+ return nil
+ }
}
// NewCache creates a new CDI Cache. The cache is populated from a set
// of CDI Spec directories. These can be specified using a WithSpecDirs
// option. The default set of directories is exposed in DefaultSpecDirs.
func NewCache(options ...Option) (*Cache, error) {
- c := &Cache{}
-
- if err := c.Configure(options...); err != nil {
- return nil, err
- }
- if len(c.specDirs) == 0 {
- c.Configure(WithSpecDirs(DefaultSpecDirs...))
+ c := &Cache{
+ autoRefresh: true,
+ watch: &watch{},
}
- return c, c.Refresh()
+ WithSpecDirs(DefaultSpecDirs...)(c)
+ c.Lock()
+ defer c.Unlock()
+
+ return c, c.configure(options...)
}
-// Configure applies options to the cache. Updates the cache if options have
-// changed.
+// Configure applies options to the Cache. Updates and refreshes the
+// Cache if options have changed.
func (c *Cache) Configure(options ...Option) error {
if len(options) == 0 {
return nil
@@ -65,17 +83,54 @@ func (c *Cache) Configure(options ...Option) error {
c.Lock()
defer c.Unlock()
+ return c.configure(options...)
+}
+
+// Configure the Cache. Start/stop CDI Spec directory watch, refresh
+// the Cache if necessary.
+func (c *Cache) configure(options ...Option) error {
+ var err error
+
for _, o := range options {
- if err := o(c); err != nil {
+ if err = o(c); err != nil {
return errors.Wrapf(err, "failed to apply cache options")
}
}
+ c.dirErrors = make(map[string]error)
+
+ c.watch.stop()
+ if c.autoRefresh {
+ c.watch.setup(c.specDirs, c.dirErrors)
+ c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
+ }
+ c.refresh()
+
return nil
}
// Refresh rescans the CDI Spec directories and refreshes the Cache.
+// In manual refresh mode the cache is always refreshed. In auto-
+// refresh mode the cache is only refreshed if it is out of date.
func (c *Cache) Refresh() error {
+ c.Lock()
+ defer c.Unlock()
+
+ // force a refresh in manual mode
+ if refreshed, err := c.refreshIfRequired(!c.autoRefresh); refreshed {
+ return err
+ }
+
+ // collect and return cached errors, much like refresh() does it
+ var result error
+ for _, err := range c.errors {
+ result = multierror.Append(result, err...)
+ }
+ return result
+}
+
+// Refresh the Cache by rescanning CDI Spec directories and files.
+func (c *Cache) refresh() error {
var (
specs = map[string][]*Spec{}
devices = map[string]*Device{}
@@ -135,9 +190,6 @@ func (c *Cache) Refresh() error {
delete(devices, conflict)
}
- c.Lock()
- defer c.Unlock()
-
c.specs = specs
c.devices = devices
c.errors = specErrors
@@ -149,6 +201,17 @@ func (c *Cache) Refresh() error {
return nil
}
+// RefreshIfRequired triggers a refresh if necessary.
+func (c *Cache) refreshIfRequired(force bool) (bool, error) {
+ // We need to refresh if
+ // - it's forced by an explicitly call to Refresh() in manual mode
+ // - a missing Spec dir appears (added to watch) in auto-refresh mode
+ if force || (c.autoRefresh && c.watch.update(c.dirErrors)) {
+ return true, c.refresh()
+ }
+ return false, nil
+}
+
// InjectDevices injects the given qualified devices to an OCI Spec. It
// returns any unresolvable devices and an error if injection fails for
// any of the devices.
@@ -162,6 +225,8 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
edits := &ContainerEdits{}
specs := map[*Spec]struct{}{}
@@ -190,11 +255,46 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
return nil, nil
}
+// WriteSpec writes a Spec file with the given content. Priority is used
+// as an index into the list of Spec directories to pick a directory for
+// the file, adjusting for any under- or overflows. If name has a "json"
+// or "yaml" extension it choses the encoding. Otherwise JSON encoding
+// is used with a "json" extension.
+func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error {
+ var (
+ specDir string
+ path string
+ prio int
+ spec *Spec
+ err error
+ )
+
+ if len(c.specDirs) == 0 {
+ return errors.New("no Spec directories to write to")
+ }
+
+ prio = len(c.specDirs) - 1
+ specDir = c.specDirs[prio]
+ path = filepath.Join(specDir, name)
+ if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
+ path += ".json"
+ }
+
+ spec, err = NewSpec(raw, path, prio)
+ if err != nil {
+ return err
+ }
+
+ return spec.Write(true)
+}
+
// GetDevice returns the cached device for the given qualified name.
func (c *Cache) GetDevice(device string) *Device {
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
return c.devices[device]
}
@@ -205,6 +305,8 @@ func (c *Cache) ListDevices() []string {
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
for name := range c.devices {
devices = append(devices, name)
}
@@ -220,6 +322,8 @@ func (c *Cache) ListVendors() []string {
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
for vendor := range c.specs {
vendors = append(vendors, vendor)
}
@@ -238,6 +342,8 @@ func (c *Cache) ListClasses() []string {
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
for _, specs := range c.specs {
for _, spec := range specs {
cmap[spec.GetClass()] = struct{}{}
@@ -256,6 +362,8 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
c.Lock()
defer c.Unlock()
+ c.refreshIfRequired(false)
+
return c.specs[vendor]
}
@@ -268,12 +376,158 @@ func (c *Cache) GetSpecErrors(spec *Spec) []error {
// GetErrors returns all errors encountered during the last
// cache refresh.
func (c *Cache) GetErrors() map[string][]error {
- return c.errors
+ c.Lock()
+ defer c.Unlock()
+
+ errors := map[string][]error{}
+ for path, errs := range c.errors {
+ errors[path] = errs
+ }
+ for path, err := range c.dirErrors {
+ errors[path] = []error{err}
+ }
+
+ return errors
}
// GetSpecDirectories returns the CDI Spec directories currently in use.
func (c *Cache) GetSpecDirectories() []string {
+ c.Lock()
+ defer c.Unlock()
+
dirs := make([]string, len(c.specDirs))
copy(dirs, c.specDirs)
return dirs
}
+
+// GetSpecDirErrors returns any errors related to configured Spec directories.
+func (c *Cache) GetSpecDirErrors() map[string]error {
+ if c.dirErrors == nil {
+ return nil
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ errors := make(map[string]error)
+ for dir, err := range c.dirErrors {
+ errors[dir] = err
+ }
+ return errors
+}
+
+// Our fsnotify helper wrapper.
+type watch struct {
+ watcher *fsnotify.Watcher
+ tracked map[string]bool
+}
+
+// Setup monitoring for the given Spec directories.
+func (w *watch) setup(dirs []string, dirErrors map[string]error) {
+ var (
+ dir string
+ err error
+ )
+ w.tracked = make(map[string]bool)
+ for _, dir = range dirs {
+ w.tracked[dir] = false
+ }
+
+ w.watcher, err = fsnotify.NewWatcher()
+ if err != nil {
+ for _, dir := range dirs {
+ dirErrors[dir] = errors.Wrap(err, "failed to create watcher")
+ }
+ return
+ }
+
+ w.update(dirErrors)
+}
+
+// Start watching Spec directories for relevant changes.
+func (w *watch) start(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
+ go w.watch(m, refresh, dirErrors)
+}
+
+// Stop watching directories.
+func (w *watch) stop() {
+ if w.watcher == nil {
+ return
+ }
+
+ w.watcher.Close()
+ w.tracked = nil
+}
+
+// Watch Spec directory changes, triggering a refresh if necessary.
+func (w *watch) watch(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
+ watch := w.watcher
+ if watch == nil {
+ return
+ }
+ for {
+ select {
+ case event, ok := <-watch.Events:
+ if !ok {
+ return
+ }
+
+ if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
+ continue
+ }
+ if event.Op == fsnotify.Write {
+ if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
+ continue
+ }
+ }
+
+ m.Lock()
+ if event.Op == fsnotify.Remove && w.tracked[event.Name] {
+ w.update(dirErrors, event.Name)
+ } else {
+ w.update(dirErrors)
+ }
+ refresh()
+ m.Unlock()
+
+ case _, ok := <-watch.Errors:
+ if !ok {
+ return
+ }
+ }
+ }
+}
+
+// Update watch with pending/missing or removed directories.
+func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
+ var (
+ dir string
+ ok bool
+ err error
+ update bool
+ )
+
+ for dir, ok = range w.tracked {
+ if ok {
+ continue
+ }
+
+ err = w.watcher.Add(dir)
+ if err == nil {
+ w.tracked[dir] = true
+ delete(dirErrors, dir)
+ update = true
+ } else {
+ w.tracked[dir] = false
+ dirErrors[dir] = errors.Wrap(err, "failed to monitor for changes")
+ }
+ }
+
+ for _, dir = range removed {
+ w.tracked[dir] = false
+ dirErrors[dir] = errors.New("directory removed")
+ update = true
+ }
+
+ return update
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
index 80d88b118..1295f75e9 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
@@ -85,11 +85,13 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
}
for _, d := range e.DeviceNodes {
- dev := d.ToOCI()
- if err := fillMissingInfo(&dev); err != nil {
+ dn := DeviceNode{d}
+
+ err := dn.fillMissingInfo()
+ if err != nil {
return err
}
-
+ dev := d.ToOCI()
if dev.UID == nil && spec.Process != nil {
if uid := spec.Process.User.UID; uid > 0 {
dev.UID = &uid
@@ -288,26 +290,31 @@ func ensureOCIHooks(spec *oci.Spec) {
}
// fillMissingInfo fills in missing mandatory attributes from the host device.
-func fillMissingInfo(dev *oci.LinuxDevice) error {
- if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") {
+func (d *DeviceNode) fillMissingInfo() error {
+ if d.HostPath == "" {
+ d.HostPath = d.Path
+ }
+
+ if d.Type != "" && (d.Major != 0 || d.Type == "p") {
return nil
}
- hostDev, err := runc.DeviceFromPath(dev.Path, "rwm")
+
+ hostDev, err := runc.DeviceFromPath(d.HostPath, "rwm")
if err != nil {
- return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path)
+ return errors.Wrapf(err, "failed to stat CDI host device %q", d.HostPath)
}
- if dev.Type == "" {
- dev.Type = string(hostDev.Type)
+ if d.Type == "" {
+ d.Type = string(hostDev.Type)
} else {
- if dev.Type != string(hostDev.Type) {
- return errors.Errorf("CDI device %q, host type mismatch (%s, %s)",
- dev.Path, dev.Type, string(hostDev.Type))
+ if d.Type != string(hostDev.Type) {
+ return errors.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
+ d.Path, d.HostPath, d.Type, string(hostDev.Type))
}
}
- if dev.Major == 0 && dev.Type != "p" {
- dev.Major = hostDev.Major
- dev.Minor = hostDev.Minor
+ if d.Major == 0 && d.Type != "p" {
+ d.Major = hostDev.Major
+ d.Minor = hostDev.Minor
}
return nil
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
index a9017259c..847e51254 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
@@ -67,6 +67,21 @@
//
// Cache Refresh
//
+// By default the CDI Spec cache monitors the configured Spec directories
+// and automatically refreshes itself when necessary. This behavior can be
+// disabled using the WithAutoRefresh(false) option.
+//
+// Failure to set up monitoring for a Spec directory causes the directory to
+// get ignored and an error to be recorded among the Spec directory errors.
+// These errors can be queried using the GetSpecDirErrors() function. If the
+// error condition is transient, for instance a missing directory which later
+// gets created, the corresponding error will be removed once the condition
+// is over.
+//
+// With auto-refresh enabled injecting any CDI devices can be done without
+// an explicit call to Refresh(), using a code snippet similar to the
+// following:
+//
// In a runtime implementation one typically wants to make sure the
// CDI Spec cache is up to date before performing device injection.
// A code snippet similar to the following accmplishes that:
@@ -146,5 +161,5 @@
// schema names which switch the used schema to the in-repo validation
// schema embedded into the binary or the now default no-op schema
// correspondingly. Other names are interpreted as the path to the actual
-/// validation schema to load and use.
+// validation schema to load and use.
package cdi
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
index 54f19143c..ccfab7094 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
@@ -130,7 +130,7 @@ func ValidateVendorName(vendor string) error {
}
}
if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
- return errors.Errorf("invalid vendor %q, should end with letter", vendor)
+ return errors.Errorf("invalid vendor %q, should end with a letter or digit", vendor)
}
return nil
@@ -158,7 +158,7 @@ func ValidateClassName(class string) error {
}
}
if !isAlphaNumeric(rune(class[len(class)-1])) {
- return errors.Errorf("invalid class %q, should end with letter", class)
+ return errors.Errorf("invalid class %q, should end with a letter or digit", class)
}
return nil
}
@@ -172,8 +172,11 @@ func ValidateDeviceName(name string) error {
if name == "" {
return errors.Errorf("invalid (empty) device name")
}
- if !isLetter(rune(name[0])) {
- return errors.Errorf("invalid name %q, should start with letter", name)
+ if !isAlphaNumeric(rune(name[0])) {
+ return errors.Errorf("invalid class %q, should start with a letter or digit", name)
+ }
+ if len(name) == 1 {
+ return nil
}
for _, c := range string(name[1 : len(name)-1]) {
switch {
@@ -185,7 +188,7 @@ func ValidateDeviceName(name string) error {
}
}
if !isAlphaNumeric(rune(name[len(name)-1])) {
- return errors.Errorf("invalid name %q, should start with letter", name)
+ return errors.Errorf("invalid name %q, should end with a letter or digit", name)
}
return nil
}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
index fa6e0af69..d5bd54b0b 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
@@ -19,6 +19,7 @@ package cdi
import (
"sync"
+ cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
oci "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -40,6 +41,8 @@ type Registry interface {
// RegistryRefresher is the registry interface for refreshing the
// cache of CDI Specs and devices.
//
+// Configure reconfigures the registry with the given options.
+//
// Refresh rescans all CDI Spec directories and updates the
// state of the cache to reflect any changes. It returns any
// errors encountered during the refresh.
@@ -50,10 +53,15 @@ type Registry interface {
// GetSpecDirectories returns the set up CDI Spec directories
// currently in use. The directories are returned in the scan
// order of Refresh().
+//
+// GetSpecDirErrors returns any errors related to the configured
+// Spec directories.
type RegistryRefresher interface {
+ Configure(...Option) error
Refresh() error
GetErrors() map[string][]error
GetSpecDirectories() []string
+ GetSpecDirErrors() map[string]error
}
// RegistryResolver is the registry interface for injecting CDI
@@ -90,11 +98,15 @@ type RegistryDeviceDB interface {
//
// GetSpecErrors returns any errors for the Spec encountered during
// the last cache refresh.
+//
+// WriteSpec writes the Spec with the given content and name to the
+// last Spec directory.
type RegistrySpecDB interface {
ListVendors() []string
ListClasses() []string
GetVendorSpecs(vendor string) []*Spec
GetSpecErrors(*Spec) []error
+ WriteSpec(raw *cdi.Spec, name string) error
}
type registry struct {
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
index ad017fec7..13c294592 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
@@ -45,10 +45,11 @@ var (
// WithSpecDirs returns an option to override the CDI Spec directories.
func WithSpecDirs(dirs ...string) Option {
return func(c *Cache) error {
- c.specDirs = make([]string, len(dirs))
+ specDirs := make([]string, len(dirs))
for i, dir := range dirs {
- c.specDirs[i] = filepath.Clean(dir)
+ specDirs[i] = filepath.Clean(dir)
}
+ c.specDirs = specDirs
return nil
}
}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
index 46fca2dac..9a5d451c9 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
@@ -17,6 +17,7 @@
package cdi
import (
+ "encoding/json"
"io/ioutil"
"os"
"path/filepath"
@@ -35,6 +36,7 @@ var (
"0.2.0": {},
"0.3.0": {},
"0.4.0": {},
+ "0.5.0": {},
}
// Externally set CDI Spec validation function.
@@ -68,7 +70,7 @@ func ReadSpec(path string, priority int) (*Spec, error) {
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
}
- raw, err := parseSpec(data)
+ raw, err := ParseSpec(data)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
}
@@ -109,6 +111,56 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
return spec, nil
}
+// Write the CDI Spec to the file associated with it during instantiation
+// by NewSpec() or ReadSpec().
+func (s *Spec) Write(overwrite bool) error {
+ var (
+ data []byte
+ dir string
+ tmp *os.File
+ err error
+ )
+
+ err = validateSpec(s.Spec)
+ if err != nil {
+ return err
+ }
+
+ if filepath.Ext(s.path) == ".yaml" {
+ data, err = yaml.Marshal(s.Spec)
+ } else {
+ data, err = json.Marshal(s.Spec)
+ }
+ if err != nil {
+ return errors.Wrap(err, "failed to marshal Spec file")
+ }
+
+ dir = filepath.Dir(s.path)
+ err = os.MkdirAll(dir, 0o755)
+ if err != nil {
+ return errors.Wrap(err, "failed to create Spec dir")
+ }
+
+ tmp, err = os.CreateTemp(dir, "spec.*.tmp")
+ if err != nil {
+ return errors.Wrap(err, "failed to create Spec file")
+ }
+ _, err = tmp.Write(data)
+ tmp.Close()
+ if err != nil {
+ return errors.Wrap(err, "failed to write Spec file")
+ }
+
+ 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")
+ }
+
+ return err
+}
+
// GetVendor returns the vendor of this Spec.
func (s *Spec) GetVendor() string {
return s.vendor
@@ -183,8 +235,8 @@ func validateVersion(version string) error {
return nil
}
-// Parse raw CDI Spec file data.
-func parseSpec(data []byte) (*cdi.Spec, error) {
+// ParseSpec parses CDI Spec data into a raw CDI Spec.
+func ParseSpec(data []byte) (*cdi.Spec, error) {
var raw *cdi.Spec
err := yaml.UnmarshalStrict(data, &raw)
if err != nil {
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go
new file mode 100644
index 000000000..cca825c60
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go
@@ -0,0 +1,48 @@
+/*
+ Copyright © 2022 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 (
+ "os"
+
+ "github.com/pkg/errors"
+ "golang.org/x/sys/unix"
+)
+
+// Rename src to dst, both relative to the directory dir. If dst already exists
+// refuse renaming with an error unless overwrite is explicitly asked for.
+func renameIn(dir, src, dst string, overwrite bool) error {
+ var flags uint
+
+ dirf, err := os.Open(dir)
+ if err != nil {
+ return errors.Wrap(err, "rename failed")
+ }
+ defer dirf.Close()
+
+ if !overwrite {
+ flags = unix.RENAME_NOREPLACE
+ }
+
+ dirFd := int(dirf.Fd())
+ err = unix.Renameat2(dirFd, src, dirFd, dst, flags)
+ if err != nil {
+ return errors.Wrap(err, "rename failed")
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
new file mode 100644
index 000000000..285e04e27
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
@@ -0,0 +1,39 @@
+//go:build !linux
+// +build !linux
+
+/*
+ Copyright © 2022 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 (
+ "os"
+ "path/filepath"
+)
+
+// Rename src to dst, both relative to the directory dir. If dst already exists
+// refuse renaming with an error unless overwrite is explicitly asked for.
+func renameIn(dir, src, dst string, overwrite bool) error {
+ src = filepath.Join(dir, src)
+ dst = filepath.Join(dir, dst)
+
+ _, err := os.Stat(dst)
+ if err == nil && !overwrite {
+ return os.ErrExist
+ }
+
+ return os.Rename(src, dst)
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go
index e16174f9d..3fa2e814b 100644
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go
@@ -3,7 +3,7 @@ package specs
import "os"
// CurrentVersion is the current version of the Spec.
-const CurrentVersion = "0.4.0"
+const CurrentVersion = "0.5.0"
// Spec is the base configuration for CDI
type Spec struct {
@@ -31,6 +31,7 @@ type ContainerEdits struct {
// DeviceNode represents a device node that needs to be added to the OCI spec.
type DeviceNode struct {
Path string `json:"path"`
+ HostPath string `json:"hostPath,omitempty"`
Type string `json:"type,omitempty"`
Major int64 `json:"major,omitempty"`
Minor int64 `json:"minor,omitempty"`