diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 8e65248e24..2031dd42ff 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 672d0a69ff..6c518e6789 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 diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go index 33aac48d59..b851328147 100644 --- a/test/e2e/manifest_test.go +++ b/test/e2e/manifest_test.go @@ -8,6 +8,7 @@ import ( . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman manifest", func() { @@ -49,6 +50,16 @@ var _ = Describe("Podman manifest", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman manifest inspect", func() { + session := podmanTest.Podman([]string{"manifest", "inspect", BB}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"manifest", "inspect", "docker.io/library/busybox"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman manifest add", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout()