diff --git a/docs/linked-containers.md b/docs/linked-containers.md index 6960b5b..240fb97 100644 --- a/docs/linked-containers.md +++ b/docs/linked-containers.md @@ -1,3 +1,5 @@ Watchtower will detect if there are links between any of the running containers and ensures that things are stopped/started in a way that won't break any of the links. If an update is detected for one of the dependencies in a group of linked containers, watchtower will stop and start all of the containers in the correct order so that the application comes back up correctly. -For example, imagine you were running a _mysql_ container and a _wordpress_ container which had been linked to the _mysql_ container. If watchtower were to detect that the _mysql_ container required an update, it would first shut down the linked _wordpress_ container followed by the _mysql_ container. When restarting the containers it would handle _mysql_ first and then _wordpress_ to ensure that the link continued to work. \ No newline at end of file +For example, imagine you were running a _mysql_ container and a _wordpress_ container which had been linked to the _mysql_ container. If watchtower were to detect that the _mysql_ container required an update, it would first shut down the linked _wordpress_ container followed by the _mysql_ container. When restarting the containers it would handle _mysql_ first and then _wordpress_ to ensure that the link continued to work. + +If you want to override existing links you can use special `com.centurylinklabs.watchtower.depends-on` label with dependent container names, separated by a comma. diff --git a/pkg/container/container.go b/pkg/container/container.go index fb495fe..bc2f600 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -95,6 +95,13 @@ func (c Container) Enabled() (bool, bool) { func (c Container) Links() []string { var links []string + dependsOnLabelValue := c.getLabelValueOrEmpty(dependsOnLabel) + + if dependsOnLabelValue != "" { + links := strings.Split(dependsOnLabelValue, ",") + return links + } + if (c.containerInfo != nil) && (c.containerInfo.HostConfig != nil) { for _, link := range c.containerInfo.HostConfig.Links { name := strings.Split(link, ":")[0] diff --git a/pkg/container/container_test.go b/pkg/container/container_test.go index fe838f7..4f0f544 100644 --- a/pkg/container/container_test.go +++ b/pkg/container/container_test.go @@ -181,6 +181,42 @@ var _ = Describe("the container", func() { Expect(imageName).To(Equal(name + ":latest")) }) }) + + When("fetching container links", func() { + When("the depends on label is present", func() { + It("should fetch depending containers from it", func() { + c = mockContainerWithLabels(map[string]string{ + "com.centurylinklabs.watchtower.depends-on": "postgres", + }) + links := c.Links() + Expect(links).To(SatisfyAll(ContainElement("postgres"), HaveLen(1))) + }) + It("should fetch depending containers if there are many", func() { + c = mockContainerWithLabels(map[string]string{ + "com.centurylinklabs.watchtower.depends-on": "postgres,redis", + }) + links := c.Links() + Expect(links).To(SatisfyAll(ContainElement("postgres"), ContainElement("redis"), HaveLen(2))) + }) + It("should fetch depending containers if label is blank", func() { + c = mockContainerWithLabels(map[string]string{ + "com.centurylinklabs.watchtower.depends-on": "", + }) + links := c.Links() + Expect(links).To(HaveLen(0)) + }) + }) + When("the depends on label is not present", func() { + It("should fetch depending containers from host config links", func() { + c = mockContainerWithLinks([]string{ + "redis:test-containrrr", + "postgres:test-containrrr", + }) + links := c.Links() + Expect(links).To(SatisfyAll(ContainElement("redis"), ContainElement("postgres"), HaveLen(2))) + }) + }) + }) }) }) @@ -190,6 +226,23 @@ func mockContainerWithImageName(name string) *Container { return container } +func mockContainerWithLinks(links []string) *Container { + content := types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{ + ID: "container_id", + Image: "image", + Name: "test-containrrr", + HostConfig: &container.HostConfig{ + Links: links, + }, + }, + Config: &container.Config{ + Labels: map[string]string{}, + }, + } + return NewContainer(&content, nil) +} + func mockContainerWithLabels(labels map[string]string) *Container { content := types.ContainerJSON{ ContainerJSONBase: &types.ContainerJSONBase{ diff --git a/pkg/container/metadata.go b/pkg/container/metadata.go index fe5a055..2c1b933 100644 --- a/pkg/container/metadata.go +++ b/pkg/container/metadata.go @@ -4,6 +4,7 @@ const ( watchtowerLabel = "com.centurylinklabs.watchtower" signalLabel = "com.centurylinklabs.watchtower.stop-signal" enableLabel = "com.centurylinklabs.watchtower.enable" + dependsOnLabel = "com.centurylinklabs.watchtower.depends-on" zodiacLabel = "com.centurylinklabs.zodiac.original-image" preCheckLabel = "com.centurylinklabs.watchtower.lifecycle.pre-check" postCheckLabel = "com.centurylinklabs.watchtower.lifecycle.post-check"