Merge master and fix conflicts

This commit is contained in:
DarkAEther 2020-11-22 14:14:00 +05:30
commit 18c5bc3f0f
35 changed files with 1021 additions and 132 deletions

View file

@ -46,7 +46,7 @@ var _ = Describe("the actions package", func() {
When("given an empty array", func() {
It("should not do anything", func() {
client.TestData.Containers = []container.Container{}
err := actions.CheckForMultipleWatchtowerInstances(client, false)
err := actions.CheckForMultipleWatchtowerInstances(client, false, "")
Expect(err).NotTo(HaveOccurred())
})
})
@ -60,7 +60,7 @@ var _ = Describe("the actions package", func() {
time.Now(),
make([]string,0)),
}
err := actions.CheckForMultipleWatchtowerInstances(client, false)
err := actions.CheckForMultipleWatchtowerInstances(client, false, "")
Expect(err).NotTo(HaveOccurred())
})
})
@ -93,7 +93,7 @@ var _ = Describe("the actions package", func() {
})
It("should stop all but the latest one", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, false)
err := actions.CheckForMultipleWatchtowerInstances(client, false, "")
Expect(err).NotTo(HaveOccurred())
})
})
@ -125,12 +125,12 @@ var _ = Describe("the actions package", func() {
)
})
It("should try to delete the image if the cleanup flag is true", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, true)
err := actions.CheckForMultipleWatchtowerInstances(client, true, "")
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImage()).To(BeTrue())
})
It("should not try to delete the image if the cleanup flag is false", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, false)
err := actions.CheckForMultipleWatchtowerInstances(client, false, "")
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImage()).To(BeFalse())
})

View file

@ -19,10 +19,11 @@ import (
// CheckForMultipleWatchtowerInstances will ensure that there are not multiple instances of the
// watchtower running simultaneously. If multiple watchtower containers are detected, this function
// will stop and remove all but the most recently started container.
func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool) error {
// will stop and remove all but the most recently started container. This behaviour can be bypassed
// if a scope UID is defined.
func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool, scope string) error {
awaitDockerClient()
containers, err := client.ListContainers(filters.WatchtowerContainersFilter)
containers, err := client.ListContainers(filters.FilterByScope(scope, filters.WatchtowerContainersFilter))
if err != nil {
log.Fatal(err)

View file

@ -36,3 +36,22 @@ func CreateMockContainer(id string, name string, image string, created time.Time
},
)
}
// CreateMockContainerWithConfig creates a container substitute valid for testing
func CreateMockContainerWithConfig(id string, name string, image string, created time.Time, config *container2.Config) container.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
Image: image,
Name: name,
Created: created.String(),
},
Config: config,
}
return *container.NewContainer(
&content,
&types.ImageInspect{
ID: image,
},
)
}

View file

@ -1,6 +1,7 @@
package actions
import (
"errors"
"github.com/containrrr/watchtower/internal/util"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/lifecycle"
@ -41,11 +42,13 @@ func PrepareContainerList(client container.Client, params types.UpdateParams) ([
return nil, err
}
for i, container := range containers {
stale, err := client.IsContainerStale(container)
for i, targetContainer := range containers {
stale, err := client.IsContainerStale(targetContainer)
if stale && !params.NoRestart && !params.MonitorOnly && !targetContainer.IsMonitorOnly() && !targetContainer.HasImageInfo() {
err = errors.New("no available image info")
}
if err != nil {
log.Infof("Unable to update container %s. Proceeding to next.", containers[i].Name())
log.Debug(err)
log.Infof("Unable to update container %q: %v. Proceeding to next.", containers[i].Name(), err)
stale = false
}
containers[i].Stale = stale
@ -76,11 +79,13 @@ func Update(client container.Client, params types.UpdateParams) error {
lifecycle.ExecutePreChecks(client, params)
}
if params.MonitorOnly {
if params.LifecycleHooks {
lifecycle.ExecutePostChecks(client, params)
containersToUpdate := []container.Container{}
if !params.MonitorOnly {
for i := len(containers) - 1; i >= 0; i-- {
if !containers[i].IsMonitorOnly() {
containersToUpdate = append(containersToUpdate, containers[i])
}
}
return nil
}
//shared map for independent and linked update
@ -113,6 +118,21 @@ func Update(client container.Client, params types.UpdateParams) error {
return nil
}
func performRollingRestart(containers []container.Container, client container.Client, params types.UpdateParams) {
cleanupImageIDs := make(map[string]bool)
for i := len(containers) - 1; i >= 0; i-- {
if containers[i].Stale {
stopStaleContainer(containers[i], client, params)
restartStaleContainer(containers[i], client, params)
}
}
if params.Cleanup {
cleanupImages(client, cleanupImageIDs)
}
}
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) {
for i := len(containers) - 1; i >= 0; i-- {
stopStaleContainer(containers[i], client, params)
@ -146,8 +166,8 @@ func restartContainersInSortedOrder(containers []container.Container, client con
if !container.Stale {
continue
}
restartStaleContainer(container, client, params)
imageIDs[container.ImageID()] = true
restartStaleContainer(staleContainer, client, params)
imageIDs[staleContainer.ImageID()] = true
}
}

View file

@ -5,6 +5,7 @@ import (
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/containrrr/watchtower/pkg/types"
container2 "github.com/docker/docker/api/types/container"
cli "github.com/docker/docker/client"
"time"
@ -228,4 +229,73 @@ var _ = Describe("the update action", func() {
})
})
})
When("watchtower has been instructed to monitor only", func() {
When("certain containers are set to monitor only", func() {
BeforeEach(func() {
client = CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
"fake-image1:latest",
time.Now()),
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
"fake-image2:latest",
time.Now(),
&container2.Config{
Labels: map[string]string{
"com.centurylinklabs.watchtower.monitor-only": "true",
},
}),
},
},
dockerClient,
false,
false,
)
})
It("should not update those containers", func() {
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() {
BeforeEach(func() {
client = CreateMockClient(
&TestData{
Containers: []container.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()),
},
},
dockerClient,
false,
false,
)
})
It("should not update any containers", func() {
err := actions.Update(client, types.UpdateParams{MonitorOnly: true})
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
})
})
})
})

View file

@ -123,6 +123,12 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
viper.GetBool("WATCHTOWER_LIFECYCLE_HOOKS"),
"Enable the execution of commands triggered by pre- and post-update lifecycle hooks")
flags.BoolP(
"rolling-restart",
"",
viper.GetBool("WATCHTOWER_ROLLING_RESTART"),
"Restart containers one at a time")
flags.BoolP(
"http-api",
"",
@ -134,6 +140,17 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
"",
viper.GetString("WATCHTOWER_HTTP_API_TOKEN"),
"Sets an authentication token to HTTP API requests.")
// https://no-color.org/
flags.BoolP(
"no-color",
"",
viper.IsSet("NO_COLOR"),
"Disable ANSI color escape codes in log output")
flags.StringP(
"scope",
"",
viper.GetString("WATCHTOWER_SCOPE"),
"Defines a monitoring scope for the Watchtower instance.")
}
// RegisterNotificationFlags that are used by watchtower to send notifications