mirror of https://github.com/containrrr/watchtower
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			131 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
package actions
 | 
						|
 | 
						|
import (
 | 
						|
	"math/rand"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/containrrr/watchtower/pkg/container"
 | 
						|
	t "github.com/containrrr/watchtower/pkg/types"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 | 
						|
)
 | 
						|
 | 
						|
// UpdateParams contains all different options available to alter the behavior of the Update func
 | 
						|
type UpdateParams struct {
 | 
						|
	Filter      t.Filter
 | 
						|
	Cleanup     bool
 | 
						|
	NoRestart   bool
 | 
						|
	Timeout     time.Duration
 | 
						|
	MonitorOnly bool
 | 
						|
}
 | 
						|
 | 
						|
// 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, params UpdateParams) error {
 | 
						|
	log.Debug("Checking containers for updated images")
 | 
						|
 | 
						|
	containers, err := client.ListContainers(params.Filter)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	for i, container := range containers {
 | 
						|
		stale, err := client.IsContainerStale(container)
 | 
						|
		if err != nil {
 | 
						|
			log.Infof("Unable to update container %s. Proceeding to next.", containers[i].Name())
 | 
						|
			log.Debug(err)
 | 
						|
			stale = false
 | 
						|
		}
 | 
						|
		containers[i].Stale = stale
 | 
						|
	}
 | 
						|
 | 
						|
	containers, err = container.SortByDependencies(containers)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	checkDependencies(containers)
 | 
						|
 | 
						|
	if params.MonitorOnly {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Stop stale containers in reverse order
 | 
						|
	for i := len(containers) - 1; i >= 0; i-- {
 | 
						|
		container := containers[i]
 | 
						|
 | 
						|
		if container.IsWatchtower() {
 | 
						|
			log.Debugf("This is the watchtower container %s", containers[i].Name())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if container.Stale {
 | 
						|
			if err := client.StopContainer(container, params.Timeout); err != nil {
 | 
						|
				log.Error(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Restart stale containers in sorted order
 | 
						|
	for _, container := range containers {
 | 
						|
		if container.Stale {
 | 
						|
			// 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
 | 
						|
			// instance so that the new one can adopt the old name.
 | 
						|
			if container.IsWatchtower() {
 | 
						|
				if err := client.RenameContainer(container, randName()); err != nil {
 | 
						|
					log.Error(err)
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if !params.NoRestart {
 | 
						|
				if err := client.StartContainer(container); err != nil {
 | 
						|
					log.Error(err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if params.Cleanup {
 | 
						|
				client.RemoveImage(container)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func checkDependencies(containers []container.Container) {
 | 
						|
 | 
						|
	for i, parent := range containers {
 | 
						|
		if parent.Stale {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
	LinkLoop:
 | 
						|
		for _, linkName := range parent.Links() {
 | 
						|
			for _, child := range containers {
 | 
						|
				if child.Name() == linkName && child.Stale {
 | 
						|
					containers[i].Stale = true
 | 
						|
					break LinkLoop
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Generates a random, 32-character, Docker-compatible container name.
 | 
						|
func randName() string {
 | 
						|
	b := make([]rune, 32)
 | 
						|
	for i := range b {
 | 
						|
		b[i] = letters[rand.Intn(len(letters))]
 | 
						|
	}
 | 
						|
 | 
						|
	return string(b)
 | 
						|
}
 |