From 54bd0ae71671084c53bdd7e675e23c657d410280 Mon Sep 17 00:00:00 2001
From: TomSweeneyRedHat <tsweeney@redhat.com>
Date: Tue, 20 Mar 2018 13:13:09 -0400
Subject: Remove image via storage if a buildah container is associated

Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>

Closes: #522
Approved by: mheon
---
 cmd/podman/rmi.go     |  4 ++++
 libpod/runtime_img.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index 244e07a06..c1be6dabb 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/containers/storage"
 	"github.com/pkg/errors"
 	"github.com/projectatomic/libpod/libpod/image"
 	"github.com/urfave/cli"
@@ -76,6 +77,9 @@ func rmiCmd(c *cli.Context) error {
 	for _, img := range imagesToDelete {
 		msg, err := runtime.RemoveImage(img, c.Bool("force"))
 		if err != nil {
+			if errors.Cause(err) == storage.ErrImageUsedByContainer {
+				fmt.Printf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID())
+			}
 			if lastError != nil {
 				fmt.Fprintln(os.Stderr, lastError)
 			}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index b891fcde9..ef1791884 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -12,6 +12,7 @@ import (
 	"github.com/containers/image/pkg/sysregistries"
 	"github.com/containers/image/tarball"
 	"github.com/containers/image/types"
+	"github.com/containers/storage"
 	"github.com/containers/storage/pkg/archive"
 	ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
@@ -123,8 +124,61 @@ func (r *Runtime) RemoveImage(image *image.Image, force bool) (string, error) {
 		// reponames and no force is applied, we error out.
 		return "", fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID())
 	}
+	err = image.Remove(force)
+	if err != nil && errors.Cause(err) == storage.ErrImageUsedByContainer {
+		if errStorage := r.rmStorageContainers(force, image); errStorage == nil {
+			// Containers associated with the image should be deleted now,
+			// let's try removing the image again.
+			err = image.Remove(force)
+		} else {
+			err = errStorage
+		}
+	}
+	return image.ID(), err
+}
 
-	return image.ID(), image.Remove(force)
+// Remove containers that are in storage rather than Podman.
+func (r *Runtime) rmStorageContainers(force bool, image *image.Image) error {
+	ctrIDs, err := storageContainers(image.ID(), r.store)
+	if err != nil {
+		return errors.Wrapf(err, "error getting containers for image %q", image.ID())
+	}
+
+	if len(ctrIDs) > 0 && !force {
+		return storage.ErrImageUsedByContainer
+	}
+
+	if len(ctrIDs) > 0 && force {
+		if err = removeStorageContainers(ctrIDs, r.store); err != nil {
+			return errors.Wrapf(err, "error removing containers %v for image %q", ctrIDs, image.ID())
+		}
+	}
+	return nil
+}
+
+// Returns a list of storage containers associated with the given ImageReference
+func storageContainers(imageID string, store storage.Store) ([]string, error) {
+	ctrIDs := []string{}
+	containers, err := store.Containers()
+	if err != nil {
+		return nil, err
+	}
+	for _, ctr := range containers {
+		if ctr.ImageID == imageID {
+			ctrIDs = append(ctrIDs, ctr.ID)
+		}
+	}
+	return ctrIDs, nil
+}
+
+// Removes the containers passed in the array.
+func removeStorageContainers(ctrIDs []string, store storage.Store) error {
+	for _, ctrID := range ctrIDs {
+		if err := store.DeleteContainer(ctrID); err != nil {
+			return errors.Wrapf(err, "could not remove container %q", ctrID)
+		}
+	}
+	return nil
 }
 
 // GetRegistries gets the searchable registries from the global registration file.
-- 
cgit v1.2.3-54-g00ecf