mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-22 05:40:50 +02:00
feat(docs): add template preview (#1777)
feat(filters): Allow explicitly filtering containers by name. Resolves #1566B
This commit is contained in:
parent
9b28fbc24d
commit
9ffb9ba8e7
5 changed files with 104 additions and 5 deletions
|
@ -35,6 +35,7 @@ var (
|
||||||
noRestart bool
|
noRestart bool
|
||||||
monitorOnly bool
|
monitorOnly bool
|
||||||
enableLabel bool
|
enableLabel bool
|
||||||
|
disableContainers []string
|
||||||
notifier t.Notifier
|
notifier t.Notifier
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
lifecycleHooks bool
|
lifecycleHooks bool
|
||||||
|
@ -93,6 +94,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
enableLabel, _ = f.GetBool("label-enable")
|
enableLabel, _ = f.GetBool("label-enable")
|
||||||
|
disableContainers, _ = f.GetStringSlice("disable-containers")
|
||||||
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
|
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
|
||||||
rollingRestart, _ = f.GetBool("rolling-restart")
|
rollingRestart, _ = f.GetBool("rolling-restart")
|
||||||
scope, _ = f.GetString("scope")
|
scope, _ = f.GetString("scope")
|
||||||
|
@ -134,7 +136,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
// Run is the main execution flow of the command
|
// Run is the main execution flow of the command
|
||||||
func Run(c *cobra.Command, names []string) {
|
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")
|
runOnce, _ := c.PersistentFlags().GetBool("run-once")
|
||||||
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
|
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
|
||||||
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")
|
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")
|
||||||
|
|
|
@ -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
|
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.
|
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
|
## Without updating containers
|
||||||
Will only monitor for new images, send notifications and invoke
|
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
|
the [pre-check/post-check hooks](https://containrrr.dev/watchtower/lifecycle-hooks/), but will __not__ update the
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -85,6 +86,13 @@ func RegisterSystemFlags(rootCmd *cobra.Command) {
|
||||||
envBool("WATCHTOWER_LABEL_ENABLE"),
|
envBool("WATCHTOWER_LABEL_ENABLE"),
|
||||||
"Watch containers where the com.centurylinklabs.watchtower.enable label is true")
|
"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(
|
flags.StringP(
|
||||||
"log-format",
|
"log-format",
|
||||||
"l",
|
"l",
|
||||||
|
|
|
@ -13,7 +13,7 @@ func WatchtowerContainersFilter(c t.FilterableContainer) bool { return c.IsWatch
|
||||||
// NoFilter will not filter out any containers
|
// NoFilter will not filter out any containers
|
||||||
func NoFilter(t.FilterableContainer) bool { return true }
|
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 {
|
func FilterByNames(names []string, baseFilter t.Filter) t.Filter {
|
||||||
if len(names) == 0 {
|
if len(names) == 0 {
|
||||||
return baseFilter
|
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
|
// FilterByEnableLabel returns all containers that have the enabled label set
|
||||||
func FilterByEnableLabel(baseFilter t.Filter) t.Filter {
|
func FilterByEnableLabel(baseFilter t.Filter) t.Filter {
|
||||||
return func(c t.FilterableContainer) bool {
|
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
|
// 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{}
|
sb := strings.Builder{}
|
||||||
filter := NoFilter
|
filter := NoFilter
|
||||||
filter = FilterByNames(names, filter)
|
filter = FilterByNames(names, filter)
|
||||||
|
filter = FilterByDisableNames(disableNames, filter)
|
||||||
|
|
||||||
if len(names) > 0 {
|
if len(names) > 0 {
|
||||||
sb.WriteString("which name matches \"")
|
sb.WriteString("which name matches \"")
|
||||||
|
@ -118,6 +135,16 @@ func BuildFilter(names []string, enableLabel bool, scope string) (t.Filter, stri
|
||||||
}
|
}
|
||||||
sb.WriteString(`", `)
|
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 enableLabel {
|
||||||
// If label filtering is enabled, containers should only be considered
|
// If label filtering is enabled, containers should only be considered
|
||||||
|
|
|
@ -171,7 +171,7 @@ func TestFilterByImage(t *testing.T) {
|
||||||
func TestBuildFilter(t *testing.T) {
|
func TestBuildFilter(t *testing.T) {
|
||||||
names := []string{"test", "valid"}
|
names := []string{"test", "valid"}
|
||||||
|
|
||||||
filter, desc := BuildFilter(names, false, "")
|
filter, desc := BuildFilter(names, []string{}, false, "")
|
||||||
assert.Contains(t, desc, "test")
|
assert.Contains(t, desc, "test")
|
||||||
assert.Contains(t, desc, "or")
|
assert.Contains(t, desc, "or")
|
||||||
assert.Contains(t, desc, "valid")
|
assert.Contains(t, desc, "valid")
|
||||||
|
@ -210,7 +210,7 @@ func TestBuildFilterEnableLabel(t *testing.T) {
|
||||||
var names []string
|
var names []string
|
||||||
names = append(names, "test")
|
names = append(names, "test")
|
||||||
|
|
||||||
filter, desc := BuildFilter(names, true, "")
|
filter, desc := BuildFilter(names, []string{}, true, "")
|
||||||
assert.Contains(t, desc, "using enable label")
|
assert.Contains(t, desc, "using enable label")
|
||||||
|
|
||||||
container := new(mocks.FilterableContainer)
|
container := new(mocks.FilterableContainer)
|
||||||
|
@ -235,3 +235,52 @@ func TestBuildFilterEnableLabel(t *testing.T) {
|
||||||
assert.False(t, filter(container))
|
assert.False(t, filter(container))
|
||||||
container.AssertExpectations(t)
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue