Monitor-only for individual containers (#652)

* Add monitor-only label

* Add tests for monitor-only

* Treat missing monitor-only label as if the option was set to false

* Add docs for container-based monitor-only

* Add function doc

* Fix monitor-only logic
pull/656/head
David H 4 years ago committed by GitHub
parent 98b518612b
commit bde421be0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -162,7 +162,7 @@ Environment Variable: WATCHTOWER_LABEL_ENABLE
**Do not** update containers that have `com.centurylinklabs.watchtower.enable` label set to false and no `--label-enable` argument is passed. Note that only one or the other (targeting by enable label) can be used at the same time to target containers. **Do not** update containers that have `com.centurylinklabs.watchtower.enable` label set to false and no `--label-enable` argument is passed. Note that only one or the other (targeting by enable label) can be used at the same time to target containers.
## Without updating containers ## Without updating containers
Will only monitor for new images, not update the containers. Will only monitor for new images, send notifications and invoke the [pre-check/post-check hooks](https://containrrr.dev/watchtower/lifecycle-hooks/), but will **not** update the containers.
> ### ⚠️ Please note > ### ⚠️ Please note
> >
@ -175,6 +175,8 @@ Environment Variable: WATCHTOWER_MONITOR_ONLY
Default: false Default: false
``` ```
Note that monitor-only can also be specified on a per-container basis with the `com.centurylinklabs.watchtower.monitor-only` label set on those containers.
## Without restarting containers ## Without restarting containers
Do not restart containers after updating. This option can be useful when the start of the containers Do not restart containers after updating. This option can be useful when the start of the containers
is managed by an external system such as systemd. is managed by an external system such as systemd.

@ -1,5 +1,12 @@
By default, watchtower will watch all containers. However, sometimes only some containers should be updated. By default, watchtower will watch all containers. However, sometimes only some containers should be updated.
There are two options:
- **Fully exclude**: You can choose to exclude containers entirely from being watched by watchtower.
- **Monitor only**: In this mode, watchtower checks for container updates, sends notifications and invokes the [pre-check/post-check hooks](https://containrrr.dev/watchtower/lifecycle-hooks/) on the containers but does **not** perform the update.
## Full Exclude
If you need to exclude some containers, set the _com.centurylinklabs.watchtower.enable_ label to `false`. If you need to exclude some containers, set the _com.centurylinklabs.watchtower.enable_ label to `false`.
```docker ```docker
@ -29,3 +36,23 @@ If you wish to create a monitoring scope, you will need to [run multiple instanc
Watchtower filters running containers by testing them against each configured criteria. A container is monitored if all criteria are met. For example: Watchtower filters running containers by testing them against each configured criteria. A container is monitored if all criteria are met. For example:
- If a container's name is on the monitoring name list (not empty `--name` argument) but it is not enabled (_centurylinklabs.watchtower.enable=false_), it won't be monitored; - If a container's name is on the monitoring name list (not empty `--name` argument) but it is not enabled (_centurylinklabs.watchtower.enable=false_), it won't be monitored;
- If a container's name is not on the monitoring name list (not empty `--name` argument), even if it is enabled (_centurylinklabs.watchtower.enable=true_ and `--label-enable` flag is set), it won't be monitored; - If a container's name is not on the monitoring name list (not empty `--name` argument), even if it is enabled (_centurylinklabs.watchtower.enable=true_ and `--label-enable` flag is set), it won't be monitored;
## Monitor Only
Individual containers can be marked to only be monitored (without being updated).
To do so, set the *com.centurylinklabs.watchtower.monitor-only* label to `true` on that container.
```docker
LABEL com.centurylinklabs.watchtower.monitor-only="true"
```
Or, it can be specified as part of the `docker run` command line:
```bash
docker run -d --label=com.centurylinklabs.watchtower.monitor-only=true someimage
```
When the label is specified on a container, watchtower treats that container exactly as if [`WATCHTOWER_MONITOR_ONLY`](https://containrrr.dev/watchtower/arguments/#without_updating_containers) was set, but the effect is limited to the individual container.

@ -27,3 +27,22 @@ func CreateMockContainer(id string, name string, image string, created time.Time
}, },
) )
} }
// CreateMockContainerWithConfig creates a container substitute valid for testing
func CreateMockContainerWithConfig(id string, name string, image string, created time.Time, config *container2.Config) container.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
Image: image,
Name: name,
Created: created.String(),
},
Config: config,
}
return *container.NewContainer(
&content,
&types.ImageInspect{
ID: image,
},
)
}

@ -28,7 +28,7 @@ func Update(client container.Client, params types.UpdateParams) error {
for i, targetContainer := range containers { for i, targetContainer := range containers {
stale, err := client.IsContainerStale(targetContainer) stale, err := client.IsContainerStale(targetContainer)
if stale && !params.NoRestart && !params.MonitorOnly && !targetContainer.HasImageInfo() { if stale && !params.NoRestart && !params.MonitorOnly && !targetContainer.IsMonitorOnly() && !targetContainer.HasImageInfo() {
err = errors.New("no available image info") err = errors.New("no available image info")
} }
if err != nil { if err != nil {
@ -45,18 +45,20 @@ func Update(client container.Client, params types.UpdateParams) error {
checkDependencies(containers) checkDependencies(containers)
if params.MonitorOnly { containersToUpdate := []container.Container{}
if params.LifecycleHooks { if !params.MonitorOnly {
lifecycle.ExecutePostChecks(client, params) for i := len(containers) - 1; i >= 0; i-- {
if !containers[i].IsMonitorOnly() {
containersToUpdate = append(containersToUpdate, containers[i])
}
} }
return nil
} }
if params.RollingRestart { if params.RollingRestart {
performRollingRestart(containers, client, params) performRollingRestart(containersToUpdate, client, params)
} else { } else {
stopContainersInReversedOrder(containers, client, params) stopContainersInReversedOrder(containersToUpdate, client, params)
restartContainersInSortedOrder(containers, client, params) restartContainersInSortedOrder(containersToUpdate, client, params)
} }
if params.LifecycleHooks { if params.LifecycleHooks {
lifecycle.ExecutePostChecks(client, params) lifecycle.ExecutePostChecks(client, params)

@ -5,6 +5,7 @@ import (
"github.com/containrrr/watchtower/pkg/container" "github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/container/mocks" "github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/containrrr/watchtower/pkg/types" "github.com/containrrr/watchtower/pkg/types"
container2 "github.com/docker/docker/api/types/container"
cli "github.com/docker/docker/client" cli "github.com/docker/docker/client"
"time" "time"
@ -80,4 +81,73 @@ var _ = Describe("the update action", func() {
}) })
}) })
}) })
When("watchtower has been instructed to monitor only", func() {
When("certain containers are set to monitor only", func() {
BeforeEach(func() {
client = CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
"fake-image1:latest",
time.Now()),
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
"fake-image2:latest",
time.Now(),
&container2.Config{
Labels: map[string]string{
"com.centurylinklabs.watchtower.monitor-only": "true",
},
}),
},
},
dockerClient,
false,
false,
)
})
It("should not update those containers", func() {
err := actions.Update(client, types.UpdateParams{Cleanup: true})
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
})
})
When("monitor only is set globally", func() {
BeforeEach(func() {
client = CreateMockClient(
&TestData{
Containers: []container.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
"fake-image:latest",
time.Now()),
CreateMockContainer(
"test-container-02",
"test-container-02",
"fake-image:latest",
time.Now()),
},
},
dockerClient,
false,
false,
)
})
It("should not update any containers", func() {
err := actions.Update(client, types.UpdateParams{MonitorOnly: true})
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(0))
})
})
})
}) })

@ -90,6 +90,22 @@ func (c Container) Enabled() (bool, bool) {
return parsedBool, true return parsedBool, true
} }
// IsMonitorOnly returns the value of the monitor-only label. If the label
// is not set then false is returned.
func (c Container) IsMonitorOnly() bool {
rawBool, ok := c.getLabelValue(monitorOnlyLabel)
if !ok {
return false
}
parsedBool, err := strconv.ParseBool(rawBool)
if err != nil {
return false
}
return parsedBool
}
// Scope returns the value of the scope UID label and if the label // Scope returns the value of the scope UID label and if the label
// was set. // was set.
func (c Container) Scope() (string, bool) { func (c Container) Scope() (string, bool) {

@ -4,6 +4,7 @@ const (
watchtowerLabel = "com.centurylinklabs.watchtower" watchtowerLabel = "com.centurylinklabs.watchtower"
signalLabel = "com.centurylinklabs.watchtower.stop-signal" signalLabel = "com.centurylinklabs.watchtower.stop-signal"
enableLabel = "com.centurylinklabs.watchtower.enable" enableLabel = "com.centurylinklabs.watchtower.enable"
monitorOnlyLabel = "com.centurylinklabs.watchtower.monitor-only"
dependsOnLabel = "com.centurylinklabs.watchtower.depends-on" dependsOnLabel = "com.centurylinklabs.watchtower.depends-on"
zodiacLabel = "com.centurylinklabs.zodiac.original-image" zodiacLabel = "com.centurylinklabs.zodiac.original-image"
scope = "com.centurylinklabs.watchtower.scope" scope = "com.centurylinklabs.watchtower.scope"

Loading…
Cancel
Save