diff --git a/pkg/cdi/container-edits_test.go b/pkg/cdi/container-edits_test.go index e279e07..7fb51a9 100644 --- a/pkg/cdi/container-edits_test.go +++ b/pkg/cdi/container-edits_test.go @@ -17,7 +17,6 @@ package cdi import ( - "os" "testing" cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" @@ -28,7 +27,6 @@ import ( func TestValidateContainerEdits(t *testing.T) { type testCase struct { name string - spec *oci.Spec edits *cdi.ContainerEdits invalid bool } @@ -624,8 +622,3 @@ func TestAppend(t *testing.T) { }) } } - -func fileMode(mode int) *os.FileMode { - fm := os.FileMode(mode) - return &fm -} diff --git a/specs-go/oci.go b/specs-go/oci.go index d709ecb..229ad52 100644 --- a/specs-go/oci.go +++ b/specs-go/oci.go @@ -1,84 +1,9 @@ package specs import ( - "errors" - "fmt" - spec "github.com/opencontainers/runtime-spec/specs-go" ) -// ApplyOCIEditsForDevice applies devices OCI edits, in other words -// it finds the device in the CDI spec and applies the OCI patches that device -// requires to the OCI specification. -func ApplyOCIEditsForDevice(config *spec.Spec, cdi *Spec, dev string) error { - for _, d := range cdi.Devices { - if d.Name != dev { - continue - } - - return ApplyEditsToOCISpec(config, &d.ContainerEdits) - } - - return fmt.Errorf("CDI: device %q not found for spec %q", dev, cdi.Kind) -} - -// ApplyOCIEdits applies the OCI edits the CDI spec declares globally -func ApplyOCIEdits(config *spec.Spec, cdi *Spec) error { - return ApplyEditsToOCISpec(config, &cdi.ContainerEdits) -} - -// ApplyEditsToOCISpec applies the specified edits to the OCI spec. -func ApplyEditsToOCISpec(config *spec.Spec, edits *ContainerEdits) error { - if config == nil { - return errors.New("spec is nil") - } - if edits == nil { - return nil - } - - if len(edits.Env) > 0 { - if config.Process == nil { - config.Process = &spec.Process{} - } - config.Process.Env = append(config.Process.Env, edits.Env...) - } - - for _, d := range edits.DeviceNodes { - if config.Linux == nil { - config.Linux = &spec.Linux{} - } - config.Linux.Devices = append(config.Linux.Devices, d.ToOCI()) - } - - for _, m := range edits.Mounts { - config.Mounts = append(config.Mounts, m.ToOCI()) - } - - for _, h := range edits.Hooks { - if config.Hooks == nil { - config.Hooks = &spec.Hooks{} - } - switch h.HookName { - case "prestart": - config.Hooks.Prestart = append(config.Hooks.Prestart, h.ToOCI()) - case "createRuntime": - config.Hooks.CreateRuntime = append(config.Hooks.CreateRuntime, h.ToOCI()) - case "createContainer": - config.Hooks.CreateContainer = append(config.Hooks.CreateContainer, h.ToOCI()) - case "startContainer": - config.Hooks.StartContainer = append(config.Hooks.StartContainer, h.ToOCI()) - case "poststart": - config.Hooks.Poststart = append(config.Hooks.Poststart, h.ToOCI()) - case "poststop": - config.Hooks.Poststop = append(config.Hooks.Poststop, h.ToOCI()) - default: - fmt.Printf("CDI: Unknown hook %q\n", h.HookName) - } - } - - return nil -} - // ToOCI returns the opencontainers runtime Spec Hook for this Hook. func (h *Hook) ToOCI() spec.Hook { return spec.Hook{ diff --git a/specs-go/oci_test.go b/specs-go/oci_test.go deleted file mode 100644 index e142c48..0000000 --- a/specs-go/oci_test.go +++ /dev/null @@ -1,428 +0,0 @@ -package specs - -import ( - "testing" - - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/stretchr/testify/require" -) - -func TestApplyEditsToOCISpec(t *testing.T) { - testCases := []struct { - name string - config *spec.Spec - edits *ContainerEdits - expectedResult spec.Spec - expectedError bool - }{ - { - name: "nil spec", - expectedError: true, - }, - { - name: "nil edits", - config: &spec.Spec{}, - edits: nil, - }, - { - name: "add env to the empty spec", - config: &spec.Spec{}, - edits: &ContainerEdits{ - Env: []string{"BAR=BARVALUE1"}, - }, - expectedResult: spec.Spec{ - Process: &spec.Process{ - Env: []string{"BAR=BARVALUE1"}, - }, - }, - }, - { - name: "add device nodes to the empty spec", - config: &spec.Spec{}, - edits: &ContainerEdits{ - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/vendorctl", - }, - }, - }, - expectedResult: spec.Spec{ - Linux: &spec.Linux{ - Devices: []spec.LinuxDevice{ - {Path: "/dev/vendorctl"}, - }, - }, - }, - }, - { - name: "add mounts to the empty spec", - config: &spec.Spec{}, - edits: &ContainerEdits{ - Mounts: []*Mount{ - { - HostPath: "/dev/vendorctl", - ContainerPath: "/dev/vendorctl", - }, - }, - }, - expectedResult: spec.Spec{ - Mounts: []spec.Mount{ - { - Source: "/dev/vendorctl", - Destination: "/dev/vendorctl", - }, - }, - }, - }, - { - name: "add hooks to the empty spec", - config: &spec.Spec{}, - edits: &ContainerEdits{ - Hooks: []*Hook{ - { - HookName: "prestart", - Path: "/usr/local/bin/prestart-vendor-hook", - Args: []string{"--verbose"}, - Env: []string{"VENDOR_ENV1=value1"}, - }, - { - HookName: "createRuntime", - Path: "/usr/local/bin/cr-vendor-hook", - Args: []string{"--debug"}, - Env: []string{"VENDOR_ENV2=value2"}, - }, - { - HookName: "createContainer", - Path: "/usr/local/bin/cc-vendor-hook", - Args: []string{"--create"}, - Env: []string{"VENDOR_ENV3=value3"}, - }, - { - HookName: "startContainer", - Path: "/usr/local/bin/sc-vendor-hook", - Args: []string{"--start"}, - Env: []string{"VENDOR_ENV4=value4"}, - }, - { - HookName: "poststart", - Path: "/usr/local/bin/poststart-vendor-hook", - Env: []string{"VENDOR_ENV5=value5"}, - }, - { - HookName: "poststop", - Path: "/usr/local/bin/poststop-vendor-hook", - }, - }, - }, - expectedResult: spec.Spec{ - Hooks: &spec.Hooks{ - Prestart: []spec.Hook{ - { - Path: "/usr/local/bin/prestart-vendor-hook", - Args: []string{"--verbose"}, - Env: []string{"VENDOR_ENV1=value1"}, - }, - }, - CreateRuntime: []spec.Hook{ - { - Path: "/usr/local/bin/cr-vendor-hook", - Args: []string{"--debug"}, - Env: []string{"VENDOR_ENV2=value2"}, - }, - }, - CreateContainer: []spec.Hook{ - { - Path: "/usr/local/bin/cc-vendor-hook", - Args: []string{"--create"}, - Env: []string{"VENDOR_ENV3=value3"}, - }, - }, - StartContainer: []spec.Hook{ - { - Path: "/usr/local/bin/sc-vendor-hook", - Args: []string{"--start"}, - Env: []string{"VENDOR_ENV4=value4"}, - }, - }, - Poststart: []spec.Hook{ - { - Path: "/usr/local/bin/poststart-vendor-hook", - Env: []string{"VENDOR_ENV5=value5"}, - }, - }, - Poststop: []spec.Hook{ - { - Path: "/usr/local/bin/poststop-vendor-hook", - }, - }, - }, - }, - }, - { - name: "unknown hook", - config: &spec.Spec{}, - edits: &ContainerEdits{ - Hooks: []*Hook{ - { - HookName: "unknown", - Path: "/usr/local/bin/prestart-vendor-hook", - Args: []string{"--verbose"}, - Env: []string{"VENDOR_ENV1=value1"}, - }, - }, - }, - expectedResult: spec.Spec{ - Hooks: &spec.Hooks{}, - }, - }, - { - name: "multiple edits", - config: &spec.Spec{ - Version: "1.0.2", - Process: &spec.Process{ - Env: []string{"ENV=value"}, - }, - Root: &spec.Root{ - Path: "/chroot/root1", - Readonly: true, - }, - Hostname: "some.host.com", - Mounts: []spec.Mount{ - { - Source: "/source", - Destination: "/destination", - Type: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - }, - Hooks: &spec.Hooks{ - Prestart: []spec.Hook{ - { - Path: "/bin/hook", - Args: []string{"--prestart"}, - Env: []string{"HOOKENV=hookval"}, - }, - }, - }, - }, - edits: &ContainerEdits{ - Env: []string{"BAR=BARVALUE1"}, - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/device1", - }, - }, - Hooks: []*Hook{ - { - HookName: "prestart", - Path: "/bin/vendor-hook", - }, - { - HookName: "poststart", - Path: "/bin/poststart", - Args: []string{"--verbose"}, - Env: []string{"VENDOR_ENV1=value1"}, - }, - }, - Mounts: []*Mount{ - { - HostPath: "/mnt/mount1", - ContainerPath: "/mnt/mount1", - Options: []string{"noexec", "noatime"}, - }, - }, - }, - expectedResult: spec.Spec{ - Version: "1.0.2", - Process: &spec.Process{ - Env: []string{"ENV=value", "BAR=BARVALUE1"}, - }, - Root: &spec.Root{ - Path: "/chroot/root1", - Readonly: true, - }, - Hostname: "some.host.com", - Linux: &spec.Linux{ - Devices: []spec.LinuxDevice{ - {Path: "/dev/device1"}, - }, - }, - Mounts: []spec.Mount{ - { - Source: "/source", - Destination: "/destination", - Type: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Source: "/mnt/mount1", - Destination: "/mnt/mount1", - Options: []string{"noexec", "noatime"}, - }, - }, - Hooks: &spec.Hooks{ - Prestart: []spec.Hook{ - { - Path: "/bin/hook", - Args: []string{"--prestart"}, - Env: []string{"HOOKENV=hookval"}, - }, - { - Path: "/bin/vendor-hook", - }, - }, - Poststart: []spec.Hook{ - { - Path: "/bin/poststart", - Args: []string{"--verbose"}, - Env: []string{"VENDOR_ENV1=value1"}, - }, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := ApplyEditsToOCISpec(tc.config, tc.edits) - if tc.expectedError { - require.Error(t, err) - return - } - require.NoError(t, err) - if tc.edits != nil { - require.Equal(t, tc.expectedResult, *tc.config) - } - }) - } -} -func TestApplyOCIEdits(t *testing.T) { - testCases := []struct { - name string - config *spec.Spec - cdiSpec *Spec - expectedResult spec.Spec - }{ - { - name: "edit empty spec", - config: &spec.Spec{}, - cdiSpec: &Spec{ - Version: "0.3.0", - Kind: "vendor.com/device", - Devices: []Device{}, - ContainerEdits: ContainerEdits{ - Env: []string{"FOO=VALID_SPEC", "BAR=BARVALUE1"}, - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/device1", - }, - }, - }, - }, - expectedResult: spec.Spec{ - Process: &spec.Process{ - Env: []string{"FOO=VALID_SPEC", "BAR=BARVALUE1"}, - }, - Linux: &spec.Linux{ - Devices: []spec.LinuxDevice{ - {Path: "/dev/device1"}, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := ApplyOCIEdits(tc.config, tc.cdiSpec) - require.NoError(t, err) - require.Equal(t, tc.expectedResult, *tc.config) - }) - } -} - -func TestApplyOCIEditsForDevice(t *testing.T) { - testCases := []struct { - name string - config *spec.Spec - cdiSpec *Spec - dev string - expectedResult spec.Spec - expectedError bool - }{ - { - name: "add device to the empty spec", - config: &spec.Spec{}, - cdiSpec: &Spec{ - Version: "0.3.0", - Kind: "vendor.com/device", - Devices: []Device{ - { - Name: "Vendor device XYZ", - ContainerEdits: ContainerEdits{ - Env: []string{"FOO=VALID_SPEC", "BAR=BARVALUE1"}, - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/device1", - }, - }, - }, - }, - { - Name: "Vendor device ABC", - ContainerEdits: ContainerEdits{ - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/devABC", - }, - }, - }, - }, - }, - }, - expectedResult: spec.Spec{ - Process: &spec.Process{ - Env: []string{"FOO=VALID_SPEC", "BAR=BARVALUE1"}, - }, - Linux: &spec.Linux{ - Devices: []spec.LinuxDevice{ - {Path: "/dev/device1"}, - }, - }, - }, - }, - { - name: "device not found", - config: &spec.Spec{}, - cdiSpec: &Spec{ - Version: "0.3.0", - Kind: "vendor.com/device", - Devices: []Device{ - { - Name: "Vendor device ABC", - ContainerEdits: ContainerEdits{ - DeviceNodes: []*DeviceNode{ - { - Path: "/dev/devABC", - }, - }, - }, - }, - }, - }, - expectedError: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := ApplyOCIEditsForDevice(tc.config, tc.cdiSpec, "Vendor device XYZ") - if tc.expectedError { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Equal(t, tc.expectedResult, *tc.config) - }) - } -}