Skip to content

Commit

Permalink
feat: add support for "none" scope
Browse files Browse the repository at this point in the history
  • Loading branch information
piksel committed Oct 14, 2023
1 parent 72e437f commit f309ad4
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 19 deletions.
5 changes: 5 additions & 0 deletions docs/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ Environment Variable: WATCHTOWER_HTTP_API_PERIODIC_POLLS
Update containers that have a `com.centurylinklabs.watchtower.scope` label set with the same value as the given argument.
This enables [running multiple instances](https://containrrr.dev/watchtower/running-multiple-instances).

!!! note "Filter by lack of scope"
If you want other instances of watchtower to ignore the scoped containers, set this argument to `none`.
When omitted, watchtower will update all containers regardless of scope.


```text
Argument: --scope
Environment Variable: WATCHTOWER_SCOPE
Expand Down
38 changes: 26 additions & 12 deletions docs/running-multiple-instances.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
By default, Watchtower will clean up other instances and won't allow multiple instances running on the same Docker host or swarm. It is possible to override this behavior by defining a [scope](https://containrrr.github.io/watchtower/arguments/#filter_by_scope) to each running instance.

Notice that:
- Multiple instances can't run with the same scope;
- An instance without a scope will clean up other running instances, even if they have a defined scope;
!!! note
- Multiple instances can't run with the same scope;
- An instance without a scope will clean up other running instances, even if they have a defined scope;
- Supplying `none` as the scope will treat `com.centurylinklabs.watchtower.scope=none`, `com.centurylinklabs.watchtower.scope=` and the lack of a `com.centurylinklabs.watchtower.scope` label as the scope `none`. This effectly enables you to run both scoped and unscoped watchtower instances on the same machine.

To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the _com.centurylinklabs.watchtower.scope_ label with the same value for the containers you want to include in this instance's scope (including the instance itself).
To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the `com.centurylinklabs.watchtower.scope` label with the same value for the containers you want to include in this instance's scope (including the instance itself).

For example, in a Docker Compose config file:

```yaml
version: '3'

services:
app-monitored-by-watchtower:
app-with-scope:
image: myapps/monitored-by-watchtower
labels:
- "com.centurylinklabs.watchtower.scope=myscope"
labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]

watchtower:
scoped-watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
command: --interval 30 --scope myscope
labels:
- "com.centurylinklabs.watchtower.scope=myscope"
labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]

unscoped-app-a:
image: myapps/app-a

unscoped-app-b:
image: myapps/app-b
labels: [ "com.centurylinklabs.watchtower.scope=none" ]

unscoped-app-c:
image: myapps/app-b
labels: [ "com.centurylinklabs.watchtower.scope=" ]

unscoped-watchtower:
image: containrrr/watchtower
volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
command: --interval 30 --scope none
```
21 changes: 14 additions & 7 deletions pkg/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,14 @@ func FilterByDisabledLabel(baseFilter t.Filter) t.Filter {

// FilterByScope returns all containers that belongs to a specific scope
func FilterByScope(scope string, baseFilter t.Filter) t.Filter {
if scope == "" {
return baseFilter
}

return func(c t.FilterableContainer) bool {
containerScope, ok := c.Scope()
if ok && containerScope == scope {
containerScope, containerHasScope := c.Scope()

if !containerHasScope || containerScope == "" {
containerScope = "none"
}

if containerScope == scope {
return baseFilter(c)
}

Expand Down Expand Up @@ -152,7 +153,13 @@ func BuildFilter(names []string, disableNames []string, enableLabel bool, scope
filter = FilterByEnableLabel(filter)
sb.WriteString("using enable label, ")
}
if scope != "" {

if scope == "none" {
// If a scope has explicitly defined as "none", containers should only be considered
// if they do not have a scope defined, or if it's explicitly set to "none".
filter = FilterByScope(scope, filter)
sb.WriteString(`without a scope, "`)
} else if scope != "" {
// If a scope has been defined, containers should only be considered
// if the scope is specifically set.
filter = FilterByScope(scope, filter)
Expand Down
27 changes: 27 additions & 0 deletions pkg/filters/filters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,33 @@ func TestFilterByScope(t *testing.T) {
container.AssertExpectations(t)
}

func TestFilterByNoneScope(t *testing.T) {
scope := "none"

filter := FilterByScope(scope, NoFilter)
assert.NotNil(t, filter)

container := new(mocks.FilterableContainer)
container.On("Scope").Return("anyscope", true)
assert.False(t, filter(container))
container.AssertExpectations(t)

container = new(mocks.FilterableContainer)
container.On("Scope").Return("", false)
assert.True(t, filter(container))
container.AssertExpectations(t)

container = new(mocks.FilterableContainer)
container.On("Scope").Return("", true)
assert.True(t, filter(container))
container.AssertExpectations(t)

container = new(mocks.FilterableContainer)
container.On("Scope").Return("none", true)
assert.True(t, filter(container))
container.AssertExpectations(t)
}

func TestFilterByDisabledLabel(t *testing.T) {
filter := FilterByDisabledLabel(NoFilter)
assert.NotNil(t, filter)
Expand Down

0 comments on commit f309ad4

Please sign in to comment.