From 22c8270135620bbbeb988076378173e452ec04af Mon Sep 17 00:00:00 2001
From: Daniel J Walsh <dwalsh@redhat.com>
Date: Sat, 3 Oct 2020 06:56:42 -0400
Subject: fix podman container exists and diff for storage containers

Current these commands only check if a container exists in libpod. With
this fix, the commands will also check if they are in containers/storage.

This allows users to look at differences within a buildah or CRI-O container.

Currently buildah diff does not exists, so this helps out in that situation
as well as in CRI-O since the cri does not implement a diff command.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
---
 docs/source/markdown/podman-container-exists.1.md |  2 --
 libpod/diff.go                                    | 20 ++++++++++++--------
 libpod/runtime_cstorage.go                        |  4 ++++
 pkg/api/handlers/libpod/containers.go             | 13 +++++++++++--
 pkg/domain/infra/abi/containers.go                | 12 +++++++++---
 test/system/030-run.bats                          | 17 +++++++++++++++++
 test/system/140-diff.bats                         | 22 ++++++++++++++++++++++
 7 files changed, 75 insertions(+), 15 deletions(-)

diff --git a/docs/source/markdown/podman-container-exists.1.md b/docs/source/markdown/podman-container-exists.1.md
index d24df2fc8..b8be07cb9 100644
--- a/docs/source/markdown/podman-container-exists.1.md
+++ b/docs/source/markdown/podman-container-exists.1.md
@@ -24,7 +24,6 @@ Check if an container called `webclient` exists in local storage (the container
 $ podman container exists webclient
 $ echo $?
 0
-$
 ```
 
 Check if an container called `webbackend` exists in local storage (the container does not actually exist).
@@ -32,7 +31,6 @@ Check if an container called `webbackend` exists in local storage (the container
 $ podman container exists webbackend
 $ echo $?
 1
-$
 ```
 
 ## SEE ALSO
diff --git a/libpod/diff.go b/libpod/diff.go
index 5335d701c..43f4d2e96 100644
--- a/libpod/diff.go
+++ b/libpod/diff.go
@@ -62,18 +62,22 @@ func (r *Runtime) ApplyDiffTarStream(to string, diff io.Reader) error {
 func (r *Runtime) getLayerID(id string) (string, error) {
 	var toLayer string
 	toImage, err := r.imageRuntime.NewFromLocal(id)
+	if err == nil {
+		return toImage.TopLayer(), nil
+	}
+
+	targetID, err := r.store.Lookup(id)
 	if err != nil {
-		toCtr, err := r.store.Container(id)
+		targetID = id
+	}
+	toCtr, err := r.store.Container(targetID)
+	if err != nil {
+		toLayer, err = layers.FullID(r.store, targetID)
 		if err != nil {
-			toLayer, err = layers.FullID(r.store, id)
-			if err != nil {
-				return "", errors.Errorf("layer, image, or container %s does not exist", id)
-			}
-		} else {
-			toLayer = toCtr.LayerID
+			return "", errors.Errorf("layer, image, or container %s does not exist", id)
 		}
 	} else {
-		toLayer = toImage.TopLayer()
+		toLayer = toCtr.LayerID
 	}
 	return toLayer, nil
 }
diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go
index 03eebeefc..61fdd42d3 100644
--- a/libpod/runtime_cstorage.go
+++ b/libpod/runtime_cstorage.go
@@ -52,6 +52,10 @@ func (r *Runtime) ListStorageContainers() ([]*StorageContainer, error) {
 	return finalCtrs, nil
 }
 
+func (r *Runtime) StorageContainer(idOrName string) (*storage.Container, error) {
+	return r.store.Container(idOrName)
+}
+
 // RemoveStorageContainer removes a container from c/storage.
 // The container WILL NOT be removed if it exists in libpod.
 // Accepts ID or full name of container.
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 7dde51102..8068ea1b8 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -11,6 +11,7 @@ import (
 	"github.com/containers/podman/v2/pkg/api/handlers/compat"
 	"github.com/containers/podman/v2/pkg/api/handlers/utils"
 	"github.com/containers/podman/v2/pkg/domain/entities"
+	"github.com/containers/podman/v2/pkg/domain/infra/abi"
 	"github.com/containers/podman/v2/pkg/ps"
 	"github.com/gorilla/schema"
 	"github.com/pkg/errors"
@@ -19,8 +20,12 @@ import (
 
 func ContainerExists(w http.ResponseWriter, r *http.Request) {
 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
+	// Now use the ABI implementation to prevent us from having duplicate
+	// code.
+	containerEngine := abi.ContainerEngine{Libpod: runtime}
+
 	name := utils.GetName(r)
-	_, err := runtime.LookupContainer(name)
+	report, err := containerEngine.ContainerExists(r.Context(), name)
 	if err != nil {
 		if errors.Cause(err) == define.ErrNoSuchCtr {
 			utils.ContainerNotFound(w, name, err)
@@ -30,7 +35,11 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
 		return
 
 	}
-	utils.WriteResponse(w, http.StatusNoContent, "")
+	if report.Value {
+		utils.WriteResponse(w, http.StatusNoContent, "")
+	} else {
+		utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
+	}
 }
 
 func ListContainers(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 614fd5fe0..191ad8eec 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -74,11 +74,17 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
 	return
 }
 
-// TODO: Should return *entities.ContainerExistsReport, error
+// ContainerExists returns whether the container exists in container storage
 func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
 	_, err := ic.Libpod.LookupContainer(nameOrID)
-	if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
-		return nil, err
+	if err != nil {
+		if errors.Cause(err) != define.ErrNoSuchCtr {
+			return nil, err
+		}
+		// Check if container exists in storage
+		if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil {
+			err = nil
+		}
 	}
 	return &entities.BoolReport{Value: err == nil}, nil
 }
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 28dc7c7a7..a82743c70 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -460,4 +460,21 @@ json-file | f
     is "$output" "$expect" "podman run with --tz=local, matches host"
 }
 
+@test "podman container exists" {
+    rand=$(random_string 30)
+    run_podman 1 container exists myctr
+
+    run_podman create --name myctr $IMAGE /bin/true
+    run_podman container exists myctr
+
+    # Create a container that podman does not know about
+    run buildah from $IMAGE
+    cid="$output"
+
+    # exists should succeed
+    run_podman container exists $cid
+
+    run buildah rm $cid
+}
+
 # vim: filetype=sh
diff --git a/test/system/140-diff.bats b/test/system/140-diff.bats
index 01ec5430e..d0f33e438 100644
--- a/test/system/140-diff.bats
+++ b/test/system/140-diff.bats
@@ -32,4 +32,26 @@ load helpers
     run_podman rm $n
 }
 
+@test "podman diff with buildah container " {
+    rand_file=$(random_string 10)
+    run buildah from --name buildahctr $IMAGE
+    run buildah run buildahctr sh -c "touch /$rand_file;rm /etc/services"
+
+    run_podman diff --format json buildahctr
+
+    # Expected results for each type of diff
+    declare -A expect=(
+        [added]="/$rand_file"
+        [changed]="/etc"
+        [deleted]="/etc/services"
+    )
+
+    for field in ${!expect[@]}; do
+        result=$(jq -r -c ".${field}[]" <<<"$output")
+        is "$result" "${expect[$field]}" "$field"
+    done
+
+    run buildah rm buildahctr
+}
+
 # vim: filetype=sh
-- 
cgit v1.2.3-54-g00ecf