summaryrefslogtreecommitdiff
path: root/libpod/image
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@redhat.com>2019-09-25 16:43:50 -0400
committerNalin Dahyabhai <nalin@redhat.com>2019-10-29 13:35:19 -0400
commit803357334ce72e14f263a0951d83cdab3fff2bde (patch)
treea32dc20e5c2a6e0ef733031ce065c463ea90a9f2 /libpod/image
parent05c65e88e3992eb57aee9f0dcb622de8a6d81a70 (diff)
downloadpodman-803357334ce72e14f263a0951d83cdab3fff2bde.tar.gz
podman-803357334ce72e14f263a0951d83cdab3fff2bde.tar.bz2
podman-803357334ce72e14f263a0951d83cdab3fff2bde.zip
image: don't get confused by lists
When an image can be opened as an ImageSource but not an Image, handle the case where it's an image list all by itself, the case where it's an image for a different architecture/OS combination, or the case where it's both. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Diffstat (limited to 'libpod/image')
-rw-r--r--libpod/image/image.go206
1 files changed, 122 insertions, 84 deletions
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5c530858a..2ab88f2e9 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -3,6 +3,7 @@ package image
import (
"context"
"encoding/json"
+ stderrors "errors"
"fmt"
"io"
"io/ioutil"
@@ -16,6 +17,7 @@ import (
"github.com/containers/image/v5/directory"
dockerarchive "github.com/containers/image/v5/docker/archive"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest"
ociarchive "github.com/containers/image/v5/oci/archive"
is "github.com/containers/image/v5/storage"
@@ -42,8 +44,8 @@ import (
type Image struct {
// Adding these two structs for now but will cull when we near
// completion of this library.
- imgRef types.Image
- storeRef types.ImageReference
+ imgRef types.Image
+ imgSrcRef types.ImageSource
inspect.ImageData
inspect.ImageResult
inspectInfo *types.ImageInspectInfo
@@ -72,7 +74,10 @@ type InfoImage struct {
}
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
-var ErrRepoTagNotFound = errors.New("unable to match user input to any specific repotag")
+var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag")
+
+// ErrImageIsBareList is the error returned when the image is just a list or index
+var ErrImageIsBareList = stderrors.New("image contains a manifest list or image index, but no runnable image")
// NewImageRuntimeFromStore creates an ImageRuntime based on a provided store
func NewImageRuntimeFromStore(store storage.Store) *Runtime {
@@ -294,9 +299,18 @@ func (i *Image) Digest() digest.Digest {
return i.image.Digest
}
+// GetManifest returns the image's manifest as a byte array
+// and manifest type as a string.
+func (i *Image) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
+ imgSrcRef, err := i.toImageSourceRef(ctx)
+ if err != nil {
+ return nil, "", err
+ }
+ return imgSrcRef.GetManifest(ctx, instanceDigest)
+}
+
// Manifest returns the image's manifest as a byte array
-// and manifest type as a string. The manifest type is
-// MediaTypeImageManifest from ociv1.
+// and manifest type as a string.
func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) {
imgRef, err := i.toImageRef(ctx)
if err != nil {
@@ -379,26 +393,6 @@ func (i *Image) Remove(ctx context.Context, force bool) error {
return nil
}
-// TODO: Rework this method to not require an assembly of the fq name with transport
-/*
-// GetManifest tries to GET an images manifest, returns nil on success and err on failure
-func (i *Image) GetManifest() error {
- pullRef, err := alltransports.ParseImageName(i.assembleFqNameTransport())
- if err != nil {
- return errors.Errorf("unable to parse '%s'", i.Names()[0])
- }
- imageSource, err := pullRef.NewImageSource(nil)
- if err != nil {
- return errors.Wrapf(err, "unable to create new image source")
- }
- _, _, err = imageSource.GetManifest(nil)
- if err == nil {
- return nil
- }
- return err
-}
-*/
-
// getImage retrieves an image matching the given name or hash from system
// storage
// If no matching image can be found, an error is returned
@@ -612,76 +606,98 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
// MatchesID returns a bool based on if the input id
// matches the image's id
+// TODO: This isn't used anywhere, so remove it
func (i *Image) MatchesID(id string) bool {
return strings.HasPrefix(i.ID(), id)
}
-// toStorageReference returns a *storageReference from an Image
-func (i *Image) toStorageReference() (types.ImageReference, error) {
- var lookupName string
- if i.storeRef == nil {
- if i.image != nil {
- lookupName = i.ID()
- } else {
- lookupName = i.InputName
- }
- storeRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, lookupName)
- if err != nil {
- return nil, err
- }
- i.storeRef = storeRef
- }
- return i.storeRef, nil
-}
-
// ToImageRef returns an image reference type from an image
// TODO: Hopefully we can remove this exported function for mheon
func (i *Image) ToImageRef(ctx context.Context) (types.Image, error) {
return i.toImageRef(ctx)
}
-// toImageRef returns an Image Reference type from an image
-func (i *Image) toImageRef(ctx context.Context) (types.Image, error) {
+// toImageSourceRef returns an ImageSource Reference type from an image
+func (i *Image) toImageSourceRef(ctx context.Context) (types.ImageSource, error) {
if i == nil {
- return nil, errors.Errorf("cannot convert nil image to image reference")
+ return nil, errors.Errorf("cannot convert nil image to image source reference")
}
- if i.imgRef == nil {
+ if i.imgSrcRef == nil {
ref, err := is.Transport.ParseStoreReference(i.imageruntime.store, "@"+i.ID())
if err != nil {
return nil, errors.Wrapf(err, "error parsing reference to image %q", i.ID())
}
- imgRef, err := ref.NewImage(ctx, nil)
+ imgSrcRef, err := ref.NewImageSource(ctx, nil)
if err != nil {
- return nil, errors.Wrapf(err, "error reading image %q", i.ID())
+ return nil, errors.Wrapf(err, "error reading image %q as image source", i.ID())
}
- i.imgRef = imgRef
+ i.imgSrcRef = imgSrcRef
}
- return i.imgRef, nil
-}
-
-// sizer knows its size.
-type sizer interface {
- Size() (int64, error)
+ return i.imgSrcRef, nil
}
//Size returns the size of the image
func (i *Image) Size(ctx context.Context) (*uint64, error) {
- storeRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID())
- if err != nil {
- return nil, err
+ if i.image == nil {
+ localImage, err := i.getLocalImage()
+ if err != nil {
+ return nil, err
+ }
+ i.image = localImage
+ }
+ if sum, err := i.imageruntime.store.ImageSize(i.ID()); err == nil && sum >= 0 {
+ usum := uint64(sum)
+ return &usum, nil
+ }
+ return nil, errors.Errorf("unable to determine size")
+}
+
+// toImageRef returns an Image Reference type from an image
+func (i *Image) toImageRef(ctx context.Context) (types.Image, error) {
+ if i == nil {
+ return nil, errors.Errorf("cannot convert nil image to image reference")
}
- systemContext := &types.SystemContext{}
- img, err := storeRef.NewImageSource(ctx, systemContext)
+ imgSrcRef, err := i.toImageSourceRef(ctx)
if err != nil {
return nil, err
}
- if s, ok := img.(sizer); ok {
- if sum, err := s.Size(); err == nil {
- usum := uint64(sum)
- return &usum, nil
+ if i.imgRef == nil {
+ systemContext := &types.SystemContext{}
+ unparsedDefaultInstance := image.UnparsedInstance(imgSrcRef, nil)
+ imgRef, err := image.FromUnparsedImage(ctx, systemContext, unparsedDefaultInstance)
+ if err != nil {
+ // check for a "tried-to-treat-a-bare-list-like-a-runnable-image" problem, else
+ // return info about the not-a-bare-list runnable image part of this storage.Image
+ if manifestBytes, manifestType, err2 := imgSrcRef.GetManifest(ctx, nil); err2 == nil {
+ if manifest.MIMETypeIsMultiImage(manifestType) {
+ if list, err3 := manifest.ListFromBlob(manifestBytes, manifestType); err3 == nil {
+ switch manifestType {
+ case ociv1.MediaTypeImageIndex:
+ err = errors.Wrapf(ErrImageIsBareList, "%q is an image index", i.InputName)
+ case manifest.DockerV2ListMediaType:
+ err = errors.Wrapf(ErrImageIsBareList, "%q is a manifest list", i.InputName)
+ default:
+ err = errors.Wrapf(ErrImageIsBareList, "%q", i.InputName)
+ }
+ for _, instanceDigest := range list.Instances() {
+ instance := instanceDigest
+ unparsedInstance := image.UnparsedInstance(imgSrcRef, &instance)
+ if imgRef2, err4 := image.FromUnparsedImage(ctx, systemContext, unparsedInstance); err4 == nil {
+ imgRef = imgRef2
+ err = nil
+ break
+ }
+ }
+ }
+ }
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading image %q as image", i.ID())
+ }
}
+ i.imgRef = imgRef
}
- return nil, errors.Errorf("unable to determine size")
+ return i.imgRef, nil
}
// DriverData gets the driver data from the store on a layer
@@ -708,6 +724,9 @@ type History struct {
func (i *Image) History(ctx context.Context) ([]*History, error) {
img, err := i.toImageRef(ctx)
if err != nil {
+ if errors.Cause(err) == ErrImageIsBareList {
+ return nil, nil
+ }
return nil, err
}
oci, err := img.OCIConfig(ctx)
@@ -853,7 +872,10 @@ func (i *Image) GetLabel(ctx context.Context, label string) (string, error) {
func (i *Image) Annotations(ctx context.Context) (map[string]string, error) {
imageManifest, manifestType, err := i.Manifest(ctx)
if err != nil {
- return nil, err
+ imageManifest, manifestType, err = i.GetManifest(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
}
annotations := make(map[string]string)
switch manifestType {
@@ -868,24 +890,19 @@ func (i *Image) Annotations(ctx context.Context) (map[string]string, error) {
return annotations, nil
}
-// ociv1Image converts and image to an imgref and then an
-// ociv1 image type
+// ociv1Image converts an image to an imgref and then returns its config blob
+// converted to an ociv1 image type
func (i *Image) ociv1Image(ctx context.Context) (*ociv1.Image, error) {
imgRef, err := i.toImageRef(ctx)
if err != nil {
return nil, err
}
-
return imgRef.OCIConfig(ctx)
}
func (i *Image) imageInspectInfo(ctx context.Context) (*types.ImageInspectInfo, error) {
if i.inspectInfo == nil {
- sr, err := i.toStorageReference()
- if err != nil {
- return nil, err
- }
- ic, err := sr.NewImage(ctx, &types.SystemContext{})
+ ic, err := i.toImageRef(ctx)
if err != nil {
return nil, err
}
@@ -906,20 +923,20 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
ociv1Img, err := i.ociv1Image(ctx)
if err != nil {
- return nil, err
+ ociv1Img = &ociv1.Image{}
}
info, err := i.imageInspectInfo(ctx)
if err != nil {
- return nil, err
+ info = &types.ImageInspectInfo{}
}
annotations, err := i.Annotations(ctx)
if err != nil {
return nil, err
}
- size, err := i.Size(ctx)
- if err != nil {
- return nil, err
+ size := int64(-1)
+ if usize, err := i.Size(ctx); err == nil {
+ size = int64(*usize)
}
repoDigests, err := i.RepoDigests()
@@ -932,7 +949,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
return nil, err
}
- _, manifestType, err := i.Manifest(ctx)
+ _, manifestType, err := i.GetManifest(ctx, nil)
if err != nil {
return nil, errors.Wrapf(err, "unable to determine manifest type")
}
@@ -952,8 +969,8 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
Os: ociv1Img.OS,
Config: &ociv1Img.Config,
Version: info.DockerVersion,
- Size: int64(*size),
- VirtualSize: int64(*size),
+ Size: size,
+ VirtualSize: size,
Annotations: annotations,
Digest: i.Digest(),
Labels: info.Labels,
@@ -1082,6 +1099,9 @@ func splitString(input string) string {
func (i *Image) IsParent(ctx context.Context) (bool, error) {
children, err := i.getChildren(ctx, 1)
if err != nil {
+ if errors.Cause(err) == ErrImageIsBareList {
+ return false, nil
+ }
return false, err
}
return len(children) > 0, nil
@@ -1165,6 +1185,9 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) {
// fetch the configuration for the child image
child, err := i.ociv1Image(ctx)
if err != nil {
+ if errors.Cause(err) == ErrImageIsBareList {
+ return nil, nil
+ }
return nil, err
}
for _, img := range images {
@@ -1205,12 +1228,24 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) {
// GetChildren returns a list of the imageIDs that depend on the image
func (i *Image) GetChildren(ctx context.Context) ([]string, error) {
- return i.getChildren(ctx, 0)
+ children, err := i.getChildren(ctx, 0)
+ if err != nil {
+ if errors.Cause(err) == ErrImageIsBareList {
+ return nil, nil
+ }
+ return nil, err
+ }
+ return children, nil
}
// getChildren returns a list of at most "max" imageIDs that depend on the image
func (i *Image) getChildren(ctx context.Context, max int) ([]string, error) {
var children []string
+
+ if _, err := i.toImageRef(ctx); err != nil {
+ return nil, nil
+ }
+
images, err := i.imageruntime.GetImages()
if err != nil {
return nil, err
@@ -1301,6 +1336,9 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
}
ociv1Img, err := i.ociv1Image(ctx)
if err != nil {
+ if errors.Cause(err) == ErrImageIsBareList {
+ return "", nil
+ }
return "", err
}
if len(ociv1Img.History) > 0 {