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
	 Ed Bartosh
					Ed Bartosh