From e109a7a6cec3d4165bd17a8f39c3305f8ec965fe Mon Sep 17 00:00:00 2001 From: Simon Aronsson Date: Sun, 21 Jul 2019 19:58:19 +0200 Subject: [PATCH] refactor: extract types and pkgs to new files --- actions/actions_suite_test.go | 3 ++- actions/update.go | 3 ++- cmd/root.go | 5 ++-- container/client.go | 8 +++--- container/container.go | 11 +++++---- container/filters.go | 30 ++++++++--------------- {container => internal/util}/util.go | 10 ++++---- {container => internal/util}/util_test.go | 16 ++++++------ notifications/email.go | 6 ++--- notifications/msteams.go | 3 ++- notifications/notifier.go | 10 +++----- notifications/slack.go | 3 ++- pkg/types/filter.go | 5 ++++ pkg/types/filterable_container.go | 9 +++++++ pkg/types/notifier.go | 6 +++++ 15 files changed, 71 insertions(+), 57 deletions(-) rename {container => internal/util}/util.go (72%) rename {container => internal/util}/util_test.go (86%) create mode 100644 pkg/types/filter.go create mode 100644 pkg/types/filterable_container.go create mode 100644 pkg/types/notifier.go diff --git a/actions/actions_suite_test.go b/actions/actions_suite_test.go index fcbe461..0afab92 100644 --- a/actions/actions_suite_test.go +++ b/actions/actions_suite_test.go @@ -10,6 +10,7 @@ import ( "github.com/containrrr/watchtower/container/mocks" "github.com/docker/docker/api/types" + t "github.com/containrrr/watchtower/pkg/types" cli "github.com/docker/docker/client" . "github.com/onsi/ginkgo" @@ -149,7 +150,7 @@ type TestData struct { Containers []container.Container } -func (client mockClient) ListContainers(f container.Filter) ([]container.Container, error) { +func (client mockClient) ListContainers(f t.Filter) ([]container.Container, error) { return client.TestData.Containers, nil } diff --git a/actions/update.go b/actions/update.go index a288729..8bfa14b 100644 --- a/actions/update.go +++ b/actions/update.go @@ -5,6 +5,7 @@ import ( "time" "github.com/containrrr/watchtower/container" + t "github.com/containrrr/watchtower/pkg/types" log "github.com/sirupsen/logrus" ) @@ -14,7 +15,7 @@ var ( // UpdateParams contains all different options available to alter the behavior of the Update func type UpdateParams struct { - Filter container.Filter + Filter t.Filter Cleanup bool NoRestart bool Timeout time.Duration diff --git a/cmd/root.go b/cmd/root.go index a64a045..2514a0d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,6 +11,7 @@ import ( "github.com/containrrr/watchtower/container" "github.com/containrrr/watchtower/internal/flags" "github.com/containrrr/watchtower/notifications" + t "github.com/containrrr/watchtower/pkg/types" "github.com/robfig/cron" log "github.com/sirupsen/logrus" @@ -123,7 +124,7 @@ func Run(c *cobra.Command, names []string) { os.Exit(1) } -func runUpgradesOnSchedule(filter container.Filter) error { +func runUpgradesOnSchedule(filter t.Filter) error { tryLockSem := make(chan bool, 1) tryLockSem <- true @@ -164,7 +165,7 @@ func runUpgradesOnSchedule(filter container.Filter) error { return nil } -func runUpdatesWithNotifications(filter container.Filter) { +func runUpdatesWithNotifications(filter t.Filter) { notifier.StartNotification() updateParams := actions.UpdateParams{ Filter: filter, diff --git a/container/client.go b/container/client.go index b707d0c..b6abdd9 100644 --- a/container/client.go +++ b/container/client.go @@ -5,10 +5,10 @@ import ( "io/ioutil" "time" + t "github.com/containrrr/watchtower/pkg/types" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" - - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" dockerclient "github.com/docker/docker/client" log "github.com/sirupsen/logrus" @@ -22,7 +22,7 @@ const ( // A Client is the interface through which watchtower interacts with the // Docker API. type Client interface { - ListContainers(Filter) ([]Container, error) + ListContainers(t.Filter) ([]Container, error) StopContainer(Container, time.Duration) error StartContainer(Container) error RenameContainer(Container, string) error @@ -58,7 +58,7 @@ type dockerClient struct { includeStopped bool } -func (client dockerClient) ListContainers(fn Filter) ([]Container, error) { +func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) { cs := []Container{} bg := context.Background() diff --git a/container/container.go b/container/container.go index 66ae505..14b0e86 100644 --- a/container/container.go +++ b/container/container.go @@ -2,6 +2,7 @@ package container import ( "fmt" + "github.com/containrrr/watchtower/internal/util" "strconv" "strings" @@ -146,19 +147,19 @@ func (c Container) runtimeConfig() *dockercontainer.Config { config.User = "" } - if sliceEqual(config.Cmd, imageConfig.Cmd) { + if util.SliceEqual(config.Cmd, imageConfig.Cmd) { config.Cmd = nil } - if sliceEqual(config.Entrypoint, imageConfig.Entrypoint) { + if util.SliceEqual(config.Entrypoint, imageConfig.Entrypoint) { config.Entrypoint = nil } - config.Env = sliceSubtract(config.Env, imageConfig.Env) + config.Env = util.SliceSubtract(config.Env, imageConfig.Env) - config.Labels = stringMapSubtract(config.Labels, imageConfig.Labels) + config.Labels = util.StringMapSubtract(config.Labels, imageConfig.Labels) - config.Volumes = structMapSubtract(config.Volumes, imageConfig.Volumes) + config.Volumes = util.StructMapSubtract(config.Volumes, imageConfig.Volumes) // subtract ports exposed in image from container for k := range config.ExposedPorts { diff --git a/container/filters.go b/container/filters.go index 1b3fbbd..b4d4911 100644 --- a/container/filters.go +++ b/container/filters.go @@ -1,30 +1,20 @@ package container -// A Filter is a prototype for a function that can be used to filter the -// results from a call to the ListContainers() method on the Client. -type Filter func(FilterableContainer) bool - -// A FilterableContainer is the interface which is used to filter -// containers. -type FilterableContainer interface { - Name() string - IsWatchtower() bool - Enabled() (bool, bool) -} +import t "github.com/containrrr/watchtower/pkg/types" // WatchtowerContainersFilter filters only watchtower containers -func WatchtowerContainersFilter(c FilterableContainer) bool { return c.IsWatchtower() } +func WatchtowerContainersFilter(c t.FilterableContainer) bool { return c.IsWatchtower() } // Filter no containers and returns all -func noFilter(FilterableContainer) bool { return true } +func noFilter(t.FilterableContainer) bool { return true } // Filters containers which don't have a specified name -func filterByNames(names []string, baseFilter Filter) Filter { +func filterByNames(names []string, baseFilter t.Filter) t.Filter { if len(names) == 0 { return baseFilter } - return func(c FilterableContainer) bool { + return func(c t.FilterableContainer) bool { for _, name := range names { if (name == c.Name()) || (name == c.Name()[1:]) { return baseFilter(c) @@ -35,8 +25,8 @@ func filterByNames(names []string, baseFilter Filter) Filter { } // Filters out containers that don't have the 'enableLabel' -func filterByEnableLabel(baseFilter Filter) Filter { - return func(c FilterableContainer) bool { +func filterByEnableLabel(baseFilter t.Filter) t.Filter { + return func(c t.FilterableContainer) bool { // If label filtering is enabled, containers should only be considered // if the label is specifically set. _, ok := c.Enabled() @@ -49,8 +39,8 @@ func filterByEnableLabel(baseFilter Filter) Filter { } // Filters out containers that have a 'enableLabel' and is set to disable. -func filterByDisabledLabel(baseFilter Filter) Filter { - return func(c FilterableContainer) bool { +func filterByDisabledLabel(baseFilter t.Filter) t.Filter { + return func(c t.FilterableContainer) bool { enabledLabel, ok := c.Enabled() if ok && !enabledLabel { // If the label has been set and it demands a disable @@ -62,7 +52,7 @@ func filterByDisabledLabel(baseFilter Filter) Filter { } // BuildFilter creates the needed filter of containers -func BuildFilter(names []string, enableLabel bool) Filter { +func BuildFilter(names []string, enableLabel bool) t.Filter { filter := noFilter filter = filterByNames(names, filter) if enableLabel { diff --git a/container/util.go b/internal/util/util.go similarity index 72% rename from container/util.go rename to internal/util/util.go index 3db01d1..924e9c2 100644 --- a/container/util.go +++ b/internal/util/util.go @@ -1,6 +1,6 @@ -package container +package util -func sliceEqual(s1, s2 []string) bool { +func SliceEqual(s1, s2 []string) bool { if len(s1) != len(s2) { return false } @@ -14,7 +14,7 @@ func sliceEqual(s1, s2 []string) bool { return true } -func sliceSubtract(a1, a2 []string) []string { +func SliceSubtract(a1, a2 []string) []string { a := []string{} for _, e1 := range a1 { @@ -35,7 +35,7 @@ func sliceSubtract(a1, a2 []string) []string { return a } -func stringMapSubtract(m1, m2 map[string]string) map[string]string { +func StringMapSubtract(m1, m2 map[string]string) map[string]string { m := map[string]string{} for k1, v1 := range m1 { @@ -51,7 +51,7 @@ func stringMapSubtract(m1, m2 map[string]string) map[string]string { return m } -func structMapSubtract(m1, m2 map[string]struct{}) map[string]struct{} { +func StructMapSubtract(m1, m2 map[string]struct{}) map[string]struct{} { m := map[string]struct{}{} for k1, v1 := range m1 { diff --git a/container/util_test.go b/internal/util/util_test.go similarity index 86% rename from container/util_test.go rename to internal/util/util_test.go index 8c4eef9..e5bf6ba 100644 --- a/container/util_test.go +++ b/internal/util/util_test.go @@ -1,8 +1,8 @@ -package container +package util import ( - "testing" "github.com/stretchr/testify/assert" + "testing" ) @@ -11,7 +11,7 @@ func TestSliceEqual_True(t *testing.T) { s1 := []string{"a", "b", "c"} s2 := []string{"a", "b", "c"} - result := sliceEqual(s1, s2) + result := SliceEqual(s1, s2) assert.True(t, result) } @@ -20,7 +20,7 @@ func TestSliceEqual_DifferentLengths(t *testing.T) { s1 := []string{"a", "b", "c"} s2 := []string{"a", "b", "c", "d"} - result := sliceEqual(s1, s2) + result := SliceEqual(s1, s2) assert.False(t, result) } @@ -29,7 +29,7 @@ func TestSliceEqual_DifferentContents(t *testing.T) { s1 := []string{"a", "b", "c"} s2 := []string{"a", "b", "d"} - result := sliceEqual(s1, s2) + result := SliceEqual(s1, s2) assert.False(t, result) } @@ -38,7 +38,7 @@ func TestSliceSubtract(t *testing.T) { a1 := []string{"a", "b", "c"} a2 := []string{"a", "c"} - result := sliceSubtract(a1, a2) + result := SliceSubtract(a1, a2) assert.Equal(t, []string{"b"}, result) assert.Equal(t, []string{"a", "b", "c"}, a1) assert.Equal(t, []string{"a", "c"}, a2) @@ -48,7 +48,7 @@ func TestStringMapSubtract(t *testing.T) { m1 := map[string]string{"a": "a", "b": "b", "c": "sea"} m2 := map[string]string{"a": "a", "c": "c"} - result := stringMapSubtract(m1, m2) + result := StringMapSubtract(m1, m2) assert.Equal(t, map[string]string{"b": "b", "c": "sea"}, result) assert.Equal(t, map[string]string{"a": "a", "b": "b", "c": "sea"}, m1) assert.Equal(t, map[string]string{"a": "a", "c": "c"}, m2) @@ -59,7 +59,7 @@ func TestStructMapSubtract(t *testing.T) { m1 := map[string]struct{}{"a": x, "b": x, "c": x} m2 := map[string]struct{}{"a": x, "c": x} - result := structMapSubtract(m1, m2) + result := StructMapSubtract(m1, m2) assert.Equal(t, map[string]struct{}{"b": x}, result) assert.Equal(t, map[string]struct{}{"a": x, "b": x, "c": x}, m1) assert.Equal(t, map[string]struct{}{"a": x, "c": x}, m2) diff --git a/notifications/email.go b/notifications/email.go index fd3f4e8..8a0ebfe 100644 --- a/notifications/email.go +++ b/notifications/email.go @@ -9,7 +9,7 @@ import ( "time" "strconv" - + t "github.com/containrrr/watchtower/pkg/types" log "github.com/sirupsen/logrus" ) @@ -17,7 +17,7 @@ const ( emailType = "email" ) -// Implements typeNotifier, logrus.Hook +// Implements Notifier, logrus.Hook // The default logrus email integration would have several issues: // - It would send one email per log output // - It would only send errors @@ -31,7 +31,7 @@ type emailTypeNotifier struct { logLevels []log.Level } -func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) typeNotifier { +func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier { flags := c.PersistentFlags() from, _ := flags.GetString("notification-email-from") diff --git a/notifications/msteams.go b/notifications/msteams.go index 325c452..b356814 100644 --- a/notifications/msteams.go +++ b/notifications/msteams.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/cobra" "net/http" + t "github.com/containrrr/watchtower/pkg/types" log "github.com/sirupsen/logrus" "io/ioutil" ) @@ -21,7 +22,7 @@ type msTeamsTypeNotifier struct { data bool } -func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) typeNotifier { +func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Notifier { flags := cmd.PersistentFlags() diff --git a/notifications/notifier.go b/notifications/notifier.go index 145eccd..b14ba24 100644 --- a/notifications/notifier.go +++ b/notifications/notifier.go @@ -1,19 +1,17 @@ package notifications import ( + ty "github.com/containrrr/watchtower/pkg/types" "github.com/johntdyer/slackrus" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -type typeNotifier interface { - StartNotification() - SendNotification() -} + // Notifier can send log output as notification to admins, with optional batching. type Notifier struct { - types []typeNotifier + types []ty.Notifier } // NewNotifier creates and returns a new Notifier, using global configuration. @@ -34,7 +32,7 @@ func NewNotifier(c *cobra.Command) *Notifier { types, _ := f.GetStringSlice("notifications") for _, t := range types { - var tn typeNotifier + var tn ty.Notifier switch t { case emailType: tn = newEmailNotifier(c, acceptedLogLevels) diff --git a/notifications/slack.go b/notifications/slack.go index 709c9ba..0bc8ae1 100644 --- a/notifications/slack.go +++ b/notifications/slack.go @@ -4,6 +4,7 @@ import ( "github.com/johntdyer/slackrus" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + t "github.com/containrrr/watchtower/pkg/types" ) const ( @@ -14,7 +15,7 @@ type slackTypeNotifier struct { slackrus.SlackrusHook } -func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) typeNotifier { +func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier { flags := c.PersistentFlags() hookURL, _ := flags.GetString("notification-slack-hook-url") diff --git a/pkg/types/filter.go b/pkg/types/filter.go new file mode 100644 index 0000000..514e4bd --- /dev/null +++ b/pkg/types/filter.go @@ -0,0 +1,5 @@ +package types + +// A Filter is a prototype for a function that can be used to filter the +// results from a call to the ListContainers() method on the Client. +type Filter func(FilterableContainer) bool diff --git a/pkg/types/filterable_container.go b/pkg/types/filterable_container.go new file mode 100644 index 0000000..d89b910 --- /dev/null +++ b/pkg/types/filterable_container.go @@ -0,0 +1,9 @@ +package types + +// A FilterableContainer is the interface which is used to filter +// containers. +type FilterableContainer interface { + Name() string + IsWatchtower() bool + Enabled() (bool, bool) +} diff --git a/pkg/types/notifier.go b/pkg/types/notifier.go new file mode 100644 index 0000000..f073552 --- /dev/null +++ b/pkg/types/notifier.go @@ -0,0 +1,6 @@ +package types + +type Notifier interface { + StartNotification() + SendNotification() +}