From 59f31d86acdf54019b081ca86340d5c33dbcf3b9 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <rothberg@redhat.com>
Date: Thu, 15 Jul 2021 11:27:25 +0200
Subject: auto-update: add --dry-run

Add a --dry-run flag to `podman auto-update` which will look for new
images but won't perform any pull or restart any service or container.

The "UPDATED" column will now indicate the availability of a newer image
via "pending".

```
$ podman auto-update --dry-run
UNIT                    CONTAINER            IMAGE                   POLICY      UPDATED
container-test.service  08fd34e533fd (test)  localhost:5000/busybox  registry    false
```

Fixes: #9949
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
---
 pkg/autoupdate/autoupdate.go        | 26 +++++++++++++++-----------
 pkg/domain/entities/auto-update.go  |  8 ++++++--
 pkg/domain/infra/abi/auto-update.go |  6 +-----
 3 files changed, 22 insertions(+), 18 deletions(-)

(limited to 'pkg')

diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go
index fd95c319c..c51e2cd03 100644
--- a/pkg/autoupdate/autoupdate.go
+++ b/pkg/autoupdate/autoupdate.go
@@ -75,12 +75,6 @@ func LookupPolicy(s string) (Policy, error) {
 	return "", errors.Errorf("invalid auto-update policy %q: valid policies are %+q", s, keys)
 }
 
-// Options include parameters for auto updates.
-type Options struct {
-	// Authfile to use when contacting registries.
-	Authfile string
-}
-
 // ValidateImageReference checks if the specified imageName is a fully-qualified
 // image reference to the docker transport (without digest).  Such a reference
 // includes a domain, name and tag (e.g., quay.io/podman/stable:latest).  The
@@ -120,7 +114,7 @@ func ValidateImageReference(imageName string) error {
 //
 // It returns a slice of successfully restarted systemd units and a slice of
 // errors encountered during auto update.
-func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options Options) ([]*entities.AutoUpdateReport, []error) {
+func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) {
 	// Create a map from `image ID -> []*Container`.
 	containerMap, errs := imageContainersMap(runtime)
 	if len(containerMap) == 0 {
@@ -183,7 +177,7 @@ func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options Options) (
 }
 
 // autoUpdateRegistry updates the image/container according to the "registry" policy.
-func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod.Container, updatedRawImages map[string]bool, options *Options, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) {
+func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod.Container, updatedRawImages map[string]bool, options *entities.AutoUpdateOptions, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) {
 	cid := ctr.ID()
 	rawImageName := ctr.RawImageName()
 	if rawImageName == "" {
@@ -225,6 +219,11 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod.
 		return report, nil
 	}
 
+	if options.DryRun {
+		report.Updated = "pending"
+		return report, nil
+	}
+
 	if _, err := updateImage(ctx, runtime, rawImageName, options); err != nil {
 		return report, errors.Wrapf(err, "error registry auto-updating container %q: image update for %q failed", cid, rawImageName)
 	}
@@ -240,7 +239,7 @@ func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod.
 }
 
 // autoUpdateRegistry updates the image/container according to the "local" policy.
-func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.Container, options *Options, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) {
+func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.Container, options *entities.AutoUpdateOptions, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) {
 	cid := ctr.ID()
 	rawImageName := ctr.RawImageName()
 	if rawImageName == "" {
@@ -272,6 +271,11 @@ func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.C
 		return report, nil
 	}
 
+	if options.DryRun {
+		report.Updated = "pending"
+		return report, nil
+	}
+
 	logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName)
 	if err := restartSystemdUnit(ctr, unit, conn); err != nil {
 		return report, err
@@ -346,7 +350,7 @@ func imageContainersMap(runtime *libpod.Runtime) (map[string]policyMapper, []err
 
 // getAuthfilePath returns an authfile path, if set. The authfile label in the
 // container, if set, as precedence over the one set in the options.
-func getAuthfilePath(ctr *libpod.Container, options *Options) string {
+func getAuthfilePath(ctr *libpod.Container, options *entities.AutoUpdateOptions) string {
 	labels := ctr.Labels()
 	authFilePath, exists := labels[AuthfileLabel]
 	if exists {
@@ -375,7 +379,7 @@ func newerLocalImageAvailable(runtime *libpod.Runtime, img *libimage.Image, rawI
 }
 
 // updateImage pulls the specified image.
-func updateImage(ctx context.Context, runtime *libpod.Runtime, name string, options *Options) (*libimage.Image, error) {
+func updateImage(ctx context.Context, runtime *libpod.Runtime, name string, options *entities.AutoUpdateOptions) (*libimage.Image, error) {
 	pullOptions := &libimage.PullOptions{}
 	pullOptions.AuthFilePath = options.Authfile
 	pullOptions.Writer = os.Stderr
diff --git a/pkg/domain/entities/auto-update.go b/pkg/domain/entities/auto-update.go
index d74462b86..eed617bf8 100644
--- a/pkg/domain/entities/auto-update.go
+++ b/pkg/domain/entities/auto-update.go
@@ -4,6 +4,10 @@ package entities
 type AutoUpdateOptions struct {
 	// Authfile to use when contacting registries.
 	Authfile string
+	// Only check for but do not perform any update.  If an update is
+	// pending, it will be indicated in the Updated field of
+	// AutoUpdateReport.
+	DryRun bool
 }
 
 // AutoUpdateReport contains the results from running auto-update.
@@ -18,7 +22,7 @@ type AutoUpdateReport struct {
 	Policy string
 	// SystemdUnit running a container configured for auto updates.
 	SystemdUnit string
-	// Indicates whether the image was updated and the container (and
-	// systemd unit) restarted.
+	// Indicates the update status: true, false, failed, pending (see
+	// DryRun).
 	Updated string
 }
diff --git a/pkg/domain/infra/abi/auto-update.go b/pkg/domain/infra/abi/auto-update.go
index daa882ecf..b98ee1cb2 100644
--- a/pkg/domain/infra/abi/auto-update.go
+++ b/pkg/domain/infra/abi/auto-update.go
@@ -8,9 +8,5 @@ import (
 )
 
 func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) {
-	// Convert the entities options to the autoupdate ones.  We can't use
-	// them in the entities package as low-level packages must not leak
-	// into the remote client.
-	autoOpts := autoupdate.Options{Authfile: options.Authfile}
-	return autoupdate.AutoUpdate(ctx, ic.Libpod, autoOpts)
+	return autoupdate.AutoUpdate(ctx, ic.Libpod, options)
 }
-- 
cgit v1.2.3-54-g00ecf