1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
package container // import "github.com/docker/docker/integration/container"
import (
"bytes"
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"testing"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/testutil"
"github.com/docker/docker/testutil/daemon"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
func TestCreateWithCDIDevices(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "linux", "CDI devices are only supported on Linux")
skip.If(t, testEnv.IsRemoteDaemon, "cannot run cdi tests with a remote daemon")
ctx := testutil.StartSpan(baseContext, t)
cwd, err := os.Getwd()
assert.NilError(t, err)
configPath := filepath.Join(cwd, "daemon.json")
err = os.WriteFile(configPath, []byte(`{"features": {"cdi": true}}`), 0o644)
defer os.Remove(configPath)
assert.NilError(t, err)
d := daemon.New(t)
d.StartWithBusybox(ctx, t, "--config-file", configPath, "--cdi-spec-dir="+filepath.Join(cwd, "testdata", "cdi"))
defer d.Stop(t)
apiClient := d.NewClientT(t)
id := container.Run(ctx, t, apiClient,
container.WithCmd("/bin/sh", "-c", "env"),
container.WithCDIDevices("vendor1.com/device=foo"),
)
defer apiClient.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
inspect, err := apiClient.ContainerInspect(ctx, id)
assert.NilError(t, err)
expectedRequests := []containertypes.DeviceRequest{
{
Driver: "cdi",
DeviceIDs: []string{"vendor1.com/device=foo"},
},
}
assert.Check(t, is.DeepEqual(inspect.HostConfig.DeviceRequests, expectedRequests))
poll.WaitOn(t, container.IsStopped(ctx, apiClient, id))
reader, err := apiClient.ContainerLogs(ctx, id, containertypes.LogsOptions{
ShowStdout: true,
})
assert.NilError(t, err)
actualStdout := new(bytes.Buffer)
actualStderr := io.Discard
_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
assert.NilError(t, err)
outlines := strings.Split(actualStdout.String(), "\n")
assert.Assert(t, is.Contains(outlines, "FOO=injected"))
}
func TestCDISpecDirsAreInSystemInfo(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows") // d.Start fails on Windows with `protocol not available`
// TODO: This restriction can be relaxed with https://github.com/moby/moby/pull/46158
skip.If(t, testEnv.IsRootless, "the t.TempDir test creates a folder with incorrect permissions for rootless")
testCases := []struct {
description string
config map[string]interface{}
specDirs []string
expectedInfoCDISpecDirs []string
}{
{
description: "CDI enabled with no spec dirs specified returns default",
config: map[string]interface{}{"features": map[string]bool{"cdi": true}},
specDirs: nil,
expectedInfoCDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
},
{
description: "CDI enabled with specified spec dirs are returned",
config: map[string]interface{}{"features": map[string]bool{"cdi": true}},
specDirs: []string{"/foo/bar", "/baz/qux"},
expectedInfoCDISpecDirs: []string{"/foo/bar", "/baz/qux"},
},
{
description: "CDI enabled with empty string as spec dir returns empty slice",
config: map[string]interface{}{"features": map[string]bool{"cdi": true}},
specDirs: []string{""},
expectedInfoCDISpecDirs: []string{},
},
{
description: "CDI enabled with empty config option returns empty slice",
config: map[string]interface{}{"features": map[string]bool{"cdi": true}, "cdi-spec-dirs": []string{}},
expectedInfoCDISpecDirs: []string{},
},
{
description: "CDI disabled with no spec dirs specified returns empty slice",
specDirs: nil,
expectedInfoCDISpecDirs: []string{},
},
{
description: "CDI disabled with specified spec dirs returns empty slice",
specDirs: []string{"/foo/bar", "/baz/qux"},
expectedInfoCDISpecDirs: []string{},
},
{
description: "CDI disabled with empty string as spec dir returns empty slice",
specDirs: []string{""},
expectedInfoCDISpecDirs: []string{},
},
{
description: "CDI disabled with empty config option returns empty slice",
config: map[string]interface{}{"cdi-spec-dirs": []string{}},
expectedInfoCDISpecDirs: []string{},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
var opts []daemon.Option
d := daemon.New(t, opts...)
var args []string
for _, specDir := range tc.specDirs {
args = append(args, "--cdi-spec-dir="+specDir)
}
if tc.config != nil {
configPath := filepath.Join(t.TempDir(), "daemon.json")
configFile, err := os.Create(configPath)
assert.NilError(t, err)
defer configFile.Close()
err = json.NewEncoder(configFile).Encode(tc.config)
assert.NilError(t, err)
args = append(args, "--config-file="+configPath)
}
d.Start(t, args...)
defer d.Stop(t)
info := d.Info(t)
assert.Check(t, is.DeepEqual(tc.expectedInfoCDISpecDirs, info.CDISpecDirs))
})
}
}
|