Allow Windows Devices to be activated for HyperV Isolation
If not using the containerd backend, this will still fail, but later. Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
This commit is contained in:
parent
c60f70f112
commit
92f13bad88
|
@ -254,7 +254,7 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
devices, err := setupWindowsDevices(c.HostConfig.Devices, isHyperV)
|
devices, err := setupWindowsDevices(c.HostConfig.Devices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -452,15 +452,11 @@ func readCredentialSpecFile(id, root, location string) (string, error) {
|
||||||
return string(bcontents[:]), nil
|
return string(bcontents[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupWindowsDevices(devices []containertypes.DeviceMapping, isHyperV bool) (specDevices []specs.WindowsDevice, err error) {
|
func setupWindowsDevices(devices []containertypes.DeviceMapping) (specDevices []specs.WindowsDevice, err error) {
|
||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isHyperV {
|
|
||||||
return nil, errors.New("device assignment is not supported for HyperV containers")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, deviceMapping := range devices {
|
for _, deviceMapping := range devices {
|
||||||
srcParts := strings.SplitN(deviceMapping.PathOnHost, "/", 2)
|
srcParts := strings.SplitN(deviceMapping.PathOnHost, "/", 2)
|
||||||
if len(srcParts) != 2 {
|
if len(srcParts) != 2 {
|
||||||
|
|
|
@ -313,56 +313,44 @@ func setRegistryOpenKeyFunc(t *testing.T, key *dummyRegistryKey, err ...error) f
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetupWindowsDevices(t *testing.T) {
|
func TestSetupWindowsDevices(t *testing.T) {
|
||||||
t.Run("it does nothing if there are no devices and HyperV is disabled", func(t *testing.T) {
|
t.Run("it does nothing if there are no devices", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices(nil, false)
|
devices, err := setupWindowsDevices(nil)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it does nothing if there are no devices and HyperV is enabled", func(t *testing.T) {
|
t.Run("it fails if any devices are blank", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices(nil, true)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: ""}})
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Equal(t, len(devices), 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("it fails if there are devices and HyperV is enabled", func(t *testing.T) {
|
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "anything"}}, true)
|
|
||||||
assert.ErrorContains(t, err, "device assignment is not supported for HyperV containers")
|
|
||||||
assert.Equal(t, len(devices), 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("it fails if any devices are blank and HyperV is disabled", func(t *testing.T) {
|
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: ""}}, false)
|
|
||||||
assert.ErrorContains(t, err, "invalid device assignment path")
|
assert.ErrorContains(t, err, "invalid device assignment path")
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it fails if all devices do not contain '/' and HyperV is disabled", func(t *testing.T) {
|
t.Run("it fails if all devices do not contain '/'", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "anything"}, {PathOnHost: "goes"}}, false)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "anything"}, {PathOnHost: "goes"}})
|
||||||
assert.ErrorContains(t, err, "invalid device assignment path")
|
assert.ErrorContains(t, err, "invalid device assignment path")
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it fails if any devices do not contain '/' and HyperV is disabled", func(t *testing.T) {
|
t.Run("it fails if any devices do not contain '/'", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "goes"}}, false)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "goes"}})
|
||||||
assert.ErrorContains(t, err, "invalid device assignment path")
|
assert.ErrorContains(t, err, "invalid device assignment path")
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it fails if all devices do not have IDType 'class' and HyperV is disabled", func(t *testing.T) {
|
t.Run("it fails if all devices do not have IDType 'class'", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "klass/anything"}, {PathOnHost: "klass/goes"}}, false)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "klass/anything"}, {PathOnHost: "klass/goes"}})
|
||||||
assert.ErrorContains(t, err, "invalid device assignment type: 'klass' should be 'class'")
|
assert.ErrorContains(t, err, "invalid device assignment type: 'klass' should be 'class'")
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it fails if any devices do not have IDType 'class' and HyperV is disabled", func(t *testing.T) {
|
t.Run("it fails if any devices do not have IDType 'class'", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "klass/goes"}}, false)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "klass/goes"}})
|
||||||
assert.ErrorContains(t, err, "invalid device assignment type: 'klass' should be 'class'")
|
assert.ErrorContains(t, err, "invalid device assignment type: 'klass' should be 'class'")
|
||||||
assert.Equal(t, len(devices), 0)
|
assert.Equal(t, len(devices), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("it creates devices if all devices have IDType 'class' and HyperV is disabled", func(t *testing.T) {
|
t.Run("it creates devices if all devices have IDType 'class'", func(t *testing.T) {
|
||||||
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "class/goes"}}, false)
|
devices, err := setupWindowsDevices([]containertypes.DeviceMapping{{PathOnHost: "class/anything"}, {PathOnHost: "class/goes"}})
|
||||||
expectedDevices := []specs.WindowsDevice{{IDType: "class", ID: "anything"}, {IDType: "class", ID: "goes"}}
|
expectedDevices := []specs.WindowsDevice{{IDType: "class", ID: "anything"}, {IDType: "class", ID: "goes"}}
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, len(devices), len(expectedDevices))
|
assert.Equal(t, len(devices), len(expectedDevices))
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/integration/internal/container"
|
"github.com/docker/docker/integration/internal/container"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
|
@ -22,32 +23,64 @@ func TestWindowsDevices(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
testData := []struct {
|
testData := []struct {
|
||||||
doc string
|
doc string
|
||||||
devices []string
|
devices []string
|
||||||
expectedExitCode int
|
isolation containertypes.Isolation
|
||||||
expectedStdout string
|
expectedStartFailure bool
|
||||||
expectedStderr string
|
expectedStartFailureMessage string
|
||||||
|
expectedExitCode int
|
||||||
|
expectedStdout string
|
||||||
|
expectedStderr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
doc: "no device mounted",
|
doc: "process/no device mounted",
|
||||||
|
isolation: containertypes.IsolationProcess,
|
||||||
expectedExitCode: 1,
|
expectedExitCode: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
doc: "class/5B45201D-F2F2-4F3B-85BB-30FF1F953599 mounted",
|
doc: "process/class/5B45201D-F2F2-4F3B-85BB-30FF1F953599 mounted",
|
||||||
devices: []string{"class/5B45201D-F2F2-4F3B-85BB-30FF1F953599"},
|
devices: []string{"class/5B45201D-F2F2-4F3B-85BB-30FF1F953599"},
|
||||||
|
isolation: containertypes.IsolationProcess,
|
||||||
expectedStdout: "/Windows/System32/HostDriverStore/FileRepository",
|
expectedStdout: "/Windows/System32/HostDriverStore/FileRepository",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
doc: "hyperv/no device mounted",
|
||||||
|
isolation: containertypes.IsolationHyperV,
|
||||||
|
expectedExitCode: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "hyperv/class/5B45201D-F2F2-4F3B-85BB-30FF1F953599 mounted",
|
||||||
|
devices: []string{"class/5B45201D-F2F2-4F3B-85BB-30FF1F953599"},
|
||||||
|
isolation: containertypes.IsolationHyperV,
|
||||||
|
expectedStartFailure: !testEnv.RuntimeIsWindowsContainerd(),
|
||||||
|
expectedStartFailureMessage: "device assignment is not supported for HyperV containers",
|
||||||
|
expectedStdout: "/Windows/System32/HostDriverStore/FileRepository",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range testData {
|
for _, d := range testData {
|
||||||
d := d
|
d := d
|
||||||
t.Run(d.doc, func(t *testing.T) {
|
t.Run(d.doc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
deviceOptions := []func(*container.TestContainerConfig){container.WithIsolation(containertypes.IsolationProcess)}
|
deviceOptions := []func(*container.TestContainerConfig){container.WithIsolation(d.isolation)}
|
||||||
for _, deviceName := range d.devices {
|
for _, deviceName := range d.devices {
|
||||||
deviceOptions = append(deviceOptions, container.WithWindowsDevice(deviceName))
|
deviceOptions = append(deviceOptions, container.WithWindowsDevice(deviceName))
|
||||||
}
|
}
|
||||||
id := container.Run(ctx, t, client, deviceOptions...)
|
|
||||||
|
id := container.Create(ctx, t, client, deviceOptions...)
|
||||||
|
|
||||||
|
// Hyper-V isolation is failing even with no actual devices added.
|
||||||
|
// TODO: Once https://github.com/moby/moby/issues/43395 is resolved,
|
||||||
|
// remove this skip.If and validate the expected behaviour under Hyper-V.
|
||||||
|
skip.If(t, d.isolation == containertypes.IsolationHyperV && !d.expectedStartFailure, "FIXME. HyperV isolation setup is probably incorrect in the test")
|
||||||
|
|
||||||
|
err := client.ContainerStart(ctx, id, types.ContainerStartOptions{})
|
||||||
|
if d.expectedStartFailure {
|
||||||
|
assert.ErrorContains(t, err, d.expectedStartFailureMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond))
|
poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue