CDI: update go.mod and vendor deps
Signed-off-by: Ed Bartosh <eduard.bartosh@intel.com>
This commit is contained in:
		
							
								
								
									
										139
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
/*
 | 
			
		||||
   Copyright © 2021-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 (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// AnnotationPrefix is the prefix for CDI container annotation keys.
 | 
			
		||||
	AnnotationPrefix = "cdi.k8s.io/"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// UpdateAnnotations updates annotations with a plugin-specific CDI device
 | 
			
		||||
// injection request for the given devices. Upon any error a non-nil error
 | 
			
		||||
// is returned and annotations are left intact. By convention plugin should
 | 
			
		||||
// be in the format of "vendor.device-type".
 | 
			
		||||
func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error) {
 | 
			
		||||
	key, err := AnnotationKey(plugin, deviceID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return annotations, errors.Wrap(err, "CDI annotation failed")
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := annotations[key]; ok {
 | 
			
		||||
		return annotations, errors.Errorf("CDI annotation failed, key %q used", key)
 | 
			
		||||
	}
 | 
			
		||||
	value, err := AnnotationValue(devices)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return annotations, errors.Wrap(err, "CDI annotation failed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if annotations == nil {
 | 
			
		||||
		annotations = make(map[string]string)
 | 
			
		||||
	}
 | 
			
		||||
	annotations[key] = value
 | 
			
		||||
 | 
			
		||||
	return annotations, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseAnnotations parses annotations for CDI device injection requests.
 | 
			
		||||
// The keys and devices from all such requests are collected into slices
 | 
			
		||||
// which are returned as the result. All devices are expected to be fully
 | 
			
		||||
// qualified CDI device names. If any device fails this check empty slices
 | 
			
		||||
// are returned along with a non-nil error. The annotations are expected
 | 
			
		||||
// to be formatted by, or in a compatible fashion to UpdateAnnotations().
 | 
			
		||||
func ParseAnnotations(annotations map[string]string) ([]string, []string, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		keys    []string
 | 
			
		||||
		devices []string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for key, value := range annotations {
 | 
			
		||||
		if !strings.HasPrefix(key, AnnotationPrefix) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, d := range strings.Split(value, ",") {
 | 
			
		||||
			if !IsQualifiedName(d) {
 | 
			
		||||
				return nil, nil, errors.Errorf("invalid CDI device name %q", d)
 | 
			
		||||
			}
 | 
			
		||||
			devices = append(devices, d)
 | 
			
		||||
		}
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return keys, devices, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AnnotationKey returns a unique annotation key for an device allocation
 | 
			
		||||
// by a K8s device plugin. pluginName should be in the format of
 | 
			
		||||
// "vendor.device-type". deviceID is the ID of the device the plugin is
 | 
			
		||||
// allocating. It is used to make sure that the generated key is unique
 | 
			
		||||
// even if multiple allocations by a single plugin needs to be annotated.
 | 
			
		||||
func AnnotationKey(pluginName, deviceID string) (string, error) {
 | 
			
		||||
	const maxNameLen = 63
 | 
			
		||||
 | 
			
		||||
	if pluginName == "" {
 | 
			
		||||
		return "", errors.New("invalid plugin name, empty")
 | 
			
		||||
	}
 | 
			
		||||
	if deviceID == "" {
 | 
			
		||||
		return "", errors.New("invalid deviceID, empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_")
 | 
			
		||||
 | 
			
		||||
	if len(name) > maxNameLen {
 | 
			
		||||
		return "", errors.Errorf("invalid plugin+deviceID %q, too long", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c := rune(name[0]); !isAlphaNumeric(c) {
 | 
			
		||||
		return "", errors.Errorf("invalid name %q, first '%c' should be alphanumeric",
 | 
			
		||||
			name, c)
 | 
			
		||||
	}
 | 
			
		||||
	if len(name) > 2 {
 | 
			
		||||
		for _, c := range name[1 : len(name)-1] {
 | 
			
		||||
			switch {
 | 
			
		||||
			case isAlphaNumeric(c):
 | 
			
		||||
			case c == '_' || c == '-' || c == '.':
 | 
			
		||||
			default:
 | 
			
		||||
				return "", errors.Errorf("invalid name %q, invalid charcter '%c'",
 | 
			
		||||
					name, c)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if c := rune(name[len(name)-1]); !isAlphaNumeric(c) {
 | 
			
		||||
		return "", errors.Errorf("invalid name %q, last '%c' should be alphanumeric",
 | 
			
		||||
			name, c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return AnnotationPrefix + name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AnnotationValue returns an annotation value for the given devices.
 | 
			
		||||
func AnnotationValue(devices []string) (string, error) {
 | 
			
		||||
	value, sep := "", ""
 | 
			
		||||
	for _, d := range devices {
 | 
			
		||||
		if _, _, _, err := ParseQualifiedName(d); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		value += sep + d
 | 
			
		||||
		sep = ","
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										279
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,279 @@
 | 
			
		||||
/*
 | 
			
		||||
   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
 | 
			
		||||
 | 
			
		||||
	if ociSpec == nil {
 | 
			
		||||
		return devices, errors.Errorf("can't inject devices, nil OCI Spec")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
 | 
			
		||||
	edits := &ContainerEdits{}
 | 
			
		||||
	specs := map[*Spec]struct{}{}
 | 
			
		||||
 | 
			
		||||
	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{}{}
 | 
			
		||||
			edits.Append(d.GetSpec().edits())
 | 
			
		||||
		}
 | 
			
		||||
		edits.Append(d.edits())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if unresolved != nil {
 | 
			
		||||
		return unresolved, errors.Errorf("unresolvable CDI devices %s",
 | 
			
		||||
			strings.Join(devices, ", "))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := edits.Apply(ociSpec); err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to inject 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										357
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,357 @@
 | 
			
		||||
/*
 | 
			
		||||
   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"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"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"
 | 
			
		||||
 | 
			
		||||
	runc "github.com/opencontainers/runc/libcontainer/devices"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
		dev := d.ToOCI()
 | 
			
		||||
		if err := fillMissingInfo(&dev); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if dev.UID == nil && spec.Process != nil {
 | 
			
		||||
			if uid := spec.Process.User.UID; uid > 0 {
 | 
			
		||||
				dev.UID = &uid
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if dev.GID == nil && spec.Process != nil {
 | 
			
		||||
			if gid := spec.Process.User.GID; gid > 0 {
 | 
			
		||||
				dev.GID = &gid
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		specgen.RemoveDevice(dev.Path)
 | 
			
		||||
		specgen.AddDevice(dev)
 | 
			
		||||
 | 
			
		||||
		if dev.Type == "b" || dev.Type == "c" {
 | 
			
		||||
			access := d.Permissions
 | 
			
		||||
			if access == "" {
 | 
			
		||||
				access = "rwm"
 | 
			
		||||
			}
 | 
			
		||||
			specgen.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, access)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(e.Mounts) > 0 {
 | 
			
		||||
		for _, m := range e.Mounts {
 | 
			
		||||
			specgen.RemoveMount(m.ContainerPath)
 | 
			
		||||
			specgen.AddMount(m.ToOCI())
 | 
			
		||||
		}
 | 
			
		||||
		sortMounts(&specgen)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append other edits into this one. If called with a nil receiver,
 | 
			
		||||
// allocates and returns newly allocated edits.
 | 
			
		||||
func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits {
 | 
			
		||||
	if o == nil || o.ContainerEdits == nil {
 | 
			
		||||
		return e
 | 
			
		||||
	}
 | 
			
		||||
	if e == nil {
 | 
			
		||||
		e = &ContainerEdits{}
 | 
			
		||||
	}
 | 
			
		||||
	if e.ContainerEdits == nil {
 | 
			
		||||
		e.ContainerEdits = &specs.ContainerEdits{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	e.Env = append(e.Env, o.Env...)
 | 
			
		||||
	e.DeviceNodes = append(e.DeviceNodes, o.DeviceNodes...)
 | 
			
		||||
	e.Hooks = append(e.Hooks, o.Hooks...)
 | 
			
		||||
	e.Mounts = append(e.Mounts, o.Mounts...)
 | 
			
		||||
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	validTypes := map[string]struct{}{
 | 
			
		||||
		"":  {},
 | 
			
		||||
		"b": {},
 | 
			
		||||
		"c": {},
 | 
			
		||||
		"u": {},
 | 
			
		||||
		"p": {},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.Path == "" {
 | 
			
		||||
		return errors.New("invalid (empty) device path")
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := validTypes[d.Type]; !ok {
 | 
			
		||||
		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{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fillMissingInfo fills in missing mandatory attributes from the host device.
 | 
			
		||||
func fillMissingInfo(dev *oci.LinuxDevice) error {
 | 
			
		||||
	if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	hostDev, err := runc.DeviceFromPath(dev.Path, "rwm")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dev.Type == "" {
 | 
			
		||||
		dev.Type = string(hostDev.Type)
 | 
			
		||||
	} else {
 | 
			
		||||
		if dev.Type != string(hostDev.Type) {
 | 
			
		||||
			return errors.Errorf("CDI device %q, host type mismatch (%s, %s)",
 | 
			
		||||
				dev.Path, dev.Type, string(hostDev.Type))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if dev.Major == 0 && dev.Type != "p" {
 | 
			
		||||
		dev.Major = hostDev.Major
 | 
			
		||||
		dev.Minor = hostDev.Minor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sortMounts sorts the mounts in the given OCI Spec.
 | 
			
		||||
func sortMounts(specgen *ocigen.Generator) {
 | 
			
		||||
	mounts := specgen.Mounts()
 | 
			
		||||
	specgen.ClearMounts()
 | 
			
		||||
	sort.Sort(orderedMounts(mounts))
 | 
			
		||||
	specgen.Config.Mounts = mounts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// orderedMounts defines how to sort an OCI Spec Mount slice.
 | 
			
		||||
// This is the almost the same implementation sa used by CRI-O and Docker,
 | 
			
		||||
// with a minor tweak for stable sorting order (easier to test):
 | 
			
		||||
//   https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
 | 
			
		||||
type orderedMounts []oci.Mount
 | 
			
		||||
 | 
			
		||||
// Len returns the number of mounts. Used in sorting.
 | 
			
		||||
func (m orderedMounts) Len() int {
 | 
			
		||||
	return len(m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Less returns true if the number of parts (a/b/c would be 3 parts) in the
 | 
			
		||||
// mount indexed by parameter 1 is less than that of the mount indexed by
 | 
			
		||||
// parameter 2. Used in sorting.
 | 
			
		||||
func (m orderedMounts) Less(i, j int) bool {
 | 
			
		||||
	ip, jp := m.parts(i), m.parts(j)
 | 
			
		||||
	if ip < jp {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if jp < ip {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return m[i].Destination < m[j].Destination
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap swaps two items in an array of mounts. Used in sorting
 | 
			
		||||
func (m orderedMounts) Swap(i, j int) {
 | 
			
		||||
	m[i], m[j] = m[j], m[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parts returns the number of parts in the destination of a mount. Used in sorting.
 | 
			
		||||
func (m orderedMounts) parts(i int) int {
 | 
			
		||||
	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
/*
 | 
			
		||||
   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 {
 | 
			
		||||
	return d.edits().Apply(ociSpec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// edits returns the applicable container edits for this spec.
 | 
			
		||||
func (d *Device) edits() *ContainerEdits {
 | 
			
		||||
	return &ContainerEdits{&d.ContainerEdits}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate the device.
 | 
			
		||||
func (d *Device) validate() error {
 | 
			
		||||
	if err := ValidateDeviceName(d.Name); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	edits := d.edits()
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
// 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'.
 | 
			
		||||
//
 | 
			
		||||
// CDI Spec Validation
 | 
			
		||||
//
 | 
			
		||||
// This package performs both syntactic and semantic validation of CDI
 | 
			
		||||
// Spec file data when a Spec file is loaded via the registry or using
 | 
			
		||||
// the ReadSpec API function. As part of the semantic verification, the
 | 
			
		||||
// Spec file is verified against the CDI Spec JSON validation schema.
 | 
			
		||||
//
 | 
			
		||||
// If a valid externally provided JSON validation schema is found in
 | 
			
		||||
// the filesystem at /etc/cdi/schema/schema.json it is loaded and used
 | 
			
		||||
// as the default validation schema. If such a file is not found or
 | 
			
		||||
// fails to load, an embedded no-op schema is used.
 | 
			
		||||
//
 | 
			
		||||
// The used validation schema can also be changed programmatically using
 | 
			
		||||
// the SetSchema API convenience function. This function also accepts
 | 
			
		||||
// the special "builtin" (BuiltinSchemaName) and "none" (NoneSchemaName)
 | 
			
		||||
// schema names which switch the used schema to the in-repo validation
 | 
			
		||||
// schema embedded into the binary or the now default no-op schema
 | 
			
		||||
// correspondingly. Other names are interpreted as the path to the actual
 | 
			
		||||
/// validation schema to load and use.
 | 
			
		||||
package cdi
 | 
			
		||||
							
								
								
									
										203
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										139
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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 = ®istry{}
 | 
			
		||||
 | 
			
		||||
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 ®istry{c}, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/schema.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/schema.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
   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 (
 | 
			
		||||
	"github.com/container-orchestrated-devices/container-device-interface/schema"
 | 
			
		||||
	cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DefaultExternalSchema is the JSON schema to load if found.
 | 
			
		||||
	DefaultExternalSchema = "/etc/cdi/schema/schema.json"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SetSchema sets the Spec JSON validation schema to use.
 | 
			
		||||
func SetSchema(name string) error {
 | 
			
		||||
	s, err := schema.Load(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	schema.Set(s)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate CDI Spec against JSON Schema.
 | 
			
		||||
func validateWithSchema(raw *cdi.Spec) error {
 | 
			
		||||
	return schema.ValidateType(raw)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	SetSchema(DefaultExternalSchema)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec-dirs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										190
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
/*
 | 
			
		||||
   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": {},
 | 
			
		||||
		"0.3.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)
 | 
			
		||||
	}
 | 
			
		||||
	if raw == nil {
 | 
			
		||||
		return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spec, err := NewSpec(raw, path, priority)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return spec, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	err := validateWithSchema(raw)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
	return s.edits().Apply(ociSpec)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// edits returns the applicable global container edits for this spec.
 | 
			
		||||
func (s *Spec) edits() *ContainerEdits {
 | 
			
		||||
	return &ContainerEdits{&s.ContainerEdits}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate the Spec.
 | 
			
		||||
func (s *Spec) validate() (map[string]*Device, error) {
 | 
			
		||||
	if err := validateVersion(s.Version); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := ValidateVendorName(s.vendor); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := ValidateClassName(s.class); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validateVersion checks whether the specified spec version is supported.
 | 
			
		||||
func validateVersion(version string) error {
 | 
			
		||||
	if _, ok := validSpecVersions[version]; !ok {
 | 
			
		||||
		return errors.Errorf("invalid version %q", version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse raw CDI Spec file data.
 | 
			
		||||
func parseSpec(data []byte) (*cdi.Spec, error) {
 | 
			
		||||
	var raw *cdi.Spec
 | 
			
		||||
	err := yaml.UnmarshalStrict(data, &raw)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to unmarshal CDI Spec")
 | 
			
		||||
	}
 | 
			
		||||
	return raw, nil
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user