From d046858057f3786feee025d09338788338fd03bf Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 6 Oct 2015 17:53:05 -0700 Subject: [PATCH] Support boot2docker or VM for dev/test --- client/driver/docker.go | 44 ++++++++++++++++++++++++++++++++---- client/driver/docker_test.go | 26 +++++++++++++-------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 896529513..b7b4e3eb2 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -4,10 +4,12 @@ import ( "encoding/json" "fmt" "log" + "runtime" "strconv" "strings" docker "github.com/fsouza/go-dockerclient" + opts "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/nomad/structs" @@ -33,16 +35,47 @@ type dockerHandle struct { doneCh chan struct{} } +// getDefaultDockerHost is copied from fsouza. If it were exported we woudn't +// need this here. +func getDefaultDockerHost() (string, error) { + var defaultHost string + if runtime.GOOS == "windows" { + // If we do not have a host, default to TCP socket on Windows + defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort) + } else { + // If we do not have a host, default to unix socket + defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket) + } + return opts.ValidateHost(defaultHost) +} + func NewDockerDriver(ctx *DriverContext) Driver { return &DockerDriver{*ctx} } -// dockerClient creates *docker.Client using ClientConfig so we can get the -// correct socket for the daemon +// dockerClient creates *docker.Client. In test / dev mode we can use ENV vars +// to connect to the docker daemon. In production mode we will read +// docker.endpoint from the config file. func (d *DockerDriver) dockerClient() (*docker.Client, error) { - dockerEndpoint := d.config.Read("docker.endpoint") - client, err := docker.NewClient(dockerEndpoint) - return client, err + // In dev mode, read DOCKER_* environment variables DOCKER_HOST, + // DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH. This allows you to run tests and + // demo against boot2docker or a VM on OSX and Windows. This falls back on + // the default unix socket on linux if tests are run on linux. + // + // Also note that we need to turn on DevMode in the test configs. + if d.config.DevMode { + return docker.NewClientFromEnv() + } + + // In prod mode we'll read the docker.endpoint configuration and fall back + // on the host-specific default. We do not read from the environment. + defaultEndpoint, err := getDefaultDockerHost() + if err != nil { + return nil, fmt.Errorf("Unable to determine default docker endpoint: %s", err) + } + dockerEndpoint := d.config.ReadDefault("docker.endpoint", defaultEndpoint) + + return docker.NewClient(dockerEndpoint) } func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { @@ -408,6 +441,7 @@ func (h *dockerHandle) Kill() error { err = h.client.RemoveImage(h.imageID) if err != nil { containers, err := h.client.ListContainers(docker.ListContainersOptions{ + // The image might be in use by a stopped container, so check everything All: true, Filters: map[string][]string{ "image": []string{h.imageID}, diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 3bea8235a..2d436092d 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -9,6 +9,12 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) +func testDockerDriverContext(task string) *DriverContext { + cfg := testConfig() + cfg.DevMode = true + return NewDriverContext(task, cfg, cfg.Node, testLogger()) +} + // dockerLocated looks to see whether docker is available on this system before // we try to run tests. We'll keep it simple and just check for the CLI. func dockerLocated() bool { @@ -33,7 +39,7 @@ func TestDockerDriver_Handle(t *testing.T) { // The fingerprinter test should always pass, even if Docker is not installed. func TestDockerDriver_Fingerprint(t *testing.T) { - d := NewDockerDriver(testDriverContext("")) + d := NewDockerDriver(testDockerDriverContext("")) node := &structs.Node{ Attributes: make(map[string]string), } @@ -56,14 +62,14 @@ func TestDockerDriver_StartOpen_Wait(t *testing.T) { } task := &structs.Task{ - Name: "python-demo", + Name: "redis-demo", Config: map[string]string{ "image": "redis", }, Resources: basicResources, } - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx) @@ -93,7 +99,7 @@ func TestDockerDriver_Start_Wait(t *testing.T) { } task := &structs.Task{ - Name: "python-demo", + Name: "redis-demo", Config: map[string]string{ "image": "redis", "command": "redis-server -v", @@ -104,7 +110,7 @@ func TestDockerDriver_Start_Wait(t *testing.T) { }, } - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx) @@ -140,7 +146,7 @@ func TestDockerDriver_Start_Kill_Wait(t *testing.T) { } task := &structs.Task{ - Name: "python-demo", + Name: "redis-demo", Config: map[string]string{ "image": "redis", "command": "sleep 10", @@ -148,7 +154,7 @@ func TestDockerDriver_Start_Kill_Wait(t *testing.T) { Resources: basicResources, } - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx) @@ -219,7 +225,7 @@ func TestDocker_StartN(t *testing.T) { // Let's spin up a bunch of things var err error for idx, task := range taskList { - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx) @@ -265,7 +271,7 @@ func TestDocker_StartNVersions(t *testing.T) { // Let's spin up a bunch of things var err error for idx, task := range taskList { - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx) @@ -299,7 +305,7 @@ func TestDockerHostNet(t *testing.T) { CPU: 512, }, } - driverCtx := testDriverContext(task.Name) + driverCtx := testDockerDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) defer ctx.AllocDir.Destroy() d := NewDockerDriver(driverCtx)