diff --git a/README.md b/README.md index 93bba1d..ecc7d7b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Watchtower ![Watchtower](http://panamax.ca.tier3.io/zodiac/logo-watchtower_thumb.png) +[![Circle CI](https://circleci.com/gh/CenturyLinkLabs/watchtower.svg?style=svg)](https://circleci.com/gh/CenturyLinkLabs/watchtower)  +[![GoDoc](https://godoc.org/github.com/CenturyLinkLabs/watchtower?status.svg)](https://godoc.org/github.com/CenturyLinkLabs/watchtower)  [![](https://badge.imagelayers.io/centurylink/watchtower:latest.svg)](https://imagelayers.io/?images=centurylink/watchtower:latest 'Get your own badge on imagelayers.io') -[![Circle CI](https://circleci.com/gh/CenturyLinkLabs/watchtower.svg?style=svg)](https://circleci.com/gh/CenturyLinkLabs/watchtower) A process for watching your Docker containers and automatically restarting them whenever their base image is refreshed. diff --git a/actions/check.go b/actions/check.go index 0775ed4..e593bcd 100644 --- a/actions/check.go +++ b/actions/check.go @@ -8,6 +8,10 @@ import ( func watchtowerContainersFilter(c container.Container) bool { return c.IsWatchtower() } +// CheckPrereqs will ensure that there are not multiple instances of the +// watchtower running simultaneously. If multiple watchtower containers are +// detected, this function will stop and remove all but the most recently +// started container. func CheckPrereqs(client container.Client, cleanup bool) error { containers, err := client.ListContainers(watchtowerContainersFilter) if err != nil { diff --git a/actions/update.go b/actions/update.go index c79ef70..769c21a 100644 --- a/actions/update.go +++ b/actions/update.go @@ -15,6 +15,10 @@ var ( func allContainersFilter(container.Container) bool { return true } +// Update looks at the running Docker containers to see if any of the images +// 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, cleanup bool) error { log.Info("Checking containers for updated images") diff --git a/container/client.go b/container/client.go index 80b9110..54d9a67 100644 --- a/container/client.go +++ b/container/client.go @@ -13,8 +13,12 @@ const ( defaultStopSignal = "SIGTERM" ) +// 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(Container) bool +// A Client is the interface through which watchtower interacts with the +// Docker API. type Client interface { ListContainers(Filter) ([]Container, error) StopContainer(Container, time.Duration) error @@ -24,6 +28,8 @@ type Client interface { RemoveImage(Container) error } +// NewClient returns a new Client instance which can be used to interact with +// the Docker API. func NewClient(dockerHost string, tlsConfig *tls.Config, pullImages bool) Client { docker, err := dockerclient.NewDockerClient(dockerHost, tlsConfig) diff --git a/container/container.go b/container/container.go index b3d9fc7..024b83d 100644 --- a/container/container.go +++ b/container/container.go @@ -12,6 +12,8 @@ const ( signalLabel = "com.centurylinklabs.watchtower.stop-signal" ) +// NewContainer returns a new Container instance instantiated with the +// specified ContainerInfo and ImageInfo structs. func NewContainer(containerInfo *dockerclient.ContainerInfo, imageInfo *dockerclient.ImageInfo) *Container { return &Container{ containerInfo: containerInfo, @@ -19,6 +21,7 @@ func NewContainer(containerInfo *dockerclient.ContainerInfo, imageInfo *dockercl } } +// Container represents a running Docker container. type Container struct { Stale bool @@ -26,18 +29,25 @@ type Container struct { imageInfo *dockerclient.ImageInfo } +// ID returns the Docker container ID. func (c Container) ID() string { return c.containerInfo.Id } +// Name returns the Docker container name. func (c Container) Name() string { return c.containerInfo.Name } +// ImageID returns the ID of the Docker image that was used to start the +// container. func (c Container) ImageID() string { return c.imageInfo.Id } +// ImageName returns the name of the Docker image that was used to start the +// container. If the original image was specified without a particular tag, the +// "latest" tag is assumed. func (c Container) ImageName() string { imageName := c.containerInfo.Config.Image @@ -48,6 +58,8 @@ func (c Container) ImageName() string { return imageName } +// Links returns a list containing the names of all the containers to which +// this container is linked. func (c Container) Links() []string { var links []string @@ -61,11 +73,18 @@ func (c Container) Links() []string { return links } +// IsWatchtower returns a boolean flag indicating whether or not the current +// container is the watchtower container itself. The watchtower container is +// identified by the presence of the "com.centurylinklabs.watchtower" label in +// the container metadata. func (c Container) IsWatchtower() bool { val, ok := c.containerInfo.Config.Labels[watchtowerLabel] return ok && val == "true" } +// StopSignal returns the custom stop signal (if any) that is encoded in the +// container's metadata. If the container has not specified a custom stop +// signal, the empty string "" is returned. func (c Container) StopSignal() string { if val, ok := c.containerInfo.Config.Labels[signalLabel]; ok { return val diff --git a/container/sort.go b/container/sort.go index 1e74c12..391a8b6 100644 --- a/container/sort.go +++ b/container/sort.go @@ -12,6 +12,8 @@ type ByCreated []Container func (c ByCreated) Len() int { return len(c) } 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) if err != nil { @@ -26,6 +28,11 @@ func (c ByCreated) Less(i, j int) bool { return t1.Before(t2) } +// SortByDependencies will sort the list of containers taking into account any +// links between containers. Container with no outgoing links will be sorted to +// 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) { sorter := dependencySorter{} return sorter.Sort(containers)