environment.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package environment
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/api/types/container"
  11. "github.com/docker/docker/client"
  12. "golang.org/x/net/context"
  13. )
  14. // Execution holds informations about the test execution environment.
  15. type Execution struct {
  16. daemonPlatform string
  17. localDaemon bool
  18. experimentalDaemon bool
  19. daemonStorageDriver string
  20. isolation container.Isolation
  21. daemonPid int
  22. daemonKernelVersion string
  23. // For a local daemon on Linux, these values will be used for testing
  24. // user namespace support as the standard graph path(s) will be
  25. // appended with the root remapped uid.gid prefix
  26. dockerBasePath string
  27. volumesConfigPath string
  28. containerStoragePath string
  29. // baseImage is the name of the base image for testing
  30. // Environment variable WINDOWS_BASE_IMAGE can override this
  31. baseImage string
  32. }
  33. // New creates a new Execution struct
  34. func New() (*Execution, error) {
  35. localDaemon := true
  36. // Deterministically working out the environment in which CI is running
  37. // to evaluate whether the daemon is local or remote is not possible through
  38. // a build tag.
  39. //
  40. // For example Windows to Linux CI under Jenkins tests the 64-bit
  41. // Windows binary build with the daemon build tag, but calls a remote
  42. // Linux daemon.
  43. //
  44. // We can't just say if Windows then assume the daemon is local as at
  45. // some point, we will be testing the Windows CLI against a Windows daemon.
  46. //
  47. // Similarly, it will be perfectly valid to also run CLI tests from
  48. // a Linux CLI (built with the daemon tag) against a Windows daemon.
  49. if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
  50. localDaemon = false
  51. }
  52. info, err := getDaemonDockerInfo()
  53. if err != nil {
  54. return nil, err
  55. }
  56. daemonPlatform := info.OSType
  57. if daemonPlatform != "linux" && daemonPlatform != "windows" {
  58. return nil, fmt.Errorf("Cannot run tests against platform: %s", daemonPlatform)
  59. }
  60. baseImage := "scratch"
  61. volumesConfigPath := filepath.Join(info.DockerRootDir, "volumes")
  62. containerStoragePath := filepath.Join(info.DockerRootDir, "containers")
  63. // Make sure in context of daemon, not the local platform. Note we can't
  64. // use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
  65. if daemonPlatform == "windows" {
  66. volumesConfigPath = strings.Replace(volumesConfigPath, `/`, `\`, -1)
  67. containerStoragePath = strings.Replace(containerStoragePath, `/`, `\`, -1)
  68. baseImage = "microsoft/windowsservercore"
  69. if len(os.Getenv("WINDOWS_BASE_IMAGE")) > 0 {
  70. baseImage = os.Getenv("WINDOWS_BASE_IMAGE")
  71. fmt.Println("INFO: Windows Base image is ", baseImage)
  72. }
  73. } else {
  74. volumesConfigPath = strings.Replace(volumesConfigPath, `\`, `/`, -1)
  75. containerStoragePath = strings.Replace(containerStoragePath, `\`, `/`, -1)
  76. }
  77. var daemonPid int
  78. dest := os.Getenv("DEST")
  79. b, err := ioutil.ReadFile(filepath.Join(dest, "docker.pid"))
  80. if err == nil {
  81. if p, err := strconv.ParseInt(string(b), 10, 32); err == nil {
  82. daemonPid = int(p)
  83. }
  84. }
  85. return &Execution{
  86. localDaemon: localDaemon,
  87. daemonPlatform: daemonPlatform,
  88. daemonStorageDriver: info.Driver,
  89. daemonKernelVersion: info.KernelVersion,
  90. dockerBasePath: info.DockerRootDir,
  91. volumesConfigPath: volumesConfigPath,
  92. containerStoragePath: containerStoragePath,
  93. isolation: info.Isolation,
  94. daemonPid: daemonPid,
  95. experimentalDaemon: info.ExperimentalBuild,
  96. baseImage: baseImage,
  97. }, nil
  98. }
  99. func getDaemonDockerInfo() (types.Info, error) {
  100. // FIXME(vdemeester) should be safe to use as is
  101. client, err := client.NewEnvClient()
  102. if err != nil {
  103. return types.Info{}, err
  104. }
  105. return client.Info(context.Background())
  106. }
  107. // LocalDaemon is true if the daemon under test is on the same
  108. // host as the CLI.
  109. func (e *Execution) LocalDaemon() bool {
  110. return e.localDaemon
  111. }
  112. // DaemonPlatform is held globally so that tests can make intelligent
  113. // decisions on how to configure themselves according to the platform
  114. // of the daemon. This is initialized in docker_utils by sending
  115. // a version call to the daemon and examining the response header.
  116. func (e *Execution) DaemonPlatform() string {
  117. return e.daemonPlatform
  118. }
  119. // DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker)
  120. func (e *Execution) DockerBasePath() string {
  121. return e.dockerBasePath
  122. }
  123. // VolumesConfigPath is the path of the volume configuration for the testing daemon
  124. func (e *Execution) VolumesConfigPath() string {
  125. return e.volumesConfigPath
  126. }
  127. // ContainerStoragePath is the path where the container are stored for the testing daemon
  128. func (e *Execution) ContainerStoragePath() string {
  129. return e.containerStoragePath
  130. }
  131. // DaemonStorageDriver is held globally so that tests can know the storage
  132. // driver of the daemon. This is initialized in docker_utils by sending
  133. // a version call to the daemon and examining the response header.
  134. func (e *Execution) DaemonStorageDriver() string {
  135. return e.daemonStorageDriver
  136. }
  137. // Isolation is the isolation mode of the daemon under test
  138. func (e *Execution) Isolation() container.Isolation {
  139. return e.isolation
  140. }
  141. // DaemonPID is the pid of the main test daemon
  142. func (e *Execution) DaemonPID() int {
  143. return e.daemonPid
  144. }
  145. // ExperimentalDaemon tell whether the main daemon has
  146. // experimental features enabled or not
  147. func (e *Execution) ExperimentalDaemon() bool {
  148. return e.experimentalDaemon
  149. }
  150. // MinimalBaseImage is the image used for minimal builds (it depends on the platform)
  151. func (e *Execution) MinimalBaseImage() string {
  152. return e.baseImage
  153. }
  154. // DaemonKernelVersion is the kernel version of the daemon as a string, as returned
  155. // by an INFO call to the daemon.
  156. func (e *Execution) DaemonKernelVersion() string {
  157. return e.daemonKernelVersion
  158. }
  159. // DaemonKernelVersionNumeric is the kernel version of the daemon as an integer.
  160. // Mostly useful on Windows where DaemonKernelVersion holds the full string such
  161. // as `10.0 14393 (14393.447.amd64fre.rs1_release_inmarket.161102-0100)`, but
  162. // integration tests really only need the `14393` piece to make decisions.
  163. func (e *Execution) DaemonKernelVersionNumeric() int {
  164. if e.daemonPlatform != "windows" {
  165. return -1
  166. }
  167. v, _ := strconv.Atoi(strings.Split(e.daemonKernelVersion, " ")[1])
  168. return v
  169. }