diff options
Diffstat (limited to 'libpod/image')
-rw-r--r-- | libpod/image/image.go | 107 | ||||
-rw-r--r-- | libpod/image/parts.go | 4 | ||||
-rw-r--r-- | libpod/image/pull.go | 36 |
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") } |