environment.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package environment // import "github.com/docker/docker/internal/test/environment"
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/filters"
  10. "github.com/docker/docker/client"
  11. "github.com/docker/docker/internal/test"
  12. "github.com/docker/docker/internal/test/fixtures/load"
  13. "github.com/pkg/errors"
  14. "gotest.tools/assert"
  15. )
  16. // Execution contains information about the current test execution and daemon
  17. // under test
  18. type Execution struct {
  19. client client.APIClient
  20. DaemonInfo types.Info
  21. OSType string
  22. PlatformDefaults PlatformDefaults
  23. protectedElements protectedElements
  24. }
  25. // PlatformDefaults are defaults values for the platform of the daemon under test
  26. type PlatformDefaults struct {
  27. BaseImage string
  28. VolumesConfigPath string
  29. ContainerStoragePath string
  30. }
  31. // New creates a new Execution struct
  32. func New() (*Execution, error) {
  33. client, err := client.NewClientWithOpts(client.FromEnv)
  34. if err != nil {
  35. return nil, errors.Wrapf(err, "failed to create client")
  36. }
  37. info, err := client.Info(context.Background())
  38. if err != nil {
  39. return nil, errors.Wrapf(err, "failed to get info from daemon")
  40. }
  41. osType := getOSType(info)
  42. return &Execution{
  43. client: client,
  44. DaemonInfo: info,
  45. OSType: osType,
  46. PlatformDefaults: getPlatformDefaults(info, osType),
  47. protectedElements: newProtectedElements(),
  48. }, nil
  49. }
  50. func getOSType(info types.Info) string {
  51. // Docker EE does not set the OSType so allow the user to override this value.
  52. userOsType := os.Getenv("TEST_OSTYPE")
  53. if userOsType != "" {
  54. return userOsType
  55. }
  56. return info.OSType
  57. }
  58. func getPlatformDefaults(info types.Info, osType string) PlatformDefaults {
  59. volumesPath := filepath.Join(info.DockerRootDir, "volumes")
  60. containersPath := filepath.Join(info.DockerRootDir, "containers")
  61. switch osType {
  62. case "linux":
  63. return PlatformDefaults{
  64. BaseImage: "scratch",
  65. VolumesConfigPath: toSlash(volumesPath),
  66. ContainerStoragePath: toSlash(containersPath),
  67. }
  68. case "windows":
  69. baseImage := "microsoft/windowsservercore"
  70. if overrideBaseImage := os.Getenv("WINDOWS_BASE_IMAGE"); overrideBaseImage != "" {
  71. baseImage = overrideBaseImage
  72. if overrideBaseImageTag := os.Getenv("WINDOWS_BASE_IMAGE_TAG"); overrideBaseImageTag != "" {
  73. baseImage = baseImage + ":" + overrideBaseImageTag
  74. }
  75. }
  76. fmt.Println("INFO: Windows Base image is ", baseImage)
  77. return PlatformDefaults{
  78. BaseImage: baseImage,
  79. VolumesConfigPath: filepath.FromSlash(volumesPath),
  80. ContainerStoragePath: filepath.FromSlash(containersPath),
  81. }
  82. default:
  83. panic(fmt.Sprintf("unknown OSType for daemon: %s", osType))
  84. }
  85. }
  86. // Make sure in context of daemon, not the local platform. Note we can't
  87. // use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
  88. func toSlash(path string) string {
  89. return strings.Replace(path, `\`, `/`, -1)
  90. }
  91. // IsLocalDaemon is true if the daemon under test is on the same
  92. // host as the test process.
  93. //
  94. // Deterministically working out the environment in which CI is running
  95. // to evaluate whether the daemon is local or remote is not possible through
  96. // a build tag.
  97. //
  98. // For example Windows to Linux CI under Jenkins tests the 64-bit
  99. // Windows binary build with the daemon build tag, but calls a remote
  100. // Linux daemon.
  101. //
  102. // We can't just say if Windows then assume the daemon is local as at
  103. // some point, we will be testing the Windows CLI against a Windows daemon.
  104. //
  105. // Similarly, it will be perfectly valid to also run CLI tests from
  106. // a Linux CLI (built with the daemon tag) against a Windows daemon.
  107. func (e *Execution) IsLocalDaemon() bool {
  108. return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
  109. }
  110. // IsRemoteDaemon is true if the daemon under test is on different host
  111. // as the test process.
  112. func (e *Execution) IsRemoteDaemon() bool {
  113. return !e.IsLocalDaemon()
  114. }
  115. // DaemonAPIVersion returns the negotiated daemon api version
  116. func (e *Execution) DaemonAPIVersion() string {
  117. version, err := e.APIClient().ServerVersion(context.TODO())
  118. if err != nil {
  119. return ""
  120. }
  121. return version.APIVersion
  122. }
  123. // Print the execution details to stdout
  124. // TODO: print everything
  125. func (e *Execution) Print() {
  126. if e.IsLocalDaemon() {
  127. fmt.Println("INFO: Testing against a local daemon")
  128. } else {
  129. fmt.Println("INFO: Testing against a remote daemon")
  130. }
  131. }
  132. // APIClient returns an APIClient connected to the daemon under test
  133. func (e *Execution) APIClient() client.APIClient {
  134. return e.client
  135. }
  136. // IsUserNamespace returns whether the user namespace remapping is enabled
  137. func (e *Execution) IsUserNamespace() bool {
  138. root := os.Getenv("DOCKER_REMAP_ROOT")
  139. return root != ""
  140. }
  141. // HasExistingImage checks whether there is an image with the given reference.
  142. // Note that this is done by filtering and then checking whether there were any
  143. // results -- so ambiguous references might result in false-positives.
  144. func (e *Execution) HasExistingImage(t assert.TestingT, reference string) bool {
  145. if ht, ok := t.(test.HelperT); ok {
  146. ht.Helper()
  147. }
  148. client := e.APIClient()
  149. filter := filters.NewArgs()
  150. filter.Add("dangling", "false")
  151. filter.Add("reference", reference)
  152. imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
  153. All: true,
  154. Filters: filter,
  155. })
  156. assert.NilError(t, err, "failed to list images")
  157. return len(imageList) > 0
  158. }
  159. // EnsureFrozenImagesLinux loads frozen test images into the daemon
  160. // if they aren't already loaded
  161. func EnsureFrozenImagesLinux(testEnv *Execution) error {
  162. if testEnv.OSType == "linux" {
  163. err := load.FrozenImagesLinux(testEnv.APIClient(), frozenImages...)
  164. if err != nil {
  165. return errors.Wrap(err, "error loading frozen images")
  166. }
  167. }
  168. return nil
  169. }