Skip to content

Commit 6b155a1

Browse files
authored
Allow running periodic updates with enabled HTTP API (#916)
* Allow running periodic updates with enabled HTTP API * Add --http-api-periodic-polls to docs
1 parent e308521 commit 6b155a1

File tree

6 files changed

+55
-12
lines changed

6 files changed

+55
-12
lines changed

cmd/root.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ func Run(c *cobra.Command, names []string) {
156156
runOnce, _ := c.PersistentFlags().GetBool("run-once")
157157
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
158158
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")
159+
unblockHTTPAPI, _ := c.PersistentFlags().GetBool("http-api-periodic-polls")
159160
apiToken, _ := c.PersistentFlags().GetString("http-api-token")
160161

161162
if rollingRestart && monitorOnly {
@@ -180,10 +181,14 @@ func Run(c *cobra.Command, names []string) {
180181
logNotifyExit(err)
181182
}
182183

184+
// The lock is shared between the scheduler and the HTTP API. It only allows one update to run at a time.
185+
updateLock := make(chan bool, 1)
186+
updateLock <- true
187+
183188
httpAPI := api.New(apiToken)
184189

185190
if enableUpdateAPI {
186-
updateHandler := update.New(func() { runUpdatesWithNotifications(filter) })
191+
updateHandler := update.New(func() { runUpdatesWithNotifications(filter) }, updateLock)
187192
httpAPI.RegisterFunc(updateHandler.Path, updateHandler.Handle)
188193
}
189194

@@ -192,11 +197,11 @@ func Run(c *cobra.Command, names []string) {
192197
httpAPI.RegisterHandler(metricsHandler.Path, metricsHandler.Handle)
193198
}
194199

195-
if err := httpAPI.Start(enableUpdateAPI); err != nil {
200+
if err := httpAPI.Start(enableUpdateAPI && !unblockHTTPAPI); err != nil {
196201
log.Error("failed to start API", err)
197202
}
198203

199-
if err := runUpgradesOnSchedule(c, filter, filterDesc); err != nil {
204+
if err := runUpgradesOnSchedule(c, filter, filterDesc, updateLock); err != nil {
200205
log.Error(err)
201206
}
202207

@@ -275,17 +280,19 @@ func writeStartupMessage(c *cobra.Command, sched time.Time, filtering string) {
275280
}
276281
}
277282

278-
func runUpgradesOnSchedule(c *cobra.Command, filter t.Filter, filtering string) error {
279-
tryLockSem := make(chan bool, 1)
280-
tryLockSem <- true
283+
func runUpgradesOnSchedule(c *cobra.Command, filter t.Filter, filtering string, lock chan bool) error {
284+
if lock == nil {
285+
lock = make(chan bool, 1)
286+
lock <- true
287+
}
281288

282289
scheduler := cron.New()
283290
err := scheduler.AddFunc(
284291
scheduleSpec,
285292
func() {
286293
select {
287-
case v := <-tryLockSem:
288-
defer func() { tryLockSem <- v }()
294+
case v := <-lock:
295+
defer func() { lock <- v }()
289296
metric := runUpdatesWithNotifications(filter)
290297
metrics.RegisterScan(metric)
291298
default:
@@ -316,7 +323,7 @@ func runUpgradesOnSchedule(c *cobra.Command, filter t.Filter, filtering string)
316323
<-interrupt
317324
scheduler.Stop()
318325
log.Info("Waiting for running update to be finished...")
319-
<-tryLockSem
326+
<-lock
320327
return nil
321328
}
322329

docs/arguments.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@ Environment Variable: WATCHTOWER_HTTP_API_TOKEN
255255
Default: -
256256
```
257257

258+
## HTTP API periodic polls
259+
Keep running periodic updates if the HTTP API mode is enabled, otherwise the HTTP API would prevent periodic polls.
260+
261+
```
262+
Argument: --http-api-periodic-polls
263+
Environment Variable: WATCHTOWER_HTTP_API_PERIODIC_POLLS
264+
Type: Boolean
265+
Default: false
266+
```
267+
258268
## Filter by scope
259269
Update containers that have a `com.centurylinklabs.watchtower.scope` label set with the same value as the given argument.
260270
This enables [running multiple instances](https://containrrr.github.io/watchtower/running-multiple-instances).

docs/http-api-mode.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ services:
2828
- 8080:8080
2929
```
3030
31+
By default, enabling this mode prevents periodic polls (i.e. what is specified using `--interval` or `--schedule`). To run periodic updates regardless, pass `--http-api-periodic-polls`.
32+
3133
Notice that there is an environment variable named WATCHTOWER_HTTP_API_TOKEN. To prevent external services from accidentally triggering image updates, all of the requests have to contain a "Token" field, valued as the token defined in WATCHTOWER_HTTP_API_TOKEN, in their headers. In this case, there is a port bind to the host machine, allowing to request localhost:8080 to reach Watchtower. The following `curl` command would trigger an image update:
3234

3335
```bash

internal/flags/flags.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
151151
"",
152152
viper.GetString("WATCHTOWER_HTTP_API_TOKEN"),
153153
"Sets an authentication token to HTTP API requests.")
154+
flags.BoolP(
155+
"http-api-periodic-polls",
156+
"",
157+
viper.GetBool("WATCHTOWER_HTTP_API_PERIODIC_POLLS"),
158+
"Also run periodic updates (specified with --interval and --schedule) if HTTP API is enabled")
154159
// https://no-color.org/
155160
flags.BoolP(
156161
"no-color",

internal/flags/flags_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,18 @@ func testGetSecretsFromFiles(t *testing.T, flagName string, expected string) {
7979

8080
assert.Equal(t, expected, value)
8181
}
82+
83+
func TestHTTPAPIPeriodicPollsFlag(t *testing.T) {
84+
cmd := new(cobra.Command)
85+
SetDefaults()
86+
RegisterDockerFlags(cmd)
87+
RegisterSystemFlags(cmd)
88+
89+
err := cmd.ParseFlags([]string{"--http-api-periodic-polls"})
90+
require.NoError(t, err)
91+
92+
periodicPolls, err := cmd.PersistentFlags().GetBool("http-api-periodic-polls")
93+
require.NoError(t, err)
94+
95+
assert.Equal(t, true, periodicPolls)
96+
}

pkg/api/update/update.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ var (
1313
)
1414

1515
// New is a factory function creating a new Handler instance
16-
func New(updateFn func()) *Handler {
17-
lock = make(chan bool, 1)
18-
lock <- true
16+
func New(updateFn func(), updateLock chan bool) *Handler {
17+
if updateLock != nil {
18+
lock = updateLock
19+
} else {
20+
lock = make(chan bool, 1)
21+
lock <- true
22+
}
1923

2024
return &Handler{
2125
fn: updateFn,

0 commit comments

Comments
 (0)