|
|
|
package actions_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/containrrr/watchtower/internal/actions"
|
|
|
|
"github.com/containrrr/watchtower/pkg/types"
|
|
|
|
dockerTypes "github.com/docker/docker/api/types"
|
|
|
|
dockerContainer "github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/go-connections/nat"
|
|
|
|
|
|
|
|
. "github.com/containrrr/watchtower/internal/actions/mocks"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
|
|
. "github.com/onsi/gomega"
|
|
|
|
)
|
|
|
|
|
|
|
|
func getCommonTestData(keepContainer string) *TestData {
|
|
|
|
return &TestData{
|
|
|
|
NameOfContainerToKeep: keepContainer,
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-01",
|
|
|
|
"test-container-01",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now().AddDate(0, 0, -1)),
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now()),
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now()),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getLinkedTestData(withImageInfo bool) *TestData {
|
|
|
|
staleContainer := CreateMockContainer(
|
|
|
|
"test-container-01",
|
|
|
|
"/test-container-01",
|
|
|
|
"fake-image1:latest",
|
|
|
|
time.Now().AddDate(0, 0, -1))
|
|
|
|
|
|
|
|
var imageInfo *dockerTypes.ImageInspect
|
|
|
|
if withImageInfo {
|
|
|
|
imageInfo = CreateMockImageInfo("test-container-02")
|
|
|
|
}
|
|
|
|
linkingContainer := CreateMockContainerWithLinks(
|
|
|
|
"test-container-02",
|
|
|
|
"/test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
time.Now(),
|
|
|
|
[]string{staleContainer.Name()},
|
|
|
|
imageInfo)
|
|
|
|
|
|
|
|
return &TestData{
|
|
|
|
Staleness: map[string]bool{linkingContainer.Name(): false},
|
|
|
|
Containers: []types.Container{
|
|
|
|
staleContainer,
|
|
|
|
linkingContainer,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ = Describe("the update action", func() {
|
|
|
|
When("watchtower has been instructed to clean up", func() {
|
|
|
|
When("there are multiple containers using the same image", func() {
|
|
|
|
It("should only try to remove the image once", func() {
|
|
|
|
client := CreateMockClient(getCommonTestData(""), false, false)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
When("there are multiple containers using different images", func() {
|
|
|
|
It("should try to remove each of them", func() {
|
|
|
|
testData := getCommonTestData("")
|
|
|
|
testData.Containers = append(
|
|
|
|
testData.Containers,
|
|
|
|
CreateMockContainer(
|
|
|
|
"unique-test-container",
|
|
|
|
"unique-test-container",
|
|
|
|
"unique-fake-image:latest",
|
|
|
|
time.Now(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
client := CreateMockClient(testData, false, false)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(2))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
When("there are linked containers being updated", func() {
|
|
|
|
It("should not try to remove their images", func() {
|
|
|
|
client := CreateMockClient(getLinkedTestData(true), false, false)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
When("performing a rolling restart update", func() {
|
|
|
|
It("should try to remove the image once", func() {
|
|
|
|
client := CreateMockClient(getCommonTestData(""), false, false)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, RollingRestart: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
When("updating a linked container with missing image info", func() {
|
|
|
|
It("should gracefully fail", func() {
|
|
|
|
client := CreateMockClient(getLinkedTestData(false), false, false)
|
|
|
|
|
|
|
|
report, err := actions.Update(client, types.UpdateParams{})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// Note: Linked containers that were skipped for recreation is not counted in Failed
|
|
|
|
// If this happens, an error is emitted to the logs, so a notification should still be sent.
|
|
|
|
Expect(report.Updated()).To(HaveLen(1))
|
|
|
|
Expect(report.Fresh()).To(HaveLen(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
When("watchtower has been instructed to monitor only", func() {
|
|
|
|
When("certain containers are set to monitor only", func() {
|
|
|
|
It("should not update those containers", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-01",
|
|
|
|
"test-container-01",
|
|
|
|
"fake-image1:latest",
|
|
|
|
time.Now()),
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.monitor-only": "true",
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
When("monitor only is set globally", func() {
|
|
|
|
It("should not update any containers", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-01",
|
|
|
|
"test-container-01",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now()),
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now()),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, MonitorOnly: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
|
|
|
|
})
|
|
|
|
When("watchtower has been instructed to have label take precedence", func() {
|
|
|
|
It("it should update containers when monitor only is set to false", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.monitor-only": "false",
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, MonitorOnly: true, LabelPrecedence: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
It("it should update not containers when monitor only is set to true", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.monitor-only": "true",
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, MonitorOnly: true, LabelPrecedence: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
|
|
|
|
})
|
|
|
|
It("it should update not containers when monitor only is not set", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainer(
|
|
|
|
"test-container-01",
|
|
|
|
"test-container-01",
|
|
|
|
"fake-image:latest",
|
|
|
|
time.Now()),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, MonitorOnly: true, LabelPrecedence: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
When("watchtower has been instructed to run lifecycle hooks", func() {
|
|
|
|
|
|
|
|
When("pre-update script returns 1", func() {
|
|
|
|
It("should not update those containers", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update-timeout": "190",
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update": "/PreUpdateReturn1.sh",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, LifecycleHooks: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
When("prupddate script returns 75", func() {
|
|
|
|
It("should not update those containers", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update-timeout": "190",
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update": "/PreUpdateReturn75.sh",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, LifecycleHooks: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
When("prupddate script returns 0", func() {
|
|
|
|
It("should update those containers", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update-timeout": "190",
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update": "/PreUpdateReturn0.sh",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, LifecycleHooks: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
When("container is linked to restarting containers", func() {
|
|
|
|
It("should be marked for restart", func() {
|
|
|
|
|
|
|
|
provider := CreateMockContainerWithConfig(
|
|
|
|
"test-container-provider",
|
|
|
|
"/test-container-provider",
|
|
|
|
"fake-image2:latest",
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
})
|
|
|
|
|
|
|
|
provider.SetStale(true)
|
|
|
|
|
|
|
|
consumer := CreateMockContainerWithConfig(
|
|
|
|
"test-container-consumer",
|
|
|
|
"/test-container-consumer",
|
|
|
|
"fake-image3:latest",
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.depends-on": "test-container-provider",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
})
|
|
|
|
|
|
|
|
containers := []types.Container{
|
|
|
|
provider,
|
|
|
|
consumer,
|
|
|
|
}
|
|
|
|
|
|
|
|
Expect(provider.ToRestart()).To(BeTrue())
|
|
|
|
Expect(consumer.ToRestart()).To(BeFalse())
|
|
|
|
|
|
|
|
actions.UpdateImplicitRestart(containers)
|
|
|
|
|
|
|
|
Expect(containers[0].ToRestart()).To(BeTrue())
|
|
|
|
Expect(containers[1].ToRestart()).To(BeTrue())
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
When("container is not running", func() {
|
|
|
|
It("skip running preupdate", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update-timeout": "190",
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update": "/PreUpdateReturn1.sh",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, LifecycleHooks: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
When("container is restarting", func() {
|
|
|
|
It("skip running preupdate", func() {
|
|
|
|
client := CreateMockClient(
|
|
|
|
&TestData{
|
|
|
|
//NameOfContainerToKeep: "test-container-02",
|
|
|
|
Containers: []types.Container{
|
|
|
|
CreateMockContainerWithConfig(
|
|
|
|
"test-container-02",
|
|
|
|
"test-container-02",
|
|
|
|
"fake-image2:latest",
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
time.Now(),
|
|
|
|
&dockerContainer.Config{
|
|
|
|
Labels: map[string]string{
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update-timeout": "190",
|
|
|
|
"com.centurylinklabs.watchtower.lifecycle.pre-update": "/PreUpdateReturn1.sh",
|
|
|
|
},
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
_, err := actions.Update(client, types.UpdateParams{Cleanup: true, LifecycleHooks: true})
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|