environment.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package environment // import "github.com/docker/docker/testutil/environment"
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "testing"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/api/types/filters"
  11. "github.com/docker/docker/client"
  12. "github.com/docker/docker/testutil/fixtures/load"
  13. "github.com/pkg/errors"
  14. "gotest.tools/v3/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. // This is configured using the env client (see client.FromEnv)
  33. func New() (*Execution, error) {
  34. c, err := client.NewClientWithOpts(client.FromEnv)
  35. if err != nil {
  36. return nil, errors.Wrapf(err, "failed to create client")
  37. }
  38. return FromClient(c)
  39. }
  40. // FromClient creates a new Execution environment from the passed in client
  41. func FromClient(c *client.Client) (*Execution, error) {
  42. info, err := c.Info(context.Background())
  43. if err != nil {
  44. return nil, errors.Wrapf(err, "failed to get info from daemon")
  45. }
  46. osType := getOSType(info)
  47. return &Execution{
  48. client: c,
  49. DaemonInfo: info,
  50. OSType: osType,
  51. PlatformDefaults: getPlatformDefaults(info, osType),
  52. protectedElements: newProtectedElements(),
  53. }, nil
  54. }
  55. func getOSType(info types.Info) string {
  56. // Docker EE does not set the OSType so allow the user to override this value.
  57. userOsType := os.Getenv("TEST_OSTYPE")
  58. if userOsType != "" {
  59. return userOsType
  60. }
  61. return info.OSType
  62. }
  63. func getPlatformDefaults(info types.Info, osType string) PlatformDefaults {
  64. volumesPath := filepath.Join(info.DockerRootDir, "volumes")
  65. containersPath := filepath.Join(info.DockerRootDir, "containers")
  66. switch osType {
  67. case "linux":
  68. return PlatformDefaults{
  69. BaseImage: "scratch",
  70. VolumesConfigPath: toSlash(volumesPath),
  71. ContainerStoragePath: toSlash(containersPath),
  72. }
  73. case "windows":
  74. baseImage := "microsoft/windowsservercore"
  75. if overrideBaseImage := os.Getenv("WINDOWS_BASE_IMAGE"); overrideBaseImage != "" {
  76. baseImage = overrideBaseImage
  77. if overrideBaseImageTag := os.Getenv("WINDOWS_BASE_IMAGE_TAG"); overrideBaseImageTag != "" {
  78. baseImage = baseImage + ":" + overrideBaseImageTag
  79. }
  80. }
  81. fmt.Println("INFO: Windows Base image is ", baseImage)
  82. return PlatformDefaults{
  83. BaseImage: baseImage,
  84. VolumesConfigPath: filepath.FromSlash(volumesPath),
  85. ContainerStoragePath: filepath.FromSlash(containersPath),
  86. }
  87. default:
  88. panic(fmt.Sprintf("unknown OSType for daemon: %s", osType))
  89. }
  90. }
  91. // Make sure in context of daemon, not the local platform. Note we can't
  92. // use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
  93. func toSlash(path string) string {
  94. return strings.Replace(path, `\`, `/`, -1)
  95. }
  96. // IsLocalDaemon is true if the daemon under test is on the same
  97. // host as the test process.
  98. //
  99. // Deterministically working out the environment in which CI is running
  100. // to evaluate whether the daemon is local or remote is not possible through
  101. // a build tag.
  102. //
  103. // For example Windows to Linux CI under Jenkins tests the 64-bit
  104. // Windows binary build with the daemon build tag, but calls a remote
  105. // Linux daemon.
  106. //
  107. // We can't just say if Windows then assume the daemon is local as at
  108. // some point, we will be testing the Windows CLI against a Windows daemon.
  109. //
  110. // Similarly, it will be perfectly valid to also run CLI tests from
  111. // a Linux CLI (built with the daemon tag) against a Windows daemon.
  112. func (e *Execution) IsLocalDaemon() bool {
  113. return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
  114. }
  115. // IsRemoteDaemon is true if the daemon under test is on different host
  116. // as the test process.
  117. func (e *Execution) IsRemoteDaemon() bool {
  118. return !e.IsLocalDaemon()
  119. }
  120. // DaemonAPIVersion returns the negotiated daemon api version
  121. func (e *Execution) DaemonAPIVersion() string {
  122. version, err := e.APIClient().ServerVersion(context.TODO())
  123. if err != nil {
  124. return ""
  125. }
  126. return version.APIVersion
  127. }
  128. // Print the execution details to stdout
  129. // TODO: print everything
  130. func (e *Execution) Print() {
  131. if e.IsLocalDaemon() {
  132. fmt.Println("INFO: Testing against a local daemon")
  133. } else {
  134. fmt.Println("INFO: Testing against a remote daemon")
  135. }
  136. }
  137. // APIClient returns an APIClient connected to the daemon under test
  138. func (e *Execution) APIClient() client.APIClient {
  139. return e.client
  140. }
  141. // IsUserNamespace returns whether the user namespace remapping is enabled
  142. func (e *Execution) IsUserNamespace() bool {
  143. root := os.Getenv("DOCKER_REMAP_ROOT")
  144. return root != ""
  145. }
  146. // RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows
  147. func (e *Execution) RuntimeIsWindowsContainerd() bool {
  148. return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
  149. }
  150. // IsRootless returns whether the rootless mode is enabled
  151. func (e *Execution) IsRootless() bool {
  152. return os.Getenv("DOCKER_ROOTLESS") != ""
  153. }
  154. // IsUserNamespaceInKernel returns whether the kernel supports user namespaces
  155. func (e *Execution) IsUserNamespaceInKernel() bool {
  156. if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
  157. /*
  158. * This kernel-provided file only exists if user namespaces are
  159. * supported
  160. */
  161. return false
  162. }
  163. // We need extra check on redhat based distributions
  164. if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil {
  165. defer f.Close()
  166. b := make([]byte, 1)
  167. _, _ = f.Read(b)
  168. return string(b) != "N"
  169. }
  170. return true
  171. }
  172. // HasExistingImage checks whether there is an image with the given reference.
  173. // Note that this is done by filtering and then checking whether there were any
  174. // results -- so ambiguous references might result in false-positives.
  175. func (e *Execution) HasExistingImage(t testing.TB, reference string) bool {
  176. client := e.APIClient()
  177. filter := filters.NewArgs()
  178. filter.Add("dangling", "false")
  179. filter.Add("reference", reference)
  180. imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
  181. All: true,
  182. Filters: filter,
  183. })
  184. assert.NilError(t, err, "failed to list images")
  185. return len(imageList) > 0
  186. }
  187. // EnsureFrozenImagesLinux loads frozen test images into the daemon
  188. // if they aren't already loaded
  189. func EnsureFrozenImagesLinux(testEnv *Execution) error {
  190. if testEnv.OSType == "linux" {
  191. err := load.FrozenImagesLinux(testEnv.APIClient(), frozenImages...)
  192. if err != nil {
  193. return errors.Wrap(err, "error loading frozen images")
  194. }
  195. }
  196. return nil
  197. }