Browse code

Add support for .Node.Hostname templating in swarm services

Signed-off-by: Carlo Mion <mion00@gmail.com>

Carlo Mion authored on 2017/08/31 04:45:05
Showing 8 changed files
... ...
@@ -41,8 +41,8 @@ type containerAdapter struct {
41 41
 	dependencies exec.DependencyGetter
42 42
 }
43 43
 
44
-func newContainerAdapter(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*containerAdapter, error) {
45
-	ctnr, err := newContainerConfig(task)
44
+func newContainerAdapter(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) {
45
+	ctnr, err := newContainerConfig(task, node)
46 46
 	if err != nil {
47 47
 		return nil, err
48 48
 	}
... ...
@@ -20,8 +20,8 @@ type networkAttacherController struct {
20 20
 	closed  chan struct{}
21 21
 }
22 22
 
23
-func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
24
-	adapter, err := newContainerAdapter(b, task, dependencies)
23
+func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
24
+	adapter, err := newContainerAdapter(b, task, node, dependencies)
25 25
 	if err != nil {
26 26
 		return nil, err
27 27
 	}
... ...
@@ -48,12 +48,12 @@ type containerConfig struct {
48 48
 
49 49
 // newContainerConfig returns a validated container config. No methods should
50 50
 // return an error if this function returns without error.
51
-func newContainerConfig(t *api.Task) (*containerConfig, error) {
51
+func newContainerConfig(t *api.Task, node *api.NodeDescription) (*containerConfig, error) {
52 52
 	var c containerConfig
53
-	return &c, c.setTask(t)
53
+	return &c, c.setTask(t, node)
54 54
 }
55 55
 
56
-func (c *containerConfig) setTask(t *api.Task) error {
56
+func (c *containerConfig) setTask(t *api.Task, node *api.NodeDescription) error {
57 57
 	if t.Spec.GetContainer() == nil && t.Spec.GetAttachment() == nil {
58 58
 		return exec.ErrRuntimeUnsupported
59 59
 	}
... ...
@@ -78,7 +78,7 @@ func (c *containerConfig) setTask(t *api.Task) error {
78 78
 	c.task = t
79 79
 
80 80
 	if t.Spec.GetContainer() != nil {
81
-		preparedSpec, err := template.ExpandContainerSpec(nil, t)
81
+		preparedSpec, err := template.ExpandContainerSpec(node, t)
82 82
 		if err != nil {
83 83
 			return err
84 84
 		}
... ...
@@ -40,8 +40,8 @@ type controller struct {
40 40
 var _ exec.Controller = &controller{}
41 41
 
42 42
 // NewController returns a docker exec runner for the provided task.
43
-func newController(b executorpkg.Backend, task *api.Task, dependencies exec.DependencyGetter) (*controller, error) {
44
-	adapter, err := newContainerAdapter(b, task, dependencies)
43
+func newController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) {
44
+	adapter, err := newContainerAdapter(b, task, node, dependencies)
45 45
 	if err != nil {
46 46
 		return nil, err
47 47
 	}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"sort"
6 6
 	"strings"
7
+	"sync"
7 8
 
8 9
 	"github.com/docker/docker/api/types"
9 10
 	"github.com/docker/docker/api/types/filters"
... ...
@@ -26,6 +27,8 @@ type executor struct {
26 26
 	backend       executorpkg.Backend
27 27
 	pluginBackend plugin.Backend
28 28
 	dependencies  exec.DependencyManager
29
+	mutex         sync.Mutex // This mutex protects the following node field
30
+	node          *api.NodeDescription
29 31
 }
30 32
 
31 33
 // NewExecutor returns an executor from the docker client.
... ...
@@ -124,6 +127,11 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
124 124
 		},
125 125
 	}
126 126
 
127
+	// Save the node information in the executor field
128
+	e.mutex.Lock()
129
+	e.node = description
130
+	e.mutex.Unlock()
131
+
127 132
 	return description, nil
128 133
 }
129 134
 
... ...
@@ -168,8 +176,13 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error {
168 168
 func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
169 169
 	dependencyGetter := agent.Restrict(e.dependencies, t)
170 170
 
171
+	// Get the node description from the executor field
172
+	e.mutex.Lock()
173
+	nodeDescription := e.node
174
+	e.mutex.Unlock()
175
+
171 176
 	if t.Spec.GetAttachment() != nil {
172
-		return newNetworkAttacherController(e.backend, t, dependencyGetter)
177
+		return newNetworkAttacherController(e.backend, t, nodeDescription, dependencyGetter)
173 178
 	}
174 179
 
175 180
 	var ctlr exec.Controller
... ...
@@ -198,7 +211,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
198 198
 			return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind)
199 199
 		}
200 200
 	case *api.TaskSpec_Container:
201
-		c, err := newController(e.backend, t, dependencyGetter)
201
+		c, err := newController(e.backend, t, nodeDescription, dependencyGetter)
202 202
 		if err != nil {
203 203
 			return ctlr, err
204 204
 		}
... ...
@@ -52,7 +52,7 @@ func TestHealthStates(t *testing.T) {
52 52
 		EventsService: e,
53 53
 	}
54 54
 
55
-	controller, err := newController(daemon, task, nil)
55
+	controller, err := newController(daemon, task, nil, nil)
56 56
 	if err != nil {
57 57
 		t.Fatalf("create controller fail %v", err)
58 58
 	}
... ...
@@ -26,7 +26,8 @@ func newTestControllerWithMount(m api.Mount) (*controller, error) {
26 26
 				},
27 27
 			},
28 28
 		},
29
-	}, nil)
29
+	}, nil,
30
+		nil)
30 31
 }
31 32
 
32 33
 func TestControllerValidateMountBind(t *testing.T) {
... ...
@@ -169,8 +169,10 @@ func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
169 169
 
170 170
 func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) {
171 171
 	d := s.AddDaemon(c, true, true)
172
+	hostname, err := d.Cmd("node", "inspect", "--format", "{{.Description.Hostname}}", "self")
173
+	c.Assert(err, checker.IsNil, check.Commentf(hostname))
172 174
 
173
-	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}", "busybox", "top")
175
+	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}-{{.Node.Hostname}}", "busybox", "top")
174 176
 	c.Assert(err, checker.IsNil, check.Commentf(out))
175 177
 
176 178
 	// make sure task has been deployed.
... ...
@@ -179,7 +181,7 @@ func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) {
179 179
 	containers := d.ActiveContainers()
180 180
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0])
181 181
 	c.Assert(err, checker.IsNil, check.Commentf(out))
182
-	c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1", check.Commentf("hostname with templating invalid"))
182
+	c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1-"+strings.Split(hostname, "\n")[0], check.Commentf("hostname with templating invalid"))
183 183
 }
184 184
 
185 185
 // Test case for #24270