feat(docs): add template preview (#1777)

feat(filters): Allow explicitly filtering containers by name.

Resolves #1566B
This commit is contained in:
nils måsén 2023-10-02 16:11:04 +02:00 committed by Rodrigo Damazio Bovendorp
parent 9b28fbc24d
commit 9ffb9ba8e7
5 changed files with 104 additions and 5 deletions

View file

@ -35,6 +35,7 @@ var (
noRestart bool
monitorOnly bool
enableLabel bool
disableContainers []string
notifier t.Notifier
timeout time.Duration
lifecycleHooks bool
@ -93,6 +94,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
}
enableLabel, _ = f.GetBool("label-enable")
disableContainers, _ = f.GetStringSlice("disable-containers")
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
rollingRestart, _ = f.GetBool("rolling-restart")
scope, _ = f.GetString("scope")
@ -134,7 +136,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
// Run is the main execution flow of the command
func Run(c *cobra.Command, names []string) {
filter, filterDesc := filters.BuildFilter(names, enableLabel, scope)
filter, filterDesc := filters.BuildFilter(names, disableContainers, enableLabel, scope)
runOnce, _ := c.PersistentFlags().GetBool("run-once")
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")

View file

@ -230,6 +230,19 @@ __Do not__ Monitor and update containers that have `com.centurylinklabs.watchtow
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.
## Filter by disabling specific container names
Monitor and update containers whose names are not in a given set of names.
This can be used to exclude specific containers, when setting labels is not an option.
The listed containers will be excluded even if they have the enable filter set to true.
```text
Argument: --disable-containers, -x
Environment Variable: WATCHTOWER_DISABLE_CONTAINERS
Type: Comma-separated string list
Default: ""
```
## Without updating 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

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"regexp"
"strings"
"time"
@ -85,6 +86,13 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
envBool("WATCHTOWER_LABEL_ENABLE"),
"Watch containers where the com.centurylinklabs.watchtower.enable label is true")
flags.StringSliceP(
"disable-containers",
"x",
// Due to issue spf13/viper#380, can't use viper.GetStringSlice:
regexp.MustCompile("[, ]+").Split(viper.GetString("WATCHTOWER_DISABLE_CONTAINERS"), -1),
"Comma-separated list of containers to explicitly exclude from watching.")
flags.StringP(
"log-format",
"l",

View file

@ -13,7 +13,7 @@ func WatchtowerContainersFilter(c t.FilterableContainer) bool { return c.IsWatch
// NoFilter will not filter out any containers
func NoFilter(t.FilterableContainer) bool { return true }
// FilterByNames returns all containers that match the specified name
// FilterByNames returns all containers that match one of the specified names
func FilterByNames(names []string, baseFilter t.Filter) t.Filter {
if len(names) == 0 {
return baseFilter
@ -41,6 +41,22 @@ func FilterByNames(names []string, baseFilter t.Filter) t.Filter {
}
}
// FilterByDisableNames returns all containers that don't match any of the specified names
func FilterByDisableNames(disableNames []string, baseFilter t.Filter) t.Filter {
if len(disableNames) == 0 {
return baseFilter
}
return func(c t.FilterableContainer) bool {
for _, name := range disableNames {
if name == c.Name() || name == c.Name()[1:] {
return false
}
}
return baseFilter(c)
}
}
// FilterByEnableLabel returns all containers that have the enabled label set
func FilterByEnableLabel(baseFilter t.Filter) t.Filter {
return func(c t.FilterableContainer) bool {
@ -103,10 +119,11 @@ func FilterByImage(images []string, baseFilter t.Filter) t.Filter {
}
// BuildFilter creates the needed filter of containers
func BuildFilter(names []string, enableLabel bool, scope string) (t.Filter, string) {
func BuildFilter(names []string, disableNames []string, enableLabel bool, scope string) (t.Filter, string) {
sb := strings.Builder{}
filter := NoFilter
filter = FilterByNames(names, filter)
filter = FilterByDisableNames(disableNames, filter)
if len(names) > 0 {
sb.WriteString("which name matches \"")
@ -118,6 +135,16 @@ func BuildFilter(names []string, enableLabel bool, scope string) (t.Filter, stri
}
sb.WriteString(`", `)
}
if len(disableNames) > 0 {
sb.WriteString("not named one of \"")
for i, n := range disableNames {
sb.WriteString(n)
if i < len(disableNames)-1 {
sb.WriteString(`" or "`)
}
}
sb.WriteString(`", `)
}
if enableLabel {
// If label filtering is enabled, containers should only be considered

View file

@ -171,7 +171,7 @@ func TestFilterByImage(t *testing.T) {
func TestBuildFilter(t *testing.T) {
names := []string{"test", "valid"}
filter, desc := BuildFilter(names, false, "")
filter, desc := BuildFilter(names, []string{}, false, "")
assert.Contains(t, desc, "test")
assert.Contains(t, desc, "or")
assert.Contains(t, desc, "valid")
@ -210,7 +210,7 @@ func TestBuildFilterEnableLabel(t *testing.T) {
var names []string
names = append(names, "test")
filter, desc := BuildFilter(names, true, "")
filter, desc := BuildFilter(names, []string{}, true, "")
assert.Contains(t, desc, "using enable label")
container := new(mocks.FilterableContainer)
@ -235,3 +235,52 @@ func TestBuildFilterEnableLabel(t *testing.T) {
assert.False(t, filter(container))
container.AssertExpectations(t)
}
func TestBuildFilterDisableContainer(t *testing.T) {
filter, desc := BuildFilter([]string{}, []string{"excluded", "notfound"}, false, "")
assert.Contains(t, desc, "not named")
assert.Contains(t, desc, "excluded")
assert.Contains(t, desc, "or")
assert.Contains(t, desc, "notfound")
container := new(mocks.FilterableContainer)
container.On("Name").Return("Another")
container.On("Enabled").Return(false, false)
assert.True(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Name").Return("AnotherOne")
container.On("Enabled").Return(true, true)
assert.True(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Name").Return("test")
container.On("Enabled").Return(false, false)
assert.True(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Name").Return("excluded")
container.On("Enabled").Return(true, true)
assert.False(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Name").Return("excludedAsSubstring")
container.On("Enabled").Return(true, true)
assert.True(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Name").Return("notfound")
container.On("Enabled").Return(true, true)
assert.False(t, filter(container))
container.AssertExpectations(t)
container = new(mocks.FilterableContainer)
container.On("Enabled").Return(false, true)
assert.False(t, filter(container))
container.AssertExpectations(t)
}