summaryrefslogtreecommitdiff
path: root/libpod/image/image.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/image/image.go')
-rw-r--r--libpod/image/image.go200
1 files changed, 115 insertions, 85 deletions
diff --git a/libpod/image/image.go b/libpod/image/image.go
index b965a4640..068491f28 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -38,27 +38,19 @@ import (
"github.com/sirupsen/logrus"
)
-// imageConversions is used to cache image "cast" types
-type imageConversions struct {
- imgRef types.Image
- storeRef types.ImageReference
-}
-
// Image is the primary struct for dealing with images
// It is still very much a work in progress
type Image struct {
// Adding these two structs for now but will cull when we near
// completion of this library.
- imageConversions
+ imgRef types.Image
+ storeRef types.ImageReference
inspect.ImageData
inspect.ImageResult
- inspectInfo *types.ImageInspectInfo
- InputName string
- Local bool
- //runtime *libpod.Runtime
+ inspectInfo *types.ImageInspectInfo
+ InputName string
image *storage.Image
imageruntime *Runtime
- repotagsMap map[string][]string
}
// Runtime contains the store
@@ -119,7 +111,6 @@ func setStore(options storage.StoreOptions) (storage.Store, error) {
func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
image := Image{
InputName: img.ID,
- Local: true,
imageruntime: ir,
image: img,
}
@@ -132,7 +123,6 @@ func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
func (ir *Runtime) NewFromLocal(name string) (*Image, error) {
image := Image{
InputName: name,
- Local: true,
imageruntime: ir,
}
localImage, err := image.getLocalImage()
@@ -153,13 +143,11 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
// We don't know if the image is local or not ... check local first
newImage := Image{
InputName: name,
- Local: false,
imageruntime: ir,
}
if !forcePull {
localImage, err := newImage.getLocalImage()
if err == nil {
- newImage.Local = true
newImage.image = localImage
return &newImage, nil
}
@@ -199,7 +187,6 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im
for _, name := range imageNames {
newImage := Image{
InputName: name,
- Local: true,
imageruntime: ir,
}
img, err := newImage.getLocalImage()
@@ -299,6 +286,11 @@ func (i *Image) ID() string {
return i.image.ID
}
+// IsReadOnly returns whether the image ID comes from a local store
+func (i *Image) IsReadOnly() bool {
+ return i.image.ReadOnly
+}
+
// Digest returns the image's digest
func (i *Image) Digest() digest.Digest {
return i.image.Digest
@@ -323,7 +315,7 @@ func (i *Image) Names() []string {
// RepoDigests returns a string array of repodigests associated with the image
func (i *Image) RepoDigests() ([]string, error) {
var repoDigests []string
- digest := i.Digest()
+ imageDigest := i.Digest()
for _, name := range i.Names() {
named, err := reference.ParseNormalizedNamed(name)
@@ -331,7 +323,7 @@ func (i *Image) RepoDigests() ([]string, error) {
return nil, err
}
- canonical, err := reference.WithDigest(reference.TrimNamed(named), digest)
+ canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest)
if err != nil {
return nil, err
}
@@ -439,12 +431,25 @@ func (ir *Runtime) getImage(image string) (*Image, error) {
// GetImages retrieves all images present in storage
func (ir *Runtime) GetImages() ([]*Image, error) {
+ return ir.getImages(false)
+}
+
+// GetRWImages retrieves all read/write images present in storage
+func (ir *Runtime) GetRWImages() ([]*Image, error) {
+ return ir.getImages(true)
+}
+
+// getImages retrieves all images present in storage
+func (ir *Runtime) getImages(rwOnly bool) ([]*Image, error) {
var newImages []*Image
images, err := ir.store.Images()
if err != nil {
return nil, err
}
for _, i := range images {
+ if rwOnly && i.ReadOnly {
+ continue
+ }
// iterating over these, be careful to not iterate on the literal
// pointer.
image := i
@@ -461,12 +466,16 @@ func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.Sys
if err != nil {
return "", err
}
- defer newImg.Close()
- digest := newImg.ConfigInfo().Digest
- if err = digest.Validate(); err != nil {
+ defer func() {
+ if err := newImg.Close(); err != nil {
+ logrus.Errorf("failed to close image: %q", err)
+ }
+ }()
+ imageDigest := newImg.ConfigInfo().Digest
+ if err = imageDigest.Validate(); err != nil {
return "", errors.Wrapf(err, "error getting config info")
}
- return "@" + digest.Hex(), nil
+ return "@" + imageDigest.Hex(), nil
}
// normalizedTag returns the canonical version of tag for use in Image.Names()
@@ -495,7 +504,9 @@ func normalizedTag(tag string) (reference.Named, error) {
// TagImage adds a tag to the given image
func (i *Image) TagImage(tag string) error {
- i.reloadImage()
+ if err := i.reloadImage(); err != nil {
+ return err
+ }
ref, err := normalizedTag(tag)
if err != nil {
return err
@@ -508,14 +519,18 @@ func (i *Image) TagImage(tag string) error {
if err := i.imageruntime.store.SetNames(i.ID(), tags); err != nil {
return err
}
- i.reloadImage()
- defer i.newImageEvent(events.Tag)
+ if err := i.reloadImage(); err != nil {
+ return err
+ }
+ i.newImageEvent(events.Tag)
return nil
}
// UntagImage removes a tag from the given image
func (i *Image) UntagImage(tag string) error {
- i.reloadImage()
+ if err := i.reloadImage(); err != nil {
+ return err
+ }
var newTags []string
tags := i.Names()
if !util.StringInSlice(tag, tags) {
@@ -529,8 +544,10 @@ func (i *Image) UntagImage(tag string) error {
if err := i.imageruntime.store.SetNames(i.ID(), newTags); err != nil {
return err
}
- i.reloadImage()
- defer i.newImageEvent(events.Untag)
+ if err := i.reloadImage(); err != nil {
+ return err
+ }
+ i.newImageEvent(events.Untag)
return nil
}
@@ -566,7 +583,11 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
if err != nil {
return err
}
- defer policyContext.Destroy()
+ defer func() {
+ if err := policyContext.Destroy(); err != nil {
+ logrus.Errorf("failed to destroy policy context: %q", err)
+ }
+ }()
// Look up the source image, expecting it to be in local storage
src, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID())
@@ -580,7 +601,7 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
if err != nil {
return errors.Wrapf(err, "Error copying image to the remote destination")
}
- defer i.newImageEvent(events.Push)
+ i.newImageEvent(events.Push)
return nil
}
@@ -659,12 +680,8 @@ func (i *Image) Size(ctx context.Context) (*uint64, error) {
}
// DriverData gets the driver data from the store on a layer
-func (i *Image) DriverData() (*inspect.Data, error) {
- topLayer, err := i.Layer()
- if err != nil {
- return nil, err
- }
- return driver.GetDriverData(i.imageruntime.store, topLayer.ID)
+func (i *Image) DriverData() (*driver.Data, error) {
+ return driver.GetDriverData(i.imageruntime.store, i.TopLayer())
}
// Layer returns the image's top layer
@@ -693,13 +710,17 @@ func (i *Image) History(ctx context.Context) ([]*History, error) {
return nil, err
}
- // Use our layers list to find images that use one of them as its
+ // Use our layers list to find images that use any of them (or no
+ // layer, since every base layer is derived from an empty layer) as its
// topmost layer.
interestingLayers := make(map[string]bool)
- layer, err := i.imageruntime.store.Layer(i.TopLayer())
- if err != nil {
- return nil, err
+ var layer *storage.Layer
+ if i.TopLayer() != "" {
+ if layer, err = i.imageruntime.store.Layer(i.TopLayer()); err != nil {
+ return nil, err
+ }
}
+ interestingLayers[""] = true
for layer != nil {
interestingLayers[layer.ID] = true
if layer.Parent == "" {
@@ -795,27 +816,6 @@ func (i *Image) History(ctx context.Context) ([]*History, error) {
return allHistory, nil
}
-// historyLayerIDs goes through the images in store and checks if the top layer of an image
-// is the same as the parent of topLayerID
-func (i *Image) historyLayerIDs(topLayerID string, images []*Image, IDs *[]string) error {
- for _, image := range images {
- // Get the layer info of topLayerID
- layer, err := i.imageruntime.store.Layer(topLayerID)
- if err != nil {
- return errors.Wrapf(err, "error getting layer info %q", topLayerID)
- }
- // Check if the parent of layer is equal to the image's top layer
- // If so add the image ID to the list of IDs and find the parent of
- // the top layer of the image ID added to the list
- // Since we are checking for parent, each top layer can only have one parent
- if layer.Parent == image.TopLayer() {
- *IDs = append(*IDs, image.ID())
- return i.historyLayerIDs(image.TopLayer(), images, IDs)
- }
- }
- return nil
-}
-
// Dangling returns a bool if the image is "dangling"
func (i *Image) Dangling() bool {
return len(i.Names()) == 0
@@ -846,7 +846,7 @@ func (i *Image) GetLabel(ctx context.Context, label string) (string, error) {
// Annotations returns the annotations of an image
func (i *Image) Annotations(ctx context.Context) (map[string]string, error) {
- manifest, manifestType, err := i.Manifest(ctx)
+ imageManifest, manifestType, err := i.Manifest(ctx)
if err != nil {
return nil, err
}
@@ -854,7 +854,7 @@ func (i *Image) Annotations(ctx context.Context) (map[string]string, error) {
switch manifestType {
case ociv1.MediaTypeImageManifest:
var m ociv1.Manifest
- if err := json.Unmarshal(manifest, &m); err == nil {
+ if err := json.Unmarshal(imageManifest, &m); err == nil {
for k, v := range m.Annotations {
annotations[k] = v
}
@@ -997,11 +997,15 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
if err != nil {
return nil, err
}
- defer policyContext.Destroy()
+ defer func() {
+ if err := policyContext.Destroy(); err != nil {
+ logrus.Errorf("failed to destroy policy context: %q", err)
+ }
+ }()
copyOptions := getCopyOptions(sc, writer, nil, nil, signingOptions, "", nil)
dest, err := is.Transport.ParseStoreReference(ir.store, reference)
if err != nil {
- errors.Wrapf(err, "error getting image reference for %q", reference)
+ return nil, errors.Wrapf(err, "error getting image reference for %q", reference)
}
_, err = cp.Image(ctx, policyContext, dest, src, copyOptions)
if err != nil {
@@ -1009,7 +1013,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
}
newImage, err := ir.NewFromLocal(reference)
if err == nil {
- defer newImage.newImageEvent(events.Import)
+ newImage.newImageEvent(events.Import)
}
return newImage, err
}
@@ -1143,13 +1147,15 @@ func areParentAndChild(parent, child *imgspecv1.Image) bool {
// GetParent returns the image ID of the parent. Return nil if a parent is not found.
func (i *Image) GetParent(ctx context.Context) (*Image, error) {
+ var childLayer *storage.Layer
images, err := i.imageruntime.GetImages()
if err != nil {
return nil, err
}
- childLayer, err := i.imageruntime.store.Layer(i.TopLayer())
- if err != nil {
- return nil, err
+ if i.TopLayer() != "" {
+ if childLayer, err = i.imageruntime.store.Layer(i.TopLayer()); err != nil {
+ return nil, err
+ }
}
// fetch the configuration for the child image
child, err := i.ociv1Image(ctx)
@@ -1161,11 +1167,23 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) {
continue
}
candidateLayer := img.TopLayer()
- // as a child, our top layer is either the candidate parent's
- // layer, or one that's derived from it, so skip over any
- // candidate image where we know that isn't the case
- if candidateLayer != childLayer.Parent && candidateLayer != childLayer.ID {
- continue
+ // as a child, our top layer, if we have one, is either the
+ // candidate parent's layer, or one that's derived from it, so
+ // skip over any candidate image where we know that isn't the
+ // case
+ if childLayer != nil {
+ // The child has at least one layer, so a parent would
+ // have a top layer that's either the same as the child's
+ // top layer or the top layer's recorded parent layer,
+ // which could be an empty value.
+ if candidateLayer != childLayer.Parent && candidateLayer != childLayer.ID {
+ continue
+ }
+ } else {
+ // The child has no layers, but the candidate does.
+ if candidateLayer != "" {
+ continue
+ }
}
// fetch the configuration for the candidate image
candidate, err := img.ociv1Image(ctx)
@@ -1204,14 +1222,22 @@ func (i *Image) getChildren(ctx context.Context, max int) ([]string, error) {
if img.ID() == i.ID() {
continue
}
- candidateLayer, err := img.Layer()
- if err != nil {
- return nil, err
- }
- // if this image's top layer is not our top layer, and is not
- // based on our top layer, we can skip it
- if candidateLayer.Parent != parentLayer && candidateLayer.ID != parentLayer {
- continue
+ if img.TopLayer() == "" {
+ if parentLayer != "" {
+ // this image has no layers, but we do, so
+ // it can't be derived from this one
+ continue
+ }
+ } else {
+ candidateLayer, err := img.Layer()
+ if err != nil {
+ return nil, err
+ }
+ // if this image's top layer is not our top layer, and is not
+ // based on our top layer, we can skip it
+ if candidateLayer.Parent != parentLayer && candidateLayer.ID != parentLayer {
+ continue
+ }
}
// fetch the configuration for the candidate image
candidate, err := img.ociv1Image(ctx)
@@ -1272,7 +1298,10 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
if err != nil {
return "", err
}
- return ociv1Img.History[0].Comment, nil
+ if len(ociv1Img.History) > 0 {
+ return ociv1Img.History[0].Comment, nil
+ }
+ return "", nil
}
// Save writes a container image to the filesystem
@@ -1330,7 +1359,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
return errors.Wrapf(err, "unable to save %q", source)
}
- defer i.newImageEvent(events.Save)
+ i.newImageEvent(events.Save)
return nil
}
@@ -1443,6 +1472,7 @@ func GetLayersMapWithImageInfo(imageruntime *Runtime) (map[string]*LayerInfo, er
if err != nil {
return nil, err
}
+ layerInfoMap[""] = &LayerInfo{}
for _, img := range imgs {
e, ok := layerInfoMap[img.TopLayer]
if !ok {