From 94a4d00f4b92b941f68ff19d3eef5ddde0e9b456 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 5 Nov 2015 10:03:59 -0800 Subject: [PATCH 01/16] Set a name for the container. --- client/driver/docker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/driver/docker.go b/client/driver/docker.go index 1708c6921..349ae2f08 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -250,6 +250,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do } return docker.CreateContainerOptions{ + Name: fmt.Sprintf("%s-%s", task.Name, ctx.AllocID), Config: config, HostConfig: hostConfig, }, nil From 669635778a889fbece978e59d076273ff2d50fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Fern=C3=A1ndez?= Date: Fri, 13 Nov 2015 22:30:58 +0100 Subject: [PATCH 02/16] POC: allow to add labels for docker containers from job metadata --- client/driver/docker.go | 29 +++++++++++++++++++++++++++-- client/driver/docker_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 862587dd2..ac073cda2 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/args" + "github.com/hashicorp/nomad/client/driver/environment" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" ) @@ -43,6 +44,10 @@ func NewDockerDriver(ctx *DriverContext) Driver { return &DockerDriver{DriverContext: *ctx} } +const ( + labelPrefix = environment.MetaPrefix + "LABEL_" +) + // 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. @@ -266,9 +271,12 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do env.SetAllocDir(filepath.Join("/", allocdir.SharedAllocName)) env.SetTaskLocalDir(filepath.Join("/", allocdir.TaskLocal)) + envVars, labels := splitEnvVarsLabels(env.List()) + config := &docker.Config{ - Env: env.List(), - Image: task.Config["image"], + Env: envVars, + Labels: labels, + Image: task.Config["image"], } rawArgs, hasArgs := task.Config["args"] @@ -557,3 +565,20 @@ func (h *dockerHandle) run() { } close(h.waitCh) } + +// splitEnvVarsLabels returns environment vars and labels to set on the container +func splitEnvVarsLabels(envVars []string) ([]string, map[string]string) { + var envs []string + labels := make(map[string]string) + + for _, value := range envVars { + if strings.HasPrefix(value, labelPrefix) { + parts := strings.SplitN(value, "=", 2) + labels[strings.TrimPrefix(parts[0], labelPrefix)] = parts[1] + } else { + envs = append(envs, value) + } + } + + return envs, labels +} diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 872c2419b..246950333 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -432,3 +432,39 @@ func TestDockerHostNet(t *testing.T) { } defer handle.Kill() } + +func TestsSlitEnvVarsLabels(t *testing.T) { + + environment := []string{ + "NOMAD_IP=1.1.1.1", + "NOMAD_CPU_LIMIT=500", + "NOMAD_META_TEST1=true_one", + "NOMAD_META_LABEL_LAB1=one", + "NOMAD_META_TEST2=test_two", + "NOMAD_META_LABEL_LAB2=two", + "NOMAD_META_TEST3=true_three", + "NOMAD_META_LABEL_LAB3=three", + "NOMAD_MEMORY_LIMIT=1024", + } + + envVars, labels := splitEnvVarsLabels(environment) + if got, want := len(envVars), 6; got != want { + t.Errorf("Error on len. Got: %v. Expect %v items.", got, want) + } + if got, want := len(labels), 3; got != want { + t.Errorf("Error on len. Got: %v. Expect %v items.", got, want) + } + if got, want := envVars[3], environment[4]; got != want { + t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) + } + if got, want := envVars[4], environment[6]; got != want { + t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) + } + if got, want := labels["LAB2"], "two"; got != want { + t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) + } + if got, want := labels["LAB3"], "three"; got != want { + t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) + } + +} From bd4c6b371d09c6b3da7c2cc9ee10906f9373d518 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 16 Nov 2015 19:30:37 -0800 Subject: [PATCH 03/16] Added config reader for booleans --- client/config/config.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/config/config.go b/client/config/config.go index e5f868b79..92c05e6c3 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -1,7 +1,9 @@ package config import ( + "fmt" "io" + "strconv" "github.com/hashicorp/nomad/nomad/structs" ) @@ -73,3 +75,26 @@ func (c *Config) ReadDefault(id string, defaultValue string) string { } return defaultValue } + +// ReadBool parses the specified option as a boolean. +func (c *Config) ReadBool(id string) (bool, error) { + val, ok := c.Options[id] + if !ok { + return false, nil + } + bval, err := strconv.ParseBool(val) + if err != nil { + return false, fmt.Errorf("Failed to parse %s as bool: %s", val, err) + } + return bval, nil +} + +// ReadBoolDefault tries to parse the specified option as a boolean. If there is +// an error in parsing, the default option is returned. +func (c *Config) ReadBoolDefault(id string, defaultValue bool) bool { + val, err := c.ReadBool() + if err != nil { + return defaultValue + } + return val +} From e9bc0905d1789a26847eacb9316721f7dbbf71bf Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 16 Nov 2015 19:55:08 -0800 Subject: [PATCH 04/16] Add missing param and error in the missing case so default will work as expected --- client/config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/config/config.go b/client/config/config.go index 92c05e6c3..1044ff773 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -80,7 +80,7 @@ func (c *Config) ReadDefault(id string, defaultValue string) string { func (c *Config) ReadBool(id string) (bool, error) { val, ok := c.Options[id] if !ok { - return false, nil + return false, fmt.Errorf("Specified config is missing from options") } bval, err := strconv.ParseBool(val) if err != nil { @@ -92,7 +92,7 @@ func (c *Config) ReadBool(id string) (bool, error) { // ReadBoolDefault tries to parse the specified option as a boolean. If there is // an error in parsing, the default option is returned. func (c *Config) ReadBoolDefault(id string, defaultValue bool) bool { - val, err := c.ReadBool() + val, err := c.ReadBool(id) if err != nil { return defaultValue } From bac9bd60dc603c140395c0c66b9232f28b8504dd Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 16 Nov 2015 19:55:49 -0800 Subject: [PATCH 05/16] Improve logging and DRY some of the port mapping code --- client/driver/docker.go | 74 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index f218a911f..8d60a557e 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -97,7 +97,7 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool // Initialize docker API client client, err := d.dockerClient() if err != nil { - d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon: %s", err) + d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s", err) return false, nil } @@ -106,8 +106,10 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool return false, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) } if privileged == true { - d.logger.Printf("[DEBUG] driver.docker: privileged containers enabled. Only enable if needed") + d.logger.Printf("[INFO] driver.docker: privileged containers are enabled") node.Attributes["docker.privileged.enabled"] = "1" + } else { + d.logger.Printf("[INFO] driver.docker: privileged containers are disabled") } _, err = strconv.ParseBool(d.config.ReadDefault("docker.cleanup.container", "true")) @@ -124,7 +126,7 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool // Docker isn't available so we'll simply disable the docker driver. env, err := client.Version() if err != nil { - d.logger.Printf("[INFO] driver.docker: connection to daemon failed: %s", err) + d.logger.Printf("[INFO] driver.docker: could not connect to docker daemon at %s: %s", client.Endpoint(), err) return false, nil } node.Attributes["driver.docker"] = "1" @@ -152,8 +154,10 @@ func (d *DockerDriver) containerBinds(alloc *allocdir.AllocDir, task *structs.Ta func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, driverConfig *DockerDriverConfig) (docker.CreateContainerOptions, error) { var c docker.CreateContainerOptions if task.Resources == nil { + // Guard against missing resources. We should never have been able to + // schedule a job without specifying this. d.logger.Printf("[ERR] driver.docker: task.Resources is empty") - return c, fmt.Errorf("task.Resources is nil and we can't constrain resource usage. We shouldn't have been able to schedule this in the first place.") + return c, fmt.Errorf("task.Resources is empty") } binds, err := d.containerBinds(ctx.AllocDir, task) @@ -211,22 +215,14 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri d.logger.Printf("[DEBUG] driver.docker: binding directories %#v for %s", hostConfig.Binds, task.Config["image"]) // set privileged mode - hostPrivileged, err := strconv.ParseBool(d.config.ReadDefault("docker.privileged.enabled", "false")) - if err != nil { - return c, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) - } - - if driverConfig.Privileged { - if !hostPrivileged { - return c, fmt.Errorf(`Unable to set privileged flag since "docker.privileged.enabled" is false`) - } - - hostConfig.Privileged = driverConfig.Privileged + hostPrivileged := d.config.ReadBoolDefault("docker.privileged.enabled", false) + if driverConfig.Privileged && !hostPrivileged { + return c, fmt.Errorf(`Unable to set privileged flag since "docker.privileged.enabled" is false`) } + hostConfig.Privileged = hostPrivileged // set DNS servers dns := driverConfig.DNS - if dns != "" { for _, v := range strings.Split(dns, ",") { ip := strings.TrimSpace(v) @@ -240,7 +236,6 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri // set DNS search domains dnsSearch := driverConfig.SearchDomains - if dnsSearch != "" { for _, v := range strings.Split(dnsSearch, ",") { hostConfig.DNSSearch = append(hostConfig.DNSSearch, strings.TrimSpace(v)) @@ -250,7 +245,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri mode := driverConfig.NetworkMode if mode == "" { // docker default - d.logger.Printf("[WARN] driver.docker: no mode specified for networking, defaulting to bridge") + d.logger.Printf("[INFO] driver.docker: no mode specified for networking, defaulting to bridge") mode = "bridge" } @@ -266,7 +261,10 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri // Setup port mapping and exposed ports if len(task.Resources.Networks) == 0 { - d.logger.Print("[WARN] driver.docker: No network resources are available for port mapping") + d.logger.Print("[INFO] driver.docker: No network interfaces are available") + if len(driverConfig.PortMap[0]) > 0 { + return nil, fmt.Errorf("Trying to map ports but no network interface is available") + } } else { // TODO add support for more than one network network := task.Resources.Networks[0] @@ -274,11 +272,14 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri exposedPorts := map[docker.Port]struct{}{} for _, port := range network.ReservedPorts { - publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} - publishedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} + portStr := docker.Port(port.Value) + + publishedPorts[portStr+"/tcp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} + publishedPorts[portStr+"/udp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)\n", network.IP, port.Value, port.Value) - exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/tcp")] = struct{}{} - exposedPorts[docker.Port(strconv.Itoa(port.Value)+"/udp")] = struct{}{} + + exposedPorts[portStr+"/tcp"] = struct{}{} + exposedPorts[portStr+"/udp"] = struct{}{} d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port.Value) } @@ -288,15 +289,19 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri if !ok { containerPort = port.Value } - cp := strconv.Itoa(containerPort) - hostPort := strconv.Itoa(port.Value) - publishedPorts[docker.Port(cp+"/tcp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPort}} - publishedPorts[docker.Port(cp+"/udp")] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPort}} + + containerPortStr := docker.Port(strconv.Itoa(containerPort)) + hostPortStr := strconv.Itoa(port.Value) + + publishedPorts[containerPortStr+"/tcp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} + publishedPorts[containerPortStr+"/udp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)", network.IP, port.Value, containerPort) - exposedPorts[docker.Port(cp+"/tcp")] = struct{}{} - exposedPorts[docker.Port(cp+"/udp")] = struct{}{} - d.logger.Printf("[DEBUG] driver.docker: exposed port %s\n", hostPort) - containerToHostPortMap[cp] = port.Value + + exposedPorts[containerPortStr+"/tcp"] = struct{}{} + exposedPorts[containerPortStr+"/udp"] = struct{}{} + d.logger.Printf("[DEBUG] driver.docker: exposed port %s\n", hostPortStr) + + containerToHostPortMap[string(containerPortStr)] = port.Value } env.SetPorts(containerToHostPortMap) @@ -316,9 +321,10 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri if driverConfig.Args != "" { cmd = append(cmd, parsedArgs...) } + d.logger.Printf("[DEBUG] driver.docker: setting container startup command to: %s\n", strings.Join(cmd, " ")) config.Cmd = cmd } else if driverConfig.Args != "" { - d.logger.Println("[DEBUG] driver.docker: ignoring args because command not specified") + d.logger.Println("[DEBUG] driver.docker: ignoring command arguments because command is not specified") } config.Env = env.List() @@ -394,7 +400,7 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle err = client.PullImage(pullOptions, authOptions) if err != nil { - d.logger.Printf("[ERR] driver.docker: pulling container %s", err) + d.logger.Printf("[ERR] driver.docker: failed pulling container %s:%s: %s", repo, tag, err) return nil, fmt.Errorf("Failed to pull `%s`: %s", image, err) } d.logger.Printf("[DEBUG] driver.docker: docker pull %s:%s succeeded", repo, tag) @@ -402,7 +408,7 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle // Now that we have the image we can get the image id dockerImage, err = client.InspectImage(image) if err != nil { - d.logger.Printf("[ERR] driver.docker: getting image id for %s", image) + d.logger.Printf("[ERR] driver.docker: failed getting image id for %s", image) return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err) } } From d6ac88382723a083f9d3425e7b12e6bc05b34459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Fern=C3=A1ndez?= Date: Tue, 17 Nov 2015 07:05:11 +0100 Subject: [PATCH 06/16] revert previous commit --- client/driver/docker.go | 29 ++--------------------------- client/driver/docker_test.go | 36 ------------------------------------ 2 files changed, 2 insertions(+), 63 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index ac073cda2..862587dd2 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/args" - "github.com/hashicorp/nomad/client/driver/environment" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" ) @@ -44,10 +43,6 @@ func NewDockerDriver(ctx *DriverContext) Driver { return &DockerDriver{DriverContext: *ctx} } -const ( - labelPrefix = environment.MetaPrefix + "LABEL_" -) - // 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. @@ -271,12 +266,9 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task) (do env.SetAllocDir(filepath.Join("/", allocdir.SharedAllocName)) env.SetTaskLocalDir(filepath.Join("/", allocdir.TaskLocal)) - envVars, labels := splitEnvVarsLabels(env.List()) - config := &docker.Config{ - Env: envVars, - Labels: labels, - Image: task.Config["image"], + Env: env.List(), + Image: task.Config["image"], } rawArgs, hasArgs := task.Config["args"] @@ -565,20 +557,3 @@ func (h *dockerHandle) run() { } close(h.waitCh) } - -// splitEnvVarsLabels returns environment vars and labels to set on the container -func splitEnvVarsLabels(envVars []string) ([]string, map[string]string) { - var envs []string - labels := make(map[string]string) - - for _, value := range envVars { - if strings.HasPrefix(value, labelPrefix) { - parts := strings.SplitN(value, "=", 2) - labels[strings.TrimPrefix(parts[0], labelPrefix)] = parts[1] - } else { - envs = append(envs, value) - } - } - - return envs, labels -} diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 246950333..872c2419b 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -432,39 +432,3 @@ func TestDockerHostNet(t *testing.T) { } defer handle.Kill() } - -func TestsSlitEnvVarsLabels(t *testing.T) { - - environment := []string{ - "NOMAD_IP=1.1.1.1", - "NOMAD_CPU_LIMIT=500", - "NOMAD_META_TEST1=true_one", - "NOMAD_META_LABEL_LAB1=one", - "NOMAD_META_TEST2=test_two", - "NOMAD_META_LABEL_LAB2=two", - "NOMAD_META_TEST3=true_three", - "NOMAD_META_LABEL_LAB3=three", - "NOMAD_MEMORY_LIMIT=1024", - } - - envVars, labels := splitEnvVarsLabels(environment) - if got, want := len(envVars), 6; got != want { - t.Errorf("Error on len. Got: %v. Expect %v items.", got, want) - } - if got, want := len(labels), 3; got != want { - t.Errorf("Error on len. Got: %v. Expect %v items.", got, want) - } - if got, want := envVars[3], environment[4]; got != want { - t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) - } - if got, want := envVars[4], environment[6]; got != want { - t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) - } - if got, want := labels["LAB2"], "two"; got != want { - t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) - } - if got, want := labels["LAB3"], "three"; got != want { - t.Errorf("Error. Got: '%v'. Expect '%v'.", got, want) - } - -} From f7f83ee81c9f8234b87514200e56ccd6e12c129c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Fern=C3=A1ndez?= Date: Tue, 17 Nov 2015 14:12:49 +0100 Subject: [PATCH 07/16] allow to set labels on docker containers --- client/driver/docker.go | 26 +++++++++++----- client/driver/docker_test.go | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index f2d25f9ba..c862b58e3 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -35,14 +35,15 @@ type DockerAuthConfig struct { type DockerDriverConfig struct { DockerAuthConfig - ImageName string `mapstructure:"image"` // Container's Image Name - Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up - Args string `mapstructure:"args"` // The arguments to the Command/Entrypoint - NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, net and none - PortMap []map[string]int `mapstructure:"port_map"` // A map of host port labels and the ports exposed on the container - Privileged bool `mapstructure:"privileged"` // Flag to run the container in priviledged mode - DNS string `mapstructure:"dns_server"` // DNS Server for containers - SearchDomains string `mapstructure:"search_domains"` // DNS Search domains for containers + ImageName string `mapstructure:"image"` // Container's Image Name + Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up + Args string `mapstructure:"args"` // The arguments to the Command/Entrypoint + NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, net and none + PortMap []map[string]int `mapstructure:"port_map"` // A map of host port labels and the ports exposed on the container + Privileged bool `mapstructure:"privileged"` // Flag to run the container in priviledged mode + DNS string `mapstructure:"dns_server"` // DNS Server for containers + SearchDomains string `mapstructure:"search_domains"` // DNS Search domains for containers + Labels []map[string]string `mapstructure:"labels"` // Labels to set when the container starts up } func (c *DockerDriverConfig) Validate() error { @@ -53,6 +54,10 @@ func (c *DockerDriverConfig) Validate() error { if len(c.PortMap) > 1 { return fmt.Errorf("Only one port_map block is allowed in the docker driver config") } + + if len(c.Labels) > 1 { + return fmt.Errorf("Only one labels block is allowed in the docker driver config") + } return nil } @@ -321,6 +326,11 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri d.logger.Println("[DEBUG] driver.docker: ignoring args because command not specified") } + if len(driverConfig.Labels) == 1 { + config.Labels = driverConfig.Labels[0] + d.logger.Println("[DEBUG] driver.docker: applied labels on the container") + } + config.Env = env.List() return docker.CreateContainerOptions{ Config: config, diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 87bb58e6f..05df5a647 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -1,10 +1,12 @@ package driver import ( + "encoding/json" "fmt" "io/ioutil" "path/filepath" "reflect" + "strings" "testing" "time" @@ -433,3 +435,58 @@ func TestDockerHostNet(t *testing.T) { } defer handle.Kill() } + +func TestDockerLabels(t *testing.T) { + if !dockerIsConnected(t) { + t.SkipNow() + } + + task := taskTemplate() + task.Config["labels"] = []map[string]string{ + map[string]string{ + "label1": "value1", + "label2": "value2", + }, + } + + driverCtx := testDockerDriverContext(task.Name) + ctx := testDriverExecContext(task, driverCtx) + defer ctx.AllocDir.Destroy() + d := NewDockerDriver(driverCtx) + + handle, err := d.Start(ctx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + + client, err := docker.NewClientFromEnv() + if err != nil { + t.Fatalf("err: %v", err) + } + + // don't know if is queriable in a clean way + parts := strings.SplitN(handle.ID(), ":", 2) + var pid dockerPID + err = json.Unmarshal([]byte(parts[1]), &pid) + if err != nil { + t.Fatalf("err: %v", err) + } + + container, err := client.InspectContainer(pid.ContainerID) + if err != nil { + t.Fatalf("err: %v", err) + } + + if want, got := 2, len(container.Config.Labels); want != got { + t.Errorf("Wrong labels count for docker job. Expect: %d, got: %d", want, got) + } + + if want, got := "value1", container.Config.Labels["label1"]; want != got { + t.Errorf("Wrong label value docker job. Expect: %s, got: %s", want, got) + } + + defer handle.Kill() +} From 2521aea7d4fa7a5d6982fcdb61b0633ba7850741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Fern=C3=A1ndez?= Date: Tue, 17 Nov 2015 14:14:35 +0100 Subject: [PATCH 08/16] updating website: docker driver configuration --- website/source/docs/drivers/docker.html.md | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index dfa4c85a5..5a097fe36 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -32,25 +32,28 @@ The `docker` driver supports the following configuration in the job specificatio network mode is not supported right now and is reported as an invalid option. -* `privileged` - (optional) Privileged mode gives the container full access to +* `privileged` - (Optional) Privileged mode gives the container full access to the host. Valid options are `"true"` and `"false"` (defaults to `"false"`). Tasks with `privileged` set can only run on Nomad Agents with `docker.privileged.enabled = "true"`. -* `dns-servers` - (optional) A comma separated list of DNS servers for the container +* `dns-servers` - (Optional) A comma separated list of DNS servers for the container to use (e.g. "8.8.8.8,8.8.4.4"). *Docker API v1.10 and above only* -* `search-domains` - (optional) A comma separated list of DNS search domains for the +* `search-domains` - (Optional) A comma separated list of DNS search domains for the container to use. - + +* `labels` - (Optional) A key/value map of labels to set to the containers on start. + + **Authentication** -Registry authentication can be set per task with the following authentication -parameters. These options can provide access to private repositories that +Registry authentication can be set per task with the following authentication +parameters. These options can provide access to private repositories that utilize the docker remote api (e.g. dockerhub, quay.io) - - `auth.username` - (optional) The account username - - `auth.password` - (optional) The account password - - `auth.email` - (optional) The account email - - `auth.server-address` - (optional) The server domain/ip without the protocol + - `auth.username` - (Optional) The account username + - `auth.password` - (Optional) The account password + - `auth.email` - (Optional) The account email + - `auth.server-address` - (Optional) The server domain/ip without the protocol ### Port Mapping From 629cdd6baa92ebc6e0a776bc94002b3b7ec4e937 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 14:51:38 -0800 Subject: [PATCH 09/16] Clean up extraneous variables --- client/driver/docker.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 9b225fcb0..6924acec6 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -224,9 +224,8 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri hostConfig.Privileged = hostPrivileged // set DNS servers - dns := driverConfig.DNS - if dns != "" { - for _, v := range strings.Split(dns, ",") { + if driverConfig.DNS != "" { + for _, v := range strings.Split(driverConfig.DNS, ",") { ip := strings.TrimSpace(v) if net.ParseIP(ip) != nil { hostConfig.DNS = append(hostConfig.DNS, ip) @@ -237,9 +236,8 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri } // set DNS search domains - dnsSearch := driverConfig.SearchDomains - if dnsSearch != "" { - for _, v := range strings.Split(dnsSearch, ",") { + if driverConfig.SearchDomains != "" { + for _, v := range strings.Split(driverConfig.SearchDomains, ",") { hostConfig.DNSSearch = append(hostConfig.DNSSearch, strings.TrimSpace(v)) } } @@ -265,7 +263,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri if len(task.Resources.Networks) == 0 { d.logger.Print("[INFO] driver.docker: No network interfaces are available") if len(driverConfig.PortMap[0]) > 0 { - return nil, fmt.Errorf("Trying to map ports but no network interface is available") + return c, fmt.Errorf("Trying to map ports but no network interface is available") } } else { // TODO add support for more than one network From 3b7c252090e8fce10f2b3576d952dc09cd56a1da Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 15:17:44 -0800 Subject: [PATCH 10/16] Improve phrasing in log messages --- client/driver/docker.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 6924acec6..ecbfd053a 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -245,7 +245,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri mode := driverConfig.NetworkMode if mode == "" { // docker default - d.logger.Printf("[INFO] driver.docker: no mode specified for networking, defaulting to bridge") + d.logger.Printf("[DEBUG] driver.docker: no mode specified for networking, defaulting to bridge") mode = "bridge" } @@ -412,18 +412,17 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err) } } - d.logger.Printf("[DEBUG] driver.docker: using image %s", dockerImage.ID) - d.logger.Printf("[INFO] driver.docker: identified image %s as %s", image, dockerImage.ID) + d.logger.Printf("[DEBUG] driver.docker: identified image %s as %s", image, dockerImage.ID) config, err := d.createContainer(ctx, task, &driverConfig) if err != nil { - d.logger.Printf("[ERR] driver.docker: %s", err) - return nil, fmt.Errorf("Failed to create container config for image %s", image) + d.logger.Printf("[ERR] driver.docker: failed to create container configuration for image %s: %s", image, err) + return nil, fmt.Errorf("Failed to create container configuration for image %s: %s", image, err) } // Create a container container, err := client.CreateContainer(config) if err != nil { - d.logger.Printf("[ERR] driver.docker: %s", err) + d.logger.Printf("[ERR] driver.docker: failed to create container from image %s: %s", image, err) return nil, fmt.Errorf("Failed to create container from image %s", image) } d.logger.Printf("[INFO] driver.docker: created container %s", container.ID) @@ -538,7 +537,7 @@ func (h *dockerHandle) Kill() error { // Stop the container err := h.client.StopContainer(h.containerID, 5) if err != nil { - log.Printf("[ERR] driver.docker: failed stopping container %s", h.containerID) + log.Printf("[ERR] driver.docker: failed to stop container %s", h.containerID) return fmt.Errorf("Failed to stop container %s: %s", h.containerID, err) } log.Printf("[INFO] driver.docker: stopped container %s", h.containerID) @@ -550,7 +549,7 @@ func (h *dockerHandle) Kill() error { RemoveVolumes: true, }) if err != nil { - log.Printf("[ERR] driver.docker: removing container %s", h.containerID) + log.Printf("[ERR] driver.docker: failed to remove container %s", h.containerID) return fmt.Errorf("Failed to remove container %s: %s", h.containerID, err) } log.Printf("[INFO] driver.docker: removed container %s", h.containerID) From 8bbaf363dd71324cb73fb16a2285cab1157be2f4 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Tue, 17 Nov 2015 15:20:01 -0800 Subject: [PATCH 11/16] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5f6ff2d3..0963b992d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,12 +41,16 @@ IMPROVEMENTS: * client: Test Skip Detection [GH-221] * driver/docker: Advanced docker driver options [GH-390] * driver/docker: Docker hostname can be set [GH-426] +<<<<<<< Updated upstream * driver/docker: Mount task local and alloc directory to docker containers [GH-290] * driver/docker: Pass JVM options in java driver [GH-293, GH-297] * drivers: Use BlkioWeight rather than BlkioThrottleReadIopsDevice [GH-222] * jobspec and drivers: Driver configuration supports arbitrary struct to be passed in jobspec [GH-415] +======= + * driver/docker: Docker container name can be set [GH-389] +>>>>>>> Stashed changes BUG FIXES: From 74d95a0d2aec0c42101b265ca85eeec893c31632 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Tue, 17 Nov 2015 15:39:38 -0800 Subject: [PATCH 12/16] Update rkt docs to denote its experimental status --- website/source/docs/drivers/rkt.html.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/website/source/docs/drivers/rkt.html.md b/website/source/docs/drivers/rkt.html.md index d96a4b16f..ddbb76601 100644 --- a/website/source/docs/drivers/rkt.html.md +++ b/website/source/docs/drivers/rkt.html.md @@ -3,20 +3,22 @@ layout: "docs" page_title: "Drivers: Rkt" sidebar_current: "docs-drivers-rkt" description: |- - The Rkt task driver is used to run application containers using Rkt. + The rkt task driver is used to run application containers using rkt. --- -# Rkt Driver +# Rkt Driver - Experimental Name: `rkt` -The `Rkt` driver provides an interface for using CoreOS Rkt for running +The `rkt` driver provides an interface for using CoreOS rkt for running application containers. Currently, the driver supports launching -containers. +containers but does not support resource isolation or dynamic ports. This can +lead to resource over commitment and port conflicts and as such, this driver is +being marked as experimental and should be used with care. ## Task Configuration -The `Rkt` driver supports the following configuration in the job spec: +The `rkt` driver supports the following configuration in the job spec: * `trust_prefix` - **(Optional)** The trust prefix to be passed to rkt. Must be reachable from the box running the nomad agent. If not specified, the image is run without @@ -28,23 +30,23 @@ hash, ACI address or docker registry. ## Task Directories -The `Rkt` driver does not currently support mounting the `alloc/` and `local/` -directory. It is currently blocked by this [Rkt +The `rkt` driver does not currently support mounting the `alloc/` and `local/` +directory. It is currently blocked by this [rkt issue](https://github.com/coreos/rkt/issues/761). As such the coresponding [environment variables](/docs/jobspec/environment.html#task_dir) are not set. ## Client Requirements -The `Rkt` driver requires rkt to be installed and in your systems `$PATH`. +The `rkt` driver requires rkt to be installed and in your systems `$PATH`. The `trust_prefix` must be accessible by the node running Nomad. This can be an internal source, private to your cluster, but it must be reachable by the client over HTTP. ## Client Attributes -The `Rkt` driver will set the following client attributes: +The `rkt` driver will set the following client attributes: -* `driver.rkt` - Set to `1` if Rkt is found on the host node. Nomad determines +* `driver.rkt` - Set to `1` if rkt is found on the host node. Nomad determines this by executing `rkt version` on the host and parsing the output * `driver.rkt.version` - Version of `rkt` eg: `0.8.1` * `driver.rkt.appc.version` - Version of `appc` that `rkt` is using eg: `0.8.1` From 43aaf94383c416eec9dc814363f551d1faaaae68 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 16:31:47 -0800 Subject: [PATCH 13/16] Do Itoa once so line items are simpler --- client/driver/docker.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index ecbfd053a..47cf662af 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -272,14 +272,15 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri exposedPorts := map[docker.Port]struct{}{} for _, port := range network.ReservedPorts { - portStr := docker.Port(port.Value) + hostPortStr := strconv.Itoa(port.Value) + dockerPort := docker.Port(hostPortStr) - publishedPorts[portStr+"/tcp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} - publishedPorts[portStr+"/udp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: strconv.Itoa(port.Value)}} + publishedPorts[dockerPort+"/tcp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} + publishedPorts[dockerPort+"/udp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)\n", network.IP, port.Value, port.Value) - exposedPorts[portStr+"/tcp"] = struct{}{} - exposedPorts[portStr+"/udp"] = struct{}{} + exposedPorts[dockerPort+"/tcp"] = struct{}{} + exposedPorts[dockerPort+"/udp"] = struct{}{} d.logger.Printf("[DEBUG] driver.docker: exposed port %d\n", port.Value) } From 67ee502acb1ca3593201a6edb2d6eb09b24f3f7c Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 16:49:01 -0800 Subject: [PATCH 14/16] Added missing linebreaks for log.Printf calls --- client/driver/docker.go | 56 ++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 47cf662af..3c1e3e9cb 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -98,7 +98,7 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool // Initialize docker API client client, err := d.dockerClient() if err != nil { - d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s", err) + d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s\n", err) return false, nil } @@ -107,10 +107,10 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool return false, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) } if privileged == true { - d.logger.Printf("[INFO] driver.docker: privileged containers are enabled") + d.logger.Println("[INFO] driver.docker: privileged containers are enabled") node.Attributes["docker.privileged.enabled"] = "1" } else { - d.logger.Printf("[INFO] driver.docker: privileged containers are disabled") + d.logger.Println("[INFO] driver.docker: privileged containers are disabled") } _, err = strconv.ParseBool(d.config.ReadDefault("docker.cleanup.container", "true")) @@ -127,7 +127,7 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool // Docker isn't available so we'll simply disable the docker driver. env, err := client.Version() if err != nil { - d.logger.Printf("[INFO] driver.docker: could not connect to docker daemon at %s: %s", client.Endpoint(), err) + d.logger.Printf("[INFO] driver.docker: could not connect to docker daemon at %s: %s\n", client.Endpoint(), err) return false, nil } node.Attributes["driver.docker"] = "1" @@ -157,7 +157,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri if task.Resources == nil { // Guard against missing resources. We should never have been able to // schedule a job without specifying this. - d.logger.Printf("[ERR] driver.docker: task.Resources is empty") + d.logger.Println("[ERR] driver.docker: task.Resources is empty") return c, fmt.Errorf("task.Resources is empty") } @@ -212,9 +212,9 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri Binds: binds, } - d.logger.Printf("[DEBUG] driver.docker: using %d bytes memory for %s", hostConfig.Memory, task.Config["image"]) - d.logger.Printf("[DEBUG] driver.docker: using %d cpu shares for %s", hostConfig.CPUShares, task.Config["image"]) - d.logger.Printf("[DEBUG] driver.docker: binding directories %#v for %s", hostConfig.Binds, task.Config["image"]) + d.logger.Printf("[DEBUG] driver.docker: using %d bytes memory for %s\n", hostConfig.Memory, task.Config["image"]) + d.logger.Printf("[DEBUG] driver.docker: using %d cpu shares for %s\n", hostConfig.CPUShares, task.Config["image"]) + d.logger.Printf("[DEBUG] driver.docker: binding directories %#v for %s\n", hostConfig.Binds, task.Config["image"]) // set privileged mode hostPrivileged := d.config.ReadBoolDefault("docker.privileged.enabled", false) @@ -230,7 +230,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri if net.ParseIP(ip) != nil { hostConfig.DNS = append(hostConfig.DNS, ip) } else { - d.logger.Printf("[ERR] driver.docker: invalid ip address for container dns server: %s", ip) + d.logger.Printf("[ERR] driver.docker: invalid ip address for container dns server: %s\n", ip) } } } @@ -245,23 +245,23 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri mode := driverConfig.NetworkMode if mode == "" { // docker default - d.logger.Printf("[DEBUG] driver.docker: no mode specified for networking, defaulting to bridge") + d.logger.Println("[DEBUG] driver.docker: no mode specified for networking, defaulting to bridge") mode = "bridge" } // Ignore the container mode for now switch mode { case "default", "bridge", "none", "host": - d.logger.Printf("[DEBUG] driver.docker: using %s as network mode", mode) + d.logger.Printf("[DEBUG] driver.docker: using %s as network mode\n", mode) default: - d.logger.Printf("[ERR] driver.docker: invalid setting for network mode: %s", mode) + d.logger.Printf("[ERR] driver.docker: invalid setting for network mode: %s\n", mode) return c, fmt.Errorf("Invalid setting for network mode: %s", mode) } hostConfig.NetworkMode = mode // Setup port mapping and exposed ports if len(task.Resources.Networks) == 0 { - d.logger.Print("[INFO] driver.docker: No network interfaces are available") + d.logger.Println("[DEBUG] driver.docker: No network interfaces are available") if len(driverConfig.PortMap[0]) > 0 { return c, fmt.Errorf("Trying to map ports but no network interface is available") } @@ -296,7 +296,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task, dri publishedPorts[containerPortStr+"/tcp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} publishedPorts[containerPortStr+"/udp"] = []docker.PortBinding{docker.PortBinding{HostIP: network.IP, HostPort: hostPortStr}} - d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)", network.IP, port.Value, containerPort) + d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)\n", network.IP, port.Value, containerPort) exposedPorts[containerPortStr+"/tcp"] = struct{}{} exposedPorts[containerPortStr+"/udp"] = struct{}{} @@ -401,40 +401,40 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle err = client.PullImage(pullOptions, authOptions) if err != nil { - d.logger.Printf("[ERR] driver.docker: failed pulling container %s:%s: %s", repo, tag, err) + d.logger.Printf("[ERR] driver.docker: failed pulling container %s:%s: %s\n", repo, tag, err) return nil, fmt.Errorf("Failed to pull `%s`: %s", image, err) } - d.logger.Printf("[DEBUG] driver.docker: docker pull %s:%s succeeded", repo, tag) + d.logger.Printf("[DEBUG] driver.docker: docker pull %s:%s succeeded\n", repo, tag) // Now that we have the image we can get the image id dockerImage, err = client.InspectImage(image) if err != nil { - d.logger.Printf("[ERR] driver.docker: failed getting image id for %s", image) + d.logger.Printf("[ERR] driver.docker: failed getting image id for %s\n", image) return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err) } } - d.logger.Printf("[DEBUG] driver.docker: identified image %s as %s", image, dockerImage.ID) + d.logger.Printf("[DEBUG] driver.docker: identified image %s as %s\n", image, dockerImage.ID) config, err := d.createContainer(ctx, task, &driverConfig) if err != nil { - d.logger.Printf("[ERR] driver.docker: failed to create container configuration for image %s: %s", image, err) + d.logger.Printf("[ERR] driver.docker: failed to create container configuration for image %s: %s\n", image, err) return nil, fmt.Errorf("Failed to create container configuration for image %s: %s", image, err) } // Create a container container, err := client.CreateContainer(config) if err != nil { - d.logger.Printf("[ERR] driver.docker: failed to create container from image %s: %s", image, err) + d.logger.Printf("[ERR] driver.docker: failed to create container from image %s: %s\n", image, err) return nil, fmt.Errorf("Failed to create container from image %s", image) } - d.logger.Printf("[INFO] driver.docker: created container %s", container.ID) + d.logger.Printf("[INFO] driver.docker: created container %s\n", container.ID) // Start the container err = client.StartContainer(container.ID, container.HostConfig) if err != nil { - d.logger.Printf("[ERR] driver.docker: starting container %s", container.ID) + d.logger.Printf("[ERR] driver.docker: starting container %s\n", container.ID) return nil, fmt.Errorf("Failed to start container %s", container.ID) } - d.logger.Printf("[INFO] driver.docker: started container %s", container.ID) + d.logger.Printf("[INFO] driver.docker: started container %s\n", container.ID) // Return a driver handle h := &dockerHandle{ @@ -468,7 +468,7 @@ func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, er if err != nil { return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) } - d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s", handleID) + d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s\n", handleID) // Initialize docker API client client, err := d.dockerClient() @@ -519,7 +519,7 @@ func (h *dockerHandle) ID() string { } data, err := json.Marshal(pid) if err != nil { - h.logger.Printf("[ERR] driver.docker: failed to marshal docker PID to JSON: %s", err) + h.logger.Printf("[ERR] driver.docker: failed to marshal docker PID to JSON: %s\n", err) } return fmt.Sprintf("DOCKER:%s", string(data)) } @@ -573,12 +573,12 @@ func (h *dockerHandle) Kill() error { } inUse := len(containers) if inUse > 0 { - log.Printf("[INFO] driver.docker: image %s is still in use by %d containers", h.imageID, inUse) + log.Printf("[INFO] driver.docker: image %s is still in use by %d containers\n", h.imageID, inUse) } else { return fmt.Errorf("Failed to remove image %s", h.imageID) } } else { - log.Printf("[INFO] driver.docker: removed image %s", h.imageID) + log.Printf("[INFO] driver.docker: removed image %s\n", h.imageID) } } return nil @@ -588,7 +588,7 @@ func (h *dockerHandle) run() { // Wait for it... exitCode, err := h.client.WaitContainer(h.containerID) if err != nil { - h.logger.Printf("[ERR] driver.docker: unable to wait for %s; container already terminated", h.containerID) + h.logger.Printf("[ERR] driver.docker: unable to wait for %s; container already terminated\n", h.containerID) } if exitCode != 0 { From a2f41882468f3f4d69c737e221767dbe32622fe9 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 16:58:23 -0800 Subject: [PATCH 15/16] Streamline reading boolean values from the driver / job config --- client/driver/docker.go | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 3c1e3e9cb..d92579e23 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -102,26 +102,14 @@ func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool return false, nil } - privileged, err := strconv.ParseBool(d.config.ReadDefault("docker.privileged.enabled", "false")) - if err != nil { - return false, fmt.Errorf("Unable to parse docker.privileged.enabled: %s", err) - } - if privileged == true { + privileged := d.config.ReadBoolDefault("docker.privileged.enabled", false) + if privileged { d.logger.Println("[INFO] driver.docker: privileged containers are enabled") node.Attributes["docker.privileged.enabled"] = "1" } else { d.logger.Println("[INFO] driver.docker: privileged containers are disabled") } - _, err = strconv.ParseBool(d.config.ReadDefault("docker.cleanup.container", "true")) - if err != nil { - return false, fmt.Errorf("Unable to parse docker.cleanup.container: %s", err) - } - _, err = strconv.ParseBool(d.config.ReadDefault("docker.cleanup.image", "true")) - if err != nil { - 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. @@ -355,14 +343,8 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle return nil, fmt.Errorf("CPU limit cannot be zero") } - cleanupContainer, err := strconv.ParseBool(d.config.ReadDefault("docker.cleanup.container", "true")) - if err != nil { - return nil, fmt.Errorf("Unable to parse docker.cleanup.container: %s", err) - } - cleanupImage, err := strconv.ParseBool(d.config.ReadDefault("docker.cleanup.image", "true")) - if err != nil { - return nil, fmt.Errorf("Unable to parse docker.cleanup.image: %s", err) - } + cleanupContainer := d.config.ReadBoolDefault("docker.cleanup.container", true) + cleanupImage := d.config.ReadBoolDefault("docker.cleanup.image", true) // Initialize docker API client client, err := d.dockerClient() @@ -452,19 +434,13 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle } func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { - cleanupContainer, err := strconv.ParseBool(d.config.ReadDefault("docker.cleanup.container", "true")) - if err != nil { - return nil, fmt.Errorf("Unable to parse docker.cleanup.container: %s", err) - } - cleanupImage, err := strconv.ParseBool(d.config.ReadDefault("docker.cleanup.image", "true")) - if err != nil { - return nil, fmt.Errorf("Unable to parse docker.cleanup.image: %s", err) - } + cleanupContainer := d.config.ReadBoolDefault("docker.cleanup.container", true) + cleanupImage := d.config.ReadBoolDefault("docker.cleanup.image", true) // Split the handle pidBytes := []byte(strings.TrimPrefix(handleID, "DOCKER:")) pid := &dockerPID{} - err = json.Unmarshal(pidBytes, pid) + err := json.Unmarshal(pidBytes, pid) if err != nil { return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) } From b22b5eb3aa1cf9555ab97af3507940ec1b6a2328 Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Tue, 17 Nov 2015 17:12:45 -0800 Subject: [PATCH 16/16] Inline err assignment --- client/driver/docker.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index d92579e23..75e1278f6 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -440,8 +440,7 @@ func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, er // Split the handle pidBytes := []byte(strings.TrimPrefix(handleID, "DOCKER:")) pid := &dockerPID{} - err := json.Unmarshal(pidBytes, pid) - if err != nil { + if err := json.Unmarshal(pidBytes, pid); err != nil { return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) } d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s\n", handleID)