You've already forked watchtower
							
							
				mirror of
				https://github.com/containrrr/watchtower.git
				synced 2025-10-31 00:17:44 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package container
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"github.com/containrrr/watchtower/internal/util"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/docker/docker/api/types"
 | |
| 	dockercontainer "github.com/docker/docker/api/types/container"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	watchtowerLabel = "com.centurylinklabs.watchtower"
 | |
| 	signalLabel     = "com.centurylinklabs.watchtower.stop-signal"
 | |
| 	enableLabel     = "com.centurylinklabs.watchtower.enable"
 | |
| 	zodiacLabel     = "com.centurylinklabs.zodiac.original-image"
 | |
| )
 | |
| 
 | |
| // NewContainer returns a new Container instance instantiated with the
 | |
| // specified ContainerInfo and ImageInfo structs.
 | |
| func NewContainer(containerInfo *types.ContainerJSON, imageInfo *types.ImageInspect) *Container {
 | |
| 	return &Container{
 | |
| 		containerInfo: containerInfo,
 | |
| 		imageInfo:     imageInfo,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Container represents a running Docker container.
 | |
| type Container struct {
 | |
| 	Stale bool
 | |
| 
 | |
| 	containerInfo *types.ContainerJSON
 | |
| 	imageInfo     *types.ImageInspect
 | |
| }
 | |
| 
 | |
| // ID returns the Docker container ID.
 | |
| func (c Container) ID() string {
 | |
| 	return c.containerInfo.ID
 | |
| }
 | |
| 
 | |
| // IsRunning returns a boolean flag indicating whether or not the current
 | |
| // container is running. The status is determined by the value of the
 | |
| // container's "State.Running" property.
 | |
| func (c Container) IsRunning() bool {
 | |
| 	return c.containerInfo.State.Running
 | |
| }
 | |
| 
 | |
| // 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 {
 | |
| 	// Compatibility w/ Zodiac deployments
 | |
| 	imageName, ok := c.containerInfo.Config.Labels[zodiacLabel]
 | |
| 	if !ok {
 | |
| 		imageName = c.containerInfo.Config.Image
 | |
| 	}
 | |
| 
 | |
| 	if !strings.Contains(imageName, ":") {
 | |
| 		imageName = fmt.Sprintf("%s:latest", imageName)
 | |
| 	}
 | |
| 
 | |
| 	return imageName
 | |
| }
 | |
| 
 | |
| // Enabled returns the value of the container enabled label and if the label
 | |
| // was set.
 | |
| func (c Container) Enabled() (bool, bool) {
 | |
| 	rawBool, ok := c.containerInfo.Config.Labels[enableLabel]
 | |
| 	if !ok {
 | |
| 		return false, false
 | |
| 	}
 | |
| 
 | |
| 	parsedBool, err := strconv.ParseBool(rawBool)
 | |
| 	if err != nil {
 | |
| 		return false, false
 | |
| 	}
 | |
| 
 | |
| 	return parsedBool, true
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 
 | |
| 	if (c.containerInfo != nil) && (c.containerInfo.HostConfig != nil) {
 | |
| 		for _, link := range c.containerInfo.HostConfig.Links {
 | |
| 			name := strings.Split(link, ":")[0]
 | |
| 			links = append(links, name)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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 {
 | |
| 	return ContainsWatchtowerLabel(c.containerInfo.Config.Labels)
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 	}
 | |
| 
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| // 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
 | |
| // 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 {
 | |
| 	config := c.containerInfo.Config
 | |
| 	imageConfig := c.imageInfo.Config
 | |
| 
 | |
| 	if config.WorkingDir == imageConfig.WorkingDir {
 | |
| 		config.WorkingDir = ""
 | |
| 	}
 | |
| 
 | |
| 	if config.User == imageConfig.User {
 | |
| 		config.User = ""
 | |
| 	}
 | |
| 
 | |
| 	if util.SliceEqual(config.Cmd, imageConfig.Cmd) {
 | |
| 		config.Cmd = nil
 | |
| 	}
 | |
| 
 | |
| 	if util.SliceEqual(config.Entrypoint, imageConfig.Entrypoint) {
 | |
| 		config.Entrypoint = nil
 | |
| 	}
 | |
| 
 | |
| 	config.Env = util.SliceSubtract(config.Env, imageConfig.Env)
 | |
| 
 | |
| 	config.Labels = util.StringMapSubtract(config.Labels, imageConfig.Labels)
 | |
| 
 | |
| 	config.Volumes = util.StructMapSubtract(config.Volumes, imageConfig.Volumes)
 | |
| 
 | |
| 	// subtract ports exposed in image from container
 | |
| 	for k := range config.ExposedPorts {
 | |
| 		if _, ok := imageConfig.ExposedPorts[k]; ok {
 | |
| 			delete(config.ExposedPorts, k)
 | |
| 		}
 | |
| 	}
 | |
| 	for p := range c.containerInfo.HostConfig.PortBindings {
 | |
| 		config.ExposedPorts[p] = struct{}{}
 | |
| 	}
 | |
| 
 | |
| 	config.Image = c.ImageName()
 | |
| 	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 {
 | |
| 	hostConfig := c.containerInfo.HostConfig
 | |
| 
 | |
| 	for i, link := range hostConfig.Links {
 | |
| 		name := link[0:strings.Index(link, ":")]
 | |
| 		alias := link[strings.LastIndex(link, "/"):]
 | |
| 
 | |
| 		hostConfig.Links[i] = fmt.Sprintf("%s:%s", name, alias)
 | |
| 	}
 | |
| 
 | |
| 	return hostConfig
 | |
| }
 | |
| 
 | |
| // ContainsWatchtowerLabel takes a map of labels and values and tells
 | |
| // the consumer whether it contains a valid watchtower instance label
 | |
| func ContainsWatchtowerLabel(labels map[string]string) bool {
 | |
| 	val, ok := labels[watchtowerLabel]
 | |
| 	return ok && val == "true"
 | |
| }
 |