Skip to content

Add Aliases to Device definition #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ $ cat > /etc/cdi/vendor-annotations.json <<EOF
"devices": [
{
"name": "myDevice",
"aliases": ["device0"],
"annotations": {
"whatever": "false"
"whenever": "true"
Expand Down
6 changes: 6 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ The key words "must", "must not", "required", "shall", "shall not", "should", "s
{
"name": "<name>",

// This field contains a list of alternative names for the device.
// These names must be unique for a given kind.
"aliases": [ (optional)

],

// This field contains a set of key-value pairs that may be used to provide
// additional information to a consumer on the specific device.
"annotations": { (optional)
Expand Down
13 changes: 7 additions & 6 deletions pkg/cdi/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,15 @@ func (c *Cache) refresh() error {
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
for _, qualified := range dev.GetQualifiedNames() {
other, ok := devices[qualified]
if ok {
if resolveConflict(qualified, dev, other) {
continue
}
}
devices[qualified] = dev
}
devices[qualified] = dev
}

return nil
Expand Down
83 changes: 83 additions & 0 deletions pkg/cdi/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,89 @@ devices:
type: b
major: 10
minor: 1
`,
},
},
ociSpec: &oci.Spec{
Process: &oci.Process{
Env: []string{
"ORIG_VAR1=VAL1",
"ORIG_VAR2=VAL2",
},
},
Linux: &oci.Linux{
Devices: []oci.LinuxDevice{
{
Path: "/dev/null",
},
{
Path: "/dev/zero",
},
},
},
},
devices: []string{
"vendor1.com/device=dev1",
},
result: &oci.Spec{
Process: &oci.Process{
Env: []string{
"ORIG_VAR1=VAL1",
"ORIG_VAR2=VAL2",
"VENDOR1_SPEC_VAR1=VAL1",
"VENDOR1_VAR1=VAL1",
},
},
Linux: &oci.Linux{
Devices: []oci.LinuxDevice{
{
Path: "/dev/null",
},
{
Path: "/dev/zero",
},
{
Path: "/dev/vendor1-dev1",
Type: "b",
Major: 10,
Minor: 1,
},
},
Resources: &oci.LinuxResources{
Devices: []oci.LinuxDeviceCgroup{
{
Allow: true,
Type: "b",
Major: int64ptr(10),
Minor: int64ptr(1),
Access: "rwm",
},
},
},
},
},
},
{
name: "non-empty OCI Spec, inject one device by alias",
cdiSpecs: specDirs{
etc: map[string]string{
"vendor1.yaml": `
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
containerEdits:
env:
- VENDOR1_SPEC_VAR1=VAL1
devices:
- name: "notdev1"
aliases: ["dev1"]
containerEdits:
env:
- "VENDOR1_VAR1=VAL1"
deviceNodes:
- path: "/dev/vendor1-dev1"
type: b
major: 10
minor: 1
`,
},
},
Expand Down
22 changes: 20 additions & 2 deletions pkg/cdi/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,27 @@ func (d *Device) GetSpec() *Spec {
return d.spec
}

// GetNames returns the possible names for the device including aliases
func (d *Device) GetNames() []string {
names := []string{d.Name}
return append(names, d.Aliases...)
}

// GetQualifiedName returns the qualified name for this device.
func (d *Device) GetQualifiedName() string {
return QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), d.Name)
}

// GetQualifiedNames returns the qualified names for this device and its aliases.
func (d *Device) GetQualifiedNames() []string {
var qualifiedNames []string
for _, name := range d.GetNames() {
qualified := QualifiedName(d.spec.GetVendor(), d.spec.GetClass(), name)
qualifiedNames = append(qualifiedNames, qualified)
}
return qualifiedNames
}

// ApplyEdits applies the device-speific container edits to an OCI Spec.
func (d *Device) ApplyEdits(ociSpec *oci.Spec) error {
return d.edits().Apply(ociSpec)
Expand All @@ -66,8 +82,10 @@ func (d *Device) edits() *ContainerEdits {

// Validate the device.
func (d *Device) validate() error {
if err := ValidateDeviceName(d.Name); err != nil {
return err
for _, name := range d.GetNames() {
if err := ValidateDeviceName(name); err != nil {
return err
}
}
name := d.Name
if d.spec != nil {
Expand Down
12 changes: 9 additions & 3 deletions pkg/cdi/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,16 @@ func (s *Spec) validate() (map[string]*Device, error) {
if err != nil {
return nil, fmt.Errorf("failed add device %q: %w", d.Name, err)
}
if _, conflict := devices[d.Name]; conflict {
return nil, fmt.Errorf("invalid spec, multiple device %q", d.Name)
for _, name := range dev.GetNames() {
if _, conflict := devices[name]; conflict {
var additional string
if name != d.Name {
additional = fmt.Sprintf(" (alias of %q)", d.Name)
}
return nil, fmt.Errorf("invalid spec, multiple device name: %q%s", name, additional)
}
devices[name] = dev
}
devices[d.Name] = dev
}

return devices, nil
Expand Down
17 changes: 17 additions & 0 deletions pkg/cdi/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,23 @@ func TestRequiredVersion(t *testing.T) {
},
expectedVersion: "0.6.0",
},
{
description: "device aliases require v0.6.0",
spec: &cdi.Spec{
Devices: []cdi.Device{
{
Name: "device0",
Aliases: []string{"0", "zero"},
ContainerEdits: cdi.ContainerEdits{
Env: []string{
"FOO=bar",
},
},
},
},
},
expectedVersion: "0.6.0",
},
}

for _, tc := range testCases {
Expand Down
6 changes: 5 additions & 1 deletion pkg/cdi/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,12 @@ func requiresV060(spec *cdi.Spec) bool {
return true
}

// The v0.6.0 spec allows annotations to be specified at a device level
for _, d := range spec.Devices {
// The v0.6.0 spec allows aliases for device names
for range d.Aliases {
return true
}
// The v0.6.0 spec allows annotations to be specified at a device level
for range d.Annotations {
return true
}
Expand Down
7 changes: 7 additions & 0 deletions schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
"description": "The name of the device",
"type": "string"
},
"aliases": {
"description": "Alternative names for the device",
"type": "array",
"items": {
"type": "string"
}
},
"annotations": {
"$ref": "defs.json#/definitions/annotations"
},
Expand Down
2 changes: 2 additions & 0 deletions specs-go/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type Spec struct {
// Device is a "Device" a container runtime can add to a container
type Device struct {
Name string `json:"name"`
// Aliases provide alternative names for a device. The fully-qualified names for the devices must be globally unique.
Aliases []string `json:"aliases,omitempty"`
// Annotations add meta information per device. Note these are CDI-specific and do not affect container metadata.
Annotations map[string]string `json:"annotations,omitempty"`
ContainerEdits ContainerEdits `json:"containerEdits"`
Expand Down