mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-22 05:40:50 +02:00
add pre/post update check lifecycle hooks (#373)
* add pre/post update check lifecycle hooks * update docs for lifecycle hooks * Fix phrasing
This commit is contained in:
parent
1b3db5ed5d
commit
135467dcf6
3 changed files with 87 additions and 15 deletions
|
@ -1,45 +1,53 @@
|
||||||
|
|
||||||
## Executing commands before and after updating
|
## Executing commands before and after updating
|
||||||
|
|
||||||
> **DO NOTE**: Both commands are shell commands executed with `sh`, and therefore require the
|
> **DO NOTE**: These are shell commands executed with `sh`, and therefore require the
|
||||||
> container to provide the `sh` executable.
|
> container to provide the `sh` executable.
|
||||||
|
|
||||||
It is possible to execute a *pre-update* command and a *post-update* command
|
It is possible to execute _pre/post\-check_ and _pre/post\-update_ commands
|
||||||
**inside** every container updated by watchtower. The *pre-update* command is
|
**inside** every container updated by watchtower.
|
||||||
executed before stopping the container, and the *post-update* command is
|
|
||||||
executed after restarting the container.
|
- The _pre-check_ command is executed for each container prior to every update cycle.
|
||||||
|
- The _pre-update_ command is executed before stopping the container when an update is about to start.
|
||||||
|
- The _post-update_ command is executed after restarting the updated container
|
||||||
|
- The _post-check_ command is executed for each container post every update cycle.
|
||||||
|
|
||||||
This feature is disabled by default. To enable it, you need to set the option
|
This feature is disabled by default. To enable it, you need to set the option
|
||||||
`--enable-lifecycle-hooks` on the command line, or set the environment variable
|
`--enable-lifecycle-hooks` on the command line, or set the environment variable
|
||||||
`WATCHTOWER_LIFECYCLE_HOOKS` to `true`.
|
`WATCHTOWER_LIFECYCLE_HOOKS` to `true`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Specifying update commands
|
### Specifying update commands
|
||||||
|
|
||||||
The commands are specified using docker container labels, with
|
The commands are specified using docker container labels, the following are currently available:
|
||||||
`com.centurylinklabs.watchtower.lifecycle.pre-update-command` for the *pre-update*
|
|
||||||
command and `com.centurylinklabs.watchtower.lifecycle.post-update` for the
|
|
||||||
*post-update* command.
|
|
||||||
|
|
||||||
These labels can be declared as instructions in a Dockerfile:
|
| Type | Docker Container Label |
|
||||||
|
| ----------- | ------------------------------------------------------ |
|
||||||
|
| Pre Check | `com.centurylinklabs.watchtower.lifecycle.pre-check` |
|
||||||
|
| Pre Update | `com.centurylinklabs.watchtower.lifecycle.pre-update` |
|
||||||
|
| Post Update | `com.centurylinklabs.watchtower.lifecycle.post-update` |
|
||||||
|
| Post Check | `com.centurylinklabs.watchtower.lifecycle.post-check` |
|
||||||
|
|
||||||
|
These labels can be declared as instructions in a Dockerfile (with some example .sh files):
|
||||||
|
|
||||||
```docker
|
```docker
|
||||||
|
LABEL com.centurylinklabs.watchtower.lifecycle.pre-check="/sync.sh"
|
||||||
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="/dump-data.sh"
|
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="/dump-data.sh"
|
||||||
LABEL com.centurylinklabs.watchtower.lifecycle.post-update="/restore-data.sh"
|
LABEL com.centurylinklabs.watchtower.lifecycle.post-update="/restore-data.sh"
|
||||||
|
LABEL com.centurylinklabs.watchtower.lifecycle.post-check="/send-heartbeat.sh"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or be specified as part of the `docker run` command line:
|
Or be specified as part of the `docker run` command line:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
|
--label=com.centurylinklabs.watchtower.lifecycle.pre-check="/sync.sh" \
|
||||||
--label=com.centurylinklabs.watchtower.lifecycle.pre-update="/dump-data.sh" \
|
--label=com.centurylinklabs.watchtower.lifecycle.pre-update="/dump-data.sh" \
|
||||||
--label=com.centurylinklabs.watchtower.lifecycle.post-update="/restore-data.sh" \
|
--label=com.centurylinklabs.watchtower.lifecycle.post-update="/restore-data.sh" \
|
||||||
someimage
|
someimage
|
||||||
|
--label=com.centurylinklabs.watchtower.lifecycle.post-check="/send-heartbeat.sh" \
|
||||||
```
|
```
|
||||||
|
|
||||||
### Execution failure
|
### Execution failure
|
||||||
|
|
||||||
The failure of a command to execute, identified by an exit code different than
|
The failure of a command to execute, identified by an exit code different than
|
||||||
0, will not prevent watchtower from updating the container. Only an error
|
0, will not prevent watchtower from updating the container. Only an error
|
||||||
log statement containing the exit code will be reported.
|
log statement containing the exit code will be reported.
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
func Update(client container.Client, params UpdateParams) error {
|
func Update(client container.Client, params UpdateParams) error {
|
||||||
log.Debug("Checking containers for updated images")
|
log.Debug("Checking containers for updated images")
|
||||||
|
|
||||||
|
executePreCheck(client, params)
|
||||||
|
|
||||||
containers, err := client.ListContainers(params.Filter)
|
containers, err := client.ListContainers(params.Filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -36,12 +38,14 @@ func Update(client container.Client, params UpdateParams) error {
|
||||||
checkDependencies(containers)
|
checkDependencies(containers)
|
||||||
|
|
||||||
if params.MonitorOnly {
|
if params.MonitorOnly {
|
||||||
|
executePostCheck(client, params)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stopContainersInReversedOrder(containers, client, params)
|
stopContainersInReversedOrder(containers, client, params)
|
||||||
restartContainersInSortedOrder(containers, client, params)
|
restartContainersInSortedOrder(containers, client, params)
|
||||||
|
|
||||||
|
executePostCheck(client, params)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +127,58 @@ func checkDependencies(containers []container.Container) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func executePreCheck(client container.Client, params UpdateParams) {
|
||||||
|
containers, err := client.ListContainers(params.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, container := range containers {
|
||||||
|
executePreCheckCommand(client, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executePostCheck(client container.Client, params UpdateParams) {
|
||||||
|
containers, err := client.ListContainers(params.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, container := range containers {
|
||||||
|
executePostCheckCommand(client, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executePreCheckCommand(client container.Client, container container.Container) {
|
||||||
|
command := container.GetLifecyclePreCheckCommand()
|
||||||
|
if len(command) == 0 {
|
||||||
|
log.Debug("No pre-check command supplied. Skipping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Executing pre-check command.")
|
||||||
|
if err := client.ExecuteCommand(container.ID(), command); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executePostCheckCommand(client container.Client, container container.Container) {
|
||||||
|
command := container.GetLifecyclePostCheckCommand()
|
||||||
|
if len(command) == 0 {
|
||||||
|
log.Debug("No post-check command supplied. Skipping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Executing post-check command.")
|
||||||
|
if err := client.ExecuteCommand(container.ID(), command); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func executePreUpdateCommand(client container.Client, container container.Container) {
|
func executePreUpdateCommand(client container.Client, container container.Container) {
|
||||||
|
|
||||||
command := container.GetLifecyclePreUpdateCommand()
|
command := container.GetLifecyclePreUpdateCommand()
|
||||||
if len(command) == 0 {
|
if len(command) == 0 {
|
||||||
log.Debug("No pre-update command supplied. Skipping")
|
log.Debug("No pre-update command supplied. Skipping")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Executing pre-update command.")
|
log.Info("Executing pre-update command.")
|
||||||
|
@ -146,6 +197,7 @@ func executePostUpdateCommand(client container.Client, newContainerID string) {
|
||||||
command := newContainer.GetLifecyclePostUpdateCommand()
|
command := newContainer.GetLifecyclePostUpdateCommand()
|
||||||
if len(command) == 0 {
|
if len(command) == 0 {
|
||||||
log.Debug("No post-update command supplied. Skipping")
|
log.Debug("No post-update command supplied. Skipping")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Executing post-update command.")
|
log.Info("Executing post-update command.")
|
||||||
|
|
|
@ -5,10 +5,22 @@ const (
|
||||||
signalLabel = "com.centurylinklabs.watchtower.stop-signal"
|
signalLabel = "com.centurylinklabs.watchtower.stop-signal"
|
||||||
enableLabel = "com.centurylinklabs.watchtower.enable"
|
enableLabel = "com.centurylinklabs.watchtower.enable"
|
||||||
zodiacLabel = "com.centurylinklabs.zodiac.original-image"
|
zodiacLabel = "com.centurylinklabs.zodiac.original-image"
|
||||||
|
preCheckLabel = "com.centurylinklabs.watchtower.lifecycle.pre-check"
|
||||||
|
postCheckLabel = "com.centurylinklabs.watchtower.lifecycle.post-check"
|
||||||
preUpdateLabel = "com.centurylinklabs.watchtower.lifecycle.pre-update"
|
preUpdateLabel = "com.centurylinklabs.watchtower.lifecycle.pre-update"
|
||||||
postUpdateLabel = "com.centurylinklabs.watchtower.lifecycle.post-update"
|
postUpdateLabel = "com.centurylinklabs.watchtower.lifecycle.post-update"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetLifecyclePreCheckCommand returns the pre-check command set in the container metadata or an empty string
|
||||||
|
func (c Container) GetLifecyclePreCheckCommand() string {
|
||||||
|
return c.getLabelValueOrEmpty(preCheckLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLifecyclePostCheckCommand returns the post-check command set in the container metadata or an empty string
|
||||||
|
func (c Container) GetLifecyclePostCheckCommand() string {
|
||||||
|
return c.getLabelValueOrEmpty(postCheckLabel)
|
||||||
|
}
|
||||||
|
|
||||||
// GetLifecyclePreUpdateCommand returns the pre-update command set in the container metadata or an empty string
|
// GetLifecyclePreUpdateCommand returns the pre-update command set in the container metadata or an empty string
|
||||||
func (c Container) GetLifecyclePreUpdateCommand() string {
|
func (c Container) GetLifecyclePreUpdateCommand() string {
|
||||||
return c.getLabelValueOrEmpty(preUpdateLabel)
|
return c.getLabelValueOrEmpty(preUpdateLabel)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue