Plumb CRI Devices through to OCI WindowsDevices

There's two mappings of hostpath to IDType and ID in the wild:
- dockershim and dockerd-cri (implicitly via docker) use class/ID
-- The only supported IDType in Docker is 'class'.
-- https://github.com/aarnaud/k8s-directx-device-plugin generates this form
- https://github.com/jterry75/cri (windows_port branch) uses IDType://ID
-- hcsshim's CRI test suite generates this form

`://` is much more easily distinguishable, so I've gone with that one as
the generic separator, with `class/` as a special-case.

Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
This commit is contained in:
Paul "TBBle" Hampson
2022-03-04 04:58:06 +11:00
parent 6a25be94e9
commit 39d52118f5
5 changed files with 338 additions and 1 deletions

View File

@@ -1367,3 +1367,17 @@ func tryReadonlyMounts(mounts []mount.Mount) []mount.Mount {
}
return mounts
}
// WithWindowsDevice adds a device exposed to a Windows (WCOW or LCOW) Container
func WithWindowsDevice(idType, id string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
if idType == "" {
return errors.New("missing idType")
}
if s.Windows == nil {
s.Windows = &specs.Windows{}
}
s.Windows.Devices = append(s.Windows.Devices, specs.WindowsDevice{IDType: idType, ID: id})
return nil
}
}

View File

@@ -30,6 +30,9 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/containerd/containerd/content"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -704,3 +707,75 @@ func TestWithoutMounts(t *testing.T) {
t.Fatalf("expected %+v, got %+v", expected, s.Mounts)
}
}
func TestWithWindowsDevice(t *testing.T) {
testcases := []struct {
name string
idType string
id string
expectError bool
expectedWindowsDevices []specs.WindowsDevice
}{
{
name: "empty_idType_and_id",
idType: "",
id: "",
expectError: true,
},
{
name: "empty_idType",
idType: "",
id: "5B45201D-F2F2-4F3B-85BB-30FF1F953599",
expectError: true,
},
{
name: "empty_id",
idType: "class",
id: "",
expectError: false,
expectedWindowsDevices: []specs.WindowsDevice{{ID: "", IDType: "class"}},
},
{
name: "idType_and_id",
idType: "class",
id: "5B45201D-F2F2-4F3B-85BB-30FF1F953599",
expectError: false,
expectedWindowsDevices: []specs.WindowsDevice{{ID: "5B45201D-F2F2-4F3B-85BB-30FF1F953599", IDType: "class"}},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
spec := Spec{
Version: specs.Version,
Root: &specs.Root{},
Windows: &specs.Windows{},
}
opts := []SpecOpts{
WithWindowsDevice(tc.idType, tc.id),
}
for _, opt := range opts {
if err := opt(nil, nil, nil, &spec); err != nil {
if tc.expectError {
assert.Error(t, err)
} else {
require.NoError(t, err)
}
}
}
if len(tc.expectedWindowsDevices) != 0 {
require.NotNil(t, spec.Windows)
require.NotNil(t, spec.Windows.Devices)
assert.ElementsMatch(t, spec.Windows.Devices, tc.expectedWindowsDevices)
} else if spec.Windows != nil && spec.Windows.Devices != nil {
assert.ElementsMatch(t, spec.Windows.Devices, tc.expectedWindowsDevices)
}
})
}
}