diff options
author | Sebastian Jug <seb@stianj.ug> | 2021-03-29 20:21:00 -0400 |
---|---|---|
committer | Sebastian Jug <seb@stianj.ug> | 2021-04-20 09:18:52 -0400 |
commit | db7cff8c86a35a4b1971c3fbb2365eff9cc205d4 (patch) | |
tree | 3dc20b52f6a8b2de4acf8cb99ebff81776680959 /vendor/github.com/container-orchestrated-devices/container-device-interface/pkg | |
parent | cf2c3a1f13710014892804aacf855cd6b001a5ea (diff) | |
download | podman-db7cff8c86a35a4b1971c3fbb2365eff9cc205d4.tar.gz podman-db7cff8c86a35a4b1971c3fbb2365eff9cc205d4.tar.bz2 podman-db7cff8c86a35a4b1971c3fbb2365eff9cc205d4.zip |
Add support for CDI device configuration
- Persist CDIDevices in container config
- Add e2e test
- Log HasDevice error and add additional condition for safety
Signed-off-by: Sebastian Jug <seb@stianj.ug>
Diffstat (limited to 'vendor/github.com/container-orchestrated-devices/container-device-interface/pkg')
-rw-r--r-- | vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go new file mode 100644 index 000000000..e66fd36c0 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/devices.go @@ -0,0 +1,180 @@ +package pkg + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + cdispec "github.com/container-orchestrated-devices/container-device-interface/specs-go" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + root = "/etc/cdi" +) + +func collectCDISpecs() (map[string]*cdispec.Spec, error) { + var files []string + vendor := make(map[string]*cdispec.Spec) + + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if info == nil || info.IsDir() { + return nil + } + + if filepath.Ext(path) != ".json" { + return nil + } + + files = append(files, path) + return nil + }) + + if err != nil { + return nil, err + } + + for _, path := range files { + spec, err := loadCDIFile(path) + if err != nil { + continue + } + + if _, ok := vendor[spec.Kind]; ok { + continue + } + + vendor[spec.Kind] = spec + } + + return vendor, nil +} + +// TODO: Validate (e.g: duplicate device names) +func loadCDIFile(path string) (*cdispec.Spec, error) { + file, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + var spec *cdispec.Spec + err = json.Unmarshal([]byte(file), &spec) + if err != nil { + return nil, err + } + + return spec, nil +} + +/* +* Pattern "vendor.com/device=myDevice" with the vendor being optional + */ +func extractVendor(dev string) (string, string) { + if strings.IndexByte(dev, '=') == -1 { + return "", dev + } + + split := strings.SplitN(dev, "=", 2) + return split[0], split[1] +} + +// GetCDIForDevice returns the CDI specification that matches the device name the user provided. +func GetCDIForDevice(dev string, specs map[string]*cdispec.Spec) (*cdispec.Spec, error) { + vendor, device := extractVendor(dev) + + if vendor != "" { + s, ok := specs[vendor] + if !ok { + return nil, fmt.Errorf("Could not find vendor %q for device %q", vendor, device) + } + + for _, d := range s.Devices { + if d.Name != device { + continue + } + + return s, nil + } + + return nil, fmt.Errorf("Could not find device %q for vendor %q", device, vendor) + } + + var found []*cdispec.Spec + var vendors []string + for vendor, spec := range specs { + + for _, d := range spec.Devices { + if d.Name != device { + continue + } + + found = append(found, spec) + vendors = append(vendors, vendor) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("%q is ambiguous and currently refers to multiple devices from different vendors: %q", dev, vendors) + } + + if len(found) == 1 { + return found[0], nil + } + + return nil, fmt.Errorf("Could not find device %q", dev) +} + +// HasDevice returns true if a device is a CDI device +// an error may be returned in cases where permissions may be required +func HasDevice(dev string) (bool, error) { + specs, err := collectCDISpecs() + if err != nil { + return false, err + } + + d, err := GetCDIForDevice(dev, specs) + if err != nil { + return false, err + } + + return d != nil, nil +} + +// UpdateOCISpecForDevices updates the given OCI spec based on the requested CDI devices +func UpdateOCISpecForDevices(ociconfig *spec.Spec, devs []string) error { + specs, err := collectCDISpecs() + if err != nil { + return err + } + + return UpdateOCISpecForDevicesWithSpec(ociconfig, devs, specs) +} + +// UpdateOCISpecForDevicesWithLoggerAndSpecs is mainly used for testing +func UpdateOCISpecForDevicesWithSpec(ociconfig *spec.Spec, devs []string, specs map[string]*cdispec.Spec) error { + edits := make(map[string]*cdispec.Spec) + + for _, d := range devs { + spec, err := GetCDIForDevice(d, specs) + if err != nil { + return err + } + + edits[spec.Kind] = spec + err = cdispec.ApplyOCIEditsForDevice(ociconfig, spec, d) + if err != nil { + return err + } + } + + for _, spec := range edits { + if err := cdispec.ApplyOCIEdits(ociconfig, spec); err != nil { + return err + } + } + + return nil +} |