You've already forked watchtower
							
							
				mirror of
				https://github.com/containrrr/watchtower.git
				synced 2025-10-31 00:17:44 +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:
		
							
								
								
									
										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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user