From 49416ebf4c2e880f593162f96d90475eaad636fd Mon Sep 17 00:00:00 2001 From: Dmitry Galinsky Date: Fri, 11 Nov 2016 19:38:16 +0300 Subject: [PATCH] Add network_aliases for docker driver --- client/driver/docker.go | 23 +++++++- client/driver/docker_test.go | 69 +++++++++++++++++++--- website/source/docs/drivers/docker.html.md | 15 +++++ 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index c3a0268d3c7e..ae0f4dd2590c 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -102,6 +102,7 @@ type DockerDriverConfig struct { Args []string `mapstructure:"args"` // The arguments to the Command/Entrypoint IpcMode string `mapstructure:"ipc_mode"` // The IPC mode of the container - host and none NetworkMode string `mapstructure:"network_mode"` // The network mode of the container - host, nat and none + NetworkAliases []string `mapstructure:"network_aliases"` // The network-scoped alias for the container PidMode string `mapstructure:"pid_mode"` // The PID mode of the container - host and none UTSMode string `mapstructure:"uts_mode"` // The UTS mode of the container - host and none UsernsMode string `mapstructure:"userns_mode"` // The User namespace mode of the container - host and none @@ -154,6 +155,7 @@ func NewDockerDriverConfig(task *structs.Task, env *env.TaskEnvironment) (*Docke dconf.Command = env.ReplaceEnv(dconf.Command) dconf.IpcMode = env.ReplaceEnv(dconf.IpcMode) dconf.NetworkMode = env.ReplaceEnv(dconf.NetworkMode) + dconf.NetworkAliases = env.ParseAndReplace(dconf.NetworkAliases) dconf.PidMode = env.ReplaceEnv(dconf.PidMode) dconf.UTSMode = env.ReplaceEnv(dconf.UTSMode) dconf.Hostname = env.ReplaceEnv(dconf.Hostname) @@ -261,6 +263,9 @@ func (d *DockerDriver) Validate(config map[string]interface{}) error { "network_mode": &fields.FieldSchema{ Type: fields.TypeString, }, + "network_aliases": &fields.FieldSchema{ + Type: fields.TypeArray, + }, "pid_mode": &fields.FieldSchema{ Type: fields.TypeString, }, @@ -818,10 +823,22 @@ func (d *DockerDriver) createContainerConfig(ctx *ExecContext, task *structs.Tas containerName := fmt.Sprintf("%s-%s", task.Name, ctx.AllocID) d.logger.Printf("[DEBUG] driver.docker: setting container name to: %s", containerName) + networkingConfig := &docker.NetworkingConfig{ + EndpointsConfig: make(map[string]*docker.EndpointConfig), + } + + if len(driverConfig.NetworkAliases) > 0 { + networkingConfig.EndpointsConfig[hostConfig.NetworkMode] = &docker.EndpointConfig{ + Aliases: driverConfig.NetworkAliases, + } + d.logger.Printf("[DEBUG] driver.docker: applied network aliases on the container: [%s]%+v", hostConfig.NetworkMode, driverConfig.NetworkAliases) + } + return docker.CreateContainerOptions{ - Name: containerName, - Config: config, - HostConfig: hostConfig, + Name: containerName, + Config: config, + HostConfig: hostConfig, + NetworkingConfig: networkingConfig, }, nil } diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 69cd9f01ca3a..ff90747d874b 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -87,15 +87,11 @@ func dockerTask() (*structs.Task, int, int) { // If there is a problem during setup this function will abort or skip the test // and indicate the reason. func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle, func()) { - if !testutil.DockerIsConnected(t) { - t.SkipNow() - } - - client, err := docker.NewClientFromEnv() - if err != nil { - t.Fatalf("Failed to initialize client: %s\nStack\n%s", err, debug.Stack()) - } + client := newTestDockerClient(t) + return dockerSetupWithClient(t, task, client) +} +func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, DriverHandle, func()) { driverCtx, execCtx := testDriverContexts(task) driverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} driver := NewDockerDriver(driverCtx) @@ -119,6 +115,18 @@ func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle return client, handle, cleanup } +func newTestDockerClient(t *testing.T) *docker.Client { + if !testutil.DockerIsConnected(t) { + t.SkipNow() + } + + client, err := docker.NewClientFromEnv() + if err != nil { + t.Fatalf("Failed to initialize client: %s\nStack\n%s", err, debug.Stack()) + } + return client +} + // This test should always pass, even if docker daemon is not available func TestDockerDriver_Fingerprint(t *testing.T) { driverCtx, execCtx := testDriverContexts(&structs.Task{Name: "foo", Resources: basicResources}) @@ -589,6 +597,51 @@ func TestDockerDriver_NetworkMode_Host(t *testing.T) { } } +func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) { + // Because go-dockerclient doesn't provide api for query network aliases, just check that + // a container can be created with a 'network_aliases' property + + // Create network, network-scoped alias is supported only for containers in user defined networks + client := newTestDockerClient(t) + networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"} + network, err := client.CreateNetwork(networkOpts) + if err != nil { + t.Fatalf("err: %v", err) + } + defer client.RemoveNetwork(network.ID) + + expected := []string{"foobar"} + task := &structs.Task{ + Name: "nc-demo", + Config: map[string]interface{}{ + "image": "busybox", + "load": []string{"busybox.tar"}, + "command": "/bin/nc", + "args": []string{"-l", "127.0.0.1", "-p", "0"}, + "network_mode": network.Name, + "network_aliases": expected, + }, + Resources: &structs.Resources{ + MemoryMB: 256, + CPU: 512, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + } + + client, handle, cleanup := dockerSetupWithClient(t, task, client) + defer cleanup() + + waitForExist(t, client, handle.(*DockerHandle)) + + _, err = client.InspectContainer(handle.(*DockerHandle).ContainerID()) + if err != nil { + t.Fatalf("err: %v", err) + } +} + func TestDockerDriver_Labels(t *testing.T) { task, _, _ := dockerTask() task.Config["labels"] = []map[string]string{ diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index b4a8de9adfe2..202088fdd63c 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -126,6 +126,21 @@ The `docker` driver supports the following configuration in the job spec: pre-docker 1.9 are `default`, `bridge`, `host`, `none`, or `container:name`. See below for more details. +* `network_aliases` - (Optional) A list of network-scoped aliases, provide a way for a + container to be discovered by an alternate name by any other container within + the scope of a particular network. Network-scoped alias is supported only for + containers in user defined networks + + ```hcl + config { + network_mode = "overlay" + network_aliases = [ + "${NOMAD_TASK_NAME}", + "${NOMAD_TASK_NAME}-${NOMAD_ALLOC_INDEX}" + ] + } + ``` + * `hostname` - (Optional) The hostname to assign to the container. When launching more than one of a task (using `count`) with this option set, every container the task starts will have the same hostname.