Skip to content

Commit 3de202a

Browse files
authored
fix depends on behavior and simplify some of its logic (#908)
* fix depends on behavior and simplify some of its logic * fix comments
1 parent 4142f79 commit 3de202a

File tree

5 files changed

+83
-29
lines changed

5 files changed

+83
-29
lines changed

cmd/root.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,18 @@ func Run(c *cobra.Command, names []string) {
154154
runOnce, _ := c.PersistentFlags().GetBool("run-once")
155155
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
156156
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")
157-
158157
apiToken, _ := c.PersistentFlags().GetString("http-api-token")
159158

159+
if rollingRestart && monitorOnly {
160+
log.Fatal("Rolling restarts is not compatible with the global monitor only flag")
161+
}
162+
163+
awaitDockerClient()
164+
165+
if err := actions.CheckForSanity(client, filter, rollingRestart); err != nil {
166+
logNotifyExit(err)
167+
}
168+
160169
if runOnce {
161170
writeStartupMessage(c, time.Time{}, filterDesc)
162171
runUpdatesWithNotifications(filter)
@@ -166,7 +175,7 @@ func Run(c *cobra.Command, names []string) {
166175
}
167176

168177
if err := actions.CheckForMultipleWatchtowerInstances(client, cleanup, scope); err != nil {
169-
log.Fatal(err)
178+
logNotifyExit(err)
170179
}
171180

172181
httpAPI := api.New(apiToken)
@@ -192,6 +201,17 @@ func Run(c *cobra.Command, names []string) {
192201
os.Exit(1)
193202
}
194203

204+
func logNotifyExit(err error) {
205+
log.Error(err)
206+
notifier.Close()
207+
os.Exit(1)
208+
}
209+
210+
func awaitDockerClient() {
211+
log.Debug("Sleeping for a second to ensure the docker api client has been properly initialized.")
212+
time.Sleep(1 * time.Second)
213+
}
214+
195215
func formatDuration(d time.Duration) string {
196216
sb := strings.Builder{}
197217

internal/actions/check.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,47 @@ package actions
22

33
import (
44
"fmt"
5+
"github.com/containrrr/watchtower/pkg/types"
56
"sort"
67
"time"
78

89
"github.com/containrrr/watchtower/pkg/filters"
910
"github.com/containrrr/watchtower/pkg/sorter"
10-
"github.com/sirupsen/logrus"
1111

1212
log "github.com/sirupsen/logrus"
1313

1414
"github.com/containrrr/watchtower/pkg/container"
1515
)
1616

17+
// CheckForSanity makes sure everything is sane before starting
18+
func CheckForSanity(client container.Client, filter types.Filter, rollingRestarts bool) error {
19+
log.Debug("Making sure everything is sane before starting")
20+
21+
if rollingRestarts {
22+
containers, err := client.ListContainers(filter)
23+
if err != nil {
24+
return err
25+
}
26+
for _, c := range containers {
27+
if len(c.Links()) > 0 {
28+
return fmt.Errorf(
29+
"%q is depending on at least one other container. This is not compatible with rolling restarts",
30+
c.Name(),
31+
)
32+
}
33+
}
34+
}
35+
return nil
36+
}
37+
1738
// CheckForMultipleWatchtowerInstances will ensure that there are not multiple instances of the
1839
// watchtower running simultaneously. If multiple watchtower containers are detected, this function
1940
// will stop and remove all but the most recently started container. This behaviour can be bypassed
2041
// if a scope UID is defined.
2142
func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool, scope string) error {
22-
awaitDockerClient()
2343
containers, err := client.ListContainers(filters.FilterByScope(scope, filters.WatchtowerContainersFilter))
2444

2545
if err != nil {
26-
log.Fatal(err)
2746
return err
2847
}
2948

@@ -45,14 +64,14 @@ func cleanupExcessWatchtowers(containers []container.Container, client container
4564
for _, c := range allContainersExceptLast {
4665
if err := client.StopContainer(c, 10*time.Minute); err != nil {
4766
// logging the original here as we're just returning a count
48-
logrus.WithError(err).Error("Could not stop a previous watchtower instance.")
67+
log.WithError(err).Error("Could not stop a previous watchtower instance.")
4968
stopErrors++
5069
continue
5170
}
5271

5372
if cleanup {
5473
if err := client.RemoveImageByID(c.ImageID()); err != nil {
55-
logrus.WithError(err).Warning("Could not cleanup watchtower images, possibly because of other watchtowers instances in other scopes.")
74+
log.WithError(err).Warning("Could not cleanup watchtower images, possibly because of other watchtowers instances in other scopes.")
5675
}
5776
}
5877
}
@@ -63,8 +82,3 @@ func cleanupExcessWatchtowers(containers []container.Container, client container
6382

6483
return nil
6584
}
66-
67-
func awaitDockerClient() {
68-
log.Debug("Sleeping for a second to ensure the docker api client has been properly initialized.")
69-
time.Sleep(1 * time.Second)
70-
}

internal/actions/update.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,19 @@ func Update(client container.Client, params types.UpdateParams) (*metrics2.Metri
5050
}
5151

5252
containers, err = sorter.SortByDependencies(containers)
53+
5354
metric.Scanned = len(containers)
5455
if err != nil {
5556
return nil, err
5657
}
5758

5859
checkDependencies(containers)
5960

60-
containersToUpdate := []container.Container{}
61+
var containersToUpdate []container.Container
6162
if !params.MonitorOnly {
62-
for i := len(containers) - 1; i >= 0; i-- {
63-
if !containers[i].IsMonitorOnly() {
64-
containersToUpdate = append(containersToUpdate, containers[i])
63+
for _, c := range containers {
64+
if !c.IsMonitorOnly() {
65+
containersToUpdate = append(containersToUpdate, c)
6566
}
6667
}
6768
}
@@ -86,7 +87,7 @@ func performRollingRestart(containers []container.Container, client container.Cl
8687
failed := 0
8788

8889
for i := len(containers) - 1; i >= 0; i-- {
89-
if containers[i].Stale {
90+
if containers[i].ToRestart() {
9091
if err := stopStaleContainer(containers[i], client, params); err != nil {
9192
failed++
9293
}
@@ -119,7 +120,7 @@ func stopStaleContainer(container container.Container, client container.Client,
119120
return nil
120121
}
121122

122-
if !container.Stale {
123+
if !container.ToRestart() {
123124
return nil
124125
}
125126
if params.LifecycleHooks {
@@ -143,7 +144,7 @@ func restartContainersInSortedOrder(containers []container.Container, client con
143144
failed := 0
144145

145146
for _, c := range containers {
146-
if !c.Stale {
147+
if !c.ToRestart() {
147148
continue
148149
}
149150
if err := restartStaleContainer(c, client, params); err != nil {
@@ -183,7 +184,7 @@ func restartStaleContainer(container container.Container, client container.Clien
183184
if newContainerID, err := client.StartContainer(container); err != nil {
184185
log.Error(err)
185186
return err
186-
} else if container.Stale && params.LifecycleHooks {
187+
} else if container.ToRestart() && params.LifecycleHooks {
187188
lifecycle.ExecutePostUpdateCommand(client, newContainerID)
188189
}
189190
}
@@ -192,16 +193,19 @@ func restartStaleContainer(container container.Container, client container.Clien
192193

193194
func checkDependencies(containers []container.Container) {
194195

195-
for i, parent := range containers {
196-
if parent.ToRestart() {
196+
for _, c := range containers {
197+
if c.ToRestart() {
197198
continue
198199
}
199200

200201
LinkLoop:
201-
for _, linkName := range parent.Links() {
202-
for _, child := range containers {
203-
if child.Name() == linkName && child.ToRestart() {
204-
containers[i].Linked = true
202+
for _, linkName := range c.Links() {
203+
for _, candidate := range containers {
204+
if candidate.Name() != linkName {
205+
continue
206+
}
207+
if candidate.ToRestart() {
208+
c.LinkedToRestarting = true
205209
break LinkLoop
206210
}
207211
}

pkg/container/container.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ func NewContainer(containerInfo *types.ContainerJSON, imageInfo *types.ImageInsp
2222

2323
// Container represents a running Docker container.
2424
type Container struct {
25-
Linked bool
26-
Stale bool
25+
LinkedToRestarting bool
26+
Stale bool
2727

2828
containerInfo *types.ContainerJSON
2929
imageInfo *types.ImageInspect
@@ -142,7 +142,7 @@ func (c Container) Links() []string {
142142
// ToRestart return whether the container should be restarted, either because
143143
// is stale or linked to another stale container.
144144
func (c Container) ToRestart() bool {
145-
return c.Stale || c.Linked
145+
return c.Stale || c.LinkedToRestarting
146146
}
147147

148148
// IsWatchtower returns a boolean flag indicating whether or not the current

scripts/dependency-test.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env bash
2+
3+
# Simulates a container that will always be updated, checking whether it shuts down it's dependencies correctly.
4+
5+
docker rm -f parent || true
6+
docker rm -f depending || true
7+
8+
CHANGE=redis:latest
9+
KEEP=tutum/hello-world
10+
11+
docker tag tutum/hello-world:latest redis:latest
12+
13+
docker run -d --name parent $CHANGE
14+
docker run -d --name depending --link parent $KEEP
15+
16+
go run . --run-once --debug $@

0 commit comments

Comments
 (0)