mirror of
https://github.com/containrrr/watchtower.git
synced 2024-12-12 09:04:17 +02:00
Watchtower HTTP API based updates (#432)
* Added HTTP API trigger to update running images * Adds HTTP API authentication token parameter and handling * Exposes port 8080 in Dockerfile to allow inter-container update triggering via HTTP API * Fixes codacy issue * Adds API usage doc * Fix grammar * Moves api logic to a package of its own * Makes WT exit if token has not been set in HTTP API mode * Adds lock to prevent concurrent updates when in HTTP API mode Co-authored-by: Simon Aronsson <simme@arcticbit.se>
This commit is contained in:
parent
557f4abcb4
commit
0217e116c4
13
cmd/root.go
13
cmd/root.go
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/containrrr/watchtower/internal/actions"
|
||||
"github.com/containrrr/watchtower/internal/flags"
|
||||
"github.com/containrrr/watchtower/pkg/api"
|
||||
"github.com/containrrr/watchtower/pkg/container"
|
||||
"github.com/containrrr/watchtower/pkg/filters"
|
||||
"github.com/containrrr/watchtower/pkg/notifications"
|
||||
@ -111,6 +112,18 @@ func PreRun(cmd *cobra.Command, args []string) {
|
||||
func Run(c *cobra.Command, names []string) {
|
||||
filter := filters.BuildFilter(names, enableLabel)
|
||||
runOnce, _ := c.PersistentFlags().GetBool("run-once")
|
||||
httpAPI, _ := c.PersistentFlags().GetBool("http-api")
|
||||
|
||||
if httpAPI {
|
||||
apiToken, _ := c.PersistentFlags().GetString("http-api-token")
|
||||
|
||||
if err := api.SetupHTTPUpdates(apiToken, func() { runUpdatesWithNotifications(filter) }); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
api.WaitForHTTPUpdates()
|
||||
}
|
||||
|
||||
if runOnce {
|
||||
if noStartupMessage, _ := c.PersistentFlags().GetBool("no-startup-message"); !noStartupMessage {
|
||||
|
@ -14,5 +14,7 @@ COPY --from=alpine \
|
||||
/usr/share/zoneinfo \
|
||||
/usr/share/zoneinfo
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
COPY watchtower /
|
||||
ENTRYPOINT ["/watchtower"]
|
||||
|
@ -193,6 +193,26 @@ Run an update attempt against a container name list one time immediately and exi
|
||||
Environment Variable: WATCHTOWER_RUN_ONCE
|
||||
Type: Boolean
|
||||
Default: false
|
||||
```
|
||||
|
||||
## HTTP API Mode
|
||||
Runs Watchtower in HTTP API mode, only allowing image updates to be triggered by an HTTP request.
|
||||
|
||||
```
|
||||
Argument: --http-api
|
||||
Environment Variable: WATCHTOWER_HTTP_API
|
||||
Type: Boolean
|
||||
Default: false
|
||||
```
|
||||
|
||||
## HTTP API Token
|
||||
Sets an authentication token to HTTP API requests.
|
||||
|
||||
```
|
||||
Argument: --http-api-token
|
||||
Environment Variable: WATCHTOWER_HTTP_API_TOKEN
|
||||
Type: String
|
||||
Default: -
|
||||
```
|
||||
|
||||
## Scheduling
|
||||
|
35
docs/http-api-mode.md
Normal file
35
docs/http-api-mode.md
Normal file
@ -0,0 +1,35 @@
|
||||
Watchtower provides an HTTP API mode that enables an HTTP endpoint that can be requested to trigger container updating. The current available endpoint list is:
|
||||
|
||||
- `/v1/update` - triggers an update for all of the containers monitored by this Watchtower instance.
|
||||
|
||||
---
|
||||
|
||||
To enable this mode, use the flag `--http-api`. For example, in a Docker Compose config file:
|
||||
|
||||
```json
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
app-monitored-by-watchtower:
|
||||
image: myapps/monitored-by-watchtower
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
watchtower:
|
||||
image: containrrr/watchtower
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: --debug --http-api
|
||||
environment:
|
||||
- WATCHTOWER_HTTP_API_TOKEN=mytoken
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=false"
|
||||
ports:
|
||||
- 8080:8080
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
curl -H "Token: mytoken" localhost:8080/v1/update
|
||||
```
|
@ -113,6 +113,18 @@ 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(
|
||||
"http-api",
|
||||
"",
|
||||
viper.GetBool("WATCHTOWER_HTTP_API"),
|
||||
"Runs Watchtower in HTTP API mode, so that image updates must to be triggered by a request")
|
||||
|
||||
flags.StringP(
|
||||
"http-api-token",
|
||||
"",
|
||||
viper.GetString("WATCHTOWER_HTTP_API_TOKEN"),
|
||||
"Sets an authentication token to HTTP API requests.")
|
||||
}
|
||||
|
||||
// RegisterNotificationFlags that are used by watchtower to send notifications
|
||||
|
61
pkg/api/api.go
Normal file
61
pkg/api/api.go
Normal file
@ -0,0 +1,61 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"net/http"
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
lock chan bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
lock = make(chan bool, 1)
|
||||
lock <- true
|
||||
}
|
||||
|
||||
func SetupHTTPUpdates(apiToken string, updateFunction func()) error {
|
||||
if apiToken == "" {
|
||||
return errors.New("API token is empty or has not been set. Not starting API.")
|
||||
}
|
||||
|
||||
log.Println("Watchtower HTTP API started.")
|
||||
|
||||
http.HandleFunc("/v1/update", func(w http.ResponseWriter, r *http.Request){
|
||||
log.Info("Updates triggered by HTTP API request.")
|
||||
|
||||
_, err := io.Copy(os.Stdout, r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Header.Get("Token") != apiToken {
|
||||
log.Println("Invalid token. Not updating.")
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Valid token found. Attempting to update.")
|
||||
|
||||
select {
|
||||
case chanValue := <- lock:
|
||||
defer func() { lock <- chanValue }()
|
||||
updateFunction()
|
||||
default:
|
||||
log.Debug("Skipped. Another update already running.")
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func WaitForHTTPUpdates() error {
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user