cdi_test.go 5.0 KB

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