summaryrefslogtreecommitdiff
path: root/vendor/github.com/container-orchestrated-devices/container-device-interface
diff options
context:
space:
mode:
authorEvan Lezar <elezar@nvidia.com>2022-01-12 14:22:29 +0100
committerEvan Lezar <elezar@nvidia.com>2022-01-14 13:35:22 +0100
commit968deb7c2cd2e4fab7edf03f2a73fb62bb652171 (patch)
treeea0349b4a36a44625e5f6dda891034986da8417f /vendor/github.com/container-orchestrated-devices/container-device-interface
parent2c510146aa03c74fb00a15bcf81c62b14df9c7ea (diff)
downloadpodman-968deb7c2cd2e4fab7edf03f2a73fb62bb652171.tar.gz
podman-968deb7c2cd2e4fab7edf03f2a73fb62bb652171.tar.bz2
podman-968deb7c2cd2e4fab7edf03f2a73fb62bb652171.zip
Use new CDI API
This change updates the CDI API to commit 46367ec063fda9da931d050b308ccd768e824364 which addresses some inconistencies in the previous implementation. Signed-off-by: Evan Lezar <elezar@nvidia.com>
Diffstat (limited to 'vendor/github.com/container-orchestrated-devices/container-device-interface')
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go275
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go222
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go74
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go130
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go203
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go139
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go110
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go172
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go180
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/config.go21
-rw-r--r--vendor/github.com/container-orchestrated-devices/container-device-interface/specs-go/oci.go42
11 files changed, 1363 insertions, 205 deletions
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
new file mode 100644
index 000000000..c4befc83e
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
@@ -0,0 +1,275 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/hashicorp/go-multierror"
+ oci "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+)
+
+// Option is an option to change some aspect of default CDI behavior.
+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
+}
+
+// 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...))
+ }
+
+ return c, c.Refresh()
+}
+
+// Configure applies options to the cache. Updates the cache if options have
+// changed.
+func (c *Cache) Configure(options ...Option) error {
+ if len(options) == 0 {
+ return nil
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ for _, o := range options {
+ if err := o(c); err != nil {
+ return errors.Wrapf(err, "failed to apply cache options")
+ }
+ }
+
+ return nil
+}
+
+// Refresh rescans the CDI Spec directories and refreshes the Cache.
+func (c *Cache) Refresh() error {
+ var (
+ specs = map[string][]*Spec{}
+ devices = map[string]*Device{}
+ conflicts = map[string]struct{}{}
+ specErrors = map[string][]error{}
+ result []error
+ )
+
+ // collect errors per spec file path and once globally
+ collectError := func(err error, paths ...string) {
+ result = append(result, err)
+ for _, path := range paths {
+ specErrors[path] = append(specErrors[path], err)
+ }
+ }
+ // resolve conflicts based on device Spec priority (order of precedence)
+ resolveConflict := func(name string, dev *Device, old *Device) bool {
+ devSpec, oldSpec := dev.GetSpec(), old.GetSpec()
+ devPrio, oldPrio := devSpec.GetPriority(), oldSpec.GetPriority()
+ switch {
+ case devPrio > oldPrio:
+ return false
+ case devPrio == oldPrio:
+ devPath, oldPath := devSpec.GetPath(), oldSpec.GetPath()
+ collectError(errors.Errorf("conflicting device %q (specs %q, %q)",
+ name, devPath, oldPath), devPath, oldPath)
+ conflicts[name] = struct{}{}
+ }
+ return true
+ }
+
+ _ = scanSpecDirs(c.specDirs, func(path string, priority int, spec *Spec, err error) error {
+ path = filepath.Clean(path)
+ if err != nil {
+ collectError(errors.Wrapf(err, "failed to load CDI Spec"), path)
+ return nil
+ }
+
+ vendor := spec.GetVendor()
+ specs[vendor] = append(specs[vendor], spec)
+
+ for _, dev := range spec.devices {
+ qualified := dev.GetQualifiedName()
+ other, ok := devices[qualified]
+ if ok {
+ if resolveConflict(qualified, dev, other) {
+ continue
+ }
+ }
+ devices[qualified] = dev
+ }
+
+ return nil
+ })
+
+ for conflict := range conflicts {
+ delete(devices, conflict)
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ c.specs = specs
+ c.devices = devices
+ c.errors = specErrors
+
+ if len(result) > 0 {
+ return multierror.Append(nil, result...)
+ }
+
+ return 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.
+func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) {
+ var (
+ unresolved []string
+ specs = map[*Spec]struct{}{}
+ )
+
+ if ociSpec == nil {
+ return devices, errors.Errorf("can't inject devices, nil OCI Spec")
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ for _, device := range devices {
+ d := c.devices[device]
+ if d == nil {
+ unresolved = append(unresolved, device)
+ continue
+ }
+ if _, ok := specs[d.GetSpec()]; !ok {
+ specs[d.GetSpec()] = struct{}{}
+ d.GetSpec().ApplyEdits(ociSpec)
+ }
+ d.ApplyEdits(ociSpec)
+ }
+
+ if unresolved != nil {
+ return unresolved, errors.Errorf("unresolvable CDI devices %s",
+ strings.Join(devices, ", "))
+ }
+
+ return nil, nil
+}
+
+// GetDevice returns the cached device for the given qualified name.
+func (c *Cache) GetDevice(device string) *Device {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.devices[device]
+}
+
+// ListDevices lists all cached devices by qualified name.
+func (c *Cache) ListDevices() []string {
+ var devices []string
+
+ c.Lock()
+ defer c.Unlock()
+
+ for name := range c.devices {
+ devices = append(devices, name)
+ }
+ sort.Strings(devices)
+
+ return devices
+}
+
+// ListVendors lists all vendors known to the cache.
+func (c *Cache) ListVendors() []string {
+ var vendors []string
+
+ c.Lock()
+ defer c.Unlock()
+
+ for vendor := range c.specs {
+ vendors = append(vendors, vendor)
+ }
+ sort.Strings(vendors)
+
+ return vendors
+}
+
+// ListClasses lists all device classes known to the cache.
+func (c *Cache) ListClasses() []string {
+ var (
+ cmap = map[string]struct{}{}
+ classes []string
+ )
+
+ c.Lock()
+ defer c.Unlock()
+
+ for _, specs := range c.specs {
+ for _, spec := range specs {
+ cmap[spec.GetClass()] = struct{}{}
+ }
+ }
+ for class := range cmap {
+ classes = append(classes, class)
+ }
+ sort.Strings(classes)
+
+ return classes
+}
+
+// GetVendorSpecs returns all specs for the given vendor.
+func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.specs[vendor]
+}
+
+// GetSpecErrors returns all errors encountered for the spec during the
+// last cache refresh.
+func (c *Cache) GetSpecErrors(spec *Spec) []error {
+ return c.errors[spec.GetPath()]
+}
+
+// GetErrors returns all errors encountered during the last
+// cache refresh.
+func (c *Cache) GetErrors() map[string][]error {
+ return c.errors
+}
+
+// GetSpecDirectories returns the CDI Spec directories currently in use.
+func (c *Cache) GetSpecDirectories() []string {
+ dirs := make([]string, len(c.specDirs))
+ copy(dirs, c.specDirs)
+ return dirs
+}
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
new file mode 100644
index 000000000..767cba57b
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
@@ -0,0 +1,222 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/container-orchestrated-devices/container-device-interface/specs-go"
+ oci "github.com/opencontainers/runtime-spec/specs-go"
+ ocigen "github.com/opencontainers/runtime-tools/generate"
+)
+
+const (
+ // PrestartHook is the name of the OCI "prestart" hook.
+ PrestartHook = "prestart"
+ // CreateRuntimeHook is the name of the OCI "createRuntime" hook.
+ CreateRuntimeHook = "createRuntime"
+ // CreateContainerHook is the name of the OCI "createContainer" hook.
+ CreateContainerHook = "createContainer"
+ // StartContainerHook is the name of the OCI "startContainer" hook.
+ StartContainerHook = "startContainer"
+ // PoststartHook is the name of the OCI "poststart" hook.
+ PoststartHook = "poststart"
+ // PoststopHook is the name of the OCI "poststop" hook.
+ PoststopHook = "poststop"
+)
+
+var (
+ // Names of recognized hooks.
+ validHookNames = map[string]struct{}{
+ PrestartHook: {},
+ CreateRuntimeHook: {},
+ CreateContainerHook: {},
+ StartContainerHook: {},
+ PoststartHook: {},
+ PoststopHook: {},
+ }
+)
+
+// ContainerEdits represent updates to be applied to an OCI Spec.
+// These updates can be specific to a CDI device, or they can be
+// specific to a CDI Spec. In the former case these edits should
+// be applied to all OCI Specs where the corresponding CDI device
+// is injected. In the latter case, these edits should be applied
+// to all OCI Specs where at least one devices from the CDI Spec
+// is injected.
+type ContainerEdits struct {
+ *specs.ContainerEdits
+}
+
+// Apply edits to the given OCI Spec. Updates the OCI Spec in place.
+// Returns an error if the update fails.
+func (e *ContainerEdits) Apply(spec *oci.Spec) error {
+ if spec == nil {
+ return errors.New("can't edit nil OCI Spec")
+ }
+ if e == nil || e.ContainerEdits == nil {
+ return nil
+ }
+
+ specgen := ocigen.NewFromSpec(spec)
+ if len(e.Env) > 0 {
+ specgen.AddMultipleProcessEnv(e.Env)
+ }
+ for _, d := range e.DeviceNodes {
+ specgen.AddDevice(d.ToOCI())
+ }
+ for _, m := range e.Mounts {
+ specgen.AddMount(m.ToOCI())
+ }
+ for _, h := range e.Hooks {
+ switch h.HookName {
+ case PrestartHook:
+ specgen.AddPreStartHook(h.ToOCI())
+ case PoststartHook:
+ specgen.AddPostStartHook(h.ToOCI())
+ case PoststopHook:
+ specgen.AddPostStopHook(h.ToOCI())
+ // TODO: Maybe runtime-tools/generate should be updated with these...
+ case CreateRuntimeHook:
+ ensureOCIHooks(spec)
+ spec.Hooks.CreateRuntime = append(spec.Hooks.CreateRuntime, h.ToOCI())
+ case CreateContainerHook:
+ ensureOCIHooks(spec)
+ spec.Hooks.CreateContainer = append(spec.Hooks.CreateContainer, h.ToOCI())
+ case StartContainerHook:
+ ensureOCIHooks(spec)
+ spec.Hooks.StartContainer = append(spec.Hooks.StartContainer, h.ToOCI())
+ default:
+ return errors.Errorf("unknown hook name %q", h.HookName)
+ }
+ }
+
+ return nil
+}
+
+// Validate container edits.
+func (e *ContainerEdits) Validate() error {
+ if e == nil || e.ContainerEdits == nil {
+ return nil
+ }
+
+ if err := ValidateEnv(e.Env); err != nil {
+ return errors.Wrap(err, "invalid container edits")
+ }
+ for _, d := range e.DeviceNodes {
+ if err := (&DeviceNode{d}).Validate(); err != nil {
+ return err
+ }
+ }
+ for _, h := range e.Hooks {
+ if err := (&Hook{h}).Validate(); err != nil {
+ return err
+ }
+ }
+ for _, m := range e.Mounts {
+ if err := (&Mount{m}).Validate(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// isEmpty returns true if these edits are empty. This is valid in a
+// global Spec context but invalid in a Device context.
+func (e *ContainerEdits) isEmpty() bool {
+ if e == nil {
+ return false
+ }
+ return len(e.Env)+len(e.DeviceNodes)+len(e.Hooks)+len(e.Mounts) == 0
+}
+
+// ValidateEnv validates the given environment variables.
+func ValidateEnv(env []string) error {
+ for _, v := range env {
+ if strings.IndexByte(v, byte('=')) <= 0 {
+ return errors.Errorf("invalid environment variable %q", v)
+ }
+ }
+ return nil
+}
+
+// DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
+type DeviceNode struct {
+ *specs.DeviceNode
+}
+
+// Validate a CDI Spec DeviceNode.
+func (d *DeviceNode) Validate() error {
+ if d.Path == "" {
+ return errors.New("invalid (empty) device path")
+ }
+ if d.Type != "" && d.Type != "b" && d.Type != "c" {
+ return errors.Errorf("device %q: invalid type %q", d.Path, d.Type)
+ }
+ for _, bit := range d.Permissions {
+ if bit != 'r' && bit != 'w' && bit != 'm' {
+ return errors.Errorf("device %q: invalid persmissions %q",
+ d.Path, d.Permissions)
+ }
+ }
+ return nil
+}
+
+// Hook is a CDI Spec Hook wrapper, used for validating hooks.
+type Hook struct {
+ *specs.Hook
+}
+
+// Validate a hook.
+func (h *Hook) Validate() error {
+ if _, ok := validHookNames[h.HookName]; !ok {
+ return errors.Errorf("invalid hook name %q", h.HookName)
+ }
+ if h.Path == "" {
+ return errors.Errorf("invalid hook %q with empty path", h.HookName)
+ }
+ if err := ValidateEnv(h.Env); err != nil {
+ return errors.Wrapf(err, "invalid hook %q", h.HookName)
+ }
+ return nil
+}
+
+// Mount is a CDI Mount wrapper, used for validating mounts.
+type Mount struct {
+ *specs.Mount
+}
+
+// Validate a mount.
+func (m *Mount) Validate() error {
+ if m.HostPath == "" {
+ return errors.New("invalid mount, empty host path")
+ }
+ if m.ContainerPath == "" {
+ return errors.New("invalid mount, empty container path")
+ }
+ return nil
+}
+
+// Ensure OCI Spec hooks are not nil so we can add hooks.
+func ensureOCIHooks(spec *oci.Spec) {
+ if spec.Hooks == nil {
+ spec.Hooks = &oci.Hooks{}
+ }
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go
new file mode 100644
index 000000000..84b71429c
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go
@@ -0,0 +1,74 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
+ oci "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+)
+
+// Device represents a CDI device of a Spec.
+type Device struct {
+ *cdi.Device
+ spec *Spec
+}
+
+// Create a new Device, associate it with the given Spec.
+func newDevice(spec *Spec, d cdi.Device) (*Device, error) {
+ dev := &Device{
+ Device: &d,
+ spec: spec,
+ }
+
+ if err := dev.validate(); err != nil {
+ return nil, err
+ }
+
+ return dev, nil
+}
+
+// GetSpec returns the Spec this device is defined in.
+func (d *Device) GetSpec() *Spec {
+ return d.spec
+}
+
+// GetQualifiedName returns the qualified name for this device.
+func (d *Device) GetQualifiedName() string {
+ return QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
+}
+
+// ApplyEdits applies the device-speific container edits to an OCI Spec.
+func (d *Device) ApplyEdits(ociSpec *oci.Spec) error {
+ e := ContainerEdits{&d.ContainerEdits}
+ return e.Apply(ociSpec)
+}
+
+// Validate the device.
+func (d *Device) validate() error {
+ if err := ValidateDeviceName(d.Name); err != nil {
+ return err
+ }
+ edits := ContainerEdits{&d.ContainerEdits}
+ if edits.isEmpty() {
+ return errors.Errorf("invalid device, empty device edits")
+ }
+ if err := edits.Validate(); err != nil {
+ return errors.Wrapf(err, "invalid device %q", d.Name)
+ }
+ 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
new file mode 100644
index 000000000..4fcdc44db
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
@@ -0,0 +1,130 @@
+// Package cdi has the primary purpose of providing an API for
+// interacting with CDI and consuming CDI devices.
+//
+// For more information about Container Device Interface, please refer to
+// https://github.com/container-orchestrated-devices/container-device-interface
+//
+// Container Device Interface
+//
+// Container Device Interface, or CDI for short, provides comprehensive
+// third party device support for container runtimes. CDI uses vendor
+// provided specification files, CDI Specs for short, to describe how a
+// container's runtime environment should be modified when one or more
+// of the vendor-specific devices is injected into the container. Beyond
+// describing the low level platform-specific details of how to gain
+// basic access to a device, CDI Specs allow more fine-grained device
+// initialization, and the automatic injection of any necessary vendor-
+// or device-specific software that might be required for a container
+// to use a device or take full advantage of it.
+//
+// In the CDI device model containers request access to a device using
+// fully qualified device names, qualified names for short, consisting of
+// a vendor identifier, a device class and a device name or identifier.
+// These pieces of information together uniquely identify a device among
+// all device vendors, classes and device instances.
+//
+// This package implements an API for easy consumption of CDI. The API
+// implements discovery, loading and caching of CDI Specs and injection
+// of CDI devices into containers. This is the most common functionality
+// the vast majority of CDI consumers need. The API should be usable both
+// by OCI runtime clients and runtime implementations.
+//
+// CDI Registry
+//
+// The primary interface to interact with CDI devices is the Registry. It
+// is essentially a cache of all Specs and devices discovered in standard
+// CDI directories on the host. The registry has two main functionality,
+// injecting devices into an OCI Spec and refreshing the cache of CDI
+// Specs and devices.
+//
+// Device Injection
+//
+// Using the Registry one can inject CDI devices into a container with code
+// similar to the following snippet:
+//
+// import (
+// "fmt"
+// "strings"
+//
+// "github.com/pkg/errors"
+// log "github.com/sirupsen/logrus"
+//
+// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
+// oci "github.com/opencontainers/runtime-spec/specs-go"
+// )
+//
+// func injectCDIDevices(spec *oci.Spec, devices []string) error {
+// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
+//
+// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
+// if err != nil {
+// return errors.Wrap(err, "CDI device injection failed")
+// }
+//
+// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
+// return nil
+// }
+//
+// Cache Refresh
+//
+// 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:
+//
+// import (
+// "fmt"
+// "strings"
+//
+// "github.com/pkg/errors"
+// log "github.com/sirupsen/logrus"
+//
+// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
+// oci "github.com/opencontainers/runtime-spec/specs-go"
+// )
+//
+// func injectCDIDevices(spec *oci.Spec, devices []string) error {
+// registry := cdi.GetRegistry()
+//
+// if err := registry.Refresh(); err != nil {
+// // Note:
+// // It is up to the implementation to decide whether
+// // to abort injection on errors. A failed Refresh()
+// // does not necessarily render the registry unusable.
+// // For instance, a parse error in a Spec file for
+// // vendor A does not have any effect on devices of
+// // vendor B...
+// log.Warnf("pre-injection Refresh() failed: %v", err)
+// }
+//
+// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
+//
+// unresolved, err := registry.InjectDevices(spec, devices)
+// if err != nil {
+// return errors.Wrap(err, "CDI device injection failed")
+// }
+//
+// log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec))
+// return nil
+// }
+//
+// Generated Spec Files, Multiple Directories, Device Precedence
+//
+// There are systems where the set of available or usable CDI devices
+// changes dynamically and this needs to be reflected in the CDI Specs.
+// This is done by dynamically regenerating CDI Spec files which are
+// affected by these changes.
+//
+// CDI can collect Spec files from multiple directories. Spec files are
+// automatically assigned priorities according to which directory they
+// were loaded from. The later a directory occurs in the list of CDI
+// directories to scan, the higher priority Spec files loaded from that
+// directory are assigned to. When two or more Spec files define the
+// same device, conflict is resolved by chosing the definition from the
+// Spec file with the highest priority.
+//
+// The default CDI directory configuration is chosen to encourage
+// separating dynamically generated CDI Spec files from static ones.
+// The default directories are '/etc/cdi' and '/var/run/cdi'. By putting
+// dynamically generated Spec files under '/var/run/cdi', those take
+// precedence over static ones in '/etc/cdi'.
+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
new file mode 100644
index 000000000..54f19143c
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
@@ -0,0 +1,203 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+// QualifiedName returns the qualified name for a device.
+// The syntax for a qualified device names is
+// "<vendor>/<class>=<name>".
+// A valid vendor name may contain the following runes:
+// 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
+// A valid class name may contain the following runes:
+// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_'.
+// A valid device name may containe the following runes:
+// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
+func QualifiedName(vendor, class, name string) string {
+ return vendor + "/" + class + "=" + name
+}
+
+// IsQualifiedName tests if a device name is qualified.
+func IsQualifiedName(device string) bool {
+ _, _, _, err := ParseQualifiedName(device)
+ return err == nil
+}
+
+// ParseQualifiedName splits a qualified name into device vendor, class,
+// and name. If the device fails to parse as a qualified name, or if any
+// of the split components fail to pass syntax validation, vendor and
+// class are returned as empty, together with the verbatim input as the
+// name and an error describing the reason for failure.
+func ParseQualifiedName(device string) (string, string, string, error) {
+ vendor, class, name := ParseDevice(device)
+
+ if vendor == "" {
+ return "", "", device, errors.Errorf("unqualified device %q, missing vendor", device)
+ }
+ if class == "" {
+ return "", "", device, errors.Errorf("unqualified device %q, missing class", device)
+ }
+ if name == "" {
+ return "", "", device, errors.Errorf("unqualified device %q, missing device name", device)
+ }
+
+ if err := ValidateVendorName(vendor); err != nil {
+ return "", "", device, errors.Wrapf(err, "invalid device %q", device)
+ }
+ if err := ValidateClassName(class); err != nil {
+ return "", "", device, errors.Wrapf(err, "invalid device %q", device)
+ }
+ if err := ValidateDeviceName(name); err != nil {
+ return "", "", device, errors.Wrapf(err, "invalid device %q", device)
+ }
+
+ return vendor, class, name, nil
+}
+
+// ParseDevice tries to split a device name into vendor, class, and name.
+// If this fails, for instance in the case of unqualified device names,
+// ParseDevice returns an empty vendor and class together with name set
+// to the verbatim input.
+func ParseDevice(device string) (string, string, string) {
+ if device == "" || device[0] == '/' {
+ return "", "", device
+ }
+
+ parts := strings.SplitN(device, "=", 2)
+ if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
+ return "", "", device
+ }
+
+ name := parts[1]
+ vendor, class := ParseQualifier(parts[0])
+ if vendor == "" {
+ return "", "", device
+ }
+
+ return vendor, class, name
+}
+
+// ParseQualifier splits a device qualifier into vendor and class.
+// The syntax for a device qualifier is
+// "<vendor>/<class>"
+// If parsing fails, an empty vendor and the class set to the
+// verbatim input is returned.
+func ParseQualifier(kind string) (string, string) {
+ parts := strings.SplitN(kind, "/", 2)
+ if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
+ return "", kind
+ }
+ return parts[0], parts[1]
+}
+
+// ValidateVendorName checks the validity of a vendor name.
+// A vendor name may contain the following ASCII characters:
+// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
+// - digits ('0'-'9')
+// - underscore, dash, and dot ('_', '-', and '.')
+func ValidateVendorName(vendor string) error {
+ if vendor == "" {
+ return errors.Errorf("invalid (empty) vendor name")
+ }
+ if !isLetter(rune(vendor[0])) {
+ return errors.Errorf("invalid vendor %q, should start with letter", vendor)
+ }
+ for _, c := range string(vendor[1 : len(vendor)-1]) {
+ switch {
+ case isAlphaNumeric(c):
+ case c == '_' || c == '-' || c == '.':
+ default:
+ return errors.Errorf("invalid character '%c' in vendor name %q",
+ c, vendor)
+ }
+ }
+ if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
+ return errors.Errorf("invalid vendor %q, should end with letter", vendor)
+ }
+
+ return nil
+}
+
+// ValidateClassName checks the validity of class name.
+// A class name may contain the following ASCII characters:
+// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
+// - digits ('0'-'9')
+// - underscore and dash ('_', '-')
+func ValidateClassName(class string) error {
+ if class == "" {
+ return errors.Errorf("invalid (empty) device class")
+ }
+ if !isLetter(rune(class[0])) {
+ return errors.Errorf("invalid class %q, should start with letter", class)
+ }
+ for _, c := range string(class[1 : len(class)-1]) {
+ switch {
+ case isAlphaNumeric(c):
+ case c == '_' || c == '-':
+ default:
+ return errors.Errorf("invalid character '%c' in device class %q",
+ c, class)
+ }
+ }
+ if !isAlphaNumeric(rune(class[len(class)-1])) {
+ return errors.Errorf("invalid class %q, should end with letter", class)
+ }
+ return nil
+}
+
+// ValidateDeviceName checks the validity of a device name.
+// A device name may contain the following ASCII characters:
+// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
+// - digits ('0'-'9')
+// - underscore, dash, dot, colon ('_', '-', '.', ':')
+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)
+ }
+ for _, c := range string(name[1 : len(name)-1]) {
+ switch {
+ case isAlphaNumeric(c):
+ case c == '_' || c == '-' || c == '.' || c == ':':
+ default:
+ return errors.Errorf("invalid character '%c' in device name %q",
+ c, name)
+ }
+ }
+ if !isAlphaNumeric(rune(name[len(name)-1])) {
+ return errors.Errorf("invalid name %q, should start with letter", name)
+ }
+ return nil
+}
+
+func isLetter(c rune) bool {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
+}
+
+func isDigit(c rune) bool {
+ return '0' <= c && c <= '9'
+}
+
+func isAlphaNumeric(c rune) bool {
+ return isLetter(c) || isDigit(c)
+}
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
new file mode 100644
index 000000000..fa6e0af69
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
@@ -0,0 +1,139 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "sync"
+
+ oci "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+//
+// Registry keeps a cache of all CDI Specs installed or generated on
+// the host. Registry is the primary interface clients should use to
+// interact with CDI.
+//
+// The most commonly used Registry functions are for refreshing the
+// registry and injecting CDI devices into an OCI Spec.
+//
+type Registry interface {
+ RegistryResolver
+ RegistryRefresher
+ DeviceDB() RegistryDeviceDB
+ SpecDB() RegistrySpecDB
+}
+
+// RegistryRefresher is the registry interface for refreshing the
+// cache of CDI Specs and devices.
+//
+// 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.
+//
+// GetErrors returns all errors encountered for any of the scanned
+// Spec files during the last cache refresh.
+//
+// GetSpecDirectories returns the set up CDI Spec directories
+// currently in use. The directories are returned in the scan
+// order of Refresh().
+type RegistryRefresher interface {
+ Refresh() error
+ GetErrors() map[string][]error
+ GetSpecDirectories() []string
+}
+
+// RegistryResolver is the registry interface for injecting CDI
+// devices into an OCI Spec.
+//
+// InjectDevices takes an OCI Spec and injects into it a set of
+// CDI devices given by qualified name. It returns the names of
+// any unresolved devices and an error if injection fails.
+type RegistryResolver interface {
+ InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
+}
+
+// RegistryDeviceDB is the registry interface for querying devices.
+//
+// GetDevice returns the CDI device for the given qualified name. If
+// the device is not GetDevice returns nil.
+//
+// ListDevices returns a slice with the names of qualified device
+// known. The returned slice is sorted.
+type RegistryDeviceDB interface {
+ GetDevice(device string) *Device
+ ListDevices() []string
+}
+
+// RegistrySpecDB is the registry interface for querying CDI Specs.
+//
+// ListVendors returns a slice with all vendors known. The returned
+// slice is sorted.
+//
+// ListClasses returns a slice with all classes known. The returned
+// slice is sorted.
+//
+// GetVendorSpecs returns a slice of all Specs for the vendor.
+//
+// GetSpecErrors returns any errors for the Spec encountered during
+// the last cache refresh.
+type RegistrySpecDB interface {
+ ListVendors() []string
+ ListClasses() []string
+ GetVendorSpecs(vendor string) []*Spec
+ GetSpecErrors(*Spec) []error
+}
+
+type registry struct {
+ *Cache
+}
+
+var _ Registry = &registry{}
+
+var (
+ reg *registry
+ initOnce sync.Once
+)
+
+// GetRegistry returns the CDI registry. If any options are given, those
+// are applied to the registry.
+func GetRegistry(options ...Option) Registry {
+ var new bool
+ initOnce.Do(func() {
+ reg, _ = getRegistry(options...)
+ new = true
+ })
+ if !new && len(options) > 0 {
+ reg.Configure(options...)
+ reg.Refresh()
+ }
+ return reg
+}
+
+// DeviceDB returns the registry interface for querying devices.
+func (r *registry) DeviceDB() RegistryDeviceDB {
+ return r
+}
+
+// SpecDB returns the registry interface for querying Specs.
+func (r *registry) SpecDB() RegistrySpecDB {
+ return r
+}
+
+func getRegistry(options ...Option) (*registry, error) {
+ c, err := NewCache(options...)
+ return &registry{c}, err
+}
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
new file mode 100644
index 000000000..ad017fec7
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
@@ -0,0 +1,110 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+)
+
+const (
+ // DefaultStaticDir is the default directory for static CDI Specs.
+ DefaultStaticDir = "/etc/cdi"
+ // DefaultDynamicDir is the default directory for generated CDI Specs
+ DefaultDynamicDir = "/var/run/cdi"
+)
+
+var (
+ // DefaultSpecDirs is the default Spec directory configuration.
+ // While altering this variable changes the package defaults,
+ // the preferred way of overriding the default directories is
+ // to use a WithSpecDirs options. Otherwise the change is only
+ // effective if it takes place before creating the Registry or
+ // other Cache instances.
+ DefaultSpecDirs = []string{DefaultStaticDir, DefaultDynamicDir}
+ // ErrStopScan can be returned from a ScanSpecFunc to stop the scan.
+ ErrStopScan = errors.New("stop Spec scan")
+)
+
+// 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))
+ for i, dir := range dirs {
+ c.specDirs[i] = filepath.Clean(dir)
+ }
+ return nil
+ }
+}
+
+// scanSpecFunc is a function for processing CDI Spec files.
+type scanSpecFunc func(string, int, *Spec, error) error
+
+// ScanSpecDirs scans the given directories looking for CDI Spec files,
+// which are all files with a '.json' or '.yaml' suffix. For every Spec
+// file discovered, ScanSpecDirs loads a Spec from the file then calls
+// the scan function passing it the path to the file, the priority (the
+// index of the directory in the slice of directories given), the Spec
+// itself, and any error encountered while loading the Spec.
+//
+// Scanning stops once all files have been processed or when the scan
+// function returns an error. The result of ScanSpecDirs is the error
+// returned by the scan function, if any. The special error ErrStopScan
+// can be used to terminate the scan gracefully without ScanSpecDirs
+// returning an error. ScanSpecDirs silently skips any subdirectories.
+func scanSpecDirs(dirs []string, scanFn scanSpecFunc) error {
+ var (
+ spec *Spec
+ err error
+ )
+
+ for priority, dir := range dirs {
+ 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 {
+ return err
+ }
+ // first call from Walk is for dir itself, others we skip
+ if info.IsDir() {
+ if path == dir {
+ return nil
+ }
+ return filepath.SkipDir
+ }
+
+ // ignore obviously non-Spec files
+ if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" {
+ return nil
+ }
+
+ if err != nil {
+ return scanFn(path, priority, nil, err)
+ }
+
+ spec, err = ReadSpec(path, priority)
+ return scanFn(path, priority, spec, err)
+ })
+
+ if err != nil && err != ErrStopScan {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
new file mode 100644
index 000000000..20f188498
--- /dev/null
+++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
@@ -0,0 +1,172 @@
+/*
+ Copyright © 2021 The CDI Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package cdi
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ oci "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "sigs.k8s.io/yaml"
+
+ cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
+)
+
+var (
+ // Valid CDI Spec versions.
+ validSpecVersions = map[string]struct{}{
+ "0.1.0": {},
+ "0.2.0": {},
+ }
+)
+
+// Spec represents a single CDI Spec. It is usually loaded from a
+// file and stored in a cache. The Spec has an associated priority.
+// This priority is inherited from the associated priority of the
+// CDI Spec directory that contains the CDI Spec file and is used
+// to resolve conflicts if multiple CDI Spec files contain entries
+// for the same fully qualified device.
+type Spec struct {
+ *cdi.Spec
+ vendor string
+ class string
+ path string
+ priority int
+ devices map[string]*Device
+}
+
+// ReadSpec reads the given CDI Spec file. The resulting Spec is
+// assigned the given priority. If reading or parsing the Spec
+// data fails ReadSpec returns a nil Spec and an error.
+func ReadSpec(path string, priority int) (*Spec, error) {
+ data, err := ioutil.ReadFile(path)
+ switch {
+ case os.IsNotExist(err):
+ return nil, err
+ case err != nil:
+ return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
+ }
+
+ raw, err := parseSpec(data)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
+ }
+
+ return NewSpec(raw, path, priority)
+}
+
+// NewSpec creates a new Spec from the given CDI Spec data. The
+// Spec is marked as loaded from the given path with the given
+// priority. If Spec data validation fails NewSpec returns a nil
+// Spec and an error.
+func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
+ var err error
+
+ spec := &Spec{
+ Spec: raw,
+ path: filepath.Clean(path),
+ priority: priority,
+ }
+
+ spec.vendor, spec.class = ParseQualifier(spec.Kind)
+
+ if spec.devices, err = spec.validate(); err != nil {
+ return nil, errors.Wrap(err, "invalid CDI Spec")
+ }
+
+ return spec, nil
+}
+
+// GetVendor returns the vendor of this Spec.
+func (s *Spec) GetVendor() string {
+ return s.vendor
+}
+
+// GetClass returns the device class of this Spec.
+func (s *Spec) GetClass() string {
+ return s.class
+}
+
+// GetDevice returns the device for the given unqualified name.
+func (s *Spec) GetDevice(name string) *Device {
+ return s.devices[name]
+}
+
+// GetPath returns the filesystem path of this Spec.
+func (s *Spec) GetPath() string {
+ return s.path
+}
+
+// GetPriority returns the priority of this Spec.
+func (s *Spec) GetPriority() int {
+ return s.priority
+}
+
+// ApplyEdits applies the Spec's global-scope container edits to an OCI Spec.
+func (s *Spec) ApplyEdits(ociSpec *oci.Spec) error {
+ e := ContainerEdits{&s.ContainerEdits}
+ return e.Apply(ociSpec)
+}
+
+// Validate the Spec.
+func (s *Spec) validate() (map[string]*Device, error) {
+ if _, ok := validSpecVersions[s.Version]; !ok {
+ return nil, errors.Errorf("invalid version %q", s.Version)
+ }
+ if err := ValidateVendorName(s.vendor); err != nil {
+ return nil, err
+ }
+ if err := ValidateClassName(s.class); err != nil {
+ return nil, err
+ }
+ edits := &ContainerEdits{&s.ContainerEdits}
+ if err := edits.Validate(); err != nil {
+ return nil, err
+ }
+
+ devices := make(map[string]*Device)
+ for _, d := range s.Devices {
+ dev, err := newDevice(s, d)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed add device %q", d.Name)
+ }
+ if _, conflict := devices[d.Name]; conflict {
+ return nil, errors.Errorf("invalid spec, multiple device %q", d.Name)
+ }
+ devices[d.Name] = dev
+ }
+
+ return devices, nil
+}
+
+// Parse raw CDI Spec file data.
+func parseSpec(data []byte) (*cdi.Spec, error) {
+ raw := &cdi.Spec{}
+ err := yaml.UnmarshalStrict(data, &raw)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal CDI Spec")
+ }
+ return raw, validateJSONSchema(raw)
+}
+
+// Validate CDI Spec against JSON Schema.
+func validateJSONSchema(raw *cdi.Spec) error {
+ // TODO
+ return nil
+}
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go
deleted file mode 100644
index e66fd36c0..000000000
--- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go
+++ /dev/null
@@ -1,180 +0,0 @@
-package pkg
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- cdispec "github.com/container-orchestrated-devices/container-device-interface/specs-go"
- spec "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-const (
- root = "/etc/cdi"
-)
-
-func collectCDISpecs() (map[string]*cdispec.Spec, error) {
- var files []string
- vendor := make(map[string]*cdispec.Spec)
-
- err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
- if info == nil || info.IsDir() {
- return nil
- }
-
- if filepath.Ext(path) != ".json" {
- return nil
- }
-
- files = append(files, path)
- return nil
- })
-
- if err != nil {
- return nil, err
- }
-
- for _, path := range files {
- spec, err := loadCDIFile(path)
- if err != nil {
- continue
- }
-
- if _, ok := vendor[spec.Kind]; ok {
- continue
- }
-
- vendor[spec.Kind] = spec
- }
-
- return vendor, nil
-}
-
-// TODO: Validate (e.g: duplicate device names)
-func loadCDIFile(path string) (*cdispec.Spec, error) {
- file, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, err
- }
-
- var spec *cdispec.Spec
- err = json.Unmarshal([]byte(file), &spec)
- if err != nil {
- return nil, err
- }
-
- return spec, nil
-}
-
-/*
-* Pattern "vendor.com/device=myDevice" with the vendor being optional
- */
-func extractVendor(dev string) (string, string) {
- if strings.IndexByte(dev, '=') == -1 {
- return "", dev
- }
-
- split := strings.SplitN(dev, "=", 2)
- return split[0], split[1]
-}
-
-// GetCDIForDevice returns the CDI specification that matches the device name the user provided.
-func GetCDIForDevice(dev string, specs map[string]*cdispec.Spec) (*cdispec.Spec, error) {
- vendor, device := extractVendor(dev)
-
- if vendor != "" {
- s, ok := specs[vendor]
- if !ok {
- return nil, fmt.Errorf("Could not find vendor %q for device %q", vendor, device)
- }
-
- for _, d := range s.Devices {
- if d.Name != device {
- continue
- }
-
- return s, nil
- }
-
- return nil, fmt.Errorf("Could not find device %q for vendor %q", device, vendor)
- }
-
- var found []*cdispec.Spec
- var vendors []string
- for vendor, spec := range specs {
-
- for _, d := range spec.Devices {
- if d.Name != device {
- continue
- }
-
- found = append(found, spec)
- vendors = append(vendors, vendor)
- }
- }
-
- if len(found) > 1 {
- return nil, fmt.Errorf("%q is ambiguous and currently refers to multiple devices from different vendors: %q", dev, vendors)
- }
-
- if len(found) == 1 {
- return found[0], nil
- }
-
- return nil, fmt.Errorf("Could not find device %q", dev)
-}
-
-// HasDevice returns true if a device is a CDI device
-// an error may be returned in cases where permissions may be required
-func HasDevice(dev string) (bool, error) {
- specs, err := collectCDISpecs()
- if err != nil {
- return false, err
- }
-
- d, err := GetCDIForDevice(dev, specs)
- if err != nil {
- return false, err
- }
-
- return d != nil, nil
-}
-
-// UpdateOCISpecForDevices updates the given OCI spec based on the requested CDI devices
-func UpdateOCISpecForDevices(ociconfig *spec.Spec, devs []string) error {
- specs, err := collectCDISpecs()
- if err != nil {
- return err
- }
-
- return UpdateOCISpecForDevicesWithSpec(ociconfig, devs, specs)
-}
-
-// UpdateOCISpecForDevicesWithLoggerAndSpecs is mainly used for testing
-func UpdateOCISpecForDevicesWithSpec(ociconfig *spec.Spec, devs []string, specs map[string]*cdispec.Spec) error {
- edits := make(map[string]*cdispec.Spec)
-
- for _, d := range devs {
- spec, err := GetCDIForDevice(d, specs)
- if err != nil {
- return err
- }
-
- edits[spec.Kind] = spec
- err = cdispec.ApplyOCIEditsForDevice(ociconfig, spec, d)
- if err != nil {
- return err
- }
- }
-
- for _, spec := range edits {
- if err := cdispec.ApplyOCIEdits(ociconfig, spec); err != nil {
- return err
- }
- }
-
- return nil
-}
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 0223bb703..20914e5b6 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
@@ -1,20 +1,20 @@
package specs
+import "os"
+
// Spec is the base configuration for CDI
type Spec struct {
Version string `json:"cdiVersion"`
Kind string `json:"kind"`
- KindShort []string `json:"kindShort,omitempty"`
ContainerRuntime []string `json:"containerRuntime,omitempty"`
- Devices []Devices `json:"devices"`
+ Devices []Device `json:"devices"`
ContainerEdits ContainerEdits `json:"containerEdits,omitempty"`
}
-// Devices is a "Device" a container runtime can add to a container
-type Devices struct {
+// Device is a "Device" a container runtime can add to a container
+type Device struct {
Name string `json:"name"`
- NameShort []string `json:"nameShort"`
ContainerEdits ContainerEdits `json:"containerEdits"`
}
@@ -28,9 +28,14 @@ type ContainerEdits struct {
// DeviceNode represents a device node that needs to be added to the OCI spec.
type DeviceNode struct {
- HostPath string `json:"hostPath"`
- ContainerPath string `json:"containerPath"`
- Permissions []string `json:"permissions,omitempty"`
+ Path string `json:"path"`
+ Type string `json:"type,omitempty"`
+ Major int64 `json:"major,omitempty"`
+ Minor int64 `json:"minor,omitempty"`
+ FileMode *os.FileMode `json:"fileMode,omitempty"`
+ Permissions string `json:"permissions,omitempty"`
+ UID *uint32 `json:"uid,omitempty"`
+ GID *uint32 `json:"gid,omitempty"`
}
// Mount represents a mount 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 c59cda55d..10bc9fa23 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
@@ -37,20 +37,21 @@ func ApplyEditsToOCISpec(config *spec.Spec, edits *ContainerEdits) error {
}
if len(edits.Env) > 0 {
-
if config.Process == nil {
config.Process = &spec.Process{}
}
-
config.Process.Env = append(config.Process.Env, edits.Env...)
}
for _, d := range edits.DeviceNodes {
- config.Mounts = append(config.Mounts, toOCIDevice(d))
+ if config.Linux == nil {
+ config.Linux = &spec.Linux{}
+ }
+ config.Linux.Devices = append(config.Linux.Devices, d.ToOCI())
}
for _, m := range edits.Mounts {
- config.Mounts = append(config.Mounts, toOCIMount(m))
+ config.Mounts = append(config.Mounts, m.ToOCI())
}
for _, h := range edits.Hooks {
@@ -59,17 +60,17 @@ func ApplyEditsToOCISpec(config *spec.Spec, edits *ContainerEdits) error {
}
switch h.HookName {
case "prestart":
- config.Hooks.Prestart = append(config.Hooks.Prestart, toOCIHook(h))
+ config.Hooks.Prestart = append(config.Hooks.Prestart, h.ToOCI())
case "createRuntime":
- config.Hooks.CreateRuntime = append(config.Hooks.CreateRuntime, toOCIHook(h))
+ config.Hooks.CreateRuntime = append(config.Hooks.CreateRuntime, h.ToOCI())
case "createContainer":
- config.Hooks.CreateContainer = append(config.Hooks.CreateContainer, toOCIHook(h))
+ config.Hooks.CreateContainer = append(config.Hooks.CreateContainer, h.ToOCI())
case "startContainer":
- config.Hooks.StartContainer = append(config.Hooks.StartContainer, toOCIHook(h))
+ config.Hooks.StartContainer = append(config.Hooks.StartContainer, h.ToOCI())
case "poststart":
- config.Hooks.Poststart = append(config.Hooks.Poststart, toOCIHook(h))
+ config.Hooks.Poststart = append(config.Hooks.Poststart, h.ToOCI())
case "poststop":
- config.Hooks.Poststop = append(config.Hooks.Poststop, toOCIHook(h))
+ config.Hooks.Poststop = append(config.Hooks.Poststop, h.ToOCI())
default:
fmt.Printf("CDI: Unknown hook %q\n", h.HookName)
}
@@ -78,7 +79,8 @@ func ApplyEditsToOCISpec(config *spec.Spec, edits *ContainerEdits) error {
return nil
}
-func toOCIHook(h *Hook) spec.Hook {
+// ToOCI returns the opencontainers runtime Spec Hook for this Hook.
+func (h *Hook) ToOCI() spec.Hook {
return spec.Hook{
Path: h.Path,
Args: h.Args,
@@ -87,7 +89,8 @@ func toOCIHook(h *Hook) spec.Hook {
}
}
-func toOCIMount(m *Mount) spec.Mount {
+// ToOCI returns the opencontainers runtime Spec Mount for this Mount.
+func (m *Mount) ToOCI() spec.Mount {
return spec.Mount{
Source: m.HostPath,
Destination: m.ContainerPath,
@@ -95,10 +98,15 @@ func toOCIMount(m *Mount) spec.Mount {
}
}
-func toOCIDevice(d *DeviceNode) spec.Mount {
- return spec.Mount{
- Source: d.HostPath,
- Destination: d.ContainerPath,
- Options: d.Permissions,
+// ToOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
+func (d *DeviceNode) ToOCI() spec.LinuxDevice {
+ return spec.LinuxDevice{
+ Path: d.Path,
+ Type: d.Type,
+ Major: d.Major,
+ Minor: d.Minor,
+ FileMode: d.FileMode,
+ UID: d.UID,
+ GID: d.GID,
}
}