cdi_test.go 5.0 KB

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