diff options
Diffstat (limited to 'libpod/image/image.go')
-rw-r--r-- | libpod/image/image.go | 206 |
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 { |