Browse code

Add integration test coverage for configs

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2017/04/04 09:59:09
Showing 7 changed files
... ...
@@ -118,6 +118,9 @@ type NodeConstructor func(*swarm.Node)
118 118
 // SecretConstructor defines a swarm secret constructor
119 119
 type SecretConstructor func(*swarm.Secret)
120 120
 
121
+// ConfigConstructor defines a swarm config constructor
122
+type ConfigConstructor func(*swarm.Config)
123
+
121 124
 // SpecConstructor defines a swarm spec constructor
122 125
 type SpecConstructor func(*swarm.Spec)
123 126
 
... ...
@@ -409,6 +412,59 @@ func (d *Swarm) UpdateSecret(c *check.C, id string, f ...SecretConstructor) {
409 409
 	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
410 410
 }
411 411
 
412
+// CreateConfig creates a config given the specified spec
413
+func (d *Swarm) CreateConfig(c *check.C, configSpec swarm.ConfigSpec) string {
414
+	status, out, err := d.SockRequest("POST", "/configs/create", configSpec)
415
+
416
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
417
+	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out)))
418
+
419
+	var scr types.ConfigCreateResponse
420
+	c.Assert(json.Unmarshal(out, &scr), checker.IsNil)
421
+	return scr.ID
422
+}
423
+
424
+// ListConfigs returns the list of the current swarm configs
425
+func (d *Swarm) ListConfigs(c *check.C) []swarm.Config {
426
+	status, out, err := d.SockRequest("GET", "/configs", nil)
427
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
428
+	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
429
+
430
+	configs := []swarm.Config{}
431
+	c.Assert(json.Unmarshal(out, &configs), checker.IsNil)
432
+	return configs
433
+}
434
+
435
+// GetConfig returns a swarm config identified by the specified id
436
+func (d *Swarm) GetConfig(c *check.C, id string) *swarm.Config {
437
+	var config swarm.Config
438
+	status, out, err := d.SockRequest("GET", "/configs/"+id, nil)
439
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
440
+	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
441
+	c.Assert(json.Unmarshal(out, &config), checker.IsNil)
442
+	return &config
443
+}
444
+
445
+// DeleteConfig removes the swarm config identified by the specified id
446
+func (d *Swarm) DeleteConfig(c *check.C, id string) {
447
+	status, out, err := d.SockRequest("DELETE", "/configs/"+id, nil)
448
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
449
+	c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out)))
450
+}
451
+
452
+// UpdateConfig updates the swarm config identified by the specified id
453
+// Currently, only label update is supported.
454
+func (d *Swarm) UpdateConfig(c *check.C, id string, f ...ConfigConstructor) {
455
+	config := d.GetConfig(c, id)
456
+	for _, fn := range f {
457
+		fn(config)
458
+	}
459
+	url := fmt.Sprintf("/configs/%s/update?version=%d", config.ID, config.Version.Index)
460
+	status, out, err := d.SockRequest("POST", url, config.Spec)
461
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
462
+	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
463
+}
464
+
412 465
 // GetSwarm returns the current swarm object
413 466
 func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm {
414 467
 	var sw swarm.Swarm
415 468
new file mode 100644
... ...
@@ -0,0 +1,118 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"fmt"
6
+	"net/http"
7
+
8
+	"github.com/docker/docker/api/types/swarm"
9
+	"github.com/docker/docker/integration-cli/checker"
10
+	"github.com/go-check/check"
11
+)
12
+
13
+func (s *DockerSwarmSuite) TestAPISwarmConfigsEmptyList(c *check.C) {
14
+	d := s.AddDaemon(c, true, true)
15
+
16
+	configs := d.ListConfigs(c)
17
+	c.Assert(configs, checker.NotNil)
18
+	c.Assert(len(configs), checker.Equals, 0, check.Commentf("configs: %#v", configs))
19
+}
20
+
21
+func (s *DockerSwarmSuite) TestAPISwarmConfigsCreate(c *check.C) {
22
+	d := s.AddDaemon(c, true, true)
23
+
24
+	testName := "test_config"
25
+	id := d.CreateConfig(c, swarm.ConfigSpec{
26
+		Annotations: swarm.Annotations{
27
+			Name: testName,
28
+		},
29
+		Data: []byte("TESTINGDATA"),
30
+	})
31
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
32
+
33
+	configs := d.ListConfigs(c)
34
+	c.Assert(len(configs), checker.Equals, 1, check.Commentf("configs: %#v", configs))
35
+	name := configs[0].Spec.Annotations.Name
36
+	c.Assert(name, checker.Equals, testName, check.Commentf("configs: %s", name))
37
+}
38
+
39
+func (s *DockerSwarmSuite) TestAPISwarmConfigsDelete(c *check.C) {
40
+	d := s.AddDaemon(c, true, true)
41
+
42
+	testName := "test_config"
43
+	id := d.CreateConfig(c, swarm.ConfigSpec{Annotations: swarm.Annotations{
44
+		Name: testName,
45
+	},
46
+		Data: []byte("TESTINGDATA"),
47
+	})
48
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
49
+
50
+	config := d.GetConfig(c, id)
51
+	c.Assert(config.ID, checker.Equals, id, check.Commentf("config: %v", config))
52
+
53
+	d.DeleteConfig(c, config.ID)
54
+	status, out, err := d.SockRequest("GET", "/configs/"+id, nil)
55
+	c.Assert(err, checker.IsNil)
56
+	c.Assert(status, checker.Equals, http.StatusNotFound, check.Commentf("config delete: %s", string(out)))
57
+}
58
+
59
+func (s *DockerSwarmSuite) TestAPISwarmConfigsUpdate(c *check.C) {
60
+	d := s.AddDaemon(c, true, true)
61
+
62
+	testName := "test_config"
63
+	id := d.CreateConfig(c, swarm.ConfigSpec{
64
+		Annotations: swarm.Annotations{
65
+			Name: testName,
66
+			Labels: map[string]string{
67
+				"test": "test1",
68
+			},
69
+		},
70
+		Data: []byte("TESTINGDATA"),
71
+	})
72
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
73
+
74
+	config := d.GetConfig(c, id)
75
+	c.Assert(config.ID, checker.Equals, id, check.Commentf("config: %v", config))
76
+
77
+	// test UpdateConfig with full ID
78
+	d.UpdateConfig(c, id, func(s *swarm.Config) {
79
+		s.Spec.Labels = map[string]string{
80
+			"test": "test1",
81
+		}
82
+	})
83
+
84
+	config = d.GetConfig(c, id)
85
+	c.Assert(config.Spec.Labels["test"], checker.Equals, "test1", check.Commentf("config: %v", config))
86
+
87
+	// test UpdateConfig with full name
88
+	d.UpdateConfig(c, config.Spec.Name, func(s *swarm.Config) {
89
+		s.Spec.Labels = map[string]string{
90
+			"test": "test2",
91
+		}
92
+	})
93
+
94
+	config = d.GetConfig(c, id)
95
+	c.Assert(config.Spec.Labels["test"], checker.Equals, "test2", check.Commentf("config: %v", config))
96
+
97
+	// test UpdateConfig with prefix ID
98
+	d.UpdateConfig(c, id[:1], func(s *swarm.Config) {
99
+		s.Spec.Labels = map[string]string{
100
+			"test": "test3",
101
+		}
102
+	})
103
+
104
+	config = d.GetConfig(c, id)
105
+	c.Assert(config.Spec.Labels["test"], checker.Equals, "test3", check.Commentf("config: %v", config))
106
+
107
+	// test UpdateConfig in updating Data which is not supported in daemon
108
+	// this test will produce an error in func UpdateConfig
109
+	config = d.GetConfig(c, id)
110
+	config.Spec.Data = []byte("TESTINGDATA2")
111
+
112
+	url := fmt.Sprintf("/configs/%s/update?version=%d", config.ID, config.Version.Index)
113
+	status, out, err := d.SockRequest("POST", url, config.Spec)
114
+
115
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
116
+	c.Assert(status, checker.Equals, http.StatusInternalServerError, check.Commentf("output: %q", string(out)))
117
+}
0 118
new file mode 100644
... ...
@@ -0,0 +1,131 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"io/ioutil"
6
+	"os"
7
+	"strings"
8
+
9
+	"github.com/docker/docker/api/types/swarm"
10
+	"github.com/docker/docker/integration-cli/checker"
11
+	"github.com/go-check/check"
12
+)
13
+
14
+func (s *DockerSwarmSuite) TestConfigCreate(c *check.C) {
15
+	d := s.AddDaemon(c, true, true)
16
+
17
+	testName := "test_config"
18
+	id := d.CreateConfig(c, swarm.ConfigSpec{
19
+		Annotations: swarm.Annotations{
20
+			Name: testName,
21
+		},
22
+		Data: []byte("TESTINGDATA"),
23
+	})
24
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
25
+
26
+	config := d.GetConfig(c, id)
27
+	c.Assert(config.Spec.Name, checker.Equals, testName)
28
+}
29
+
30
+func (s *DockerSwarmSuite) TestConfigCreateWithLabels(c *check.C) {
31
+	d := s.AddDaemon(c, true, true)
32
+
33
+	testName := "test_config"
34
+	id := d.CreateConfig(c, swarm.ConfigSpec{
35
+		Annotations: swarm.Annotations{
36
+			Name: testName,
37
+			Labels: map[string]string{
38
+				"key1": "value1",
39
+				"key2": "value2",
40
+			},
41
+		},
42
+		Data: []byte("TESTINGDATA"),
43
+	})
44
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
45
+
46
+	config := d.GetConfig(c, id)
47
+	c.Assert(config.Spec.Name, checker.Equals, testName)
48
+	c.Assert(len(config.Spec.Labels), checker.Equals, 2)
49
+	c.Assert(config.Spec.Labels["key1"], checker.Equals, "value1")
50
+	c.Assert(config.Spec.Labels["key2"], checker.Equals, "value2")
51
+}
52
+
53
+// Test case for 28884
54
+func (s *DockerSwarmSuite) TestConfigCreateResolve(c *check.C) {
55
+	d := s.AddDaemon(c, true, true)
56
+
57
+	name := "test_config"
58
+	id := d.CreateConfig(c, swarm.ConfigSpec{
59
+		Annotations: swarm.Annotations{
60
+			Name: name,
61
+		},
62
+		Data: []byte("foo"),
63
+	})
64
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
65
+
66
+	fake := d.CreateConfig(c, swarm.ConfigSpec{
67
+		Annotations: swarm.Annotations{
68
+			Name: id,
69
+		},
70
+		Data: []byte("fake foo"),
71
+	})
72
+	c.Assert(fake, checker.Not(checker.Equals), "", check.Commentf("configs: %s", fake))
73
+
74
+	out, err := d.Cmd("config", "ls")
75
+	c.Assert(err, checker.IsNil)
76
+	c.Assert(out, checker.Contains, name)
77
+	c.Assert(out, checker.Contains, fake)
78
+
79
+	out, err = d.Cmd("config", "rm", id)
80
+	c.Assert(out, checker.Contains, id)
81
+
82
+	// Fake one will remain
83
+	out, err = d.Cmd("config", "ls")
84
+	c.Assert(err, checker.IsNil)
85
+	c.Assert(out, checker.Not(checker.Contains), name)
86
+	c.Assert(out, checker.Contains, fake)
87
+
88
+	// Remove based on name prefix of the fake one
89
+	// (which is the same as the ID of foo one) should not work
90
+	// as search is only done based on:
91
+	// - Full ID
92
+	// - Full Name
93
+	// - Partial ID (prefix)
94
+	out, err = d.Cmd("config", "rm", id[:5])
95
+	c.Assert(out, checker.Not(checker.Contains), id)
96
+	out, err = d.Cmd("config", "ls")
97
+	c.Assert(err, checker.IsNil)
98
+	c.Assert(out, checker.Not(checker.Contains), name)
99
+	c.Assert(out, checker.Contains, fake)
100
+
101
+	// Remove based on ID prefix of the fake one should succeed
102
+	out, err = d.Cmd("config", "rm", fake[:5])
103
+	c.Assert(out, checker.Contains, fake[:5])
104
+	out, err = d.Cmd("config", "ls")
105
+	c.Assert(err, checker.IsNil)
106
+	c.Assert(out, checker.Not(checker.Contains), name)
107
+	c.Assert(out, checker.Not(checker.Contains), id)
108
+	c.Assert(out, checker.Not(checker.Contains), fake)
109
+}
110
+
111
+func (s *DockerSwarmSuite) TestConfigCreateWithFile(c *check.C) {
112
+	d := s.AddDaemon(c, true, true)
113
+
114
+	testFile, err := ioutil.TempFile("", "configCreateTest")
115
+	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
116
+	defer os.Remove(testFile.Name())
117
+
118
+	testData := "TESTINGDATA"
119
+	_, err = testFile.Write([]byte(testData))
120
+	c.Assert(err, checker.IsNil, check.Commentf("failed to write to temporary file"))
121
+
122
+	testName := "test_config"
123
+	out, err := d.Cmd("config", "create", testName, testFile.Name())
124
+	c.Assert(err, checker.IsNil)
125
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out))
126
+
127
+	id := strings.TrimSpace(out)
128
+	config := d.GetConfig(c, id)
129
+	c.Assert(config.Spec.Name, checker.Equals, testName)
130
+}
0 131
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"encoding/json"
6
+
7
+	"github.com/docker/docker/api/types/swarm"
8
+	"github.com/docker/docker/integration-cli/checker"
9
+	"github.com/go-check/check"
10
+)
11
+
12
+func (s *DockerSwarmSuite) TestConfigInspect(c *check.C) {
13
+	d := s.AddDaemon(c, true, true)
14
+
15
+	testName := "test_config"
16
+	id := d.CreateConfig(c, swarm.ConfigSpec{
17
+		Annotations: swarm.Annotations{
18
+			Name: testName,
19
+		},
20
+		Data: []byte("TESTINGDATA"),
21
+	})
22
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
23
+
24
+	config := d.GetConfig(c, id)
25
+	c.Assert(config.Spec.Name, checker.Equals, testName)
26
+
27
+	out, err := d.Cmd("config", "inspect", testName)
28
+	c.Assert(err, checker.IsNil, check.Commentf(out))
29
+
30
+	var configs []swarm.Config
31
+	c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil)
32
+	c.Assert(configs, checker.HasLen, 1)
33
+}
34
+
35
+func (s *DockerSwarmSuite) TestConfigInspectMultiple(c *check.C) {
36
+	d := s.AddDaemon(c, true, true)
37
+
38
+	testNames := []string{
39
+		"test0",
40
+		"test1",
41
+	}
42
+	for _, n := range testNames {
43
+		id := d.CreateConfig(c, swarm.ConfigSpec{
44
+			Annotations: swarm.Annotations{
45
+				Name: n,
46
+			},
47
+			Data: []byte("TESTINGDATA"),
48
+		})
49
+		c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
50
+
51
+		config := d.GetConfig(c, id)
52
+		c.Assert(config.Spec.Name, checker.Equals, n)
53
+
54
+	}
55
+
56
+	args := []string{
57
+		"config",
58
+		"inspect",
59
+	}
60
+	args = append(args, testNames...)
61
+	out, err := d.Cmd(args...)
62
+	c.Assert(err, checker.IsNil, check.Commentf(out))
63
+
64
+	var configs []swarm.Config
65
+	c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil)
66
+	c.Assert(configs, checker.HasLen, 2)
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"strings"
6
+
7
+	"github.com/docker/docker/api/types/swarm"
8
+	"github.com/docker/docker/integration-cli/checker"
9
+	"github.com/go-check/check"
10
+)
11
+
12
+func (s *DockerSwarmSuite) TestConfigList(c *check.C) {
13
+	d := s.AddDaemon(c, true, true)
14
+
15
+	testName0 := "test0"
16
+	testName1 := "test1"
17
+
18
+	// create config test0
19
+	id0 := d.CreateConfig(c, swarm.ConfigSpec{
20
+		Annotations: swarm.Annotations{
21
+			Name:   testName0,
22
+			Labels: map[string]string{"type": "test"},
23
+		},
24
+		Data: []byte("TESTINGDATA0"),
25
+	})
26
+	c.Assert(id0, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id0))
27
+
28
+	config := d.GetConfig(c, id0)
29
+	c.Assert(config.Spec.Name, checker.Equals, testName0)
30
+
31
+	// create config test1
32
+	id1 := d.CreateConfig(c, swarm.ConfigSpec{
33
+		Annotations: swarm.Annotations{
34
+			Name:   testName1,
35
+			Labels: map[string]string{"type": "production"},
36
+		},
37
+		Data: []byte("TESTINGDATA1"),
38
+	})
39
+	c.Assert(id1, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id1))
40
+
41
+	config = d.GetConfig(c, id1)
42
+	c.Assert(config.Spec.Name, checker.Equals, testName1)
43
+
44
+	// test by command `docker config ls`
45
+	out, err := d.Cmd("config", "ls")
46
+	c.Assert(err, checker.IsNil, check.Commentf(out))
47
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
48
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
49
+
50
+	// test filter by name `docker config ls --filter name=xxx`
51
+	args := []string{
52
+		"config",
53
+		"ls",
54
+		"--filter",
55
+		"name=test0",
56
+	}
57
+	out, err = d.Cmd(args...)
58
+	c.Assert(err, checker.IsNil, check.Commentf(out))
59
+
60
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
61
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1)
62
+
63
+	// test filter by id `docker config ls --filter id=xxx`
64
+	args = []string{
65
+		"config",
66
+		"ls",
67
+		"--filter",
68
+		"id=" + id1,
69
+	}
70
+	out, err = d.Cmd(args...)
71
+	c.Assert(err, checker.IsNil, check.Commentf(out))
72
+
73
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0)
74
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
75
+
76
+	// test filter by label `docker config ls --filter label=xxx`
77
+	args = []string{
78
+		"config",
79
+		"ls",
80
+		"--filter",
81
+		"label=type",
82
+	}
83
+	out, err = d.Cmd(args...)
84
+	c.Assert(err, checker.IsNil, check.Commentf(out))
85
+
86
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
87
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
88
+
89
+	args = []string{
90
+		"config",
91
+		"ls",
92
+		"--filter",
93
+		"label=type=test",
94
+	}
95
+	out, err = d.Cmd(args...)
96
+	c.Assert(err, checker.IsNil, check.Commentf(out))
97
+
98
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName0)
99
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1)
100
+
101
+	args = []string{
102
+		"config",
103
+		"ls",
104
+		"--filter",
105
+		"label=type=production",
106
+	}
107
+	out, err = d.Cmd(args...)
108
+	c.Assert(err, checker.IsNil, check.Commentf(out))
109
+
110
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0)
111
+	c.Assert(strings.TrimSpace(out), checker.Contains, testName1)
112
+
113
+	// test invalid filter `docker config ls --filter noexisttype=xxx`
114
+	args = []string{
115
+		"config",
116
+		"ls",
117
+		"--filter",
118
+		"noexisttype=test0",
119
+	}
120
+	out, err = d.Cmd(args...)
121
+	c.Assert(err, checker.NotNil, check.Commentf(out))
122
+
123
+	c.Assert(strings.TrimSpace(out), checker.Contains, "Error response from daemon: Invalid filter 'noexisttype'")
124
+}
... ...
@@ -211,6 +211,153 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *check.C
211 211
 	c.Assert(err, checker.IsNil, check.Commentf(out))
212 212
 }
213 213
 
214
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *check.C) {
215
+	d := s.AddDaemon(c, true, true)
216
+
217
+	serviceName := "test-service-config"
218
+	testName := "test_config"
219
+	id := d.CreateConfig(c, swarm.ConfigSpec{
220
+		Annotations: swarm.Annotations{
221
+			Name: testName,
222
+		},
223
+		Data: []byte("TESTINGDATA"),
224
+	})
225
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
226
+
227
+	out, err := d.Cmd("service", "create", "--name", serviceName, "--config", testName, "busybox", "top")
228
+	c.Assert(err, checker.IsNil, check.Commentf(out))
229
+
230
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
231
+	c.Assert(err, checker.IsNil)
232
+
233
+	var refs []swarm.ConfigReference
234
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
235
+	c.Assert(refs, checker.HasLen, 1)
236
+
237
+	c.Assert(refs[0].ConfigName, checker.Equals, testName)
238
+	c.Assert(refs[0].File, checker.Not(checker.IsNil))
239
+	c.Assert(refs[0].File.Name, checker.Equals, testName)
240
+	c.Assert(refs[0].File.UID, checker.Equals, "0")
241
+	c.Assert(refs[0].File.GID, checker.Equals, "0")
242
+
243
+	out, err = d.Cmd("service", "rm", serviceName)
244
+	c.Assert(err, checker.IsNil, check.Commentf(out))
245
+	d.DeleteConfig(c, testName)
246
+}
247
+
248
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check.C) {
249
+	d := s.AddDaemon(c, true, true)
250
+
251
+	testPaths := map[string]string{
252
+		"app":             "/etc/config",
253
+		"test_config":     "test_config",
254
+		"relative_config": "relative/config",
255
+	}
256
+
257
+	var configFlags []string
258
+
259
+	for testName, testTarget := range testPaths {
260
+		id := d.CreateConfig(c, swarm.ConfigSpec{
261
+			Annotations: swarm.Annotations{
262
+				Name: testName,
263
+			},
264
+			Data: []byte("TESTINGDATA " + testName + " " + testTarget),
265
+		})
266
+		c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
267
+
268
+		configFlags = append(configFlags, "--config", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
269
+	}
270
+
271
+	serviceName := "svc"
272
+	serviceCmd := []string{"service", "create", "--name", serviceName}
273
+	serviceCmd = append(serviceCmd, configFlags...)
274
+	serviceCmd = append(serviceCmd, "busybox", "top")
275
+	out, err := d.Cmd(serviceCmd...)
276
+	c.Assert(err, checker.IsNil, check.Commentf(out))
277
+
278
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
279
+	c.Assert(err, checker.IsNil)
280
+
281
+	var refs []swarm.ConfigReference
282
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
283
+	c.Assert(refs, checker.HasLen, len(testPaths))
284
+
285
+	var tasks []swarm.Task
286
+	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
287
+		tasks = d.GetServiceTasks(c, serviceName)
288
+		return len(tasks) > 0, nil
289
+	}, checker.Equals, true)
290
+
291
+	task := tasks[0]
292
+	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
293
+		if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" {
294
+			task = d.GetTask(c, task.ID)
295
+		}
296
+		return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil
297
+	}, checker.Equals, true)
298
+
299
+	for testName, testTarget := range testPaths {
300
+		path := testTarget
301
+		if !filepath.IsAbs(path) {
302
+			path = filepath.Join("/", path)
303
+		}
304
+		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
305
+		c.Assert(err, checker.IsNil)
306
+		c.Assert(out, checker.Equals, "TESTINGDATA "+testName+" "+testTarget)
307
+	}
308
+
309
+	out, err = d.Cmd("service", "rm", serviceName)
310
+	c.Assert(err, checker.IsNil, check.Commentf(out))
311
+}
312
+
313
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *check.C) {
314
+	d := s.AddDaemon(c, true, true)
315
+
316
+	id := d.CreateConfig(c, swarm.ConfigSpec{
317
+		Annotations: swarm.Annotations{
318
+			Name: "myconfig",
319
+		},
320
+		Data: []byte("TESTINGDATA"),
321
+	})
322
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
323
+
324
+	serviceName := "svc"
325
+	out, err := d.Cmd("service", "create", "--name", serviceName, "--config", "source=myconfig,target=target1", "--config", "source=myconfig,target=target2", "busybox", "top")
326
+	c.Assert(err, checker.IsNil, check.Commentf(out))
327
+
328
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
329
+	c.Assert(err, checker.IsNil)
330
+
331
+	var refs []swarm.ConfigReference
332
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
333
+	c.Assert(refs, checker.HasLen, 2)
334
+
335
+	var tasks []swarm.Task
336
+	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
337
+		tasks = d.GetServiceTasks(c, serviceName)
338
+		return len(tasks) > 0, nil
339
+	}, checker.Equals, true)
340
+
341
+	task := tasks[0]
342
+	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
343
+		if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" {
344
+			task = d.GetTask(c, task.ID)
345
+		}
346
+		return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil
347
+	}, checker.Equals, true)
348
+
349
+	for _, target := range []string{"target1", "target2"} {
350
+		c.Assert(err, checker.IsNil, check.Commentf(out))
351
+		path := filepath.Join("/", target)
352
+		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
353
+		c.Assert(err, checker.IsNil)
354
+		c.Assert(out, checker.Equals, "TESTINGDATA")
355
+	}
356
+
357
+	out, err = d.Cmd("service", "rm", serviceName)
358
+	c.Assert(err, checker.IsNil, check.Commentf(out))
359
+}
360
+
214 361
 func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) {
215 362
 	d := s.AddDaemon(c, true, true)
216 363
 	out, err := d.Cmd("service", "create", "--detach=true", "--mount", "type=tmpfs,target=/foo,tmpfs-size=1MB", "busybox", "sh", "-c", "mount | grep foo; tail -f /dev/null")
... ...
@@ -128,3 +128,45 @@ func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) {
128 128
 	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
129 129
 	c.Assert(refs, checker.HasLen, 0)
130 130
 }
131
+
132
+func (s *DockerSwarmSuite) TestServiceUpdateConfigs(c *check.C) {
133
+	d := s.AddDaemon(c, true, true)
134
+	testName := "test_config"
135
+	id := d.CreateConfig(c, swarm.ConfigSpec{
136
+		Annotations: swarm.Annotations{
137
+			Name: testName,
138
+		},
139
+		Data: []byte("TESTINGDATA"),
140
+	})
141
+	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
142
+	testTarget := "/testing"
143
+	serviceName := "test"
144
+
145
+	out, err := d.Cmd("service", "create", "--name", serviceName, "busybox", "top")
146
+	c.Assert(err, checker.IsNil, check.Commentf(out))
147
+
148
+	// add config
149
+	out, err = d.CmdRetryOutOfSequence("service", "update", "test", "--config-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
150
+	c.Assert(err, checker.IsNil, check.Commentf(out))
151
+
152
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
153
+	c.Assert(err, checker.IsNil)
154
+
155
+	var refs []swarm.ConfigReference
156
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
157
+	c.Assert(refs, checker.HasLen, 1)
158
+
159
+	c.Assert(refs[0].ConfigName, checker.Equals, testName)
160
+	c.Assert(refs[0].File, checker.Not(checker.IsNil))
161
+	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
162
+
163
+	// remove
164
+	out, err = d.CmdRetryOutOfSequence("service", "update", "test", "--config-rm", testName)
165
+	c.Assert(err, checker.IsNil, check.Commentf(out))
166
+
167
+	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
168
+	c.Assert(err, checker.IsNil)
169
+
170
+	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
171
+	c.Assert(refs, checker.HasLen, 0)
172
+}