mirror of
https://github.com/containers/podman.git
synced 2025-06-24 19:42:56 +08:00
Merge pull request #15267 from containers/dependabot/go_modules/github.com/container-orchestrated-devices/container-device-interface-0.5.0
build(deps): bump github.com/container-orchestrated-devices/container-device-interface from 0.4.0 to 0.5.0
This commit is contained in:
2
go.mod
2
go.mod
@ -8,7 +8,7 @@ require (
|
|||||||
github.com/buger/goterm v1.0.4
|
github.com/buger/goterm v1.0.4
|
||||||
github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0
|
github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0
|
||||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0
|
github.com/checkpoint-restore/go-criu/v5 v5.3.0
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.4.0
|
github.com/container-orchestrated-devices/container-device-interface v0.5.0
|
||||||
github.com/containernetworking/cni v1.1.2
|
github.com/containernetworking/cni v1.1.2
|
||||||
github.com/containernetworking/plugins v1.1.1
|
github.com/containernetworking/plugins v1.1.1
|
||||||
github.com/containers/buildah v1.27.0
|
github.com/containers/buildah v1.27.0
|
||||||
|
4
go.sum
4
go.sum
@ -281,8 +281,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h
|
|||||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
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/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.4.0 h1:b/mROkfDr1W8fJ25T66iVheHFnWixgyxTOSbO8i7jp4=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.0 h1:BPFG0J1R8bW7z69DtG98K+/l8jsVYXD/o2fmgKXby2s=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.4.0/go.mod h1:E1zcucIkq9P3eyNmY+68dBQsTcsXJh9cgRo2IVNScKQ=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.0/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-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-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
||||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||||
|
284
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
284
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
@ -22,6 +22,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -37,26 +39,42 @@ type Cache struct {
|
|||||||
specs map[string][]*Spec
|
specs map[string][]*Spec
|
||||||
devices map[string]*Device
|
devices map[string]*Device
|
||||||
errors map[string][]error
|
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
|
// NewCache creates a new CDI Cache. The cache is populated from a set
|
||||||
// of CDI Spec directories. These can be specified using a WithSpecDirs
|
// of CDI Spec directories. These can be specified using a WithSpecDirs
|
||||||
// option. The default set of directories is exposed in DefaultSpecDirs.
|
// option. The default set of directories is exposed in DefaultSpecDirs.
|
||||||
func NewCache(options ...Option) (*Cache, error) {
|
func NewCache(options ...Option) (*Cache, error) {
|
||||||
c := &Cache{}
|
c := &Cache{
|
||||||
|
autoRefresh: true,
|
||||||
if err := c.Configure(options...); err != nil {
|
watch: &watch{},
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(c.specDirs) == 0 {
|
|
||||||
c.Configure(WithSpecDirs(DefaultSpecDirs...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Configure applies options to the Cache. Updates and refreshes the
|
||||||
// changed.
|
// Cache if options have changed.
|
||||||
func (c *Cache) Configure(options ...Option) error {
|
func (c *Cache) Configure(options ...Option) error {
|
||||||
if len(options) == 0 {
|
if len(options) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@ -65,17 +83,54 @@ func (c *Cache) Configure(options ...Option) error {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
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 {
|
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")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
// 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 {
|
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 (
|
var (
|
||||||
specs = map[string][]*Spec{}
|
specs = map[string][]*Spec{}
|
||||||
devices = map[string]*Device{}
|
devices = map[string]*Device{}
|
||||||
@ -135,9 +190,6 @@ func (c *Cache) Refresh() error {
|
|||||||
delete(devices, conflict)
|
delete(devices, conflict)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
c.specs = specs
|
c.specs = specs
|
||||||
c.devices = devices
|
c.devices = devices
|
||||||
c.errors = specErrors
|
c.errors = specErrors
|
||||||
@ -149,6 +201,17 @@ func (c *Cache) Refresh() error {
|
|||||||
return nil
|
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
|
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
||||||
// returns any unresolvable devices and an error if injection fails for
|
// returns any unresolvable devices and an error if injection fails for
|
||||||
// any of the devices.
|
// any of the devices.
|
||||||
@ -162,6 +225,8 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
edits := &ContainerEdits{}
|
edits := &ContainerEdits{}
|
||||||
specs := map[*Spec]struct{}{}
|
specs := map[*Spec]struct{}{}
|
||||||
|
|
||||||
@ -190,11 +255,46 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
|||||||
return nil, nil
|
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.
|
// GetDevice returns the cached device for the given qualified name.
|
||||||
func (c *Cache) GetDevice(device string) *Device {
|
func (c *Cache) GetDevice(device string) *Device {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
return c.devices[device]
|
return c.devices[device]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +305,8 @@ func (c *Cache) ListDevices() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for name := range c.devices {
|
for name := range c.devices {
|
||||||
devices = append(devices, name)
|
devices = append(devices, name)
|
||||||
}
|
}
|
||||||
@ -220,6 +322,8 @@ func (c *Cache) ListVendors() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for vendor := range c.specs {
|
for vendor := range c.specs {
|
||||||
vendors = append(vendors, vendor)
|
vendors = append(vendors, vendor)
|
||||||
}
|
}
|
||||||
@ -238,6 +342,8 @@ func (c *Cache) ListClasses() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for _, specs := range c.specs {
|
for _, specs := range c.specs {
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
cmap[spec.GetClass()] = struct{}{}
|
cmap[spec.GetClass()] = struct{}{}
|
||||||
@ -256,6 +362,8 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
return c.specs[vendor]
|
return c.specs[vendor]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,12 +376,158 @@ func (c *Cache) GetSpecErrors(spec *Spec) []error {
|
|||||||
// GetErrors returns all errors encountered during the last
|
// GetErrors returns all errors encountered during the last
|
||||||
// cache refresh.
|
// cache refresh.
|
||||||
func (c *Cache) GetErrors() map[string][]error {
|
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.
|
// GetSpecDirectories returns the CDI Spec directories currently in use.
|
||||||
func (c *Cache) GetSpecDirectories() []string {
|
func (c *Cache) GetSpecDirectories() []string {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
dirs := make([]string, len(c.specDirs))
|
dirs := make([]string, len(c.specDirs))
|
||||||
copy(dirs, c.specDirs)
|
copy(dirs, c.specDirs)
|
||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSpecDirErrors returns any errors related to configured Spec directories.
|
||||||
|
func (c *Cache) GetSpecDirErrors() map[string]error {
|
||||||
|
if c.dirErrors == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
errors := make(map[string]error)
|
||||||
|
for dir, err := range c.dirErrors {
|
||||||
|
errors[dir] = err
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our fsnotify helper wrapper.
|
||||||
|
type watch struct {
|
||||||
|
watcher *fsnotify.Watcher
|
||||||
|
tracked map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup monitoring for the given Spec directories.
|
||||||
|
func (w *watch) setup(dirs []string, dirErrors map[string]error) {
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
w.tracked = make(map[string]bool)
|
||||||
|
for _, dir = range dirs {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
w.watcher, err = fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
for _, dir := range dirs {
|
||||||
|
dirErrors[dir] = errors.Wrap(err, "failed to create watcher")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.update(dirErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start watching Spec directories for relevant changes.
|
||||||
|
func (w *watch) start(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||||
|
go w.watch(m, refresh, dirErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop watching directories.
|
||||||
|
func (w *watch) stop() {
|
||||||
|
if w.watcher == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.watcher.Close()
|
||||||
|
w.tracked = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch Spec directory changes, triggering a refresh if necessary.
|
||||||
|
func (w *watch) watch(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||||
|
watch := w.watcher
|
||||||
|
if watch == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watch.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if event.Op == fsnotify.Write {
|
||||||
|
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
if event.Op == fsnotify.Remove && w.tracked[event.Name] {
|
||||||
|
w.update(dirErrors, event.Name)
|
||||||
|
} else {
|
||||||
|
w.update(dirErrors)
|
||||||
|
}
|
||||||
|
refresh()
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
case _, ok := <-watch.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update watch with pending/missing or removed directories.
|
||||||
|
func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
update bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for dir, ok = range w.tracked {
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.watcher.Add(dir)
|
||||||
|
if err == nil {
|
||||||
|
w.tracked[dir] = true
|
||||||
|
delete(dirErrors, dir)
|
||||||
|
update = true
|
||||||
|
} else {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
dirErrors[dir] = errors.Wrap(err, "failed to monitor for changes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir = range removed {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
dirErrors[dir] = errors.New("directory removed")
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return update
|
||||||
|
}
|
||||||
|
@ -85,11 +85,13 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range e.DeviceNodes {
|
for _, d := range e.DeviceNodes {
|
||||||
dev := d.ToOCI()
|
dn := DeviceNode{d}
|
||||||
if err := fillMissingInfo(&dev); err != nil {
|
|
||||||
|
err := dn.fillMissingInfo()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
dev := d.ToOCI()
|
||||||
if dev.UID == nil && spec.Process != nil {
|
if dev.UID == nil && spec.Process != nil {
|
||||||
if uid := spec.Process.User.UID; uid > 0 {
|
if uid := spec.Process.User.UID; uid > 0 {
|
||||||
dev.UID = &uid
|
dev.UID = &uid
|
||||||
@ -288,26 +290,31 @@ func ensureOCIHooks(spec *oci.Spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
||||||
func fillMissingInfo(dev *oci.LinuxDevice) error {
|
func (d *DeviceNode) fillMissingInfo() error {
|
||||||
if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") {
|
if d.HostPath == "" {
|
||||||
return nil
|
d.HostPath = d.Path
|
||||||
}
|
|
||||||
hostDev, err := runc.DeviceFromPath(dev.Path, "rwm")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dev.Type == "" {
|
if d.Type != "" && (d.Major != 0 || d.Type == "p") {
|
||||||
dev.Type = string(hostDev.Type)
|
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 {
|
} else {
|
||||||
if dev.Type != string(hostDev.Type) {
|
if d.Type != string(hostDev.Type) {
|
||||||
return errors.Errorf("CDI device %q, host type mismatch (%s, %s)",
|
return errors.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
|
||||||
dev.Path, dev.Type, string(hostDev.Type))
|
d.Path, d.HostPath, d.Type, string(hostDev.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dev.Major == 0 && dev.Type != "p" {
|
if d.Major == 0 && d.Type != "p" {
|
||||||
dev.Major = hostDev.Major
|
d.Major = hostDev.Major
|
||||||
dev.Minor = hostDev.Minor
|
d.Minor = hostDev.Minor
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -67,6 +67,21 @@
|
|||||||
//
|
//
|
||||||
// Cache Refresh
|
// 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
|
// In a runtime implementation one typically wants to make sure the
|
||||||
// CDI Spec cache is up to date before performing device injection.
|
// CDI Spec cache is up to date before performing device injection.
|
||||||
// A code snippet similar to the following accmplishes that:
|
// 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 names which switch the used schema to the in-repo validation
|
||||||
// schema embedded into the binary or the now default no-op schema
|
// schema embedded into the binary or the now default no-op schema
|
||||||
// correspondingly. Other names are interpreted as the path to the actual
|
// 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
|
package cdi
|
||||||
|
@ -130,7 +130,7 @@ func ValidateVendorName(vendor string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
|
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
|
return nil
|
||||||
@ -158,7 +158,7 @@ func ValidateClassName(class string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(class[len(class)-1])) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@ -172,8 +172,11 @@ func ValidateDeviceName(name string) error {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
return errors.Errorf("invalid (empty) device name")
|
return errors.Errorf("invalid (empty) device name")
|
||||||
}
|
}
|
||||||
if !isLetter(rune(name[0])) {
|
if !isAlphaNumeric(rune(name[0])) {
|
||||||
return errors.Errorf("invalid name %q, should start with letter", name)
|
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]) {
|
for _, c := range string(name[1 : len(name)-1]) {
|
||||||
switch {
|
switch {
|
||||||
@ -185,7 +188,7 @@ func ValidateDeviceName(name string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(name[len(name)-1])) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package cdi
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
oci "github.com/opencontainers/runtime-spec/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
|
// RegistryRefresher is the registry interface for refreshing the
|
||||||
// cache of CDI Specs and devices.
|
// cache of CDI Specs and devices.
|
||||||
//
|
//
|
||||||
|
// Configure reconfigures the registry with the given options.
|
||||||
|
//
|
||||||
// Refresh rescans all CDI Spec directories and updates the
|
// Refresh rescans all CDI Spec directories and updates the
|
||||||
// state of the cache to reflect any changes. It returns any
|
// state of the cache to reflect any changes. It returns any
|
||||||
// errors encountered during the refresh.
|
// errors encountered during the refresh.
|
||||||
@ -50,10 +53,15 @@ type Registry interface {
|
|||||||
// GetSpecDirectories returns the set up CDI Spec directories
|
// GetSpecDirectories returns the set up CDI Spec directories
|
||||||
// currently in use. The directories are returned in the scan
|
// currently in use. The directories are returned in the scan
|
||||||
// order of Refresh().
|
// order of Refresh().
|
||||||
|
//
|
||||||
|
// GetSpecDirErrors returns any errors related to the configured
|
||||||
|
// Spec directories.
|
||||||
type RegistryRefresher interface {
|
type RegistryRefresher interface {
|
||||||
|
Configure(...Option) error
|
||||||
Refresh() error
|
Refresh() error
|
||||||
GetErrors() map[string][]error
|
GetErrors() map[string][]error
|
||||||
GetSpecDirectories() []string
|
GetSpecDirectories() []string
|
||||||
|
GetSpecDirErrors() map[string]error
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryResolver is the registry interface for injecting CDI
|
// RegistryResolver is the registry interface for injecting CDI
|
||||||
@ -90,11 +98,15 @@ type RegistryDeviceDB interface {
|
|||||||
//
|
//
|
||||||
// GetSpecErrors returns any errors for the Spec encountered during
|
// GetSpecErrors returns any errors for the Spec encountered during
|
||||||
// the last cache refresh.
|
// the last cache refresh.
|
||||||
|
//
|
||||||
|
// WriteSpec writes the Spec with the given content and name to the
|
||||||
|
// last Spec directory.
|
||||||
type RegistrySpecDB interface {
|
type RegistrySpecDB interface {
|
||||||
ListVendors() []string
|
ListVendors() []string
|
||||||
ListClasses() []string
|
ListClasses() []string
|
||||||
GetVendorSpecs(vendor string) []*Spec
|
GetVendorSpecs(vendor string) []*Spec
|
||||||
GetSpecErrors(*Spec) []error
|
GetSpecErrors(*Spec) []error
|
||||||
|
WriteSpec(raw *cdi.Spec, name string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type registry struct {
|
type registry struct {
|
||||||
|
@ -45,10 +45,11 @@ var (
|
|||||||
// WithSpecDirs returns an option to override the CDI Spec directories.
|
// WithSpecDirs returns an option to override the CDI Spec directories.
|
||||||
func WithSpecDirs(dirs ...string) Option {
|
func WithSpecDirs(dirs ...string) Option {
|
||||||
return func(c *Cache) error {
|
return func(c *Cache) error {
|
||||||
c.specDirs = make([]string, len(dirs))
|
specDirs := make([]string, len(dirs))
|
||||||
for i, dir := range dirs {
|
for i, dir := range dirs {
|
||||||
c.specDirs[i] = filepath.Clean(dir)
|
specDirs[i] = filepath.Clean(dir)
|
||||||
}
|
}
|
||||||
|
c.specDirs = specDirs
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package cdi
|
package cdi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -35,6 +36,7 @@ var (
|
|||||||
"0.2.0": {},
|
"0.2.0": {},
|
||||||
"0.3.0": {},
|
"0.3.0": {},
|
||||||
"0.4.0": {},
|
"0.4.0": {},
|
||||||
|
"0.5.0": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Externally set CDI Spec validation function.
|
// Externally set CDI Spec validation function.
|
||||||
@ -68,7 +70,7 @@ func ReadSpec(path string, priority int) (*Spec, error) {
|
|||||||
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
|
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := parseSpec(data)
|
raw, err := ParseSpec(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
|
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
|
||||||
}
|
}
|
||||||
@ -109,6 +111,56 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
|
|||||||
return spec, nil
|
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.
|
// GetVendor returns the vendor of this Spec.
|
||||||
func (s *Spec) GetVendor() string {
|
func (s *Spec) GetVendor() string {
|
||||||
return s.vendor
|
return s.vendor
|
||||||
@ -183,8 +235,8 @@ func validateVersion(version string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse raw CDI Spec file data.
|
// ParseSpec parses CDI Spec data into a raw CDI Spec.
|
||||||
func parseSpec(data []byte) (*cdi.Spec, error) {
|
func ParseSpec(data []byte) (*cdi.Spec, error) {
|
||||||
var raw *cdi.Spec
|
var raw *cdi.Spec
|
||||||
err := yaml.UnmarshalStrict(data, &raw)
|
err := yaml.UnmarshalStrict(data, &raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
48
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go
generated
vendored
Normal file
48
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 The CDI Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cdi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rename src to dst, both relative to the directory dir. If dst already exists
|
||||||
|
// refuse renaming with an error unless overwrite is explicitly asked for.
|
||||||
|
func renameIn(dir, src, dst string, overwrite bool) error {
|
||||||
|
var flags uint
|
||||||
|
|
||||||
|
dirf, err := os.Open(dir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "rename failed")
|
||||||
|
}
|
||||||
|
defer dirf.Close()
|
||||||
|
|
||||||
|
if !overwrite {
|
||||||
|
flags = unix.RENAME_NOREPLACE
|
||||||
|
}
|
||||||
|
|
||||||
|
dirFd := int(dirf.Fd())
|
||||||
|
err = unix.Renameat2(dirFd, src, dirFd, dst, flags)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "rename failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
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)
|
||||||
|
}
|
@ -3,7 +3,7 @@ package specs
|
|||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
// CurrentVersion is the current version of the Spec.
|
// CurrentVersion is the current version of the Spec.
|
||||||
const CurrentVersion = "0.4.0"
|
const CurrentVersion = "0.5.0"
|
||||||
|
|
||||||
// Spec is the base configuration for CDI
|
// Spec is the base configuration for CDI
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
@ -31,6 +31,7 @@ type ContainerEdits struct {
|
|||||||
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
||||||
type DeviceNode struct {
|
type DeviceNode struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
HostPath string `json:"hostPath,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
Major int64 `json:"major,omitempty"`
|
Major int64 `json:"major,omitempty"`
|
||||||
Minor int64 `json:"minor,omitempty"`
|
Minor int64 `json:"minor,omitempty"`
|
||||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -61,7 +61,7 @@ github.com/checkpoint-restore/go-criu/v5/rpc
|
|||||||
github.com/checkpoint-restore/go-criu/v5/stats
|
github.com/checkpoint-restore/go-criu/v5/stats
|
||||||
# github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
# github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/chzyer/readline
|
github.com/chzyer/readline
|
||||||
# github.com/container-orchestrated-devices/container-device-interface v0.4.0
|
# github.com/container-orchestrated-devices/container-device-interface v0.5.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/container-orchestrated-devices/container-device-interface/pkg/cdi
|
github.com/container-orchestrated-devices/container-device-interface/pkg/cdi
|
||||||
github.com/container-orchestrated-devices/container-device-interface/specs-go
|
github.com/container-orchestrated-devices/container-device-interface/specs-go
|
||||||
|
Reference in New Issue
Block a user