environment.go 7.3 KB

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