Skip to content

Commit

Permalink
fix allowing inspect manifest of non-local image
Browse files Browse the repository at this point in the history
Add support of `podman manifest inspect` returning manifest list of non-local manifest.
Close #containers#7726

Signed-off-by: Qi Wang <[email protected]>
  • Loading branch information
QiWang19 committed Sep 30, 2020
1 parent f86e01a commit 7ac8000
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 21 deletions.
17 changes: 10 additions & 7 deletions pkg/api/handlers/libpod/manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
85 changes: 71 additions & 14 deletions pkg/domain/infra/abi/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package abi

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand All @@ -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"

Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 7ac8000

Please sign in to comment.