From d2c5b6ee9f5240e23aa9fa0f374b89a9726d2200 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Mon, 3 Apr 2017 17:59:09 -0700 Subject: [PATCH] Add integration test coverage for configs Signed-off-by: Aaron Lehmann --- integration-cli/daemon/daemon_swarm.go | 56 +++++++ .../docker_api_swarm_config_test.go | 118 ++++++++++++++ .../docker_cli_config_create_test.go | 131 ++++++++++++++++ .../docker_cli_config_inspect_test.go | 68 ++++++++ integration-cli/docker_cli_config_ls_test.go | 125 +++++++++++++++ .../docker_cli_service_create_test.go | 147 ++++++++++++++++++ .../docker_cli_service_update_test.go | 42 +++++ 7 files changed, 687 insertions(+) create mode 100644 integration-cli/docker_api_swarm_config_test.go create mode 100644 integration-cli/docker_cli_config_create_test.go create mode 100644 integration-cli/docker_cli_config_inspect_test.go create mode 100644 integration-cli/docker_cli_config_ls_test.go diff --git a/integration-cli/daemon/daemon_swarm.go b/integration-cli/daemon/daemon_swarm.go index 48ceae0470..a7058c512d 100644 --- a/integration-cli/daemon/daemon_swarm.go +++ b/integration-cli/daemon/daemon_swarm.go @@ -118,6 +118,9 @@ type NodeConstructor func(*swarm.Node) // SecretConstructor defines a swarm secret constructor type SecretConstructor func(*swarm.Secret) +// ConfigConstructor defines a swarm config constructor +type ConfigConstructor func(*swarm.Config) + // SpecConstructor defines a swarm spec constructor type SpecConstructor func(*swarm.Spec) @@ -409,6 +412,59 @@ func (d *Swarm) UpdateSecret(c *check.C, id string, f ...SecretConstructor) { c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) } +// CreateConfig creates a config given the specified spec +func (d *Swarm) CreateConfig(c *check.C, configSpec swarm.ConfigSpec) string { + status, out, err := d.SockRequest("POST", "/configs/create", configSpec) + + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out))) + + var scr types.ConfigCreateResponse + c.Assert(json.Unmarshal(out, &scr), checker.IsNil) + return scr.ID +} + +// ListConfigs returns the list of the current swarm configs +func (d *Swarm) ListConfigs(c *check.C) []swarm.Config { + status, out, err := d.SockRequest("GET", "/configs", nil) + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) + + configs := []swarm.Config{} + c.Assert(json.Unmarshal(out, &configs), checker.IsNil) + return configs +} + +// GetConfig returns a swarm config identified by the specified id +func (d *Swarm) GetConfig(c *check.C, id string) *swarm.Config { + var config swarm.Config + status, out, err := d.SockRequest("GET", "/configs/"+id, nil) + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) + c.Assert(json.Unmarshal(out, &config), checker.IsNil) + return &config +} + +// DeleteConfig removes the swarm config identified by the specified id +func (d *Swarm) DeleteConfig(c *check.C, id string) { + status, out, err := d.SockRequest("DELETE", "/configs/"+id, nil) + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out))) +} + +// UpdateConfig updates the swarm config identified by the specified id +// Currently, only label update is supported. +func (d *Swarm) UpdateConfig(c *check.C, id string, f ...ConfigConstructor) { + config := d.GetConfig(c, id) + for _, fn := range f { + fn(config) + } + url := fmt.Sprintf("/configs/%s/update?version=%d", config.ID, config.Version.Index) + status, out, err := d.SockRequest("POST", url, config.Spec) + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out))) +} + // GetSwarm returns the current swarm object func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm { var sw swarm.Swarm diff --git a/integration-cli/docker_api_swarm_config_test.go b/integration-cli/docker_api_swarm_config_test.go new file mode 100644 index 0000000000..5d80ad0661 --- /dev/null +++ b/integration-cli/docker_api_swarm_config_test.go @@ -0,0 +1,118 @@ +// +build !windows + +package main + +import ( + "fmt" + "net/http" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/integration-cli/checker" + "github.com/go-check/check" +) + +func (s *DockerSwarmSuite) TestAPISwarmConfigsEmptyList(c *check.C) { + d := s.AddDaemon(c, true, true) + + configs := d.ListConfigs(c) + c.Assert(configs, checker.NotNil) + c.Assert(len(configs), checker.Equals, 0, check.Commentf("configs: %#v", configs)) +} + +func (s *DockerSwarmSuite) TestAPISwarmConfigsCreate(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + configs := d.ListConfigs(c) + c.Assert(len(configs), checker.Equals, 1, check.Commentf("configs: %#v", configs)) + name := configs[0].Spec.Annotations.Name + c.Assert(name, checker.Equals, testName, check.Commentf("configs: %s", name)) +} + +func (s *DockerSwarmSuite) TestAPISwarmConfigsDelete(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.ID, checker.Equals, id, check.Commentf("config: %v", config)) + + d.DeleteConfig(c, config.ID) + status, out, err := d.SockRequest("GET", "/configs/"+id, nil) + c.Assert(err, checker.IsNil) + c.Assert(status, checker.Equals, http.StatusNotFound, check.Commentf("config delete: %s", string(out))) +} + +func (s *DockerSwarmSuite) TestAPISwarmConfigsUpdate(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + Labels: map[string]string{ + "test": "test1", + }, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.ID, checker.Equals, id, check.Commentf("config: %v", config)) + + // test UpdateConfig with full ID + d.UpdateConfig(c, id, func(s *swarm.Config) { + s.Spec.Labels = map[string]string{ + "test": "test1", + } + }) + + config = d.GetConfig(c, id) + c.Assert(config.Spec.Labels["test"], checker.Equals, "test1", check.Commentf("config: %v", config)) + + // test UpdateConfig with full name + d.UpdateConfig(c, config.Spec.Name, func(s *swarm.Config) { + s.Spec.Labels = map[string]string{ + "test": "test2", + } + }) + + config = d.GetConfig(c, id) + c.Assert(config.Spec.Labels["test"], checker.Equals, "test2", check.Commentf("config: %v", config)) + + // test UpdateConfig with prefix ID + d.UpdateConfig(c, id[:1], func(s *swarm.Config) { + s.Spec.Labels = map[string]string{ + "test": "test3", + } + }) + + config = d.GetConfig(c, id) + c.Assert(config.Spec.Labels["test"], checker.Equals, "test3", check.Commentf("config: %v", config)) + + // test UpdateConfig in updating Data which is not supported in daemon + // this test will produce an error in func UpdateConfig + config = d.GetConfig(c, id) + config.Spec.Data = []byte("TESTINGDATA2") + + url := fmt.Sprintf("/configs/%s/update?version=%d", config.ID, config.Version.Index) + status, out, err := d.SockRequest("POST", url, config.Spec) + + c.Assert(err, checker.IsNil, check.Commentf(string(out))) + c.Assert(status, checker.Equals, http.StatusInternalServerError, check.Commentf("output: %q", string(out))) +} diff --git a/integration-cli/docker_cli_config_create_test.go b/integration-cli/docker_cli_config_create_test.go new file mode 100644 index 0000000000..b823254874 --- /dev/null +++ b/integration-cli/docker_cli_config_create_test.go @@ -0,0 +1,131 @@ +// +build !windows + +package main + +import ( + "io/ioutil" + "os" + "strings" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/integration-cli/checker" + "github.com/go-check/check" +) + +func (s *DockerSwarmSuite) TestConfigCreate(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.Spec.Name, checker.Equals, testName) +} + +func (s *DockerSwarmSuite) TestConfigCreateWithLabels(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + Labels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.Spec.Name, checker.Equals, testName) + c.Assert(len(config.Spec.Labels), checker.Equals, 2) + c.Assert(config.Spec.Labels["key1"], checker.Equals, "value1") + c.Assert(config.Spec.Labels["key2"], checker.Equals, "value2") +} + +// Test case for 28884 +func (s *DockerSwarmSuite) TestConfigCreateResolve(c *check.C) { + d := s.AddDaemon(c, true, true) + + name := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: name, + }, + Data: []byte("foo"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + fake := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: id, + }, + Data: []byte("fake foo"), + }) + c.Assert(fake, checker.Not(checker.Equals), "", check.Commentf("configs: %s", fake)) + + out, err := d.Cmd("config", "ls") + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, name) + c.Assert(out, checker.Contains, fake) + + out, err = d.Cmd("config", "rm", id) + c.Assert(out, checker.Contains, id) + + // Fake one will remain + out, err = d.Cmd("config", "ls") + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Not(checker.Contains), name) + c.Assert(out, checker.Contains, fake) + + // Remove based on name prefix of the fake one + // (which is the same as the ID of foo one) should not work + // as search is only done based on: + // - Full ID + // - Full Name + // - Partial ID (prefix) + out, err = d.Cmd("config", "rm", id[:5]) + c.Assert(out, checker.Not(checker.Contains), id) + out, err = d.Cmd("config", "ls") + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Not(checker.Contains), name) + c.Assert(out, checker.Contains, fake) + + // Remove based on ID prefix of the fake one should succeed + out, err = d.Cmd("config", "rm", fake[:5]) + c.Assert(out, checker.Contains, fake[:5]) + out, err = d.Cmd("config", "ls") + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Not(checker.Contains), name) + c.Assert(out, checker.Not(checker.Contains), id) + c.Assert(out, checker.Not(checker.Contains), fake) +} + +func (s *DockerSwarmSuite) TestConfigCreateWithFile(c *check.C) { + d := s.AddDaemon(c, true, true) + + testFile, err := ioutil.TempFile("", "configCreateTest") + c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file")) + defer os.Remove(testFile.Name()) + + testData := "TESTINGDATA" + _, err = testFile.Write([]byte(testData)) + c.Assert(err, checker.IsNil, check.Commentf("failed to write to temporary file")) + + testName := "test_config" + out, err := d.Cmd("config", "create", testName, testFile.Name()) + c.Assert(err, checker.IsNil) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out)) + + id := strings.TrimSpace(out) + config := d.GetConfig(c, id) + c.Assert(config.Spec.Name, checker.Equals, testName) +} diff --git a/integration-cli/docker_cli_config_inspect_test.go b/integration-cli/docker_cli_config_inspect_test.go new file mode 100644 index 0000000000..ba4e80f070 --- /dev/null +++ b/integration-cli/docker_cli_config_inspect_test.go @@ -0,0 +1,68 @@ +// +build !windows + +package main + +import ( + "encoding/json" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/integration-cli/checker" + "github.com/go-check/check" +) + +func (s *DockerSwarmSuite) TestConfigInspect(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.Spec.Name, checker.Equals, testName) + + out, err := d.Cmd("config", "inspect", testName) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + var configs []swarm.Config + c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil) + c.Assert(configs, checker.HasLen, 1) +} + +func (s *DockerSwarmSuite) TestConfigInspectMultiple(c *check.C) { + d := s.AddDaemon(c, true, true) + + testNames := []string{ + "test0", + "test1", + } + for _, n := range testNames { + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: n, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + config := d.GetConfig(c, id) + c.Assert(config.Spec.Name, checker.Equals, n) + + } + + args := []string{ + "config", + "inspect", + } + args = append(args, testNames...) + out, err := d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + var configs []swarm.Config + c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil) + c.Assert(configs, checker.HasLen, 2) +} diff --git a/integration-cli/docker_cli_config_ls_test.go b/integration-cli/docker_cli_config_ls_test.go new file mode 100644 index 0000000000..5c07012614 --- /dev/null +++ b/integration-cli/docker_cli_config_ls_test.go @@ -0,0 +1,125 @@ +// +build !windows + +package main + +import ( + "strings" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/integration-cli/checker" + "github.com/go-check/check" +) + +func (s *DockerSwarmSuite) TestConfigList(c *check.C) { + d := s.AddDaemon(c, true, true) + + testName0 := "test0" + testName1 := "test1" + + // create config test0 + id0 := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName0, + Labels: map[string]string{"type": "test"}, + }, + Data: []byte("TESTINGDATA0"), + }) + c.Assert(id0, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id0)) + + config := d.GetConfig(c, id0) + c.Assert(config.Spec.Name, checker.Equals, testName0) + + // create config test1 + id1 := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName1, + Labels: map[string]string{"type": "production"}, + }, + Data: []byte("TESTINGDATA1"), + }) + c.Assert(id1, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id1)) + + config = d.GetConfig(c, id1) + c.Assert(config.Spec.Name, checker.Equals, testName1) + + // test by command `docker config ls` + out, err := d.Cmd("config", "ls") + c.Assert(err, checker.IsNil, check.Commentf(out)) + c.Assert(strings.TrimSpace(out), checker.Contains, testName0) + c.Assert(strings.TrimSpace(out), checker.Contains, testName1) + + // test filter by name `docker config ls --filter name=xxx` + args := []string{ + "config", + "ls", + "--filter", + "name=test0", + } + out, err = d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Contains, testName0) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1) + + // test filter by id `docker config ls --filter id=xxx` + args = []string{ + "config", + "ls", + "--filter", + "id=" + id1, + } + out, err = d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0) + c.Assert(strings.TrimSpace(out), checker.Contains, testName1) + + // test filter by label `docker config ls --filter label=xxx` + args = []string{ + "config", + "ls", + "--filter", + "label=type", + } + out, err = d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Contains, testName0) + c.Assert(strings.TrimSpace(out), checker.Contains, testName1) + + args = []string{ + "config", + "ls", + "--filter", + "label=type=test", + } + out, err = d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Contains, testName0) + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName1) + + args = []string{ + "config", + "ls", + "--filter", + "label=type=production", + } + out, err = d.Cmd(args...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), testName0) + c.Assert(strings.TrimSpace(out), checker.Contains, testName1) + + // test invalid filter `docker config ls --filter noexisttype=xxx` + args = []string{ + "config", + "ls", + "--filter", + "noexisttype=test0", + } + out, err = d.Cmd(args...) + c.Assert(err, checker.NotNil, check.Commentf(out)) + + c.Assert(strings.TrimSpace(out), checker.Contains, "Error response from daemon: Invalid filter 'noexisttype'") +} diff --git a/integration-cli/docker_cli_service_create_test.go b/integration-cli/docker_cli_service_create_test.go index 0ff7feb594..95f82e7d44 100644 --- a/integration-cli/docker_cli_service_create_test.go +++ b/integration-cli/docker_cli_service_create_test.go @@ -211,6 +211,153 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *check.C c.Assert(err, checker.IsNil, check.Commentf(out)) } +func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *check.C) { + d := s.AddDaemon(c, true, true) + + serviceName := "test-service-config" + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + out, err := d.Cmd("service", "create", "--name", serviceName, "--config", testName, "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) + c.Assert(err, checker.IsNil) + + var refs []swarm.ConfigReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 1) + + c.Assert(refs[0].ConfigName, checker.Equals, testName) + c.Assert(refs[0].File, checker.Not(checker.IsNil)) + c.Assert(refs[0].File.Name, checker.Equals, testName) + c.Assert(refs[0].File.UID, checker.Equals, "0") + c.Assert(refs[0].File.GID, checker.Equals, "0") + + out, err = d.Cmd("service", "rm", serviceName) + c.Assert(err, checker.IsNil, check.Commentf(out)) + d.DeleteConfig(c, testName) +} + +func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check.C) { + d := s.AddDaemon(c, true, true) + + testPaths := map[string]string{ + "app": "/etc/config", + "test_config": "test_config", + "relative_config": "relative/config", + } + + var configFlags []string + + for testName, testTarget := range testPaths { + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA " + testName + " " + testTarget), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + configFlags = append(configFlags, "--config", fmt.Sprintf("source=%s,target=%s", testName, testTarget)) + } + + serviceName := "svc" + serviceCmd := []string{"service", "create", "--name", serviceName} + serviceCmd = append(serviceCmd, configFlags...) + serviceCmd = append(serviceCmd, "busybox", "top") + out, err := d.Cmd(serviceCmd...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) + c.Assert(err, checker.IsNil) + + var refs []swarm.ConfigReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, len(testPaths)) + + var tasks []swarm.Task + waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { + tasks = d.GetServiceTasks(c, serviceName) + return len(tasks) > 0, nil + }, checker.Equals, true) + + task := tasks[0] + waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { + if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" { + task = d.GetTask(c, task.ID) + } + return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil + }, checker.Equals, true) + + for testName, testTarget := range testPaths { + path := testTarget + if !filepath.IsAbs(path) { + path = filepath.Join("/", path) + } + out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Equals, "TESTINGDATA "+testName+" "+testTarget) + } + + out, err = d.Cmd("service", "rm", serviceName) + c.Assert(err, checker.IsNil, check.Commentf(out)) +} + +func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *check.C) { + d := s.AddDaemon(c, true, true) + + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: "myconfig", + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + + serviceName := "svc" + out, err := d.Cmd("service", "create", "--name", serviceName, "--config", "source=myconfig,target=target1", "--config", "source=myconfig,target=target2", "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) + c.Assert(err, checker.IsNil) + + var refs []swarm.ConfigReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 2) + + var tasks []swarm.Task + waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { + tasks = d.GetServiceTasks(c, serviceName) + return len(tasks) > 0, nil + }, checker.Equals, true) + + task := tasks[0] + waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { + if task.NodeID == "" || task.Status.ContainerStatus.ContainerID == "" { + task = d.GetTask(c, task.ID) + } + return task.NodeID != "" && task.Status.ContainerStatus.ContainerID != "", nil + }, checker.Equals, true) + + for _, target := range []string{"target1", "target2"} { + c.Assert(err, checker.IsNil, check.Commentf(out)) + path := filepath.Join("/", target) + out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Equals, "TESTINGDATA") + } + + out, err = d.Cmd("service", "rm", serviceName) + c.Assert(err, checker.IsNil, check.Commentf(out)) +} + func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) { d := s.AddDaemon(c, true, true) 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") diff --git a/integration-cli/docker_cli_service_update_test.go b/integration-cli/docker_cli_service_update_test.go index 1480d13e5c..db42adbeb4 100644 --- a/integration-cli/docker_cli_service_update_test.go +++ b/integration-cli/docker_cli_service_update_test.go @@ -128,3 +128,45 @@ func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) { c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) c.Assert(refs, checker.HasLen, 0) } + +func (s *DockerSwarmSuite) TestServiceUpdateConfigs(c *check.C) { + d := s.AddDaemon(c, true, true) + testName := "test_config" + id := d.CreateConfig(c, swarm.ConfigSpec{ + Annotations: swarm.Annotations{ + Name: testName, + }, + Data: []byte("TESTINGDATA"), + }) + c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) + testTarget := "/testing" + serviceName := "test" + + out, err := d.Cmd("service", "create", "--name", serviceName, "busybox", "top") + c.Assert(err, checker.IsNil, check.Commentf(out)) + + // add config + out, err = d.CmdRetryOutOfSequence("service", "update", "test", "--config-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget)) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) + c.Assert(err, checker.IsNil) + + var refs []swarm.ConfigReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 1) + + c.Assert(refs[0].ConfigName, checker.Equals, testName) + c.Assert(refs[0].File, checker.Not(checker.IsNil)) + c.Assert(refs[0].File.Name, checker.Equals, testTarget) + + // remove + out, err = d.CmdRetryOutOfSequence("service", "update", "test", "--config-rm", testName) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName) + c.Assert(err, checker.IsNil) + + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 0) +}