Merge pull request #7374 from soulseen/update-cdi-version
This commit is contained in:
commit
99ee82d0b6
2
go.mod
2
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
|
||||
|
4
go.sum
4
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=
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
292
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
292
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
@ -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
|
||||
}
|
||||
|
@ -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") {
|
||||
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)
|
||||
func (d *DeviceNode) fillMissingInfo() error {
|
||||
if d.HostPath == "" {
|
||||
d.HostPath = d.Path
|
||||
}
|
||||
|
||||
if dev.Type == "" {
|
||||
dev.Type = string(hostDev.Type)
|
||||
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 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultExternalSchema is the JSON schema to load if found.
|
||||
DefaultExternalSchema = "/etc/cdi/schema/schema.json"
|
||||
)
|
||||
// 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
|
||||
|
||||
// SetSchema sets the Spec JSON validation schema to use.
|
||||
func SetSchema(name string) error {
|
||||
s, err := schema.Load(name)
|
||||
dirf, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "rename failed")
|
||||
}
|
||||
schema.Set(s)
|
||||
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
|
||||
}
|
||||
|
||||
// Validate CDI Spec against JSON Schema.
|
||||
func validateWithSchema(raw *cdi.Spec) error {
|
||||
return schema.ValidateType(raw)
|
||||
}
|
||||
|
||||
func init() {
|
||||
SetSchema(DefaultExternalSchema)
|
||||
}
|
39
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
generated
vendored
Normal file
39
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
@ -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
|
127
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json
generated
vendored
127
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json
generated
vendored
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
253
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go
generated
vendored
253
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go
generated
vendored
@ -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
|
||||
}
|
@ -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"
|
||||
]
|
||||
}
|
@ -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.
|
||||
|
@ -95,6 +95,7 @@ func (m *Mount) ToOCI() spec.Mount {
|
||||
Source: m.HostPath,
|
||||
Destination: m.ContainerPath,
|
||||
Options: m.Options,
|
||||
Type: m.Type,
|
||||
}
|
||||
}
|
||||
|
||||
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user