diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index ded3533515..8077efdb05 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -41,8 +41,8 @@ type containerAdapter struct { dependencies exec.DependencyGetter } -func newContainerAdapter(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*containerAdapter, error) { - ctnr, err := newContainerConfig(task) +func newContainerAdapter(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) { + ctnr, err := newContainerConfig(task, node) if err != nil { return nil, err } diff --git a/daemon/cluster/executor/container/attachment.go b/daemon/cluster/executor/container/attachment.go index 2aab47e462..405aa2db6e 100644 --- a/daemon/cluster/executor/container/attachment.go +++ b/daemon/cluster/executor/container/attachment.go @@ -20,8 +20,8 @@ type networkAttacherController struct { closed chan struct{} } -func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*networkAttacherController, error) { - adapter, err := newContainerAdapter(b, task, dependencies) +func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) { + adapter, err := newContainerAdapter(b, task, node, dependencies) if err != nil { return nil, err } diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index 8ea4126d73..59ac9bf215 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -48,12 +48,12 @@ type containerConfig struct { // newContainerConfig returns a validated container config. No methods should // return an error if this function returns without error. -func newContainerConfig(t *api.Task) (*containerConfig, error) { +func newContainerConfig(t *api.Task, node *api.NodeDescription) (*containerConfig, error) { var c containerConfig - return &c, c.setTask(t) + return &c, c.setTask(t, node) } -func (c *containerConfig) setTask(t *api.Task) error { +func (c *containerConfig) setTask(t *api.Task, node *api.NodeDescription) error { if t.Spec.GetContainer() == nil && t.Spec.GetAttachment() == nil { return exec.ErrRuntimeUnsupported } @@ -78,7 +78,7 @@ func (c *containerConfig) setTask(t *api.Task) error { c.task = t if t.Spec.GetContainer() != nil { - preparedSpec, err := template.ExpandContainerSpec(nil, t) + preparedSpec, err := template.ExpandContainerSpec(node, t) if err != nil { return err } diff --git a/daemon/cluster/executor/container/controller.go b/daemon/cluster/executor/container/controller.go index 3ba4302d55..dda12591a9 100644 --- a/daemon/cluster/executor/container/controller.go +++ b/daemon/cluster/executor/container/controller.go @@ -40,8 +40,8 @@ type controller struct { var _ exec.Controller = &controller{} // NewController returns a docker exec runner for the provided task. -func newController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*controller, error) { - adapter, err := newContainerAdapter(b, task, dependencies) +func newController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) { + adapter, err := newContainerAdapter(b, task, node, dependencies) if err != nil { return nil, err } diff --git a/daemon/cluster/executor/container/executor.go b/daemon/cluster/executor/container/executor.go index fb339f14fc..de44e26dc6 100644 --- a/daemon/cluster/executor/container/executor.go +++ b/daemon/cluster/executor/container/executor.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" "strings" + "sync" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" @@ -26,6 +27,8 @@ type executor struct { backend executorpkg.Backend pluginBackend plugin.Backend dependencies exec.DependencyManager + mutex sync.Mutex // This mutex protects the following node field + node *api.NodeDescription } // NewExecutor returns an executor from the docker client. @@ -124,6 +127,11 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) { }, } + // Save the node information in the executor field + e.mutex.Lock() + e.node = description + e.mutex.Unlock() + return description, nil } @@ -168,8 +176,13 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error { func (e *executor) Controller(t *api.Task) (exec.Controller, error) { dependencyGetter := agent.Restrict(e.dependencies, t) + // Get the node description from the executor field + e.mutex.Lock() + nodeDescription := e.node + e.mutex.Unlock() + if t.Spec.GetAttachment() != nil { - return newNetworkAttacherController(e.backend, t, dependencyGetter) + return newNetworkAttacherController(e.backend, t, nodeDescription, dependencyGetter) } var ctlr exec.Controller @@ -198,7 +211,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) { return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind) } case *api.TaskSpec_Container: - c, err := newController(e.backend, t, dependencyGetter) + c, err := newController(e.backend, t, nodeDescription, dependencyGetter) if err != nil { return ctlr, err } diff --git a/daemon/cluster/executor/container/health_test.go b/daemon/cluster/executor/container/health_test.go index b6f188557f..450865edd3 100644 --- a/daemon/cluster/executor/container/health_test.go +++ b/daemon/cluster/executor/container/health_test.go @@ -52,7 +52,7 @@ func TestHealthStates(t *testing.T) { EventsService: e, } - controller, err := newController(daemon, task, nil) + controller, err := newController(daemon, task, nil, nil) if err != nil { t.Fatalf("create controller fail %v", err) } diff --git a/daemon/cluster/executor/container/validate_test.go b/daemon/cluster/executor/container/validate_test.go index 9d98e2c008..43e224a8fa 100644 --- a/daemon/cluster/executor/container/validate_test.go +++ b/daemon/cluster/executor/container/validate_test.go @@ -26,7 +26,8 @@ func newTestControllerWithMount(m api.Mount) (*controller, error) { }, }, }, - }, nil) + }, nil, + nil) } func TestControllerValidateMountBind(t *testing.T) { diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go index 5ecb010b29..c56cf1fe6c 100644 --- a/integration-cli/docker_cli_swarm_test.go +++ b/integration-cli/docker_cli_swarm_test.go @@ -169,8 +169,10 @@ func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) { func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) { d := s.AddDaemon(c, true, true) + hostname, err := d.Cmd("node", "inspect", "--format", "{{.Description.Hostname}}", "self") + c.Assert(err, checker.IsNil, check.Commentf(hostname)) - out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}", "busybox", "top") + out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}-{{.Node.Hostname}}", "busybox", "top") c.Assert(err, checker.IsNil, check.Commentf(out)) // make sure task has been deployed. @@ -179,7 +181,7 @@ func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) { containers := d.ActiveContainers() out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0]) c.Assert(err, checker.IsNil, check.Commentf(out)) - c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1", check.Commentf("hostname with templating invalid")) + c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1-"+strings.Split(hostname, "\n")[0], check.Commentf("hostname with templating invalid")) } // Test case for #24270