summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.sum1
-rw-r--r--pkg/autoupdate/autoupdate.go143
2 files changed, 106 insertions, 38 deletions
diff --git a/go.sum b/go.sum
index 0de830099..f3940d4f6 100644
--- a/go.sum
+++ b/go.sum
@@ -209,6 +209,7 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
github.com/containers/ocicrypt v1.1.1 h1:prL8l9w3ntVqXvNH1CiNn5ENjcCnr38JqpSyvKKB4GI=
github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
+github.com/containers/podman v1.9.3 h1:GLQceCWhFkeTTYSU2qrpS3rsNYrrsilkUMF9LwNp4lg=
github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw=
github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go
index e271b9466..ad790079e 100644
--- a/pkg/autoupdate/autoupdate.go
+++ b/pkg/autoupdate/autoupdate.go
@@ -33,15 +33,25 @@ type Policy string
const (
// PolicyDefault is the default policy denoting no auto updates.
PolicyDefault Policy = "disabled"
- // PolicyNewImage is the policy to update as soon as there's a new image found.
- PolicyNewImage = "image"
+ // PolicyRegistryImage is the policy to update as soon as there's a new image found.
+ PolicyRegistryImage = "image"
+ // PolicyLocalImage is the policy to run auto-update based on a local image
+ PolicyLocalImage = "local"
)
// Map for easy lookups of supported policies.
var supportedPolicies = map[string]Policy{
"": PolicyDefault,
"disabled": PolicyDefault,
- "image": PolicyNewImage,
+ "image": PolicyRegistryImage,
+ "registry": PolicyRegistryImage,
+ "local": PolicyLocalImage,
+}
+
+// Struct for tying a container to it's autoupdate policy
+type PolicyContainer struct {
+ p Policy
+ ctr *libpod.Container
}
// LookupPolicy looks up the corresponding Policy for the specified
@@ -99,11 +109,17 @@ func ValidateImageReference(imageName string) error {
}
// AutoUpdate looks up containers with a specified auto-update policy and acts
-// accordingly. If the policy is set to PolicyNewImage, it checks if the image
+// accordingly.
+//
+// If the policy is set to PolicyRegistryImage, it checks if the image
// on the remote registry is different than the local one. If the image digests
// differ, it pulls the remote image and restarts the systemd unit running the
// container.
//
+// If the policy is set to PolicyLocalImage, it checks if the image
+// of a running container is different than the local one. If the image digests
+// differ, it restarts the systemd unit with the new image.
+//
// It returns a slice of successfully restarted systemd units and a slice of
// errors encountered during auto update.
func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
@@ -134,7 +150,7 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
// Update images.
containersToRestart := []*libpod.Container{}
updatedRawImages := make(map[string]bool)
- for imageID, containers := range containerMap {
+ for imageID, policyContainers := range containerMap {
image, exists := imageMap[imageID]
if !exists {
errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID))
@@ -143,34 +159,53 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
// Now we have to check if the image of any containers must be updated.
// Note that the image ID is NOT enough for this check as a given image
// may have multiple tags.
- for i, ctr := range containers {
- rawImageName := ctr.RawImageName()
+ for i, pc := range policyContainers {
+ cid := pc.ctr.ID()
+ rawImageName := pc.ctr.RawImageName()
if rawImageName == "" {
- errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", ctr.ID()))
- }
- labels := ctr.Labels()
- authFilePath, exists := labels[AuthfileLabel]
- if exists {
- options.Authfile = authFilePath
+ errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", pc.ctr.ID()))
}
- needsUpdate, err := newerImageAvailable(runtime, image, rawImageName, options)
- if err != nil {
- errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", ctr.ID(), rawImageName))
- continue
- }
- if !needsUpdate {
- continue
- }
- logrus.Infof("Auto-updating container %q using image %q", ctr.ID(), rawImageName)
- if _, updated := updatedRawImages[rawImageName]; !updated {
- _, err = updateImage(runtime, rawImageName, options)
+
+ switch pc.p {
+ // Sanity Check, should be unreachable code
+ case PolicyDefault:
+ errs = append(errs, errors.Errorf("error auto-updating container %q: invalid policy", cid))
+
+ // Registry Autoupdate Containers pull new images and are flagged for restart.
+ case PolicyRegistryImage:
+ readAuthenticationPath(pc.ctr, options)
+ needsUpdate, err := newerRemoteImageAvailable(runtime, image, rawImageName, options)
if err != nil {
- errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image update for %q failed", ctr.ID(), rawImageName))
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", cid, rawImageName))
continue
}
- updatedRawImages[rawImageName] = true
+
+ if needsUpdate {
+ logrus.Infof("Auto-updating container %q using registry image %q", cid, rawImageName)
+ if _, updated := updatedRawImages[rawImageName]; !updated {
+ _, err = updateImage(runtime, rawImageName, options)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image update for %q failed", cid, rawImageName))
+ continue
+ }
+ updatedRawImages[rawImageName] = true
+ }
+ containersToRestart = append(containersToRestart, policyContainers[i].ctr)
+ }
+ // Local Autoupdate Containers with an update are flagged for restart.
+ case PolicyLocalImage:
+ // This avoids restarting containers unnecessarily.
+ needsUpdate, err := newerLocalImageAvailable(image, rawImageName)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: image check for %q failed", cid, rawImageName))
+ continue
+ }
+
+ if needsUpdate {
+ logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName)
+ containersToRestart = append(containersToRestart, policyContainers[i].ctr)
+ }
}
- containersToRestart = append(containersToRestart, containers[i])
}
}
@@ -198,14 +233,14 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
// imageContainersMap generates a map[image ID] -> [containers using the image]
// of all containers with a valid auto-update policy.
-func imageContainersMap(runtime *libpod.Runtime) (map[string][]*libpod.Container, []error) {
+func imageContainersMap(runtime *libpod.Runtime) (map[string][]PolicyContainer, []error) {
allContainers, err := runtime.GetAllContainers()
if err != nil {
return nil, []error{err}
}
errors := []error{}
- imageMap := make(map[string][]*libpod.Container)
+ containerMap := make(map[string][]PolicyContainer)
for i, ctr := range allContainers {
state, err := ctr.State()
if err != nil {
@@ -230,22 +265,35 @@ func imageContainersMap(runtime *libpod.Runtime) (map[string][]*libpod.Container
continue
}
- // Skip non-image labels (could be explicitly disabled).
- if policy != PolicyNewImage {
+ // Skip labels not related to autoupdate
+ if policy != PolicyDefault {
+ id, _ := ctr.Image()
+ pc := PolicyContainer{
+ p: policy,
+ ctr: allContainers[i],
+ }
+ containerMap[id] = append(containerMap[id], pc)
+ // Now we know that `ctr` is configured for auto updates.
+ } else {
continue
}
-
- // Now we know that `ctr` is configured for auto updates.
- id, _ := ctr.Image()
- imageMap[id] = append(imageMap[id], allContainers[i])
}
- return imageMap, errors
+ return containerMap, errors
+}
+
+// readAuthenticationPath reads a container's labels and reads authentication path into options
+func readAuthenticationPath(ctr *libpod.Container, options Options) {
+ labels := ctr.Labels()
+ authFilePath, exists := labels[AuthfileLabel]
+ if exists {
+ options.Authfile = authFilePath
+ }
}
-// newerImageAvailable returns true if there corresponding image on the remote
+// newerRemoteImageAvailable returns true if there corresponding image on the remote
// registry is newer.
-func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName string, options Options) (bool, error) {
+func newerRemoteImageAvailable(runtime *libpod.Runtime, img *image.Image, origName string, options Options) (bool, error) {
remoteRef, err := docker.ParseReference("//" + origName)
if err != nil {
return false, err
@@ -282,6 +330,25 @@ func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName str
return img.Digest().String() != remoteDigest.String(), nil
}
+// newerLocalImageAvailable returns true if the container and local image have different digests
+func newerLocalImageAvailable(img *image.Image, rawImageName string) (bool, error) {
+ rt, err := libpod.NewRuntime(context.TODO())
+ if err != nil {
+ return false, err
+ }
+
+ localImg, err := rt.ImageRuntime().NewFromLocal(rawImageName)
+ if err != nil {
+ return false, err
+ }
+
+ localDigest := localImg.Digest().String()
+
+ ctrDigest := img.Digest().String()
+
+ return localDigest != ctrDigest, nil
+}
+
// updateImage pulls the specified image.
func updateImage(runtime *libpod.Runtime, name string, options Options) (*image.Image, error) {
sys := runtime.SystemContext()