fix: always use container interface (#1516)

pull/1466/head
nils måsén 1 year ago committed by GitHub
parent 25fdb40312
commit dd1ec09668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,12 +1,13 @@
package actions_test
import (
"github.com/sirupsen/logrus"
"testing"
"time"
"github.com/sirupsen/logrus"
"github.com/containrrr/watchtower/internal/actions"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/types"
. "github.com/containrrr/watchtower/internal/actions/mocks"
. "github.com/onsi/ginkgo"
@ -37,7 +38,7 @@ var _ = Describe("the actions package", func() {
It("should not do anything", func() {
client := CreateMockClient(
&TestData{
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container",
"test-container",
@ -59,7 +60,7 @@ var _ = Describe("the actions package", func() {
client = CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
@ -89,7 +90,7 @@ var _ = Describe("the actions package", func() {
BeforeEach(func() {
client = CreateMockClient(
&TestData{
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",

@ -2,16 +2,15 @@ package actions
import (
"fmt"
"github.com/containrrr/watchtower/pkg/types"
"sort"
"time"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/filters"
"github.com/containrrr/watchtower/pkg/sorter"
"github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
"github.com/containrrr/watchtower/pkg/container"
)
// CheckForSanity makes sure everything is sane before starting
@ -55,7 +54,7 @@ func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool,
return cleanupExcessWatchtowers(containers, client, cleanup)
}
func cleanupExcessWatchtowers(containers []container.Container, client container.Client, cleanup bool) error {
func cleanupExcessWatchtowers(containers []types.Container, client container.Client, cleanup bool) error {
var stopErrors int
sort.Sort(sorter.ByCreated(containers))

@ -5,8 +5,6 @@ import (
"fmt"
"time"
"github.com/containrrr/watchtower/pkg/container"
t "github.com/containrrr/watchtower/pkg/types"
)
@ -21,7 +19,7 @@ type MockClient struct {
type TestData struct {
TriedToRemoveImageCount int
NameOfContainerToKeep string
Containers []container.Container
Containers []t.Container
Staleness map[string]bool
}
@ -40,12 +38,12 @@ func CreateMockClient(data *TestData, pullImages bool, removeVolumes bool) MockC
}
// ListContainers is a mock method returning the provided container testdata
func (client MockClient) ListContainers(_ t.Filter) ([]container.Container, error) {
func (client MockClient) ListContainers(_ t.Filter) ([]t.Container, error) {
return client.TestData.Containers, nil
}
// StopContainer is a mock method
func (client MockClient) StopContainer(c container.Container, _ time.Duration) error {
func (client MockClient) StopContainer(c t.Container, _ time.Duration) error {
if c.Name() == client.TestData.NameOfContainerToKeep {
return errors.New("tried to stop the instance we want to keep")
}
@ -53,12 +51,12 @@ func (client MockClient) StopContainer(c container.Container, _ time.Duration) e
}
// StartContainer is a mock method
func (client MockClient) StartContainer(_ container.Container) (t.ContainerID, error) {
func (client MockClient) StartContainer(_ t.Container) (t.ContainerID, error) {
return "", nil
}
// RenameContainer is a mock method
func (client MockClient) RenameContainer(_ container.Container, _ string) error {
func (client MockClient) RenameContainer(_ t.Container, _ string) error {
return nil
}
@ -69,7 +67,7 @@ func (client MockClient) RemoveImageByID(_ t.ImageID) error {
}
// GetContainer is a mock method
func (client MockClient) GetContainer(_ t.ContainerID) (container.Container, error) {
func (client MockClient) GetContainer(_ t.ContainerID) (t.Container, error) {
return client.TestData.Containers[0], nil
}
@ -88,7 +86,7 @@ func (client MockClient) ExecuteCommand(_ t.ContainerID, command string, _ int)
}
// IsContainerStale is true if not explicitly stated in TestData for the mock client
func (client MockClient) IsContainerStale(cont container.Container) (bool, t.ImageID, error) {
func (client MockClient) IsContainerStale(cont t.Container) (bool, t.ImageID, error) {
stale, found := client.TestData.Staleness[cont.Name()]
if !found {
stale = true
@ -97,6 +95,6 @@ func (client MockClient) IsContainerStale(cont container.Container) (bool, t.Ima
}
// WarnOnHeadPullFailed is always true for the mock client
func (client MockClient) WarnOnHeadPullFailed(_ container.Container) bool {
func (client MockClient) WarnOnHeadPullFailed(_ t.Container) bool {
return true
}

@ -14,7 +14,7 @@ import (
)
// CreateMockContainer creates a container substitute valid for testing
func CreateMockContainer(id string, name string, image string, created time.Time) container.Container {
func CreateMockContainer(id string, name string, image string, created time.Time) wt.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
@ -31,7 +31,7 @@ func CreateMockContainer(id string, name string, image string, created time.Time
ExposedPorts: map[nat.Port]struct{}{},
},
}
return *container.NewContainer(
return container.NewContainer(
&content,
CreateMockImageInfo(image),
)
@ -48,12 +48,12 @@ func CreateMockImageInfo(image string) *types.ImageInspect {
}
// CreateMockContainerWithImageInfo should only be used for testing
func CreateMockContainerWithImageInfo(id string, name string, image string, created time.Time, imageInfo types.ImageInspect) container.Container {
func CreateMockContainerWithImageInfo(id string, name string, image string, created time.Time, imageInfo types.ImageInspect) wt.Container {
return CreateMockContainerWithImageInfoP(id, name, image, created, &imageInfo)
}
// CreateMockContainerWithImageInfoP should only be used for testing
func CreateMockContainerWithImageInfoP(id string, name string, image string, created time.Time, imageInfo *types.ImageInspect) container.Container {
func CreateMockContainerWithImageInfoP(id string, name string, image string, created time.Time, imageInfo *types.ImageInspect) wt.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
@ -66,21 +66,21 @@ func CreateMockContainerWithImageInfoP(id string, name string, image string, cre
Labels: make(map[string]string),
},
}
return *container.NewContainer(
return container.NewContainer(
&content,
imageInfo,
)
}
// CreateMockContainerWithDigest should only be used for testing
func CreateMockContainerWithDigest(id string, name string, image string, created time.Time, digest string) container.Container {
func CreateMockContainerWithDigest(id string, name string, image string, created time.Time, digest string) wt.Container {
c := CreateMockContainer(id, name, image, created)
c.ImageInfo().RepoDigests = []string{digest}
return c
}
// CreateMockContainerWithConfig creates a container substitute valid for testing
func CreateMockContainerWithConfig(id string, name string, image string, running bool, restarting bool, created time.Time, config *dockerContainer.Config) container.Container {
func CreateMockContainerWithConfig(id string, name string, image string, running bool, restarting bool, created time.Time, config *dockerContainer.Config) wt.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
@ -97,14 +97,14 @@ func CreateMockContainerWithConfig(id string, name string, image string, running
},
Config: config,
}
return *container.NewContainer(
return container.NewContainer(
&content,
CreateMockImageInfo(image),
)
}
// CreateContainerForProgress creates a container substitute for tracking session/update progress
func CreateContainerForProgress(index int, idPrefix int, nameFormat string) (container.Container, wt.ImageID) {
func CreateContainerForProgress(index int, idPrefix int, nameFormat string) (wt.Container, wt.ImageID) {
indexStr := strconv.Itoa(idPrefix + index)
mockID := indexStr + strings.Repeat("0", 61-len(indexStr))
contID := "c79" + mockID
@ -120,7 +120,7 @@ func CreateContainerForProgress(index int, idPrefix int, nameFormat string) (con
}
// CreateMockContainerWithLinks should only be used for testing
func CreateMockContainerWithLinks(id string, name string, image string, created time.Time, links []string, imageInfo *types.ImageInspect) container.Container {
func CreateMockContainerWithLinks(id string, name string, image string, created time.Time, links []string, imageInfo *types.ImageInspect) wt.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
@ -136,7 +136,7 @@ func CreateMockContainerWithLinks(id string, name string, image string, created
Labels: make(map[string]string),
},
}
return *container.NewContainer(
return container.NewContainer(
&content,
imageInfo,
)

@ -57,7 +57,7 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
} else {
progress.AddScanned(targetContainer, newestImage)
}
containers[i].Stale = stale
containers[i].SetStale(stale)
if stale {
staleCount++
@ -71,7 +71,7 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
UpdateImplicitRestart(containers)
var containersToUpdate []container.Container
var containersToUpdate []types.Container
if !params.MonitorOnly {
for _, c := range containers {
if !c.IsMonitorOnly() {
@ -96,7 +96,7 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
return progress.Report(), nil
}
func performRollingRestart(containers []container.Container, client container.Client, params types.UpdateParams) map[types.ContainerID]error {
func performRollingRestart(containers []types.Container, client container.Client, params types.UpdateParams) map[types.ContainerID]error {
cleanupImageIDs := make(map[types.ImageID]bool, len(containers))
failed := make(map[types.ContainerID]error, len(containers))
@ -108,7 +108,7 @@ func performRollingRestart(containers []container.Container, client container.Cl
} else {
if err := restartStaleContainer(containers[i], client, params); err != nil {
failed[containers[i].ID()] = err
} else if containers[i].Stale {
} else if containers[i].IsStale() {
// Only add (previously) stale containers' images to cleanup
cleanupImageIDs[containers[i].ImageID()] = true
}
@ -122,7 +122,7 @@ func performRollingRestart(containers []container.Container, client container.Cl
return failed
}
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) (failed map[types.ContainerID]error, stopped map[types.ImageID]bool) {
func stopContainersInReversedOrder(containers []types.Container, client container.Client, params types.UpdateParams) (failed map[types.ContainerID]error, stopped map[types.ImageID]bool) {
failed = make(map[types.ContainerID]error, len(containers))
stopped = make(map[types.ImageID]bool, len(containers))
for i := len(containers) - 1; i >= 0; i-- {
@ -137,7 +137,7 @@ func stopContainersInReversedOrder(containers []container.Container, client cont
return
}
func stopStaleContainer(container container.Container, client container.Client, params types.UpdateParams) error {
func stopStaleContainer(container types.Container, client container.Client, params types.UpdateParams) error {
if container.IsWatchtower() {
log.Debugf("This is the watchtower container %s", container.Name())
return nil
@ -148,7 +148,7 @@ func stopStaleContainer(container container.Container, client container.Client,
}
// Perform an additional check here to prevent us from stopping a linked container we cannot restart
if container.LinkedToRestarting {
if container.IsLinkedToRestarting() {
if err := container.VerifyConfiguration(); err != nil {
return err
}
@ -174,7 +174,7 @@ func stopStaleContainer(container container.Container, client container.Client,
return nil
}
func restartContainersInSortedOrder(containers []container.Container, client container.Client, params types.UpdateParams, stoppedImages map[types.ImageID]bool) map[types.ContainerID]error {
func restartContainersInSortedOrder(containers []types.Container, client container.Client, params types.UpdateParams, stoppedImages map[types.ImageID]bool) map[types.ContainerID]error {
cleanupImageIDs := make(map[types.ImageID]bool, len(containers))
failed := make(map[types.ContainerID]error, len(containers))
@ -185,7 +185,7 @@ func restartContainersInSortedOrder(containers []container.Container, client con
if stoppedImages[c.SafeImageID()] {
if err := restartStaleContainer(c, client, params); err != nil {
failed[c.ID()] = err
} else if c.Stale {
} else if c.IsStale() {
// Only add (previously) stale containers' images to cleanup
cleanupImageIDs[c.ImageID()] = true
}
@ -210,7 +210,7 @@ func cleanupImages(client container.Client, imageIDs map[types.ImageID]bool) {
}
}
func restartStaleContainer(container container.Container, client container.Client, params types.UpdateParams) error {
func restartStaleContainer(container types.Container, client container.Client, params types.UpdateParams) error {
// 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
@ -235,7 +235,7 @@ func restartStaleContainer(container container.Container, client container.Clien
// UpdateImplicitRestart iterates through the passed containers, setting the
// `LinkedToRestarting` flag if any of it's linked containers are marked for restart
func UpdateImplicitRestart(containers []container.Container) {
func UpdateImplicitRestart(containers []types.Container) {
for ci, c := range containers {
if c.ToRestart() {
@ -249,7 +249,7 @@ func UpdateImplicitRestart(containers []container.Container) {
"linked": c.Name(),
}).Debug("container is linked to restarting")
// NOTE: To mutate the array, the `c` variable cannot be used as it's a copy
containers[ci].LinkedToRestarting = true
containers[ci].SetLinkedToRestarting(true)
}
}
@ -257,7 +257,7 @@ func UpdateImplicitRestart(containers []container.Container) {
// linkedContainerMarkedForRestart returns the name of the first link that matches a
// container marked for restart
func linkedContainerMarkedForRestart(links []string, containers []container.Container) string {
func linkedContainerMarkedForRestart(links []string, containers []types.Container) string {
for _, linkName := range links {
for _, candidate := range containers {
if candidate.Name() == linkName && candidate.ToRestart() {

@ -4,7 +4,6 @@ import (
"time"
"github.com/containrrr/watchtower/internal/actions"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/types"
dockerTypes "github.com/docker/docker/api/types"
dockerContainer "github.com/docker/docker/api/types/container"
@ -18,7 +17,7 @@ import (
func getCommonTestData(keepContainer string) *TestData {
return &TestData{
NameOfContainerToKeep: keepContainer,
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
@ -59,7 +58,7 @@ func getLinkedTestData(withImageInfo bool) *TestData {
return &TestData{
Staleness: map[string]bool{linkingContainer.Name(): false},
Containers: []container.Container{
Containers: []types.Container{
staleContainer,
linkingContainer,
},
@ -130,7 +129,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
@ -163,7 +162,7 @@ var _ = Describe("the update action", func() {
It("should not update any containers", func() {
client := CreateMockClient(
&TestData{
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
@ -194,7 +193,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
//NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
@ -227,7 +226,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
//NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
@ -259,7 +258,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
//NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
@ -300,7 +299,7 @@ var _ = Describe("the update action", func() {
ExposedPorts: map[nat.Port]struct{}{},
})
provider.Stale = true
provider.SetStale(true)
consumer := CreateMockContainerWithConfig(
"test-container-consumer",
@ -316,7 +315,7 @@ var _ = Describe("the update action", func() {
ExposedPorts: map[nat.Port]struct{}{},
})
containers := []container.Container{
containers := []types.Container{
provider,
consumer,
}
@ -338,7 +337,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
//NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",
@ -370,7 +369,7 @@ var _ = Describe("the update action", func() {
client := CreateMockClient(
&TestData{
//NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
Containers: []types.Container{
CreateMockContainerWithConfig(
"test-container-02",
"test-container-02",

@ -25,15 +25,15 @@ const defaultStopSignal = "SIGTERM"
// A Client is the interface through which watchtower interacts with the
// Docker API.
type Client interface {
ListContainers(t.Filter) ([]Container, error)
GetContainer(containerID t.ContainerID) (Container, error)
StopContainer(Container, time.Duration) error
StartContainer(Container) (t.ContainerID, error)
RenameContainer(Container, string) error
IsContainerStale(Container) (stale bool, latestImage t.ImageID, err error)
ListContainers(t.Filter) ([]t.Container, error)
GetContainer(containerID t.ContainerID) (t.Container, error)
StopContainer(t.Container, time.Duration) error
StartContainer(t.Container) (t.ContainerID, error)
RenameContainer(t.Container, string) error
IsContainerStale(t.Container) (stale bool, latestImage t.ImageID, err error)
ExecuteCommand(containerID t.ContainerID, command string, timeout int) (SkipUpdate bool, err error)
RemoveImageByID(t.ImageID) error
WarnOnHeadPullFailed(container Container) bool
WarnOnHeadPullFailed(container t.Container) bool
}
// NewClient returns a new Client instance which can be used to interact with
@ -82,7 +82,7 @@ type dockerClient struct {
ClientOptions
}
func (client dockerClient) WarnOnHeadPullFailed(container Container) bool {
func (client dockerClient) WarnOnHeadPullFailed(container t.Container) bool {
if client.WarnOnHeadFailed == WarnAlways {
return true
}
@ -93,8 +93,8 @@ func (client dockerClient) WarnOnHeadPullFailed(container Container) bool {
return registry.WarnOnAPIConsumption(container)
}
func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) {
cs := []Container{}
func (client dockerClient) ListContainers(fn t.Filter) ([]t.Container, error) {
cs := []t.Container{}
bg := context.Background()
if client.IncludeStopped && client.IncludeRestarting {
@ -149,24 +149,24 @@ func (client dockerClient) createListFilter() filters.Args {
return filterArgs
}
func (client dockerClient) GetContainer(containerID t.ContainerID) (Container, error) {
func (client dockerClient) GetContainer(containerID t.ContainerID) (t.Container, error) {
bg := context.Background()
containerInfo, err := client.api.ContainerInspect(bg, string(containerID))
if err != nil {
return Container{}, err
return &Container{}, err
}
imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image)
if err != nil {
log.Warnf("Failed to retrieve container image info: %v", err)
return Container{containerInfo: &containerInfo, imageInfo: nil}, nil
return &Container{containerInfo: &containerInfo, imageInfo: nil}, nil
}
return Container{containerInfo: &containerInfo, imageInfo: &imageInfo}, nil
return &Container{containerInfo: &containerInfo, imageInfo: &imageInfo}, nil
}
func (client dockerClient) StopContainer(c Container, timeout time.Duration) error {
func (client dockerClient) StopContainer(c t.Container, timeout time.Duration) error {
bg := context.Background()
signal := c.StopSignal()
if signal == "" {
@ -186,7 +186,7 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
// TODO: This should probably be checked.
_ = client.waitForStopOrTimeout(c, timeout)
if c.containerInfo.HostConfig.AutoRemove {
if c.ContainerInfo().HostConfig.AutoRemove {
log.Debugf("AutoRemove container %s, skipping ContainerRemove call.", shortID)
} else {
log.Debugf("Removing container %s", shortID)
@ -208,11 +208,11 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
return nil
}
func (client dockerClient) StartContainer(c Container) (t.ContainerID, error) {
func (client dockerClient) StartContainer(c t.Container) (t.ContainerID, error) {
bg := context.Background()
config := c.runtimeConfig()
hostConfig := c.hostConfig()
networkConfig := &network.NetworkingConfig{EndpointsConfig: c.containerInfo.NetworkSettings.Networks}
config := c.GetCreateConfig()
hostConfig := c.GetCreateHostConfig()
networkConfig := &network.NetworkingConfig{EndpointsConfig: c.ContainerInfo().NetworkSettings.Networks}
// simpleNetworkConfig is a networkConfig with only 1 network.
// see: https://github.com/docker/docker/issues/29265
simpleNetworkConfig := func() *network.NetworkingConfig {
@ -260,7 +260,7 @@ func (client dockerClient) StartContainer(c Container) (t.ContainerID, error) {
}
func (client dockerClient) doStartContainer(bg context.Context, c Container, creation container.CreateResponse) error {
func (client dockerClient) doStartContainer(bg context.Context, c t.Container, creation container.CreateResponse) error {
name := c.Name()
log.Debugf("Starting container %s (%s)", name, t.ContainerID(creation.ID).ShortID())
@ -271,13 +271,13 @@ func (client dockerClient) doStartContainer(bg context.Context, c Container, cre
return nil
}
func (client dockerClient) RenameContainer(c Container, newName string) error {
func (client dockerClient) RenameContainer(c t.Container, newName string) error {
bg := context.Background()
log.Debugf("Renaming container %s (%s) to %s", c.Name(), c.ID().ShortID(), newName)
return client.api.ContainerRename(bg, string(c.ID()), newName)
}
func (client dockerClient) IsContainerStale(container Container) (stale bool, latestImage t.ImageID, err error) {
func (client dockerClient) IsContainerStale(container t.Container) (stale bool, latestImage t.ImageID, err error) {
ctx := context.Background()
if !client.PullImages || container.IsNoPull() {
@ -289,8 +289,8 @@ func (client dockerClient) IsContainerStale(container Container) (stale bool, la
return client.HasNewImage(ctx, container)
}
func (client dockerClient) HasNewImage(ctx context.Context, container Container) (hasNew bool, latestImage t.ImageID, err error) {
currentImageID := t.ImageID(container.containerInfo.ContainerJSONBase.Image)
func (client dockerClient) HasNewImage(ctx context.Context, container t.Container) (hasNew bool, latestImage t.ImageID, err error) {
currentImageID := t.ImageID(container.ContainerInfo().ContainerJSONBase.Image)
imageName := container.ImageName()
newImageInfo, _, err := client.api.ImageInspectWithRaw(ctx, imageName)
@ -310,7 +310,7 @@ func (client dockerClient) HasNewImage(ctx context.Context, container Container)
// PullImage pulls the latest image for the supplied container, optionally skipping if it's digest can be confirmed
// to match the one that the registry reports via a HEAD request
func (client dockerClient) PullImage(ctx context.Context, container Container) error {
func (client dockerClient) PullImage(ctx context.Context, container t.Container) error {
containerName := container.Name()
imageName := container.ImageName()
@ -478,7 +478,7 @@ func (client dockerClient) waitForExecOrTimeout(bg context.Context, ID string, e
return false, nil
}
func (client dockerClient) waitForStopOrTimeout(c Container, waitTime time.Duration) error {
func (client dockerClient) waitForStopOrTimeout(c t.Container, waitTime time.Duration) error {
bg := context.Background()
timeout := time.After(waitTime)

@ -35,8 +35,8 @@ var _ = Describe("the client", func() {
mockServer.Close()
})
Describe("WarnOnHeadPullFailed", func() {
containerUnknown := *MockContainer(WithImageName("unknown.repo/prefix/imagename:latest"))
containerKnown := *MockContainer(WithImageName("docker.io/prefix/imagename:latest"))
containerUnknown := MockContainer(WithImageName("unknown.repo/prefix/imagename:latest"))
containerKnown := MockContainer(WithImageName("docker.io/prefix/imagename:latest"))
When(`warn on head failure is set to "always"`, func() {
c := dockerClient{ClientOptions: ClientOptions{WarnOnHeadFailed: WarnAlways}}
@ -66,7 +66,7 @@ var _ = Describe("the client", func() {
When("the image consist of a pinned hash", func() {
It("should gracefully fail with a useful message", func() {
c := dockerClient{}
pinnedContainer := *MockContainer(WithImageName("sha256:fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230b"))
pinnedContainer := MockContainer(WithImageName("sha256:fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230b"))
c.PullImage(context.Background(), pinnedContainer)
})
})
@ -74,8 +74,8 @@ var _ = Describe("the client", func() {
When("removing a running container", func() {
When("the container still exist after stopping", func() {
It("should attempt to remove the container", func() {
container := *MockContainer(WithContainerState(types.ContainerState{Running: true}))
containerStopped := *MockContainer(WithContainerState(types.ContainerState{Running: false}))
container := MockContainer(WithContainerState(types.ContainerState{Running: true}))
containerStopped := MockContainer(WithContainerState(types.ContainerState{Running: false}))
cid := container.ContainerInfo().ID
mockServer.AppendHandlers(
@ -90,7 +90,7 @@ var _ = Describe("the client", func() {
})
When("the container does not exist after stopping", func() {
It("should not cause an error", func() {
container := *MockContainer(WithContainerState(types.ContainerState{Running: true}))
container := MockContainer(WithContainerState(types.ContainerState{Running: true}))
cid := container.ContainerInfo().ID
mockServer.AppendHandlers(
@ -261,18 +261,18 @@ func withContainerImageName(matcher gt.GomegaMatcher) gt.GomegaMatcher {
return WithTransform(containerImageName, matcher)
}
func containerImageName(container Container) string {
func containerImageName(container t.Container) string {
return container.ImageName()
}
func havingRestartingState(expected bool) gt.GomegaMatcher {
return WithTransform(func(container Container) bool {
return container.containerInfo.State.Restarting
return WithTransform(func(container t.Container) bool {
return container.ContainerInfo().State.Restarting
}, Equal(expected))
}
func havingRunningState(expected bool) gt.GomegaMatcher {
return WithTransform(func(container Container) bool {
return container.containerInfo.State.Running
return WithTransform(func(container t.Container) bool {
return container.ContainerInfo().State.Running
}, Equal(expected))
}

@ -32,6 +32,26 @@ type Container struct {
imageInfo *types.ImageInspect
}
// IsLinkedToRestarting returns the current value of the LinkedToRestarting field for the container
func (c *Container) IsLinkedToRestarting() bool {
return c.LinkedToRestarting
}
// IsStale returns the current value of the Stale field for the container
func (c *Container) IsStale() bool {
return c.Stale
}
// SetLinkedToRestarting sets the LinkedToRestarting field for the container
func (c *Container) SetLinkedToRestarting(value bool) {
c.LinkedToRestarting = value
}
// SetStale implements sets the Stale field for the container
func (c *Container) SetStale(value bool) {
c.Stale = value
}
// ContainerInfo fetches JSON info for the container
func (c Container) ContainerInfo() *types.ContainerJSON {
return c.containerInfo
@ -240,18 +260,23 @@ func (c Container) StopSignal() string {
return c.getLabelValueOrEmpty(signalLabel)
}
// GetCreateConfig returns the container's current Config converted into a format
// that can be re-submitted to the Docker create API.
//
// Ideally, we'd just be able to take the ContainerConfig from the old container
// and use it as the starting point for creating the new container; however,
// the ContainerConfig that comes back from the Inspect call merges the default
// configuration (the stuff specified in the metadata for the image itself)
// with the overridden configuration (the stuff that you might specify as part
// of the "docker run"). In order to avoid unintentionally overriding the
// of the "docker run").
//
// In order to avoid unintentionally overriding the
// defaults in the new image we need to separate the override options from the
// default options. To do this we have to compare the ContainerConfig for the
// running container with the ContainerConfig from the image that container was
// started from. This function returns a ContainerConfig which contains just
// the options overridden at runtime.
func (c Container) runtimeConfig() *dockercontainer.Config {
func (c Container) GetCreateConfig() *dockercontainer.Config {
config := c.containerInfo.Config
hostConfig := c.containerInfo.HostConfig
imageConfig := c.imageInfo.Config
@ -295,9 +320,9 @@ func (c Container) runtimeConfig() *dockercontainer.Config {
return config
}
// Any links in the HostConfig need to be re-written before they can be
// re-submitted to the Docker create API.
func (c Container) hostConfig() *dockercontainer.HostConfig {
// GetCreateHostConfig returns the container's current HostConfig with any links
// re-written so that they can be re-submitted to the Docker create API.
func (c Container) GetCreateHostConfig() *dockercontainer.HostConfig {
hostConfig := c.containerInfo.HostConfig
for i, link := range hostConfig.Links {

@ -29,7 +29,7 @@ func ExecutePostChecks(client container.Client, params types.UpdateParams) {
}
// ExecutePreCheckCommand tries to run the pre-check lifecycle hook for a single container.
func ExecutePreCheckCommand(client container.Client, container container.Container) {
func ExecutePreCheckCommand(client container.Client, container types.Container) {
clog := log.WithField("container", container.Name())
command := container.GetLifecyclePreCheckCommand()
if len(command) == 0 {
@ -45,7 +45,7 @@ func ExecutePreCheckCommand(client container.Client, container container.Contain
}
// ExecutePostCheckCommand tries to run the post-check lifecycle hook for a single container.
func ExecutePostCheckCommand(client container.Client, container container.Container) {
func ExecutePostCheckCommand(client container.Client, container types.Container) {
clog := log.WithField("container", container.Name())
command := container.GetLifecyclePostCheckCommand()
if len(command) == 0 {
@ -61,7 +61,7 @@ func ExecutePostCheckCommand(client container.Client, container container.Contai
}
// ExecutePreUpdateCommand tries to run the pre-update lifecycle hook for a single container.
func ExecutePreUpdateCommand(client container.Client, container container.Container) (SkipUpdate bool, err error) {
func ExecutePreUpdateCommand(client container.Client, container types.Container) (SkipUpdate bool, err error) {
timeout := container.PreUpdateTimeout()
command := container.GetLifecyclePreUpdateCommand()
clog := log.WithField("container", container.Name())

@ -2,13 +2,14 @@ package sorter
import (
"fmt"
"github.com/containrrr/watchtower/pkg/container"
"time"
"github.com/containrrr/watchtower/pkg/types"
)
// ByCreated allows a list of Container structs to be sorted by the container's
// created date.
type ByCreated []container.Container
type ByCreated []types.Container
func (c ByCreated) Len() int { return len(c) }
func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
@ -34,18 +35,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) ([]container.Container, error) {
func SortByDependencies(containers []types.Container) ([]types.Container, error) {
sorter := dependencySorter{}
return sorter.Sort(containers)
}
type dependencySorter struct {
unvisited []container.Container
unvisited []types.Container
marked map[string]bool
sorted []container.Container
sorted []types.Container
}
func (ds *dependencySorter) Sort(containers []container.Container) ([]container.Container, error) {
func (ds *dependencySorter) Sort(containers []types.Container) ([]types.Container, error) {
ds.unvisited = containers
ds.marked = map[string]bool{}
@ -58,7 +59,7 @@ func (ds *dependencySorter) Sort(containers []container.Container) ([]container.
return ds.sorted, nil
}
func (ds *dependencySorter) visit(c container.Container) error {
func (ds *dependencySorter) visit(c types.Container) error {
if _, ok := ds.marked[c.Name()]; ok {
return fmt.Errorf("circular reference to %s", c.Name())
@ -84,7 +85,7 @@ func (ds *dependencySorter) visit(c container.Container) error {
return nil
}
func (ds *dependencySorter) findUnvisited(name string) *container.Container {
func (ds *dependencySorter) findUnvisited(name string) *types.Container {
for _, c := range ds.unvisited {
if c.Name() == name {
return &c
@ -94,7 +95,7 @@ func (ds *dependencySorter) findUnvisited(name string) *container.Container {
return nil
}
func (ds *dependencySorter) removeUnvisited(c container.Container) {
func (ds *dependencySorter) removeUnvisited(c types.Container) {
var idx int
for i := range ds.unvisited {
if ds.unvisited[i].Name() == c.Name() {

@ -1,8 +1,10 @@
package types
import (
"github.com/docker/docker/api/types"
"strings"
"github.com/docker/docker/api/types"
dc "github.com/docker/docker/api/types/container"
)
// ImageID is a hash string representing a container image
@ -62,4 +64,15 @@ type Container interface {
GetLifecyclePostCheckCommand() string
GetLifecyclePreUpdateCommand() string
GetLifecyclePostUpdateCommand() string
VerifyConfiguration() error
SetStale(bool)
IsStale() bool
IsNoPull() bool
SetLinkedToRestarting(bool)
IsLinkedToRestarting() bool
PreUpdateTimeout() int
PostUpdateTimeout() int
IsRestarting() bool
GetCreateConfig() *dc.Config
GetCreateHostConfig() *dc.HostConfig
}

Loading…
Cancel
Save