summaryrefslogtreecommitdiff
path: root/libpod/image
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/image')
-rw-r--r--libpod/image/image.go107
-rw-r--r--libpod/image/parts.go4
-rw-r--r--libpod/image/pull.go36
3 files changed, 137 insertions, 10 deletions
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5dd2c57f3..b5c4c537f 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -59,6 +59,9 @@ type Runtime struct {
SignaturePolicyPath string
}
+// 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")
+
// NewImageRuntimeFromStore creates an ImageRuntime based on a provided store
func NewImageRuntimeFromStore(store storage.Store) *Runtime {
return &Runtime{
@@ -333,9 +336,39 @@ func (i *Image) TopLayer() string {
// Remove an image; container removal for the image must be done
// outside the context of images
+// TODO: the force param does nothing as of now. Need to move container
+// handling logic here eventually.
func (i *Image) Remove(force bool) error {
- _, err := i.imageruntime.store.DeleteImage(i.ID(), true)
- return err
+ parent, err := i.GetParent()
+ if err != nil {
+ return err
+ }
+ if _, err := i.imageruntime.store.DeleteImage(i.ID(), true); err != nil {
+ return err
+ }
+ for parent != nil {
+ nextParent, err := parent.GetParent()
+ if err != nil {
+ return err
+ }
+ children, err := parent.GetChildren()
+ if err != nil {
+ return err
+ }
+ // Do not remove if image is a base image and is not untagged, or if
+ // the image has more children.
+ if (nextParent == nil && len(parent.Names()) > 0) || len(children) > 0 {
+ return nil
+ }
+ id := parent.ID()
+ if _, err := i.imageruntime.store.DeleteImage(id, true); err != nil {
+ logrus.Debugf("unable to remove intermediate image %q: %v", id, err)
+ } else {
+ fmt.Println(id)
+ }
+ parent = nextParent
+ }
+ return nil
}
// Decompose an Image
@@ -902,7 +935,7 @@ func (i *Image) MatchRepoTag(input string) (string, error) {
}
}
if maxCount == 0 {
- return "", errors.Errorf("unable to match user input to any specific repotag")
+ return "", ErrRepoTagNotFound
}
if len(results[maxCount]) > 1 {
return "", errors.Errorf("user input matched multiple repotags for the image")
@@ -916,6 +949,68 @@ func splitString(input string) string {
return split[len(split)-1]
}
+// IsParent goes through the layers in the store and checks if i.TopLayer is
+// the parent of any other layer in store. Double check that image with that
+// layer exists as well.
+func (i *Image) IsParent() (bool, error) {
+ children, err := i.GetChildren()
+ if err != nil {
+ return false, err
+ }
+ return len(children) > 0, nil
+}
+
+// GetParent returns the image ID of the parent. Return nil if a parent is not found.
+func (i *Image) GetParent() (*Image, error) {
+ images, err := i.imageruntime.GetImages()
+ if err != nil {
+ return nil, err
+ }
+ layer, err := i.imageruntime.store.Layer(i.TopLayer())
+ if err != nil {
+ return nil, err
+ }
+ for _, img := range images {
+ if img.TopLayer() == layer.Parent {
+ return img, nil
+ }
+ }
+ return nil, nil
+}
+
+// GetChildren returns a list of the imageIDs that depend on the image
+func (i *Image) GetChildren() ([]string, error) {
+ var children []string
+ images, err := i.imageruntime.GetImages()
+ if err != nil {
+ return nil, err
+ }
+ layers, err := i.imageruntime.store.Layers()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, layer := range layers {
+ if layer.Parent == i.TopLayer() {
+ if imageID := getImageOfTopLayer(images, layer.ID); len(imageID) > 0 {
+ children = append(children, imageID...)
+ }
+ }
+ }
+ return children, nil
+}
+
+// getImageOfTopLayer returns the image ID where layer is the top layer of the image
+func getImageOfTopLayer(images []*Image, layer string) []string {
+ var matches []string
+ for _, img := range images {
+ if img.TopLayer() == layer {
+ matches = append(matches, img.ID())
+ }
+ }
+ return matches
+}
+
// InputIsID returns a bool if the user input for an image
// is the image's partial or full id
func (i *Image) InputIsID() bool {
@@ -960,3 +1055,9 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
}
return ociv1Img.History[0].Comment, nil
}
+
+// HasShaInInputName returns a bool as to whether the user provide an image name that includes
+// a reference to a specific sha
+func (i *Image) HasShaInInputName() bool {
+ return strings.Contains(i.InputName, "@sha256:")
+}
diff --git a/libpod/image/parts.go b/libpod/image/parts.go
index 979f223fc..07a119c28 100644
--- a/libpod/image/parts.go
+++ b/libpod/image/parts.go
@@ -2,6 +2,7 @@ package image
import (
"fmt"
+ "strings"
"github.com/containers/image/docker/reference"
)
@@ -33,6 +34,9 @@ func decompose(input string) (imageParts, error) {
}
if !isTagged {
tag = "latest"
+ if strings.Contains(input, "@sha256:") {
+ tag = "none"
+ }
} else {
tag = ntag.Tag()
}
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index 48513509d..a5a398eb1 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -49,9 +49,10 @@ var (
)
type pullStruct struct {
- image string
- srcRef types.ImageReference
- dstRef types.ImageReference
+ image string
+ srcRef types.ImageReference
+ dstRef types.ImageReference
+ shaPullName string
}
func (ir *Runtime) getPullStruct(srcRef types.ImageReference, destName string) (*pullStruct, error) {
@@ -247,13 +248,22 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
// createNamesToPull looks at a decomposed image and determines the possible
// images names to try pulling in combination with the registries.conf file as well
func (i *Image) createNamesToPull() ([]*pullStruct, error) {
- var pullNames []*pullStruct
+ var (
+ pullNames []*pullStruct
+ imageName string
+ )
+
decomposedImage, err := decompose(i.InputName)
if err != nil {
return nil, err
}
if decomposedImage.hasRegistry {
- srcRef, err := alltransports.ParseImageName(decomposedImage.assembleWithTransport())
+ if i.HasShaInInputName() {
+ imageName = fmt.Sprintf("%s%s", decomposedImage.transport, i.InputName)
+ } else {
+ imageName = decomposedImage.assembleWithTransport()
+ }
+ srcRef, err := alltransports.ParseImageName(imageName)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse '%s'", i.InputName)
}
@@ -261,6 +271,9 @@ func (i *Image) createNamesToPull() ([]*pullStruct, error) {
image: i.InputName,
srcRef: srcRef,
}
+ if i.HasShaInInputName() {
+ ps.shaPullName = decomposedImage.assemble()
+ }
pullNames = append(pullNames, &ps)
} else {
@@ -275,7 +288,11 @@ func (i *Image) createNamesToPull() ([]*pullStruct, error) {
}
for _, registry := range searchRegistries {
decomposedImage.registry = registry
- srcRef, err := alltransports.ParseImageName(decomposedImage.assembleWithTransport())
+ imageName := decomposedImage.assembleWithTransport()
+ if i.HasShaInInputName() {
+ imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, i.InputName)
+ }
+ srcRef, err := alltransports.ParseImageName(imageName)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse '%s'", i.InputName)
}
@@ -287,8 +304,13 @@ func (i *Image) createNamesToPull() ([]*pullStruct, error) {
}
}
+ // Here we construct the destination reference
for _, pStruct := range pullNames {
- destRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, pStruct.image)
+ dstName := pStruct.image
+ if pStruct.shaPullName != "" {
+ dstName = pStruct.shaPullName
+ }
+ destRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, dstName)
if err != nil {
return nil, errors.Wrapf(err, "error parsing dest reference name")
}