diff --git a/cmd/root.go b/cmd/root.go index 265588d..63a824c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/containrrr/watchtower/pkg/filters" "os" "os/signal" "strconv" @@ -108,7 +109,7 @@ func PreRun(cmd *cobra.Command, args []string) { // Run is the main execution flow of the command func Run(c *cobra.Command, names []string) { - filter := container.BuildFilter(names, enableLabel) + filter := filters.BuildFilter(names, enableLabel) runOnce, _ := c.PersistentFlags().GetBool("run-once") if runOnce { @@ -172,7 +173,7 @@ func runUpgradesOnSchedule(filter t.Filter) error { func runUpdatesWithNotifications(filter t.Filter) { notifier.StartNotification() - updateParams := actions.UpdateParams{ + updateParams := t.UpdateParams{ Filter: filter, Cleanup: cleanup, NoRestart: noRestart, diff --git a/internal/actions/check.go b/internal/actions/check.go index b60aec5..785701f 100644 --- a/internal/actions/check.go +++ b/internal/actions/check.go @@ -3,6 +3,8 @@ package actions import ( "errors" "fmt" + "github.com/containrrr/watchtower/pkg/filters" + "github.com/containrrr/watchtower/pkg/sorter" "sort" "strings" "time" @@ -19,7 +21,7 @@ import ( // will stop and remove all but the most recently started container. func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool) error { awaitDockerClient() - containers, err := client.ListContainers(container.WatchtowerContainersFilter) + containers, err := client.ListContainers(filters.WatchtowerContainersFilter) if err != nil { log.Fatal(err) @@ -39,7 +41,7 @@ func cleanupExcessWatchtowers(containers []container.Container, client container var cleanupErrors int var stopErrors int - sort.Sort(container.ByCreated(containers)) + sort.Sort(sorter.ByCreated(containers)) allContainersExceptLast := containers[0 : len(containers)-1] for _, c := range allContainersExceptLast { diff --git a/internal/actions/update.go b/internal/actions/update.go index 5461f35..874e705 100644 --- a/internal/actions/update.go +++ b/internal/actions/update.go @@ -3,6 +3,9 @@ package actions import ( "github.com/containrrr/watchtower/internal/util" "github.com/containrrr/watchtower/pkg/container" + "github.com/containrrr/watchtower/pkg/lifecycle" + "github.com/containrrr/watchtower/pkg/sorter" + "github.com/containrrr/watchtower/pkg/types" log "github.com/sirupsen/logrus" ) @@ -10,10 +13,12 @@ import ( // used to start those containers have been updated. If a change is detected in // any of the images, the associated containers are stopped and restarted with // the new image. -func Update(client container.Client, params UpdateParams) error { +func Update(client container.Client, params types.UpdateParams) error { log.Debug("Checking containers for updated images") - executePreCheck(client, params) + if params.LifecycleHooks { + lifecycle.ExecutePreChecks(client, params) + } containers, err := client.ListContainers(params.Filter) if err != nil { @@ -30,7 +35,7 @@ func Update(client container.Client, params UpdateParams) error { containers[i].Stale = stale } - containers, err = container.SortByDependencies(containers) + containers, err = sorter.SortByDependencies(containers) if err != nil { return err } @@ -38,24 +43,28 @@ func Update(client container.Client, params UpdateParams) error { checkDependencies(containers) if params.MonitorOnly { - executePostCheck(client, params) + if params.LifecycleHooks { + lifecycle.ExecutePostChecks(client, params) + } return nil } stopContainersInReversedOrder(containers, client, params) restartContainersInSortedOrder(containers, client, params) - executePostCheck(client, params) + if params.LifecycleHooks { + lifecycle.ExecutePostChecks(client, params) + } return nil } -func stopContainersInReversedOrder(containers []container.Container, client container.Client, params UpdateParams) { +func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) { for i := len(containers) - 1; i >= 0; i-- { stopStaleContainer(containers[i], client, params) } } -func stopStaleContainer(container container.Container, client container.Client, params UpdateParams) { +func stopStaleContainer(container container.Container, client container.Client, params types.UpdateParams) { if container.IsWatchtower() { log.Debugf("This is the watchtower container %s", container.Name()) return @@ -64,15 +73,17 @@ func stopStaleContainer(container container.Container, client container.Client, if !container.Stale { return } + if params.LifecycleHooks { + lifecycle.ExecutePreUpdateCommand(client, container) - executePreUpdateCommand(client, container) + } if err := client.StopContainer(container, params.Timeout); err != nil { log.Error(err) } } -func restartContainersInSortedOrder(containers []container.Container, client container.Client, params UpdateParams) { +func restartContainersInSortedOrder(containers []container.Container, client container.Client, params types.UpdateParams) { imageIDs := make(map[string]bool) for _, container := range containers { @@ -91,7 +102,7 @@ func restartContainersInSortedOrder(containers []container.Container, client con } } -func restartStaleContainer(container container.Container, client container.Client, params UpdateParams) { +func restartStaleContainer(container container.Container, client container.Client, params types.UpdateParams) { // Since we can't shutdown a watchtower container immediately, we need to // start the new one while the old one is still running. This prevents us // from re-using the same container name so we first rename the current @@ -107,7 +118,7 @@ func restartStaleContainer(container container.Container, client container.Clien if newContainerID, err := client.StartContainer(container); err != nil { log.Error(err) } else if container.Stale && params.LifecycleHooks { - executePostUpdateCommand(client, newContainerID) + lifecycle.ExecutePostUpdateCommand(client, newContainerID) } } } @@ -130,82 +141,3 @@ func checkDependencies(containers []container.Container) { } } } - -func executePreCheck(client container.Client, params UpdateParams) { - containers, err := client.ListContainers(params.Filter) - if err != nil { - return - } - for _, container := range containers { - executePreCheckCommand(client, container) - } -} - -func executePostCheck(client container.Client, params UpdateParams) { - containers, err := client.ListContainers(params.Filter) - if err != nil { - return - } - for _, container := range containers { - executePostCheckCommand(client, container) - } -} - -func executePreCheckCommand(client container.Client, container container.Container) { - command := container.GetLifecyclePreCheckCommand() - if len(command) == 0 { - log.Debug("No pre-check command supplied. Skipping") - return - } - - log.Info("Executing pre-check command.") - if err := client.ExecuteCommand(container.ID(), command); err != nil { - log.Error(err) - } -} - -func executePostCheckCommand(client container.Client, container container.Container) { - command := container.GetLifecyclePostCheckCommand() - if len(command) == 0 { - log.Debug("No post-check command supplied. Skipping") - return - } - - log.Info("Executing post-check command.") - if err := client.ExecuteCommand(container.ID(), command); err != nil { - log.Error(err) - } -} - -func executePreUpdateCommand(client container.Client, container container.Container) { - - command := container.GetLifecyclePreUpdateCommand() - if len(command) == 0 { - log.Debug("No pre-update command supplied. Skipping") - return - } - - log.Info("Executing pre-update command.") - if err := client.ExecuteCommand(container.ID(), command); err != nil { - log.Error(err) - } -} - -func executePostUpdateCommand(client container.Client, newContainerID string) { - newContainer, err := client.GetContainer(newContainerID) - if err != nil { - log.Error(err) - return - } - - command := newContainer.GetLifecyclePostUpdateCommand() - if len(command) == 0 { - log.Debug("No post-update command supplied. Skipping") - return - } - - log.Info("Executing post-update command.") - if err := client.ExecuteCommand(newContainerID, command); err != nil { - log.Error(err) - } -} diff --git a/internal/actions/update_test.go b/internal/actions/update_test.go index 4ab6c8a..3c9befe 100644 --- a/internal/actions/update_test.go +++ b/internal/actions/update_test.go @@ -4,6 +4,7 @@ import ( "github.com/containrrr/watchtower/internal/actions" "github.com/containrrr/watchtower/pkg/container" "github.com/containrrr/watchtower/pkg/container/mocks" + "github.com/containrrr/watchtower/pkg/types" cli "github.com/docker/docker/client" "time" @@ -59,7 +60,7 @@ var _ = Describe("the update action", func() { When("there are multiple containers using the same image", func() { It("should only try to remove the image once", func() { - err := actions.Update(client, actions.UpdateParams{ Cleanup: true }) + err := actions.Update(client, types.UpdateParams{ Cleanup: true }) Expect(err).NotTo(HaveOccurred()) Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1)) }) @@ -75,7 +76,7 @@ var _ = Describe("the update action", func() { time.Now(), ), ) - err := actions.Update(client, actions.UpdateParams{ Cleanup: true }) + err := actions.Update(client, types.UpdateParams{ Cleanup: true }) Expect(err).NotTo(HaveOccurred()) Expect(client.TestData.TriedToRemoveImageCount).To(Equal(2)) }) diff --git a/pkg/container/client.go b/pkg/container/client.go index ab69d40..607b84c 100644 --- a/pkg/container/client.go +++ b/pkg/container/client.go @@ -3,6 +3,7 @@ package container import ( "bytes" "fmt" + "github.com/containrrr/watchtower/pkg/registry" "io/ioutil" "strings" "time" @@ -12,7 +13,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" - dockerclient "github.com/docker/docker/client" + sdkClient "github.com/docker/docker/client" log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -40,7 +41,7 @@ type Client interface { // * DOCKER_TLS_VERIFY whether to verify tls certificates // * DOCKER_API_VERSION the minimum docker api version to work with func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool) Client { - cli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv) + cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv) if err != nil { log.Fatalf("Error instantiating Docker client: %s", err) @@ -56,7 +57,7 @@ func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeV } type dockerClient struct { - api dockerclient.CommonAPIClient + api sdkClient.CommonAPIClient pullImages bool removeVolumes bool includeStopped bool @@ -231,53 +232,60 @@ func (client dockerClient) RenameContainer(c Container, newName string) error { return client.api.ContainerRename(bg, c.ID(), newName) } -func (client dockerClient) IsContainerStale(c Container) (bool, error) { - bg := context.Background() - oldImageInfo := c.imageInfo - imageName := c.ImageName() +func (client dockerClient) IsContainerStale(container Container) (bool, error) { + ctx := context.Background() - if client.pullImages { - log.Debugf("Pulling %s for %s", imageName, c.Name()) + if !client.pullImages { + log.Debugf("Skipping image pull.") + } else if err := client.PullImage(ctx, container); err != nil { + return false, err + } - var opts types.ImagePullOptions // ImagePullOptions can take a RegistryAuth arg to authenticate against a private registry - auth, err := EncodedAuth(imageName) - log.Debugf("Got auth value: %s", auth) - log.Debugf("Got image name: %s", imageName) - if err != nil { - log.Debugf("Error loading authentication credentials %s", err) - return false, err - } else if auth == "" { - log.Debugf("No authentication credentials found for %s", imageName) - opts = types.ImagePullOptions{} // empty/no auth credentials - } else { - opts = types.ImagePullOptions{RegistryAuth: auth, PrivilegeFunc: DefaultAuthHandler} - } + return client.HasNewImage(ctx, container) +} - response, err := client.api.ImagePull(bg, imageName, opts) - if err != nil { - log.Debugf("Error pulling image %s, %s", imageName, err) - return false, err - } - defer response.Close() +func (client dockerClient) HasNewImage(ctx context.Context, container Container) (bool, error) { + oldImageID := container.imageInfo.ID + imageName := container.ImageName() - // the pull request will be aborted prematurely unless the response is read - if _, err = ioutil.ReadAll(response); err != nil { - log.Error(err) - } + newImageInfo, _, err := client.api.ImageInspectWithRaw(ctx, imageName) + if err != nil { + return false, err } - newImageInfo, _, err := client.api.ImageInspectWithRaw(bg, imageName) + if newImageInfo.ID == oldImageID { + log.Debugf("No new images found for %s", container.Name()) + return false, nil + } + + log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID) + return true, nil +} + +func (client dockerClient) PullImage(ctx context.Context, container Container) error { + containerName := container.Name() + imageName := container.ImageName() + log.Debugf("Pulling %s for %s", imageName, containerName) + + opts, err := registry.GetPullOptions(imageName) if err != nil { - return false, err + log.Debugf("Error loading authentication credentials %s", err) + return err } - if newImageInfo.ID != oldImageInfo.ID { - log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID) - return true, nil + response, err := client.api.ImagePull(ctx, imageName, opts) + if err != nil { + log.Debugf("Error pulling image %s, %s", imageName, err) + return err } - log.Debugf("No new images found for %s", c.Name()) - return false, nil + defer response.Close() + // the pull request will be aborted prematurely unless the response is read + if _, err = ioutil.ReadAll(response); err != nil { + log.Error(err) + return err + } + return nil } func (client dockerClient) RemoveImageByID(id string) error { diff --git a/pkg/container/container.go b/pkg/container/container.go index 09e4225..99868f3 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -28,6 +28,11 @@ type Container struct { imageInfo *types.ImageInspect } +// ContainerInfo fetches JSON info for the container +func (c Container) ContainerInfo() *types.ContainerJSON { + return c.containerInfo +} + // ID returns the Docker container ID. func (c Container) ID() string { return c.containerInfo.ID diff --git a/pkg/container/container_test.go b/pkg/container/container_test.go index 9e1b213..fe838f7 100644 --- a/pkg/container/container_test.go +++ b/pkg/container/container_test.go @@ -2,6 +2,7 @@ package container import ( "github.com/containrrr/watchtower/pkg/container/mocks" + "github.com/containrrr/watchtower/pkg/filters" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" cli "github.com/docker/docker/client" @@ -34,14 +35,14 @@ var _ = Describe("the container", func() { }) When("listing containers without any filter", func() { It("should return all available containers", func() { - containers, err := client.ListContainers(noFilter) + containers, err := client.ListContainers(filters.NoFilter) Expect(err).NotTo(HaveOccurred()) Expect(len(containers) == 2).To(BeTrue()) }) }) When("listing containers with a filter matching nothing", func() { It("should return an empty array", func() { - filter := filterByNames([]string{"lollercoaster"}, noFilter) + filter := filters.FilterByNames([]string{"lollercoaster"}, filters.NoFilter) containers, err := client.ListContainers(filter) Expect(err).NotTo(HaveOccurred()) Expect(len(containers) == 0).To(BeTrue()) @@ -49,7 +50,7 @@ var _ = Describe("the container", func() { }) When("listing containers with a watchtower filter", func() { It("should return only the watchtower container", func() { - containers, err := client.ListContainers(WatchtowerContainersFilter) + containers, err := client.ListContainers(filters.WatchtowerContainersFilter) Expect(err).NotTo(HaveOccurred()) Expect(len(containers) == 1).To(BeTrue()) Expect(containers[0].ImageName()).To(Equal("containrrr/watchtower:latest")) @@ -62,7 +63,7 @@ var _ = Describe("the container", func() { pullImages: false, includeStopped: true, } - containers, err := client.ListContainers(noFilter) + containers, err := client.ListContainers(filters.NoFilter) Expect(err).NotTo(HaveOccurred()) Expect(len(containers) > 0).To(BeTrue()) }) diff --git a/pkg/container/filters.go b/pkg/filters/filters.go similarity index 63% rename from pkg/container/filters.go rename to pkg/filters/filters.go index b4d4911..b923745 100644 --- a/pkg/container/filters.go +++ b/pkg/filters/filters.go @@ -1,15 +1,15 @@ -package container +package filters import t "github.com/containrrr/watchtower/pkg/types" // WatchtowerContainersFilter filters only watchtower containers func WatchtowerContainersFilter(c t.FilterableContainer) bool { return c.IsWatchtower() } -// Filter no containers and returns all -func noFilter(t.FilterableContainer) bool { return true } +// NoFilter will not filter out any containers +func NoFilter(t.FilterableContainer) bool { return true } -// Filters containers which don't have a specified name -func filterByNames(names []string, baseFilter t.Filter) t.Filter { +// FilterByNames returns all containers that match the specified name +func FilterByNames(names []string, baseFilter t.Filter) t.Filter { if len(names) == 0 { return baseFilter } @@ -24,8 +24,8 @@ func filterByNames(names []string, baseFilter t.Filter) t.Filter { } } -// Filters out containers that don't have the 'enableLabel' -func filterByEnableLabel(baseFilter t.Filter) t.Filter { +// FilterByEnableLabel returns all containers that have the enabled label set +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. @@ -38,8 +38,8 @@ func filterByEnableLabel(baseFilter t.Filter) t.Filter { } } -// Filters out containers that have a 'enableLabel' and is set to disable. -func filterByDisabledLabel(baseFilter t.Filter) t.Filter { +// FilterByDisabledLabel returns all containers that have the enabled label set to disable +func FilterByDisabledLabel(baseFilter t.Filter) t.Filter { return func(c t.FilterableContainer) bool { enabledLabel, ok := c.Enabled() if ok && !enabledLabel { @@ -53,13 +53,13 @@ func filterByDisabledLabel(baseFilter t.Filter) t.Filter { // BuildFilter creates the needed filter of containers func BuildFilter(names []string, enableLabel bool) t.Filter { - filter := noFilter - filter = filterByNames(names, filter) + filter := NoFilter + filter = FilterByNames(names, filter) if enableLabel { // If label filtering is enabled, containers should only be considered // if the label is specifically set. - filter = filterByEnableLabel(filter) + filter = FilterByEnableLabel(filter) } - filter = filterByDisabledLabel(filter) + filter = FilterByDisabledLabel(filter) return filter } diff --git a/pkg/container/filters_test.go b/pkg/filters/filters_test.go similarity index 94% rename from pkg/container/filters_test.go rename to pkg/filters/filters_test.go index 4118335..d24b186 100644 --- a/pkg/container/filters_test.go +++ b/pkg/filters/filters_test.go @@ -1,4 +1,4 @@ -package container +package filters import ( "testing" @@ -20,7 +20,7 @@ func TestWatchtowerContainersFilter(t *testing.T) { func TestNoFilter(t *testing.T) { container := new(mocks.FilterableContainer) - assert.True(t, noFilter(container)) + assert.True(t, NoFilter(container)) container.AssertExpectations(t) } @@ -28,12 +28,12 @@ func TestNoFilter(t *testing.T) { func TestFilterByNames(t *testing.T) { var names []string - filter := filterByNames(names, nil) + filter := FilterByNames(names, nil) assert.Nil(t, filter) names = append(names, "test") - filter = filterByNames(names, noFilter) + filter = FilterByNames(names, NoFilter) assert.NotNil(t, filter) container := new(mocks.FilterableContainer) @@ -48,7 +48,7 @@ func TestFilterByNames(t *testing.T) { } func TestFilterByEnableLabel(t *testing.T) { - filter := filterByEnableLabel(noFilter) + filter := FilterByEnableLabel(NoFilter) assert.NotNil(t, filter) container := new(mocks.FilterableContainer) @@ -68,7 +68,7 @@ func TestFilterByEnableLabel(t *testing.T) { } func TestFilterByDisabledLabel(t *testing.T) { - filter := filterByDisabledLabel(noFilter) + filter := FilterByDisabledLabel(NoFilter) assert.NotNil(t, filter) container := new(mocks.FilterableContainer) diff --git a/pkg/lifecycle/lifecycle.go b/pkg/lifecycle/lifecycle.go new file mode 100644 index 0000000..9823f9d --- /dev/null +++ b/pkg/lifecycle/lifecycle.go @@ -0,0 +1,93 @@ +package lifecycle + +import ( + "github.com/containrrr/watchtower/pkg/container" + "github.com/containrrr/watchtower/pkg/types" + log "github.com/sirupsen/logrus" +) + +// ExecutePreChecks tries to run the pre-check lifecycle hook for all containers included by the current filter. +func ExecutePreChecks(client container.Client, params types.UpdateParams) { + containers, err := client.ListContainers(params.Filter) + if err != nil { + return + } + for _, container := range containers { + ExecutePreCheckCommand(client, container) + } +} + +// ExecutePostChecks tries to run the post-check lifecycle hook for all containers included by the current filter. +func ExecutePostChecks(client container.Client, params types.UpdateParams) { + containers, err := client.ListContainers(params.Filter) + if err != nil { + return + } + for _, container := range containers { + ExecutePostCheckCommand(client, container) + } +} + +// ExecutePreCheckCommand tries to run the pre-check lifecycle hook for a single container. +func ExecutePreCheckCommand(client container.Client, container container.Container) { + command := container.GetLifecyclePreCheckCommand() + if len(command) == 0 { + log.Debug("No pre-check command supplied. Skipping") + return + } + + log.Info("Executing pre-check command.") + if err := client.ExecuteCommand(container.ID(), command); err != nil { + log.Error(err) + } +} + +// ExecutePostCheckCommand tries to run the post-check lifecycle hook for a single container. +func ExecutePostCheckCommand(client container.Client, container container.Container) { + command := container.GetLifecyclePostCheckCommand() + if len(command) == 0 { + log.Debug("No post-check command supplied. Skipping") + return + } + + log.Info("Executing post-check command.") + if err := client.ExecuteCommand(container.ID(), command); err != nil { + log.Error(err) + } +} + +// ExecutePreUpdateCommand tries to run the pre-update lifecycle hook for a single container. +func ExecutePreUpdateCommand(client container.Client, container container.Container) { + + command := container.GetLifecyclePreUpdateCommand() + if len(command) == 0 { + log.Debug("No pre-update command supplied. Skipping") + return + } + + log.Info("Executing pre-update command.") + if err := client.ExecuteCommand(container.ID(), command); err != nil { + log.Error(err) + } +} + +// ExecutePostUpdateCommand tries to run the post-update lifecycle hook for a single container. +func ExecutePostUpdateCommand(client container.Client, newContainerID string) { + newContainer, err := client.GetContainer(newContainerID) + if err != nil { + log.Error(err) + return + } + + command := newContainer.GetLifecyclePostUpdateCommand() + if len(command) == 0 { + log.Debug("No post-update command supplied. Skipping") + return + } + + log.Info("Executing post-update command.") + if err := client.ExecuteCommand(newContainerID, command); err != nil { + log.Error(err) + } +} + diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go new file mode 100644 index 0000000..2d7b9a8 --- /dev/null +++ b/pkg/registry/registry.go @@ -0,0 +1,33 @@ +package registry + +import ( + "github.com/docker/docker/api/types" + log "github.com/sirupsen/logrus" +) + +// GetPullOptions creates a struct with all options needed for pulling images from a registry +func GetPullOptions(imageName string) (types.ImagePullOptions, error) { + auth, err := EncodedAuth(imageName) + log.Debugf("Got image name: %s", imageName) + if err != nil { + return types.ImagePullOptions{}, err + } + + log.Debugf("Got auth value: %s", auth) + if auth == "" { + return types.ImagePullOptions{}, nil + } + + return types.ImagePullOptions{ + RegistryAuth: auth, + PrivilegeFunc: DefaultAuthHandler, + }, nil +} + +// DefaultAuthHandler will be invoked if an AuthConfig is rejected +// It could be used to return a new value for the "X-Registry-Auth" authentication header, +// but there's no point trying again with the same value as used in AuthConfig +func DefaultAuthHandler() (string, error) { + log.Debug("Authentication request was rejected. Trying again without authentication") + return "", nil +} diff --git a/pkg/container/trust.go b/pkg/registry/trust.go similarity index 88% rename from pkg/container/trust.go rename to pkg/registry/trust.go index 63b76a6..7403d46 100644 --- a/pkg/container/trust.go +++ b/pkg/registry/trust.go @@ -1,4 +1,4 @@ -package container +package registry import ( "errors" @@ -97,11 +97,3 @@ func CredentialsStore(configFile configfile.ConfigFile) credentials.Store { func EncodeAuth(auth types.AuthConfig) (string, error) { return command.EncodeAuthToBase64(auth) } - -// DefaultAuthHandler will be invoked if an AuthConfig is rejected -// It could be used to return a new value for the "X-Registry-Auth" authentication header, -// but there's no point trying again with the same value as used in AuthConfig -func DefaultAuthHandler() (string, error) { - log.Debug("Authentication request was rejected. Trying again without authentication") - return "", nil -} diff --git a/pkg/container/trust_test.go b/pkg/registry/trust_test.go similarity index 99% rename from pkg/container/trust_test.go rename to pkg/registry/trust_test.go index 7d2ac96..8ffe1b9 100644 --- a/pkg/container/trust_test.go +++ b/pkg/registry/trust_test.go @@ -1,4 +1,4 @@ -package container +package registry import ( "github.com/stretchr/testify/assert" diff --git a/pkg/container/sort.go b/pkg/sorter/sort.go similarity index 71% rename from pkg/container/sort.go rename to pkg/sorter/sort.go index 391a8b6..1e27f1b 100644 --- a/pkg/container/sort.go +++ b/pkg/sorter/sort.go @@ -1,13 +1,14 @@ -package container +package sorter import ( "fmt" + "github.com/containrrr/watchtower/pkg/container" "time" ) // ByCreated allows a list of Container structs to be sorted by the container's // created date. -type ByCreated []Container +type ByCreated []container.Container func (c ByCreated) Len() int { return len(c) } func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] } @@ -15,12 +16,12 @@ func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // Less will compare two elements (identified by index) in the Container // list by created-date. func (c ByCreated) Less(i, j int) bool { - t1, err := time.Parse(time.RFC3339Nano, c[i].containerInfo.Created) + t1, err := time.Parse(time.RFC3339Nano, c[i].ContainerInfo().Created) if err != nil { t1 = time.Now() } - t2, _ := time.Parse(time.RFC3339Nano, c[j].containerInfo.Created) + t2, _ := time.Parse(time.RFC3339Nano, c[j].ContainerInfo().Created) if err != nil { t1 = time.Now() } @@ -33,18 +34,18 @@ func (c ByCreated) Less(i, j int) bool { // the front of the list while containers with links will be sorted after all // of their dependencies. This sort order ensures that linked containers can // be started in the correct order. -func SortByDependencies(containers []Container) ([]Container, error) { +func SortByDependencies(containers []container.Container) ([]container.Container, error) { sorter := dependencySorter{} return sorter.Sort(containers) } type dependencySorter struct { - unvisited []Container + unvisited []container.Container marked map[string]bool - sorted []Container + sorted []container.Container } -func (ds *dependencySorter) Sort(containers []Container) ([]Container, error) { +func (ds *dependencySorter) Sort(containers []container.Container) ([]container.Container, error) { ds.unvisited = containers ds.marked = map[string]bool{} @@ -57,10 +58,10 @@ func (ds *dependencySorter) Sort(containers []Container) ([]Container, error) { return ds.sorted, nil } -func (ds *dependencySorter) visit(c Container) error { +func (ds *dependencySorter) visit(c container.Container) error { if _, ok := ds.marked[c.Name()]; ok { - return fmt.Errorf("Circular reference to %s", c.Name()) + return fmt.Errorf("circular reference to %s", c.Name()) } // Mark any visited node so that circular references can be detected @@ -83,7 +84,7 @@ func (ds *dependencySorter) visit(c Container) error { return nil } -func (ds *dependencySorter) findUnvisited(name string) *Container { +func (ds *dependencySorter) findUnvisited(name string) *container.Container { for _, c := range ds.unvisited { if c.Name() == name { return &c @@ -93,7 +94,7 @@ func (ds *dependencySorter) findUnvisited(name string) *Container { return nil } -func (ds *dependencySorter) removeUnvisited(c Container) { +func (ds *dependencySorter) removeUnvisited(c container.Container) { var idx int for i := range ds.unvisited { if ds.unvisited[i].Name() == c.Name() { diff --git a/internal/actions/update_params.go b/pkg/types/update_params.go similarity index 74% rename from internal/actions/update_params.go rename to pkg/types/update_params.go index ff586c6..8c6fea7 100644 --- a/internal/actions/update_params.go +++ b/pkg/types/update_params.go @@ -1,13 +1,12 @@ -package actions +package types import ( - t "github.com/containrrr/watchtower/pkg/types" "time" ) // UpdateParams contains all different options available to alter the behavior of the Update func type UpdateParams struct { - Filter t.Filter + Filter Filter Cleanup bool NoRestart bool Timeout time.Duration