diff --git a/go.mod b/go.mod index e586c99b1e..8f8e52f009 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/buger/goterm v1.0.4 github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 github.com/checkpoint-restore/go-criu/v5 v5.3.0 - github.com/container-orchestrated-devices/container-device-interface v0.3.0 + github.com/container-orchestrated-devices/container-device-interface v0.5.3 github.com/containernetworking/cni v1.0.1 github.com/containernetworking/plugins v1.1.1 github.com/containers/buildah v1.24.3-0.20220310160415-5ec70bf01ea5 @@ -48,7 +48,7 @@ require ( github.com/onsi/gomega v1.18.1 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 - github.com/opencontainers/runc v1.1.0 + github.com/opencontainers/runc v1.1.2 github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f github.com/opencontainers/selinux v1.10.0 diff --git a/go.sum b/go.sum index ef12e3b908..aff288ee57 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/container-orchestrated-devices/container-device-interface v0.3.0 h1:tM2zdVYZY8getsFaTc7Z+v+UqDXhk5alchOHVEADes0= -github.com/container-orchestrated-devices/container-device-interface v0.3.0/go.mod h1:LGs3yHVe1wZn2XsWl4AxywYQ3NRZ6osTEZozCHQCRSM= +github.com/container-orchestrated-devices/container-device-interface v0.5.3 h1:4v6FMaa1Pn8SS0IBwgsvCsno8HRXoQvI87Uj1Zu7Tw4= +github.com/container-orchestrated-devices/container-device-interface v0.5.3/go.mod h1:SQohok453ewi9dItvUcO0MrP7K1CEQTxPDNd7OV+nxI= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -1087,8 +1087,9 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= +github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= +github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go new file mode 100644 index 0000000000..07aca4a1d3 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go @@ -0,0 +1,82 @@ +/* + 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 multierror + +import ( + "strings" +) + +// New combines several errors into a single error. Parameters that are nil are +// ignored. If no errors are passed in or all parameters are nil, then the +// result is also nil. +func New(errors ...error) error { + // Filter out nil entries. + numErrors := 0 + for _, err := range errors { + if err != nil { + errors[numErrors] = err + numErrors++ + } + } + if numErrors == 0 { + return nil + } + return multiError(errors[0:numErrors]) +} + +// multiError is the underlying implementation used by New. +// +// Beware that a null multiError is not the same as a nil error. +type multiError []error + +// multiError returns all individual error strings concatenated with "\n" +func (e multiError) Error() string { + var builder strings.Builder + for i, err := range e { + if i > 0 { + _, _ = builder.WriteString("\n") + } + _, _ = builder.WriteString(err.Error()) + } + return builder.String() +} + +// Append returns a new multi error all errors concatenated. Errors that are +// multi errors get flattened, nil is ignored. +func Append(err error, errors ...error) error { + var result multiError + if m, ok := err.(multiError); ok { + result = m + } else if err != nil { + result = append(result, err) + } + + for _, e := range errors { + if e == nil { + continue + } + if m, ok := e.(multiError); ok { + result = append(result, m...) + } else { + result = append(result, e) + } + } + if len(result) == 0 { + return nil + } + return result +} 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 5c3f803f43..dee854862d 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 @@ -17,12 +17,18 @@ package cdi import ( + "io/fs" + "os" "path/filepath" "sort" "strings" "sync" - "github.com/hashicorp/go-multierror" + 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" ) @@ -33,30 +39,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 +87,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 _, errors := range c.errors { + result = multierror.Append(result, errors...) + } + 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,18 +194,22 @@ func (c *Cache) Refresh() error { delete(devices, conflict) } - c.Lock() - defer c.Unlock() - c.specs = specs c.devices = devices c.errors = specErrors - if len(result) > 0 { - return multierror.Append(nil, result...) - } + return multierror.New(result...) +} - 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 @@ -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,85 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e return nil, nil } +// highestPrioritySpecDir returns the Spec directory with highest priority +// and its priority. +func (c *Cache) highestPrioritySpecDir() (string, int) { + if len(c.specDirs) == 0 { + return "", -1 + } + + prio := len(c.specDirs) - 1 + dir := c.specDirs[prio] + + return dir, prio +} + +// WriteSpec writes a Spec file with the given content into the highest +// priority Spec directory. If name has a "json" or "yaml" extension it +// choses the encoding. Otherwise the default YAML encoding is used. +func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { + var ( + specDir string + path string + prio int + spec *Spec + err error + ) + + specDir, prio = c.highestPrioritySpecDir() + if specDir == "" { + return errors.New("no Spec directories to write to") + } + + path = filepath.Join(specDir, name) + if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { + path += defaultSpecExt + } + + spec, err = newSpec(raw, path, prio) + if err != nil { + return err + } + + return spec.write(true) +} + +// RemoveSpec removes a Spec with the given name from the highest +// priority Spec directory. This function can be used to remove a +// Spec previously written by WriteSpec(). If the file exists and +// its removal fails RemoveSpec returns an error. +func (c *Cache) RemoveSpec(name string) error { + var ( + specDir string + path string + err error + ) + + specDir, _ = c.highestPrioritySpecDir() + if specDir == "" { + return errors.New("no Spec directories to remove from") + } + + path = filepath.Join(specDir, name) + if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { + path += defaultSpecExt + } + + err = os.Remove(path) + if err != nil && stderr.Is(err, fs.ErrNotExist) { + err = nil + } + + return err +} + // 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 +344,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 +361,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 +381,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 +401,8 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec { c.Lock() defer c.Unlock() + c.refreshIfRequired(false) + return c.specs[vendor] } @@ -268,12 +415,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(w.watcher, 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(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error, dirErrors map[string]error) { + watch := fsw + 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/cache_test_unix.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_unix.go new file mode 100644 index 0000000000..0ee5fb86f5 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_unix.go @@ -0,0 +1,26 @@ +//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 + +import "syscall" + +func osSync() { + syscall.Sync() +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go new file mode 100644 index 0000000000..c6dabf5fa8 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go @@ -0,0 +1,22 @@ +//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() {} 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 80d88b118a..9fcecf8497 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 @@ -27,8 +27,6 @@ import ( "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" - - runc "github.com/opencontainers/runc/libcontainer/devices" ) const ( @@ -85,11 +83,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 @@ -287,32 +287,6 @@ 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") { - return nil - } - hostDev, err := runc.DeviceFromPath(dev.Path, "rwm") - if err != nil { - return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path) - } - - if dev.Type == "" { - dev.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 dev.Major == 0 && dev.Type != "p" { - dev.Major = hostDev.Major - dev.Minor = hostDev.Minor - } - - return nil -} - // sortMounts sorts the mounts in the given OCI Spec. func sortMounts(specgen *ocigen.Generator) { mounts := specgen.Mounts() diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go new file mode 100644 index 0000000000..5d7ebcb557 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go @@ -0,0 +1,56 @@ +//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 + +import ( + runc "github.com/opencontainers/runc/libcontainer/devices" + "github.com/pkg/errors" +) + +// fillMissingInfo fills in missing mandatory attributes from the host device. +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(d.HostPath, "rwm") + if err != nil { + return errors.Wrapf(err, "failed to stat CDI host device %q", d.HostPath) + } + + 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)", + d.Path, d.HostPath, d.Type, string(hostDev.Type)) + } + } + 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/container-edits_windows.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_windows.go new file mode 100644 index 0000000000..fd91afa926 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_windows.go @@ -0,0 +1,27 @@ +//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 + +import "fmt" + +// fillMissingInfo fills in missing mandatory attributes from the host device. +func (d *DeviceNode) fillMissingInfo() error { + return fmt.Errorf("unimplemented") +} 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 4fcdc44db7..9646122906 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: @@ -109,10 +124,15 @@ // // Generated Spec Files, Multiple Directories, Device Precedence // -// There are systems where the set of available or usable CDI devices -// changes dynamically and this needs to be reflected in the CDI Specs. -// This is done by dynamically regenerating CDI Spec files which are -// affected by these changes. +// It is often necessary to generate Spec files dynamically. On some +// systems the available or usable set of CDI devices might change +// dynamically which then needs to be reflected in CDI Specs. For +// some device classes it makes sense to enumerate the available +// devices at every boot and generate Spec file entries for each +// device found. Some CDI devices might need special client- or +// request-specific configuration which can only be fulfilled by +// dynamically generated client-specific entries in transient Spec +// files. // // CDI can collect Spec files from multiple directories. Spec files are // automatically assigned priorities according to which directory they @@ -126,5 +146,129 @@ // separating dynamically generated CDI Spec files from static ones. // The default directories are '/etc/cdi' and '/var/run/cdi'. By putting // dynamically generated Spec files under '/var/run/cdi', those take -// precedence over static ones in '/etc/cdi'. +// precedence over static ones in '/etc/cdi'. With this scheme, static +// Spec files, typically installed by distro-specific packages, go into +// '/etc/cdi' while all the dynamically generated Spec files, transient +// or other, go into '/var/run/cdi'. +// +// Spec File Generation +// +// CDI offers two functions for writing and removing dynamically generated +// Specs from CDI Spec directories. These functions, WriteSpec() and +// RemoveSpec() implicitly follow the principle of separating dynamic Specs +// from the rest and therefore always write to and remove Specs from the +// last configured directory. +// +// Corresponding functions are also provided for generating names for Spec +// files. These functions follow a simple naming convention to ensure that +// multiple entities generating Spec files simultaneously on the same host +// do not end up using conflicting Spec file names. GenerateSpecName(), +// GenerateNameForSpec(), GenerateTransientSpecName(), and +// GenerateTransientNameForSpec() all generate names which can be passed +// as such to WriteSpec() and subsequently to RemoveSpec(). +// +// Generating a Spec file for a vendor/device class can be done with a +// code snippet similar to the following: +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateDeviceSpecs() error { +// registry := cdi.GetRegistry() +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range enumerateDevices() { +// spec.Devices = append(spec.Devices, specs.Device{ +// Name: dev.Name, +// ContainerEdits: getContainerEditsForDevice(dev), +// }) +// } +// +// specName, err := cdi.GenerateNameForSpec(spec) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.WriteSpec(spec, specName) +// } +// +// Similary, generating and later cleaning up transient Spec files can be +// done with code fragments similar to the following. These transient Spec +// files are temporary Spec files with container-specific parametrization. +// They are typically created before the associated container is created +// and removed once that container is removed. +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// devices := getContainerDevs(ctr, vendor, class) +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range devices { +// spec.Devices = append(spec.Devices, specs.Device{ +// // the generated name needs to be unique within the +// // vendor/class domain on the host/node. +// Name: generateUniqueDevName(dev, ctr), +// ContainerEdits: getEditsForContainer(dev), +// }) +// } +// +// // transientID is expected to guarantee that the Spec file name +// // generated using is unique within +// // the host/node. If more than one device is allocated with the +// // same vendor/class domain, either all generated Spec entries +// // should go to a single Spec file (like in this sample snippet), +// // or transientID should be unique for each generated Spec file. +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.WriteSpec(spec, specName) +// } +// +// func removeTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// +// return registry.RemoveSpec(specName) +// } +// +// CDI Spec Validation +// +// This package performs both syntactic and semantic validation of CDI +// Spec file data when a Spec file is loaded via the registry or using +// the ReadSpec API function. As part of the semantic verification, the +// Spec file is verified against the CDI Spec JSON validation schema. +// +// If a valid externally provided JSON validation schema is found in +// the filesystem at /etc/cdi/schema/schema.json it is loaded and used +// as the default validation schema. If such a file is not found or +// fails to load, an embedded no-op schema is used. +// +// The used validation schema can also be changed programmatically using +// the SetSchema API convenience function. This function also accepts +// the special "builtin" (BuiltinSchemaName) and "none" (NoneSchemaName) +// 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. 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 54f19143c6..ccfab7094c 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 fa6e0af693..10fab8997e 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,16 @@ 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 + RemoveSpec(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 ad017fec7e..f339349bba 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 @@ -17,10 +17,10 @@ package cdi import ( + "errors" + "io/fs" "os" "path/filepath" - - "github.com/pkg/errors" ) const ( @@ -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 } } @@ -78,6 +79,9 @@ func scanSpecDirs(dirs []string, scanFn scanSpecFunc) error { err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { // for initial stat failure Walk calls us with nil info if info == nil { + if errors.Is(err, fs.ErrNotExist) { + return nil + } return err } // first call from Walk is for dir itself, others we skip 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 adebc101f5..c558b8efed 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,9 +17,12 @@ package cdi import ( + "encoding/json" "io/ioutil" "os" "path/filepath" + "strings" + "sync" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -28,13 +31,27 @@ import ( 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 ) // Spec represents a single CDI Spec. It is usually loaded from a @@ -64,20 +81,31 @@ 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) } + if raw == nil { + return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path) + } + + spec, err := newSpec(raw, path, priority) + if err != nil { + return nil, err + } - return NewSpec(raw, path, priority) + return spec, nil } -// NewSpec creates a new Spec from the given CDI Spec data. The +// newSpec creates a new Spec from the given CDI Spec data. The // Spec is marked as loaded from the given path with the given -// priority. If Spec data validation fails NewSpec returns a nil +// priority. If Spec data validation fails newSpec returns a nil // Spec and an error. -func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { - var err error +func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { + err := validateSpec(raw) + if err != nil { + return nil, err + } spec := &Spec{ Spec: raw, @@ -85,6 +113,10 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { priority: priority, } + if ext := filepath.Ext(spec.path); ext != ".yaml" && ext != ".json" { + spec.path += defaultSpecExt + } + spec.vendor, spec.class = ParseQualifier(spec.Kind) if spec.devices, err = spec.validate(); err != nil { @@ -94,6 +126,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 @@ -168,18 +250,101 @@ func validateVersion(version string) error { return nil } -// Parse raw CDI Spec file data. -func parseSpec(data []byte) (*cdi.Spec, error) { - raw := &cdi.Spec{} +// 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 { return nil, errors.Wrap(err, "failed to unmarshal CDI Spec") } - return raw, validateJSONSchema(raw) + return raw, nil +} + +// SetSpecValidator sets a CDI Spec validator function. This function +// is used for extra CDI Spec content validation whenever a Spec file +// loaded (using ReadSpec() or written (using WriteSpec()). +func SetSpecValidator(fn func(*cdi.Spec) error) { + validatorLock.Lock() + defer validatorLock.Unlock() + specValidator = fn } -// Validate CDI Spec against JSON Schema. -func validateJSONSchema(raw *cdi.Spec) error { - // TODO +// validateSpec validates the Spec using the extneral validator. +func validateSpec(raw *cdi.Spec) error { + validatorLock.RLock() + defer validatorLock.RUnlock() + + if specValidator == nil { + return nil + } + err := specValidator(raw) + if err != nil { + return errors.Wrap(err, "Spec validation failed") + } return nil } + +// GenerateSpecName generates a vendor+class scoped Spec file name. The +// name can be passed to WriteSpec() to write a Spec file to the file +// system. +// +// vendor and class should match the vendor and class of the CDI Spec. +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +// +// This function always returns the same name for the same vendor/class +// combination. Therefore it cannot be used as such to generate multiple +// Spec file names for a single vendor and class. +func GenerateSpecName(vendor, class string) string { + return vendor + "-" + class +} + +// GenerateTransientSpecName generates a vendor+class scoped transient +// Spec file name. The name can be passed to WriteSpec() to write a Spec +// file to the file system. +// +// Transient Specs are those whose lifecycle is tied to that of some +// external entity, for instance a container. vendor and class should +// match the vendor and class of the CDI Spec. transientID should be +// unique among all CDI users on the same host that might generate +// transient Spec files using the same vendor/class combination. If +// the external entity to which the lifecycle of the tranient Spec +// is tied to has a unique ID of its own, then this is usually a +// good choice for transientID. +// +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +func GenerateTransientSpecName(vendor, class, transientID string) string { + transientID = strings.ReplaceAll(transientID, "/", "_") + return GenerateSpecName(vendor, class) + "_" + transientID +} + +// GenerateNameForSpec generates a name for the given Spec using +// GenerateSpecName with the vendor and class taken from the Spec. +// On success it returns the generated name and a nil error. If +// the Spec does not contain a valid vendor or class, it returns +// an empty name and a non-nil error. +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 GenerateSpecName(vendor, class), nil +} + +// GenerateNameForTransientSpec generates a name for the given transient +// Spec using GenerateTransientSpecName with the vendor and class taken +// from the Spec. On success it returns the generated name and a nil error. +// If the Spec does not contain a valid vendor or class, it returns an +// an empty name and a non-nil 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 GenerateTransientSpecName(vendor, class, transientID), 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 0000000000..cca825c60d --- /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 0000000000..285e04e27a --- /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 090e30e439..3fa2e814bd 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.3.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"` @@ -45,6 +46,7 @@ type Mount struct { HostPath string `json:"hostPath"` ContainerPath string `json:"containerPath"` Options []string `json:"options,omitempty"` + Type string `json:"type,omitempty"` } // Hook represents a hook that needs to be added to the OCI spec. diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go index 10bc9fa23f..14a0f6a0ba 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go @@ -95,6 +95,7 @@ func (m *Mount) ToOCI() spec.Mount { Source: m.HostPath, Destination: m.ContainerPath, Options: m.Options, + Type: m.Type, } } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index 13ebf52abe..b32af4ee53 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -55,12 +55,12 @@ func IsCgroup2HybridMode() bool { var st unix.Statfs_t err := unix.Statfs(hybridMountpoint, &st) if err != nil { - if os.IsNotExist(err) { - // ignore the "not found" error - isHybrid = false - return + isHybrid = false + if !os.IsNotExist(err) { + // Report unexpected errors. + logrus.WithError(err).Debugf("statfs(%q) failed", hybridMountpoint) } - panic(fmt.Sprintf("cannot statfs cgroup root: %s", err)) + return } isHybrid = st.Type == unix.CGROUP2_SUPER_MAGIC }) diff --git a/vendor/modules.txt b/vendor/modules.txt index a224d3da57..55bfcd95b0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -57,8 +57,9 @@ github.com/checkpoint-restore/go-criu/v5/rpc github.com/checkpoint-restore/go-criu/v5/stats # github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/readline -# github.com/container-orchestrated-devices/container-device-interface v0.3.0 +# github.com/container-orchestrated-devices/container-device-interface v0.5.3 ## explicit +github.com/container-orchestrated-devices/container-device-interface/internal/multierror github.com/container-orchestrated-devices/container-device-interface/pkg/cdi github.com/container-orchestrated-devices/container-device-interface/specs-go # github.com/containerd/cgroups v1.0.3 @@ -560,7 +561,7 @@ github.com/opencontainers/go-digest ## explicit github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/opencontainers/runc v1.1.0 +# github.com/opencontainers/runc v1.1.2 ## explicit github.com/opencontainers/runc/libcontainer/apparmor github.com/opencontainers/runc/libcontainer/cgroups