From 7ac8000cc1925c3c46a3afcd4ff23b1d09bddfe3 Mon Sep 17 00:00:00 2001
From: Qi Wang <qiwan@redhat.com>
Date: Tue, 22 Sep 2020 16:24:31 -0400
Subject: fix allowing inspect manifest of non-local image

Add support of `podman manifest inspect` returning manifest list of non-local manifest.
Close #https://github.com/containers/podman/issues/7726

Signed-off-by: Qi Wang <qiwan@redhat.com>
---
 pkg/api/handlers/libpod/manifests.go | 17 +++++---
 pkg/domain/infra/abi/manifest.go     | 85 ++++++++++++++++++++++++++++++------
 2 files changed, 81 insertions(+), 21 deletions(-)

(limited to 'pkg')

diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index 8e65248e2..2031dd42f 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -6,11 +6,13 @@ import (
 
 	"github.com/containers/buildah/manifests"
 	copy2 "github.com/containers/image/v5/copy"
+	"github.com/containers/image/v5/manifest"
 	"github.com/containers/image/v5/transports/alltransports"
 	"github.com/containers/podman/v2/libpod"
 	"github.com/containers/podman/v2/libpod/image"
 	"github.com/containers/podman/v2/pkg/api/handlers"
 	"github.com/containers/podman/v2/pkg/api/handlers/utils"
+	"github.com/containers/podman/v2/pkg/domain/infra/abi"
 	"github.com/gorilla/schema"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
@@ -48,17 +50,18 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
 func ManifestInspect(w http.ResponseWriter, r *http.Request) {
 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 	name := utils.GetName(r)
-	newImage, err := runtime.ImageRuntime().NewFromLocal(name)
-	if err != nil {
-		utils.ImageNotFound(w, name, err)
+	imageEngine := abi.ImageEngine{Libpod: runtime}
+	inspectReport, inspectError := imageEngine.ManifestInspect(r.Context(), name)
+	if inspectError != nil {
+		utils.Error(w, "Something went wrong.", http.StatusNotFound, inspectError)
 		return
 	}
-	data, err := newImage.InspectManifest()
-	if err != nil {
-		utils.InternalServerError(w, err)
+	var list manifest.Schema2List
+	if err := json.Unmarshal(inspectReport, &list); err != nil {
+		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Unmarshal()"))
 		return
 	}
-	utils.WriteResponse(w, http.StatusOK, data)
+	utils.WriteResponse(w, http.StatusOK, &list)
 }
 
 func ManifestAdd(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 672d0a69f..6c518e678 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -3,6 +3,7 @@
 package abi
 
 import (
+	"bytes"
 	"context"
 	"encoding/json"
 	"fmt"
@@ -11,15 +12,17 @@ import (
 	"strings"
 
 	"github.com/containers/buildah/manifests"
+	buildahManifests "github.com/containers/buildah/pkg/manifests"
+	"github.com/containers/buildah/util"
 	buildahUtil "github.com/containers/buildah/util"
 	cp "github.com/containers/image/v5/copy"
 	"github.com/containers/image/v5/docker"
 	"github.com/containers/image/v5/manifest"
+	"github.com/containers/image/v5/transports"
 	"github.com/containers/image/v5/transports/alltransports"
 	"github.com/containers/image/v5/types"
 	libpodImage "github.com/containers/podman/v2/libpod/image"
 	"github.com/containers/podman/v2/pkg/domain/entities"
-	"github.com/containers/podman/v2/pkg/util"
 	"github.com/opencontainers/go-digest"
 	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
 
@@ -41,28 +44,82 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin
 
 // ManifestInspect returns the content of a manifest list or image
 func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
-	dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
-	_, err := alltransports.ParseImageName(name)
+	if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
+		// return the manifest in local storage
+		if list, err := newImage.InspectManifest(); err == nil {
+			buf, err := json.MarshalIndent(list, "", "    ")
+			if err != nil {
+				return buf, errors.Wrapf(err, "error rendering manifest %s for display", name)
+			}
+			return buf, nil
+			// no return if local image is not a list of images type
+			// continue on getting valid manifest through remote serice
+		} else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
+			return nil, errors.Wrapf(err, "loading manifest %q", name)
+		}
+	}
+	sc := ir.Libpod.SystemContext()
+	refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name)
 	if err != nil {
-		_, err = alltransports.ParseImageName(dockerPrefix + name)
+		return nil, err
+	}
+	var (
+		latestErr error
+		result    []byte
+		manType   string
+		b         bytes.Buffer
+	)
+	appendErr := func(e error) {
+		if latestErr == nil {
+			latestErr = e
+		} else {
+			latestErr = errors.Wrapf(latestErr, "tried %v\n", e)
+		}
+	}
+	for _, ref := range refs {
+		src, err := ref.NewImageSource(ctx, sc)
+		if err != nil {
+			appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
+			continue
+		}
+		defer src.Close()
+
+		manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
 		if err != nil {
-			return nil, errors.Errorf("invalid image reference %q", name)
+			appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
+			continue
 		}
+
+		if !manifest.MIMETypeIsMultiImage(manifestType) {
+			appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
+			continue
+		}
+		result = manifestBytes
+		manType = manifestType
+		break
 	}
-	image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing)
-	if err != nil {
-		return nil, errors.Wrapf(err, "reading image %q", name)
+	if len(result) == 0 && latestErr != nil {
+		return nil, latestErr
 	}
+	if manType != manifest.DockerV2ListMediaType {
+		listBlob, err := manifest.ListFromBlob(result, manType)
+		if err != nil {
+			return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+		}
+		list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType)
+		if err != nil {
+			return nil, err
+		}
+		if result, err = list.Serialize(); err != nil {
+			return nil, err
+		}
 
-	list, err := image.InspectManifest()
-	if err != nil {
-		return nil, errors.Wrapf(err, "loading manifest %q", name)
 	}
-	buf, err := json.MarshalIndent(list, "", "    ")
+	err = json.Indent(&b, result, "", "    ")
 	if err != nil {
-		return buf, errors.Wrapf(err, "error rendering manifest for display")
+		return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
 	}
-	return buf, nil
+	return b.Bytes(), nil
 }
 
 // ManifestAdd adds images to the manifest list
-- 
cgit v1.2.3-54-g00ecf