moby/integration-cli/environment/environment.go
Arnaud Porterie (icecrime) 32915b1d0a Remove cmd/docker and other directories in cli/ in accordance with the new Moby project scope
Starting with this commit, integration tests should no longer rely on
the docker cli, they should be API tests instead. For the existing tests
the scripts will use a frozen version of the docker cli with a
DOCKER_API_VERSION frozen to 1.30, which should ensure that the CI remains
green at all times.

To help contributors develop and test manually with a modified docker
cli, this commit also adds a DOCKER_CLI_PATH environment variable to the
Makefile. This allows to set the path of a custom cli that will be
available inside the development container and used to run the
integration tests.

Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
Signed-off-by: Tibor Vass <tibor@docker.com>
2017-05-05 12:14:29 -07:00

229 lines
7.3 KiB
Go

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
}