From 829dd2145f80057d1274d77b365cd5cf98de6892 Mon Sep 17 00:00:00 2001 From: xiaoyang zhu Date: Wed, 7 Sep 2022 09:34:55 +0800 Subject: [PATCH] update cdi version to v0.5.1 Signed-off-by: xiaoyang zhu --- go.mod | 2 +- go.sum | 4 +- integration/client/go.mod | 3 +- integration/client/go.sum | 8 +- .../pkg/cdi/cache.go | 292 ++++++++++++++++-- .../pkg/cdi/container-edits.go | 37 ++- .../container-device-interface/pkg/cdi/doc.go | 17 +- .../pkg/cdi/qualified-device.go | 13 +- .../pkg/cdi/registry.go | 12 + .../pkg/cdi/spec-dirs.go | 12 +- .../pkg/cdi/spec.go | 83 ++++- .../pkg/cdi/{schema.go => spec_linux.go} | 40 +-- .../pkg/cdi/spec_other.go | 39 +++ .../schema/Makefile | 27 -- .../schema/defs.json | 127 -------- .../schema/schema.go | 253 --------------- .../schema/schema.json | 39 --- .../specs-go/config.go | 4 +- .../specs-go/oci.go | 1 + vendor/modules.txt | 3 +- 20 files changed, 493 insertions(+), 523 deletions(-) rename vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/{schema.go => spec_linux.go} (51%) create mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go delete mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/schema/Makefile delete mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json delete mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go delete mode 100644 vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.json diff --git a/go.mod b/go.mod index 8bd705f70152..6cafe6f259f6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20220723121359-e03a0069662f github.com/Microsoft/go-winio v0.5.2 github.com/Microsoft/hcsshim v0.10.0-rc.1 - github.com/container-orchestrated-devices/container-device-interface v0.3.1 + github.com/container-orchestrated-devices/container-device-interface v0.5.1 github.com/containerd/aufs v1.0.0 github.com/containerd/btrfs v1.0.0 github.com/containerd/cgroups v1.0.5-0.20220816231112-7083cd60b721 diff --git a/go.sum b/go.sum index 1e5482923d41..4238d118b447 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,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.1 h1:AvASKHHm6w3qMU49iPYyp8GhwbacvqjfGHUZEgvA/mQ= -github.com/container-orchestrated-devices/container-device-interface v0.3.1/go.mod h1:E1zcucIkq9P3eyNmY+68dBQsTcsXJh9cgRo2IVNScKQ= +github.com/container-orchestrated-devices/container-device-interface v0.5.1 h1:nXIUTrlEgGcA/n2geY3J7yyaGGhkocSlMkKPS4Qp4c0= +github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY= 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= diff --git a/integration/client/go.mod b/integration/client/go.mod index 3701a759db66..ca36b1a62af1 100644 --- a/integration/client/go.mod +++ b/integration/client/go.mod @@ -26,7 +26,7 @@ require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cilium/ebpf v0.9.1 // indirect - github.com/container-orchestrated-devices/container-device-interface v0.3.1 // indirect + github.com/container-orchestrated-devices/container-device-interface v0.5.1 // indirect github.com/containerd/console v1.0.3 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/containerd/fifo v1.0.0 // indirect @@ -35,6 +35,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-units v0.4.0 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/integration/client/go.sum b/integration/client/go.sum index f3d911fec8ff..0055d4e6c60d 100644 --- a/integration/client/go.sum +++ b/integration/client/go.sum @@ -182,8 +182,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/container-orchestrated-devices/container-device-interface v0.3.1 h1:AvASKHHm6w3qMU49iPYyp8GhwbacvqjfGHUZEgvA/mQ= -github.com/container-orchestrated-devices/container-device-interface v0.3.1/go.mod h1:E1zcucIkq9P3eyNmY+68dBQsTcsXJh9cgRo2IVNScKQ= +github.com/container-orchestrated-devices/container-device-interface v0.5.1 h1:nXIUTrlEgGcA/n2geY3J7yyaGGhkocSlMkKPS4Qp4c0= +github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY= github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= @@ -309,6 +309,7 @@ github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzP github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= @@ -722,6 +723,7 @@ github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1 h1:9iF github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs= 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.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -733,7 +735,6 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e h1:2Tg49TNXSTIsX8AAtmo1aQ1IbfnoUFzkOp7p2iWygtc= github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w= github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= @@ -815,6 +816,7 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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 5c3f803f4318..d653ac387fd8 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(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/container-edits.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go index 80d88b118a6d..1295f75e9ea9 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 a9017259c8ac..847e51254032 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 54f19143c663..ccfab7094cdb 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 fa6e0af693ca..d5bd54b0b949 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 ad017fec7e3c..f339349bbace 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 d74756599553..9a5d451c9a1a 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" @@ -34,7 +35,12 @@ var ( "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 ) // Spec represents a single CDI Spec. It is usually loaded from a @@ -64,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) } @@ -85,7 +91,7 @@ func ReadSpec(path string, priority int) (*Spec, error) { // 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) { - err := validateWithSchema(raw) + err := validateSpec(raw) if err != nil { return nil, err } @@ -105,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 @@ -179,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 { @@ -188,3 +244,22 @@ func parseSpec(data []byte) (*cdi.Spec, error) { } 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 NewSpec()) or written (Spec.Write()). +func SetSpecValidator(fn func(*cdi.Spec) error) { + specValidator = fn +} + +// validateSpec validates the Spec using the extneral validator. +func validateSpec(raw *cdi.Spec) error { + if specValidator == nil { + return nil + } + err := specValidator(raw) + if err != nil { + return errors.Wrap(err, "Spec validation failed") + } + return nil +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/schema.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go similarity index 51% rename from vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/schema.go rename to vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go index 7e4dc894c9df..cca825c60df1 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/schema.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go @@ -17,30 +17,32 @@ package cdi import ( - "github.com/container-orchestrated-devices/container-device-interface/schema" - cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" -) + "os" -const ( - // DefaultExternalSchema is the JSON schema to load if found. - DefaultExternalSchema = "/etc/cdi/schema/schema.json" + "github.com/pkg/errors" + "golang.org/x/sys/unix" ) -// SetSchema sets the Spec JSON validation schema to use. -func SetSchema(name string) error { - s, err := schema.Load(name) +// 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 err + return errors.Wrap(err, "rename failed") } - schema.Set(s) - return nil -} + defer dirf.Close() -// Validate CDI Spec against JSON Schema. -func validateWithSchema(raw *cdi.Spec) error { - return schema.ValidateType(raw) -} + if !overwrite { + flags = unix.RENAME_NOREPLACE + } -func init() { - SetSchema(DefaultExternalSchema) + 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 000000000000..285e04e27a31 --- /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/schema/Makefile b/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/Makefile deleted file mode 100644 index 976895f331bc..000000000000 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -VALIDATE ?= ../bin/validate -SCHEMA ?= schema.json - -test: - @FMT_RED=$$(tput setaf 1); \ - FMT_BLUE=$$(tput setaf 12); \ - FMT_CLEAR=$$(tput sgr0); \ - echo "Running Good Tests"; \ - for FILE in $$(ls "testdata/good"); do \ - FILE_PATH="testdata/good/$${FILE}"; \ - if $(VALIDATE) --schema "$(SCHEMA)" "$${FILE_PATH}" > /dev/null ; then \ - printf '%s[OK]%s %s\n' "$${FMT_BLUE}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \ - else \ - printf '%s[KO]%s %s\n' "$${FMT_RED}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \ - exit 1; \ - fi \ - done; \ - echo "Running Bad Tests"; \ - for FILE in $$(ls "testdata/bad"); do \ - FILE_PATH="testdata/bad/$${FILE}"; \ - if $(VALIDATE) --schema "$(SCHEMA)" "$${FILE_PATH}" > /dev/null ; then \ - printf '%s[KO]%s %s\n' "$${FMT_RED}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \ - exit 1; \ - else \ - printf '%s[OK]%s %s\n' "$${FMT_BLUE}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \ - fi \ - done diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json b/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json deleted file mode 100644 index 2d5acf4fae29..000000000000 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "description": "Definitions used throughout the Container Device Interface Specification", - "definitions": { - "uint32": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295 - }, - "int64": { - "type": "integer", - "minimum": -9223372036854775808, - "maximum": 9223372036854775807 - }, - "ArrayOfStrings": { - "type": "array", - "items": { - "type": "string" - } - }, - "FilePath": { - "type": "string" - }, - "Env": { - "$ref": "#/definitions/ArrayOfStrings" - }, - "DeviceNode": { - "type": "object", - "properties": { - "path": { - "$ref": "#/definitions/FilePath" - }, - "permissions": { - "type": "string" - }, - "type": { - "type": "string" - }, - "major": { - "$ref": "#/definitions/int64" - }, - "minor": { - "$ref": "#/definitions/int64" - }, - "uid": { - "$ref": "#/definitions/uint32" - }, - "gid": { - "$ref": "#/definitions/uint32" - } - }, - "required": [ - "path" - ] - }, - "Mount": { - "type": "object", - "properties": { - "hostPath": { - "$ref": "#/definitions/FilePath" - }, - "containerPath": { - "$ref": "#/definitions/FilePath" - }, - "options": { - "type": "string" - } - }, - "required": [ - "hostPath", - "containerPath" - ] - }, - "Hook": { - "type": "object", - "properties": { - "hookName": { - "type": "string" - }, - "path": { - "$ref": "#/definitions/FilePath" - }, - "args": { - "$ref": "#/definitions/ArrayOfStrings" - }, - "env": { - "$ref": "#/definitions/ArrayOfStrings" - }, - "timeout": { - "$ref": "#/definitions/uint32" - } - }, - "required": [ - "hookName", - "path" - ] - }, - "containerEdits": { - "type": "object", - "properties": { - "env": { - "type": "array", - "items": { - "ref": "#definitions/Env" - } - }, - "deviceNodes": { - "type": "array", - "items": { - "$ref": "#/definitions/DeviceNode" - } - }, - "mounts": { - "type": "array", - "items": { - "$ref": "#/definitions/Mount" - } - }, - "hooks": { - "type": "array", - "items": { - "$ref": "#/definitions/Hook" - } - } - } - } - } -} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go deleted file mode 100644 index c8731e7452bb..000000000000 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go +++ /dev/null @@ -1,253 +0,0 @@ -/* - 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 schema - -import ( - "bytes" - "embed" - "encoding/json" - "io" - "io/ioutil" - "net/http" - "path/filepath" - "strings" - - "sigs.k8s.io/yaml" - - "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" - schema "github.com/xeipuuv/gojsonschema" -) - -const ( - // BuiltinSchemaName references the builtin schema for Load()/Set(). - BuiltinSchemaName = "builtin" - // NoneSchemaName references a disabled/NOP schema for Load()/Set(). - NoneSchemaName = "none" - // DefaultSchemaName is the none schema. - DefaultSchemaName = NoneSchemaName - - // builtinSchemaFile is the builtin schema URI in our embedded FS. - builtinSchemaFile = "file:///schema.json" -) - -// Schema is a JSON validation schema. -type Schema struct { - schema *schema.Schema -} - -// Error wraps a JSON validation result. -type Error struct { - Result *schema.Result -} - -// Set sets the default validating JSON schema. -func Set(s *Schema) { - current = s -} - -// Get returns the active validating JSON schema. -func Get() *Schema { - return current -} - -// BuiltinSchema returns the builtin validating JSON Schema. -func BuiltinSchema() *Schema { - return builtin -} - -// NopSchema returns an validating JSON Schema that does no real validation. -func NopSchema() *Schema { - return &Schema{} -} - -// ReadAndValidate all data from the given reader, using the active schema for validation. -func ReadAndValidate(r io.Reader) ([]byte, error) { - return current.ReadAndValidate(r) -} - -// Validate validates the data read from an io.Reader against the active schema. -func Validate(r io.Reader) error { - return current.Validate(r) -} - -// ValidateData validates the given JSON document against the active schema. -func ValidateData(data []byte) error { - return current.ValidateData(data) -} - -// ValidateFile validates the given JSON file against the active schema. -func ValidateFile(path string) error { - return current.ValidateFile(path) -} - -// ValidateType validates a go object against the schema. -func ValidateType(obj interface{}) error { - return current.ValidateType(obj) -} - -// Load the given JSON Schema. -func Load(source string) (*Schema, error) { - var ( - loader schema.JSONLoader - err error - s *schema.Schema - ) - - source = strings.TrimSpace(source) - - switch { - case source == BuiltinSchemaName: - return BuiltinSchema(), nil - case source == NoneSchemaName, source == "": - return NopSchema(), nil - case strings.HasPrefix(source, "file://"): - case strings.HasPrefix(source, "http://"): - case strings.HasPrefix(source, "https://"): - default: - if strings.Index(source, "://") < 0 { - source, err = filepath.Abs(source) - if err != nil { - return nil, errors.Wrapf(err, - "failed to get JSON schema absolute path for %s", source) - } - source = "file://" + source - } - } - - loader = schema.NewReferenceLoader(source) - - s, err = schema.NewSchema(loader) - if err != nil { - return nil, errors.Wrap(err, "failed to load JSON schema") - } - - return &Schema{schema: s}, nil -} - -// ReadAndValidate all data from the given reader, using the schema for validation. -func (s *Schema) ReadAndValidate(r io.Reader) ([]byte, error) { - loader, reader := schema.NewReaderLoader(r) - data, err := ioutil.ReadAll(reader) - if err != nil { - return nil, errors.Wrap(err, "failed to read data for validation") - } - return data, s.validate(loader) -} - -// Validate validates the data read from an io.Reader against the schema. -func (s *Schema) Validate(r io.Reader) error { - _, err := s.ReadAndValidate(r) - return err -} - -// ValidateData validates the given JSON data against the schema. -func (s *Schema) ValidateData(data []byte) error { - var ( - any interface{} - err error - ) - - if !bytes.HasPrefix(bytes.TrimSpace(data), []byte{'{'}) { - err = yaml.Unmarshal(data, &any) - if err != nil { - return errors.Wrap(err, "failed to YAML unmarshal data for validation") - } - data, err = json.Marshal(any) - if err != nil { - return errors.Wrap(err, "failed to JSON remarshal data for validation") - } - } - - return s.validate(schema.NewBytesLoader(data)) -} - -// ValidateFile validates the given JSON file against the schema. -func (s *Schema) ValidateFile(path string) error { - if filepath.Ext(path) == ".json" { - return s.validate(schema.NewReferenceLoader("file://" + path)) - } - - data, err := ioutil.ReadFile(path) - if err != nil { - return err - } - return s.ValidateData(data) -} - -// ValidateType validates a go object against the schema. -func (s *Schema) ValidateType(obj interface{}) error { - l := schema.NewGoLoader(obj) - return s.validate(l) -} - -// Validate the (to be) loaded doc against the schema. -func (s *Schema) validate(doc schema.JSONLoader) error { - if s == nil || s.schema == nil { - return nil - } - - docErr, jsonErr := s.schema.Validate(doc) - if jsonErr != nil { - return errors.Wrap(jsonErr, "failed to load JSON data for validation") - } - if docErr.Valid() { - return nil - } - - return &Error{Result: docErr} -} - -// Error returns the given Result's error as a multierror(.Error()). -func (e *Error) Error() string { - if e == nil || e.Result == nil || e.Result.Valid() { - return "" - } - - var multi error - for _, err := range e.Result.Errors() { - multi = multierror.Append(multi, errors.Errorf("%v", err)) - } - return strings.TrimRight(multi.Error(), "\n") -} - -var ( - // our builtin schema - builtin *Schema - // currently loaded schema, builtin by default - current *Schema -) - -//go:embed *.json -var builtinFS embed.FS - -func init() { - s, err := schema.NewSchema( - schema.NewReferenceLoaderFileSystem( - builtinSchemaFile, - http.FS(builtinFS), - ), - ) - - if err != nil { - builtin = NopSchema() - } else { - builtin = &Schema{schema: s} - } - - current = builtin -} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.json b/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.json deleted file mode 100644 index 35c55b46ac25..000000000000 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "description": "Configuration Schema for the Container Device Interface", - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "cdiVersion": { - "description": "The version of the Container Device Interface Specification that the document complies with", - "type": "string" - }, - "kind": { - "description": "The kind of the device usually of the form 'vendor.com/device'", - "type": "string" - }, - "devices": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "description": "The name of the device", - "type": "string" - }, - "containerEdits": { - "$ref": "defs.json#/definitions/containerEdits" - } - }, - "required": [ - "name", - "containerEdits" - ] - } - } - }, - "required": [ - "cdiVersion", - "kind", - "devices" - ] -} 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 090e30e4396d..3fa2e814bd87 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 10bc9fa23f73..14a0f6a0ba16 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/modules.txt b/vendor/modules.txt index 44b51ab02496..159a16d45d7e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -77,10 +77,9 @@ github.com/cilium/ebpf/internal github.com/cilium/ebpf/internal/sys github.com/cilium/ebpf/internal/unix github.com/cilium/ebpf/link -# github.com/container-orchestrated-devices/container-device-interface v0.3.1 +# github.com/container-orchestrated-devices/container-device-interface v0.5.1 ## explicit; go 1.17 github.com/container-orchestrated-devices/container-device-interface/pkg/cdi -github.com/container-orchestrated-devices/container-device-interface/schema github.com/container-orchestrated-devices/container-device-interface/specs-go # github.com/containerd/aufs v1.0.0 ## explicit; go 1.13