Merge pull request #407 from hashicorp/f-docker-host-env

Change Docker ENV behavior
This commit is contained in:
Chris Bednarski 2015-11-10 19:30:11 -08:00
commit 7fb75525b0
2 changed files with 61 additions and 44 deletions

View File

@ -47,32 +47,24 @@ func NewDockerDriver(ctx *DriverContext) Driver {
// to connect to the docker daemon. In production mode we will read // to connect to the docker daemon. In production mode we will read
// docker.endpoint from the config file. // docker.endpoint from the config file.
func (d *DockerDriver) dockerClient() (*docker.Client, error) { func (d *DockerDriver) dockerClient() (*docker.Client, error) {
// In dev mode, read DOCKER_* environment variables DOCKER_HOST, // Default to using whatever is configured in docker.endpoint. If this is
// DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH. This allows you to run tests and // not specified we'll fall back on NewClientFromEnv which reads config from
// demo against boot2docker or a VM on OSX and Windows. This falls back on // the DOCKER_* environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and
// the default unix socket on linux if tests are run on linux. // DOCKER_CERT_PATH. This allows us to lock down the config in production
// // but also accept the standard ENV configs for dev and test.
// Also note that we need to turn on DevMode in the test configs. dockerEndpoint := d.config.Read("docker.endpoint")
if d.config.DevMode { if dockerEndpoint != "" {
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 := docker.DefaultDockerHost()
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) return docker.NewClient(dockerEndpoint)
} }
return docker.NewClientFromEnv()
}
func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
// Initialize docker API client // Initialize docker API client
client, err := d.dockerClient() client, err := d.dockerClient()
if err != nil { if err != nil {
d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon: %v", err) d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon: %s", err)
return false, nil return false, nil
} }
@ -94,18 +86,14 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool
return false, fmt.Errorf("Unable to parse docker.cleanup.image: %s", err) return false, fmt.Errorf("Unable to parse docker.cleanup.image: %s", err)
} }
// This is the first operation taken on the client so we'll try to
// establish a connection to the Docker daemon. If this fails it means
// Docker isn't available so we'll simply disable the docker driver.
env, err := client.Version() env, err := client.Version()
if err != nil { if err != nil {
d.logger.Printf("[DEBUG] driver.docker: could not read version from daemon: %v", err) d.logger.Printf("[INFO] driver.docker: connection to daemon failed: %s", err)
// Check the "no such file" error if the unix file is missing
if strings.Contains(err.Error(), "no such file") {
return false, nil return false, nil
} }
// We connected to the daemon but couldn't read the version so something
// is broken.
return false, err
}
node.Attributes["driver.docker"] = "1" node.Attributes["driver.docker"] = "1"
node.Attributes["driver.docker.version"] = env.Get("Version") node.Attributes["driver.docker.version"] = env.Get("Version")

View File

@ -3,12 +3,12 @@ package driver
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os/exec"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
"time" "time"
docker "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/client/driver/environment" "github.com/hashicorp/nomad/client/driver/environment"
"github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/nomad/structs"
@ -20,11 +20,37 @@ func testDockerDriverContext(task string) *DriverContext {
return NewDriverContext(task, cfg, cfg.Node, testLogger()) return NewDriverContext(task, cfg, cfg.Node, testLogger())
} }
// dockerLocated looks to see whether docker is available on this system before // dockerIsConnected checks to see if a docker daemon is available (local or remote)
// we try to run tests. We'll keep it simple and just check for the CLI. func dockerIsConnected(t *testing.T) bool {
func dockerLocated() bool { client, err := docker.NewClientFromEnv()
_, err := exec.Command("docker", "-v").CombinedOutput() if err != nil {
return err == nil return false
}
// Creating a client doesn't actually connect, so make sure we do something
// like call Version() on it.
env, err := client.Version()
if err != nil {
t.Logf("Failed to connect to docker daemon: %s", err)
return false
}
t.Logf("Successfully connected to docker daemon running version %s", env.Get("Version"))
return true
}
func dockerIsRemote(t *testing.T) bool {
client, err := docker.NewClientFromEnv()
if err != nil {
return false
}
// Technically this could be a local tcp socket but for testing purposes
// we'll just assume that tcp is only used for remote connections.
if client.Endpoint()[0:3] == "tcp" {
return true
}
return false
} }
func TestDockerDriver_Handle(t *testing.T) { func TestDockerDriver_Handle(t *testing.T) {
@ -42,7 +68,7 @@ func TestDockerDriver_Handle(t *testing.T) {
} }
} }
// The fingerprinter test should always pass, even if Docker is not installed. // This test should always pass, even if docker daemon is not available
func TestDockerDriver_Fingerprint(t *testing.T) { func TestDockerDriver_Fingerprint(t *testing.T) {
d := NewDockerDriver(testDockerDriverContext("")) d := NewDockerDriver(testDockerDriverContext(""))
node := &structs.Node{ node := &structs.Node{
@ -52,17 +78,17 @@ func TestDockerDriver_Fingerprint(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if apply != dockerLocated() { if apply != dockerIsConnected(t) {
t.Fatalf("Fingerprinter should detect Docker when it is installed") t.Fatalf("Fingerprinter should detect when docker is available")
} }
if node.Attributes["driver.docker"] != "1" { if node.Attributes["driver.docker"] != "1" {
t.Log("Docker not found. The remainder of the docker tests will be skipped.") t.Log("Docker daemon not available. The remainder of the docker tests will be skipped.")
} }
t.Logf("Found docker version %s", node.Attributes["driver.docker.version"]) t.Logf("Found docker version %s", node.Attributes["driver.docker.version"])
} }
func TestDockerDriver_StartOpen_Wait(t *testing.T) { func TestDockerDriver_StartOpen_Wait(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }
@ -99,7 +125,7 @@ func TestDockerDriver_StartOpen_Wait(t *testing.T) {
} }
func TestDockerDriver_Start_Wait(t *testing.T) { func TestDockerDriver_Start_Wait(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }
@ -147,7 +173,10 @@ func TestDockerDriver_Start_Wait(t *testing.T) {
} }
func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) { func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) {
if !dockerLocated() { // This test requires that the alloc dir be mounted into docker as a volume.
// Because this cannot happen when docker is run remotely, e.g. when running
// docker in a VM, we skip this when we detect Docker is being run remotely.
if !dockerIsConnected(t) || dockerIsRemote(t) {
t.SkipNow() t.SkipNow()
} }
@ -202,7 +231,7 @@ func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) {
} }
func TestDockerDriver_Start_Kill_Wait(t *testing.T) { func TestDockerDriver_Start_Kill_Wait(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }
@ -269,7 +298,7 @@ func taskTemplate() *structs.Task {
} }
func TestDocker_StartN(t *testing.T) { func TestDocker_StartN(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }
@ -320,7 +349,7 @@ func TestDocker_StartN(t *testing.T) {
} }
func TestDocker_StartNVersions(t *testing.T) { func TestDocker_StartNVersions(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }
@ -374,7 +403,7 @@ func TestDocker_StartNVersions(t *testing.T) {
} }
func TestDockerHostNet(t *testing.T) { func TestDockerHostNet(t *testing.T) {
if !dockerLocated() { if !dockerIsConnected(t) {
t.SkipNow() t.SkipNow()
} }