mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-21 21:30:48 +02:00
feat: add support for "none" scope (#1800)
This commit is contained in:
parent
40b8c77100
commit
dd54055143
4 changed files with 92 additions and 19 deletions
|
@ -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.
|
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).
|
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
|
```text
|
||||||
Argument: --scope
|
Argument: --scope
|
||||||
Environment Variable: WATCHTOWER_SCOPE
|
Environment Variable: WATCHTOWER_SCOPE
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
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.
|
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:
|
!!! note
|
||||||
- Multiple instances can't run with the same scope;
|
- 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;
|
- 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:
|
For example, in a Docker Compose config file:
|
||||||
|
|
||||||
|
@ -12,16 +13,29 @@ For example, in a Docker Compose config file:
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app-monitored-by-watchtower:
|
app-with-scope:
|
||||||
image: myapps/monitored-by-watchtower
|
image: myapps/monitored-by-watchtower
|
||||||
labels:
|
labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]
|
||||||
- "com.centurylinklabs.watchtower.scope=myscope"
|
|
||||||
|
|
||||||
watchtower:
|
scoped-watchtower:
|
||||||
image: containrrr/watchtower
|
image: containrrr/watchtower
|
||||||
volumes:
|
volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
command: --interval 30 --scope myscope
|
command: --interval 30 --scope myscope
|
||||||
labels:
|
labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]
|
||||||
- "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
|
||||||
```
|
```
|
||||||
|
|
|
@ -86,13 +86,14 @@ func FilterByDisabledLabel(baseFilter t.Filter) t.Filter {
|
||||||
|
|
||||||
// FilterByScope returns all containers that belongs to a specific scope
|
// FilterByScope returns all containers that belongs to a specific scope
|
||||||
func FilterByScope(scope string, baseFilter t.Filter) t.Filter {
|
func FilterByScope(scope string, baseFilter t.Filter) t.Filter {
|
||||||
if scope == "" {
|
|
||||||
return baseFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(c t.FilterableContainer) bool {
|
return func(c t.FilterableContainer) bool {
|
||||||
containerScope, ok := c.Scope()
|
containerScope, containerHasScope := c.Scope()
|
||||||
if ok && containerScope == scope {
|
|
||||||
|
if !containerHasScope || containerScope == "" {
|
||||||
|
containerScope = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerScope == scope {
|
||||||
return baseFilter(c)
|
return baseFilter(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +153,13 @@ func BuildFilter(names []string, disableNames []string, enableLabel bool, scope
|
||||||
filter = FilterByEnableLabel(filter)
|
filter = FilterByEnableLabel(filter)
|
||||||
sb.WriteString("using enable label, ")
|
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 a scope has been defined, containers should only be considered
|
||||||
// if the scope is specifically set.
|
// if the scope is specifically set.
|
||||||
filter = FilterByScope(scope, filter)
|
filter = FilterByScope(scope, filter)
|
||||||
|
|
|
@ -111,6 +111,53 @@ func TestFilterByScope(t *testing.T) {
|
||||||
container.AssertExpectations(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 TestBuildFilterNoneScope(t *testing.T) {
|
||||||
|
filter, desc := BuildFilter(nil, nil, false, "none")
|
||||||
|
|
||||||
|
assert.Contains(t, desc, "without a scope")
|
||||||
|
|
||||||
|
scoped := new(mocks.FilterableContainer)
|
||||||
|
scoped.On("Enabled").Return(false, false)
|
||||||
|
scoped.On("Scope").Return("anyscope", true)
|
||||||
|
|
||||||
|
unscoped := new(mocks.FilterableContainer)
|
||||||
|
unscoped.On("Enabled").Return(false, false)
|
||||||
|
unscoped.On("Scope").Return("", false)
|
||||||
|
|
||||||
|
assert.False(t, filter(scoped))
|
||||||
|
assert.True(t, filter(unscoped))
|
||||||
|
|
||||||
|
scoped.AssertExpectations(t)
|
||||||
|
unscoped.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFilterByDisabledLabel(t *testing.T) {
|
func TestFilterByDisabledLabel(t *testing.T) {
|
||||||
filter := FilterByDisabledLabel(NoFilter)
|
filter := FilterByDisabledLabel(NoFilter)
|
||||||
assert.NotNil(t, filter)
|
assert.NotNil(t, filter)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue