Documentation ¶
Overview ¶
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'.
Index ¶
- Constants
- Variables
- func AnnotationKey(pluginName, deviceID string) (string, error)
- func AnnotationValue(devices []string) (string, error)
- func IsQualifiedName(device string) bool
- func ParseAnnotations(annotations map[string]string) ([]string, []string, error)
- func ParseDevice(device string) (string, string, string)
- func ParseQualifiedName(device string) (string, string, string, error)
- func ParseQualifier(kind string) (string, string)
- func QualifiedName(vendor, class, name string) string
- func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, ...) (map[string]string, error)
- func ValidateClassName(class string) error
- func ValidateDeviceName(name string) error
- func ValidateEnv(env []string) error
- func ValidateVendorName(vendor string) error
- type Cache
- func (c *Cache) Configure(options ...Option) error
- func (c *Cache) GetDevice(device string) *Device
- func (c *Cache) GetErrors() map[string][]error
- func (c *Cache) GetSpecDirectories() []string
- func (c *Cache) GetSpecErrors(spec *Spec) []error
- func (c *Cache) GetVendorSpecs(vendor string) []*Spec
- func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error)
- func (c *Cache) ListClasses() []string
- func (c *Cache) ListDevices() []string
- func (c *Cache) ListVendors() []string
- func (c *Cache) Refresh() error
- type ContainerEdits
- type Device
- type DeviceNode
- type Hook
- type Mount
- type Option
- type Registry
- type RegistryDeviceDB
- type RegistryRefresher
- type RegistryResolver
- type RegistrySpecDB
- type Spec
Constants ¶
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" )
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" )
const (
// AnnotationPrefix is the prefix for CDI container annotation keys.
AnnotationPrefix = "cdi.k8s.io/"
)
Variables ¶
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") )
Functions ¶
func AnnotationKey ¶
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 AnnotationValue ¶
AnnotationValue returns an annotation value for the given devices.
func IsQualifiedName ¶
IsQualifiedName tests if a device name is qualified.
func ParseAnnotations ¶
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 ParseDevice ¶
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 ParseQualifiedName ¶
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 ParseQualifier ¶
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 QualifiedName ¶
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 UpdateAnnotations ¶
func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error)
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 ValidateClassName ¶
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 ValidateDeviceName ¶
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 ValidateEnv ¶
ValidateEnv validates the given environment variables.
func ValidateVendorName ¶
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 '.')
Types ¶
type Cache ¶
Cache stores CDI Specs loaded from Spec directories.
func NewCache ¶
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 (*Cache) Configure ¶
Configure applies options to the cache. Updates the cache if options have changed.
func (*Cache) GetSpecDirectories ¶
GetSpecDirectories returns the CDI Spec directories currently in use.
func (*Cache) GetSpecErrors ¶
GetSpecErrors returns all errors encountered for the spec during the last cache refresh.
func (*Cache) GetVendorSpecs ¶
GetVendorSpecs returns all specs for the given vendor.
func (*Cache) InjectDevices ¶
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 (*Cache) ListClasses ¶
ListClasses lists all device classes known to the cache.
func (*Cache) ListDevices ¶
ListDevices lists all cached devices by qualified name.
func (*Cache) ListVendors ¶
ListVendors lists all vendors known to the cache.
type ContainerEdits ¶
type ContainerEdits struct {
*specs.ContainerEdits
}
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.
func (*ContainerEdits) Append ¶
func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits
Append other edits into this one. If called with a nil receiver, allocates and returns newly allocated edits.
func (*ContainerEdits) Apply ¶
func (e *ContainerEdits) Apply(spec *oci.Spec) error
Apply edits to the given OCI Spec. Updates the OCI Spec in place. Returns an error if the update fails.
func (*ContainerEdits) Validate ¶
func (e *ContainerEdits) Validate() error
Validate container edits.
type Device ¶
Device represents a CDI device of a Spec.
func (*Device) ApplyEdits ¶
ApplyEdits applies the device-speific container edits to an OCI Spec.
func (*Device) GetQualifiedName ¶
GetQualifiedName returns the qualified name for this device.
type DeviceNode ¶
type DeviceNode struct {
*specs.DeviceNode
}
DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
type Hook ¶
type Hook struct {
*specs.Hook
}
Hook is a CDI Spec Hook wrapper, used for validating hooks.
type Mount ¶
type Mount struct {
*specs.Mount
}
Mount is a CDI Mount wrapper, used for validating mounts.
type Option ¶
Option is an option to change some aspect of default CDI behavior.
func WithSpecDirs ¶
WithSpecDirs returns an option to override the CDI Spec directories.
type Registry ¶
type Registry interface { RegistryResolver RegistryRefresher DeviceDB() RegistryDeviceDB SpecDB() RegistrySpecDB }
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.
func GetRegistry ¶
GetRegistry returns the CDI registry. If any options are given, those are applied to the registry.
type RegistryDeviceDB ¶
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 RegistryRefresher ¶
type RegistryRefresher interface { Refresh() error GetErrors() map[string][]error GetSpecDirectories() []string }
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 RegistryResolver ¶
type RegistryResolver interface {
InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
}
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 RegistrySpecDB ¶
type RegistrySpecDB interface { ListVendors() []string ListClasses() []string GetVendorSpecs(vendor string) []*Spec GetSpecErrors(*Spec) []error }
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 Spec ¶
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.
func NewSpec ¶
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 ReadSpec ¶
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 (*Spec) ApplyEdits ¶
ApplyEdits applies the Spec's global-scope container edits to an OCI Spec.
func (*Spec) GetPriority ¶
GetPriority returns the priority of this Spec.