summaryrefslogtreecommitdiff
path: root/pkg/autoupdate/autoupdate.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/autoupdate/autoupdate.go')
-rw-r--r--pkg/autoupdate/autoupdate.go175
1 files changed, 109 insertions, 66 deletions
diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go
index e271b9466..0a13e7e74 100644
--- a/pkg/autoupdate/autoupdate.go
+++ b/pkg/autoupdate/autoupdate.go
@@ -5,16 +5,16 @@ import (
"os"
"sort"
+ "github.com/containers/common/libimage"
+ "github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/systemd"
systemdDefine "github.com/containers/podman/v3/pkg/systemd/define"
- "github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -33,17 +33,24 @@ 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 = "registry"
+ // 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,
}
+// policyMapper is used for tying a container to it's autoupdate policy
+type policyMapper map[Policy][]*libpod.Container
+
// LookupPolicy looks up the corresponding Policy for the specified
// string. If none is found, an errors is returned including the list of
// supported policies.
@@ -99,11 +106,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) {
@@ -113,12 +126,15 @@ func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) {
return nil, errs
}
- // Create a map from `image ID -> *image.Image` for image lookups.
- imagesSlice, err := runtime.ImageRuntime().GetImages()
+ // Create a map from `image ID -> *libimage.Image` for image lookups.
+ listOptions := &libimage.ListImagesOptions{
+ Filters: []string{"readonly=false"},
+ }
+ imagesSlice, err := runtime.LibimageRuntime().ListImages(context.Background(), nil, listOptions)
if err != nil {
return nil, []error{err}
}
- imageMap := make(map[string]*image.Image)
+ imageMap := make(map[string]*libimage.Image)
for i := range imagesSlice {
imageMap[imagesSlice[i].ID()] = imagesSlice[i]
}
@@ -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, policyMapper := 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,50 @@ 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 _, registryCtr := range policyMapper[PolicyRegistryImage] {
+ cid := registryCtr.ID()
+ rawImageName := registryCtr.RawImageName()
if rawImageName == "" {
- errs = append(errs, errors.Errorf("error auto-updating container %q: raw-image name is empty", ctr.ID()))
+ errs = append(errs, errors.Errorf("error registry auto-updating container %q: raw-image name is empty", cid))
}
- labels := ctr.Labels()
- authFilePath, exists := labels[AuthfileLabel]
- if exists {
- options.Authfile = authFilePath
- }
- needsUpdate, err := newerImageAvailable(runtime, image, rawImageName, options)
+ readAuthenticationPath(registryCtr, options)
+ needsUpdate, err := newerRemoteImageAvailable(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))
+ errs = append(errs, errors.Wrapf(err, "error registry auto-updating container %q: image check for %q failed", cid, rawImageName))
continue
}
- if !needsUpdate {
+
+ 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 registry auto-updating container %q: image update for %q failed", cid, rawImageName))
+ continue
+ }
+ updatedRawImages[rawImageName] = true
+ }
+ containersToRestart = append(containersToRestart, registryCtr)
+ }
+ }
+
+ for _, localCtr := range policyMapper[PolicyLocalImage] {
+ cid := localCtr.ID()
+ rawImageName := localCtr.RawImageName()
+ if rawImageName == "" {
+ errs = append(errs, errors.Errorf("error locally auto-updating container %q: raw-image name is empty", cid))
+ }
+ // This avoids restarting containers unnecessarily.
+ needsUpdate, err := newerLocalImageAvailable(runtime, image, rawImageName)
+ if err != nil {
+ errs = append(errs, errors.Wrapf(err, "error locally auto-updating container %q: image check for %q failed", cid, rawImageName))
continue
}
- logrus.Infof("Auto-updating container %q using image %q", ctr.ID(), 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", ctr.ID(), rawImageName))
- continue
- }
- updatedRawImages[rawImageName] = true
+
+ if needsUpdate {
+ logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName)
+ containersToRestart = append(containersToRestart, localCtr)
}
- containersToRestart = append(containersToRestart, containers[i])
}
}
@@ -198,15 +230,15 @@ 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]policyMapper, []error) {
allContainers, err := runtime.GetAllContainers()
if err != nil {
return nil, []error{err}
}
errors := []error{}
- imageMap := make(map[string][]*libpod.Container)
- for i, ctr := range allContainers {
+ containerMap := make(map[string]policyMapper)
+ for _, ctr := range allContainers {
state, err := ctr.State()
if err != nil {
errors = append(errors, err)
@@ -230,28 +262,42 @@ 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 {
continue
+ } else {
+ id, _ := ctr.Image()
+ policyMap, exists := containerMap[id]
+ if !exists {
+ policyMap = make(map[Policy][]*libpod.Container)
+ }
+ policyMap[policy] = append(policyMap[policy], ctr)
+ containerMap[id] = policyMap
+ // Now we know that `ctr` is configured for auto updates.
}
-
- // 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 *libimage.Image, origName string, options Options) (bool, error) {
remoteRef, err := docker.ParseReference("//" + origName)
if err != nil {
return false, err
}
- data, err := img.Inspect(context.Background())
+ data, err := img.Inspect(context.Background(), false)
if err != nil {
return false, err
}
@@ -282,32 +328,29 @@ func newerImageAvailable(runtime *libpod.Runtime, img *image.Image, origName str
return img.Digest().String() != remoteDigest.String(), nil
}
-// updateImage pulls the specified image.
-func updateImage(runtime *libpod.Runtime, name string, options Options) (*image.Image, error) {
- sys := runtime.SystemContext()
- registryOpts := image.DockerRegistryOptions{}
- signaturePolicyPath := ""
-
- if sys != nil {
- registryOpts.OSChoice = sys.OSChoice
- registryOpts.ArchitectureChoice = sys.OSChoice
- registryOpts.DockerCertPath = sys.DockerCertPath
- signaturePolicyPath = sys.SignaturePolicyPath
+// newerLocalImageAvailable returns true if the container and local image have different digests
+func newerLocalImageAvailable(runtime *libpod.Runtime, img *libimage.Image, rawImageName string) (bool, error) {
+ localImg, _, err := runtime.LibimageRuntime().LookupImage(rawImageName, nil)
+ if err != nil {
+ return false, err
}
- newImage, err := runtime.ImageRuntime().New(context.Background(),
- docker.Transport.Name()+"://"+name,
- signaturePolicyPath,
- options.Authfile,
- os.Stderr,
- &registryOpts,
- image.SigningOptions{},
- nil,
- util.PullImageAlways,
- nil,
- )
+ 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) (*libimage.Image, error) {
+ pullOptions := &libimage.PullOptions{}
+ pullOptions.AuthFilePath = options.Authfile
+ pullOptions.Writer = os.Stderr
+
+ pulledImages, err := runtime.LibimageRuntime().Pull(context.Background(), name, config.PullPolicyAlways, pullOptions)
if err != nil {
return nil, err
}
- return newImage, nil
+ return pulledImages[0], nil
}