summaryrefslogtreecommitdiff
path: root/libpod/image/image.go
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-10-29 20:58:51 +0100
committerGitHub <noreply@github.com>2019-10-29 20:58:51 +0100
commite7540d0406c49b22de245246d16ebc6e1778df37 (patch)
treed25a5f8d259d19f2c0017d9987e93d065e577f89 /libpod/image/image.go
parent5918f3a5f1d11862fbaaca94ff25f1d9cc1309e2 (diff)
parent66c126d6dee178f96f8a120f13372802d46ea9b5 (diff)
downloadpodman-e7540d0406c49b22de245246d16ebc6e1778df37.tar.gz
podman-e7540d0406c49b22de245246d16ebc6e1778df37.tar.bz2
podman-e7540d0406c49b22de245246d16ebc6e1778df37.zip
Merge pull request #4310 from nalind/manifest-lists
Move to containers/image v5, support manifest lists
Diffstat (limited to 'libpod/image/image.go')
-rw-r--r--libpod/image/image.go284
1 files changed, 179 insertions, 105 deletions
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 9adefb5c5..c912ac2ca 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -3,26 +3,29 @@ package image
import (
"context"
"encoding/json"
+ stderrors "errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
+ "sort"
"strings"
"syscall"
"time"
- cp "github.com/containers/image/v4/copy"
- "github.com/containers/image/v4/directory"
- dockerarchive "github.com/containers/image/v4/docker/archive"
- "github.com/containers/image/v4/docker/reference"
- "github.com/containers/image/v4/manifest"
- ociarchive "github.com/containers/image/v4/oci/archive"
- is "github.com/containers/image/v4/storage"
- "github.com/containers/image/v4/tarball"
- "github.com/containers/image/v4/transports"
- "github.com/containers/image/v4/transports/alltransports"
- "github.com/containers/image/v4/types"
+ cp "github.com/containers/image/v5/copy"
+ "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"
+ "github.com/containers/image/v5/tarball"
+ "github.com/containers/image/v5/transports"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod/driver"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/inspect"
@@ -42,8 +45,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 +75,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 +300,23 @@ func (i *Image) Digest() digest.Digest {
return i.image.Digest
}
+// Digests returns the image's digests
+func (i *Image) Digests() []digest.Digest {
+ return i.image.Digests
+}
+
+// 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 {
@@ -305,29 +325,54 @@ func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) {
return imgRef.Manifest(ctx)
}
-// Names returns a string array of names associated with the image
+// Names returns a string array of names associated with the image, which may be a mixture of tags and digests
func (i *Image) Names() []string {
return i.image.Names
}
-// RepoDigests returns a string array of repodigests associated with the image
-func (i *Image) RepoDigests() ([]string, error) {
- var repoDigests []string
- imageDigest := i.Digest()
-
+// RepoTags returns a string array of repotags associated with the image
+func (i *Image) RepoTags() ([]string, error) {
+ var repoTags []string
for _, name := range i.Names() {
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, err
}
-
- canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest)
- if err != nil {
- return nil, err
+ if tagged, isTagged := named.(reference.NamedTagged); isTagged {
+ repoTags = append(repoTags, tagged.String())
}
+ }
+ return repoTags, nil
+}
+
+// RepoDigests returns a string array of repodigests associated with the image
+func (i *Image) RepoDigests() ([]string, error) {
+ var repoDigests []string
+ added := make(map[string]struct{})
- repoDigests = append(repoDigests, canonical.String())
+ for _, name := range i.Names() {
+ for _, imageDigest := range append(i.Digests(), i.Digest()) {
+ if imageDigest == "" {
+ continue
+ }
+
+ named, err := reference.ParseNormalizedNamed(name)
+ if err != nil {
+ return nil, err
+ }
+
+ canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, alreadyInList := added[canonical.String()]; !alreadyInList {
+ repoDigests = append(repoDigests, canonical.String())
+ added[canonical.String()] = struct{}{}
+ }
+ }
}
+ sort.Strings(repoDigests)
return repoDigests, nil
}
@@ -379,26 +424,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 +637,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
}
- systemContext := &types.SystemContext{}
- img, err := storeRef.NewImageSource(ctx, systemContext)
+ 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")
+ }
+ 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 +755,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 +903,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 +921,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,18 +954,23 @@ 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)
+ size := int64(-1)
+ if usize, err := i.Size(ctx); err == nil {
+ size = int64(*usize)
+ }
+
+ repoTags, err := i.RepoTags()
if err != nil {
return nil, err
}
@@ -932,7 +985,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")
}
@@ -943,7 +996,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
data := &inspect.ImageData{
ID: i.ID(),
- RepoTags: i.Names(),
+ RepoTags: repoTags,
RepoDigests: repoDigests,
Comment: comment,
Created: ociv1Img.Created,
@@ -952,8 +1005,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 +1135,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 +1221,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 +1264,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 +1372,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 {