123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- package environment
- import (
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/client"
- "github.com/docker/docker/opts"
- "golang.org/x/net/context"
- )
- var (
- // DefaultClientBinary is the name of the docker binary
- DefaultClientBinary = os.Getenv("TEST_CLIENT_BINARY")
- )
- func init() {
- if DefaultClientBinary == "" {
- // TODO: to be removed once we no longer depend on the docker cli for integration tests
- //panic("TEST_CLIENT_BINARY must be set")
- DefaultClientBinary = "docker"
- }
- }
- // Execution holds informations about the test execution environment.
- type Execution struct {
- daemonPlatform string
- localDaemon bool
- experimentalDaemon bool
- daemonStorageDriver string
- isolation container.Isolation
- daemonPid int
- daemonKernelVersion string
- // For a local daemon on Linux, these values will be used for testing
- // user namespace support as the standard graph path(s) will be
- // appended with the root remapped uid.gid prefix
- dockerBasePath string
- volumesConfigPath string
- containerStoragePath string
- // baseImage is the name of the base image for testing
- // Environment variable WINDOWS_BASE_IMAGE can override this
- baseImage string
- dockerBinary string
- protectedElements protectedElements
- }
- // New creates a new Execution struct
- func New() (*Execution, error) {
- localDaemon := true
- // Deterministically working out the environment in which CI is running
- // to evaluate whether the daemon is local or remote is not possible through
- // a build tag.
- //
- // For example Windows to Linux CI under Jenkins tests the 64-bit
- // Windows binary build with the daemon build tag, but calls a remote
- // Linux daemon.
- //
- // We can't just say if Windows then assume the daemon is local as at
- // some point, we will be testing the Windows CLI against a Windows daemon.
- //
- // Similarly, it will be perfectly valid to also run CLI tests from
- // a Linux CLI (built with the daemon tag) against a Windows daemon.
- if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
- localDaemon = false
- }
- info, err := getDaemonDockerInfo()
- if err != nil {
- return nil, err
- }
- daemonPlatform := info.OSType
- if daemonPlatform != "linux" && daemonPlatform != "windows" {
- return nil, fmt.Errorf("Cannot run tests against platform: %s", daemonPlatform)
- }
- baseImage := "scratch"
- volumesConfigPath := filepath.Join(info.DockerRootDir, "volumes")
- containerStoragePath := filepath.Join(info.DockerRootDir, "containers")
- // Make sure in context of daemon, not the local platform. Note we can't
- // use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
- if daemonPlatform == "windows" {
- volumesConfigPath = strings.Replace(volumesConfigPath, `/`, `\`, -1)
- containerStoragePath = strings.Replace(containerStoragePath, `/`, `\`, -1)
- baseImage = "microsoft/windowsservercore"
- if len(os.Getenv("WINDOWS_BASE_IMAGE")) > 0 {
- baseImage = os.Getenv("WINDOWS_BASE_IMAGE")
- fmt.Println("INFO: Windows Base image is ", baseImage)
- }
- } else {
- volumesConfigPath = strings.Replace(volumesConfigPath, `\`, `/`, -1)
- containerStoragePath = strings.Replace(containerStoragePath, `\`, `/`, -1)
- }
- var daemonPid int
- dest := os.Getenv("DEST")
- b, err := ioutil.ReadFile(filepath.Join(dest, "docker.pid"))
- if err == nil {
- if p, err := strconv.ParseInt(string(b), 10, 32); err == nil {
- daemonPid = int(p)
- }
- }
- dockerBinary, err := exec.LookPath(DefaultClientBinary)
- if err != nil {
- return nil, err
- }
- return &Execution{
- localDaemon: localDaemon,
- daemonPlatform: daemonPlatform,
- daemonStorageDriver: info.Driver,
- daemonKernelVersion: info.KernelVersion,
- dockerBasePath: info.DockerRootDir,
- volumesConfigPath: volumesConfigPath,
- containerStoragePath: containerStoragePath,
- isolation: info.Isolation,
- daemonPid: daemonPid,
- experimentalDaemon: info.ExperimentalBuild,
- baseImage: baseImage,
- dockerBinary: dockerBinary,
- protectedElements: protectedElements{
- images: map[string]struct{}{},
- },
- }, nil
- }
- func getDaemonDockerInfo() (types.Info, error) {
- // FIXME(vdemeester) should be safe to use as is
- client, err := client.NewEnvClient()
- if err != nil {
- return types.Info{}, err
- }
- return client.Info(context.Background())
- }
- // LocalDaemon is true if the daemon under test is on the same
- // host as the CLI.
- func (e *Execution) LocalDaemon() bool {
- return e.localDaemon
- }
- // DaemonPlatform is held globally so that tests can make intelligent
- // decisions on how to configure themselves according to the platform
- // of the daemon. This is initialized in docker_utils by sending
- // a version call to the daemon and examining the response header.
- func (e *Execution) DaemonPlatform() string {
- return e.daemonPlatform
- }
- // DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker)
- func (e *Execution) DockerBasePath() string {
- return e.dockerBasePath
- }
- // VolumesConfigPath is the path of the volume configuration for the testing daemon
- func (e *Execution) VolumesConfigPath() string {
- return e.volumesConfigPath
- }
- // ContainerStoragePath is the path where the container are stored for the testing daemon
- func (e *Execution) ContainerStoragePath() string {
- return e.containerStoragePath
- }
- // DaemonStorageDriver is held globally so that tests can know the storage
- // driver of the daemon. This is initialized in docker_utils by sending
- // a version call to the daemon and examining the response header.
- func (e *Execution) DaemonStorageDriver() string {
- return e.daemonStorageDriver
- }
- // Isolation is the isolation mode of the daemon under test
- func (e *Execution) Isolation() container.Isolation {
- return e.isolation
- }
- // DaemonPID is the pid of the main test daemon
- func (e *Execution) DaemonPID() int {
- return e.daemonPid
- }
- // ExperimentalDaemon tell whether the main daemon has
- // experimental features enabled or not
- func (e *Execution) ExperimentalDaemon() bool {
- return e.experimentalDaemon
- }
- // MinimalBaseImage is the image used for minimal builds (it depends on the platform)
- func (e *Execution) MinimalBaseImage() string {
- return e.baseImage
- }
- // DaemonKernelVersion is the kernel version of the daemon as a string, as returned
- // by an INFO call to the daemon.
- func (e *Execution) DaemonKernelVersion() string {
- return e.daemonKernelVersion
- }
- // DaemonKernelVersionNumeric is the kernel version of the daemon as an integer.
- // Mostly useful on Windows where DaemonKernelVersion holds the full string such
- // as `10.0 14393 (14393.447.amd64fre.rs1_release_inmarket.161102-0100)`, but
- // integration tests really only need the `14393` piece to make decisions.
- func (e *Execution) DaemonKernelVersionNumeric() int {
- if e.daemonPlatform != "windows" {
- return -1
- }
- v, _ := strconv.Atoi(strings.Split(e.daemonKernelVersion, " ")[1])
- return v
- }
- // DockerBinary returns the docker binary for this testing environment
- func (e *Execution) DockerBinary() string {
- return e.dockerBinary
- }
- // DaemonHost return the daemon host string for this test execution
- func DaemonHost() string {
- daemonURLStr := "unix://" + opts.DefaultUnixSocket
- if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
- daemonURLStr = daemonHostVar
- }
- return daemonURLStr
- }
|