Merge pull request #7215 from vrothberg/flatten-the-curve
images: speed up lists
This commit is contained in:
		
						commit
						3173a18f6f
					
				| 
						 | 
				
			
			@ -29,6 +29,26 @@ func CreatedBeforeFilter(createTime time.Time) ResultFilter {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IntermediateFilter returns filter for intermediate images (i.e., images
 | 
			
		||||
// with children and no tags).
 | 
			
		||||
func (ir *Runtime) IntermediateFilter(ctx context.Context, images []*Image) (ResultFilter, error) {
 | 
			
		||||
	tree, err := ir.layerTree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return func(i *Image) bool {
 | 
			
		||||
		if len(i.Names()) > 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		children, err := tree.children(ctx, i, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Error(err.Error())
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return len(children) == 0
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreatedAfterFilter allows you to filter on images created after
 | 
			
		||||
// the given time.Time
 | 
			
		||||
func CreatedAfterFilter(createTime time.Time) ResultFilter {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -859,26 +859,6 @@ func (i *Image) Dangling() bool {
 | 
			
		|||
	return len(i.Names()) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Intermediate returns true if the image is cache or intermediate image.
 | 
			
		||||
// Cache image has parent and child.
 | 
			
		||||
func (i *Image) Intermediate(ctx context.Context) (bool, error) {
 | 
			
		||||
	parent, err := i.IsParent(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if !parent {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	img, err := i.GetParent(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if img != nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// User returns the image's user
 | 
			
		||||
func (i *Image) User(ctx context.Context) (string, error) {
 | 
			
		||||
	imgInspect, err := i.inspect(ctx, false)
 | 
			
		||||
| 
						 | 
				
			
			@ -1217,7 +1197,7 @@ func splitString(input string) string {
 | 
			
		|||
// the parent of any other layer in store. Double check that image with that
 | 
			
		||||
// layer exists as well.
 | 
			
		||||
func (i *Image) IsParent(ctx context.Context) (bool, error) {
 | 
			
		||||
	children, err := i.getChildren(ctx, 1)
 | 
			
		||||
	children, err := i.getChildren(ctx, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Cause(err) == ErrImageIsBareList {
 | 
			
		||||
			return false, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -1292,63 +1272,16 @@ 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()
 | 
			
		||||
	tree, err := i.imageruntime.layerTree()
 | 
			
		||||
	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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Cause(err) == ErrImageIsBareList {
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, img := range images {
 | 
			
		||||
		if img.ID() == i.ID() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		candidateLayer := img.TopLayer()
 | 
			
		||||
		// 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)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// compare them
 | 
			
		||||
		if areParentAndChild(candidate, child) {
 | 
			
		||||
			return img, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil
 | 
			
		||||
	return tree.parent(ctx, i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChildren returns a list of the imageIDs that depend on the image
 | 
			
		||||
func (i *Image) GetChildren(ctx context.Context) ([]string, error) {
 | 
			
		||||
	children, err := i.getChildren(ctx, 0)
 | 
			
		||||
	children, err := i.getChildren(ctx, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Cause(err) == ErrImageIsBareList {
 | 
			
		||||
			return nil, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -1358,62 +1291,15 @@ func (i *Image) GetChildren(ctx context.Context) ([]string, error) {
 | 
			
		|||
	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()
 | 
			
		||||
// getChildren returns a list of imageIDs that depend on the image. If all is
 | 
			
		||||
// false, only the first child image is returned.
 | 
			
		||||
func (i *Image) getChildren(ctx context.Context, all bool) ([]string, error) {
 | 
			
		||||
	tree, err := i.imageruntime.layerTree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// fetch the configuration for the parent image
 | 
			
		||||
	parent, err := i.ociv1Image(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	parentLayer := i.TopLayer()
 | 
			
		||||
 | 
			
		||||
	for _, img := range images {
 | 
			
		||||
		if img.ID() == i.ID() {
 | 
			
		||||
			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)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// compare them
 | 
			
		||||
		if areParentAndChild(parent, candidate) {
 | 
			
		||||
			children = append(children, img.ID())
 | 
			
		||||
		}
 | 
			
		||||
		// if we're not building an exhaustive list, maybe we're done?
 | 
			
		||||
		if max > 0 && len(children) >= max {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return children, nil
 | 
			
		||||
	return tree.children(ctx, i, all)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InputIsID returns a bool if the user input for an image
 | 
			
		||||
| 
						 | 
				
			
			@ -1670,6 +1556,7 @@ type LayerInfo struct {
 | 
			
		|||
 | 
			
		||||
// GetLayersMapWithImageInfo returns map of image-layers, with associated information like RepoTags, parent and list of child layers.
 | 
			
		||||
func GetLayersMapWithImageInfo(imageruntime *Runtime) (map[string]*LayerInfo, error) {
 | 
			
		||||
	// TODO: evaluate if we can reuse `layerTree` here.
 | 
			
		||||
 | 
			
		||||
	// Memory allocated to store map of layers with key LayerID.
 | 
			
		||||
	// Map will build dependency chain with ParentID and ChildID(s)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,222 @@
 | 
			
		|||
package image
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// layerTree is an internal representation of local layers.
 | 
			
		||||
type layerTree struct {
 | 
			
		||||
	// nodes is the actual layer tree with layer IDs being keys.
 | 
			
		||||
	nodes map[string]*layerNode
 | 
			
		||||
	// ociCache is a cache for Image.ID -> OCI Image. Translations are done
 | 
			
		||||
	// on-demand.
 | 
			
		||||
	ociCache map[string]*ociv1.Image
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// node returns a layerNode for the specified layerID.
 | 
			
		||||
func (t *layerTree) node(layerID string) *layerNode {
 | 
			
		||||
	node, exists := t.nodes[layerID]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		node = &layerNode{}
 | 
			
		||||
		t.nodes[layerID] = node
 | 
			
		||||
	}
 | 
			
		||||
	return node
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// toOCI returns an OCI image for the specified image.
 | 
			
		||||
func (t *layerTree) toOCI(ctx context.Context, i *Image) (*ociv1.Image, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	oci, exists := t.ociCache[i.ID()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		oci, err = i.ociv1Image(ctx)
 | 
			
		||||
		t.ociCache[i.ID()] = oci
 | 
			
		||||
	}
 | 
			
		||||
	return oci, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// layerNode is a node in a layerTree.  It's ID is the key in a layerTree.
 | 
			
		||||
type layerNode struct {
 | 
			
		||||
	children []*layerNode
 | 
			
		||||
	images   []*Image
 | 
			
		||||
	parent   *layerNode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// layerTree extracts a layerTree from the layers in the local storage and
 | 
			
		||||
// relates them to the specified images.
 | 
			
		||||
func (ir *Runtime) layerTree() (*layerTree, error) {
 | 
			
		||||
	layers, err := ir.store.Layers()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	images, err := ir.GetImages()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree := layerTree{
 | 
			
		||||
		nodes:    make(map[string]*layerNode),
 | 
			
		||||
		ociCache: make(map[string]*ociv1.Image),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First build a tree purely based on layer information.
 | 
			
		||||
	for _, layer := range layers {
 | 
			
		||||
		node := tree.node(layer.ID)
 | 
			
		||||
		if layer.Parent == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		parent := tree.node(layer.Parent)
 | 
			
		||||
		node.parent = parent
 | 
			
		||||
		parent.children = append(parent.children, node)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now assign the images to each (top) layer.
 | 
			
		||||
	for i := range images {
 | 
			
		||||
		img := images[i] // do not leak loop variable outside the scope
 | 
			
		||||
		topLayer := img.TopLayer()
 | 
			
		||||
		if topLayer == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		node, exists := tree.nodes[topLayer]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return nil, errors.Errorf("top layer %s of image %s not found in layer tree", img.TopLayer(), img.ID())
 | 
			
		||||
		}
 | 
			
		||||
		node.images = append(node.images, img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &tree, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// children returns the image IDs of children . Child images are images
 | 
			
		||||
// with either the same top layer as parent or parent being the true parent
 | 
			
		||||
// layer.  Furthermore, the history of the parent and child images must match
 | 
			
		||||
// with the parent having one history item less.
 | 
			
		||||
// If all is true, all images are returned.  Otherwise, the first image is
 | 
			
		||||
// returned.
 | 
			
		||||
func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]string, error) {
 | 
			
		||||
	if parent.TopLayer() == "" {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var children []string
 | 
			
		||||
 | 
			
		||||
	parentNode, exists := t.nodes[parent.TopLayer()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return nil, errors.Errorf("layer not found in layer tree: %q", parent.TopLayer())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parentID := parent.ID()
 | 
			
		||||
	parentOCI, err := t.toOCI(ctx, parent)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// checkParent returns true if child and parent are in such a relation.
 | 
			
		||||
	checkParent := func(child *Image) (bool, error) {
 | 
			
		||||
		if parentID == child.ID() {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
		childOCI, err := t.toOCI(ctx, child)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		// History check.
 | 
			
		||||
		return areParentAndChild(parentOCI, childOCI), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// addChildrenFrom adds child images of parent to children.  Returns
 | 
			
		||||
	// true if any image is a child of parent.
 | 
			
		||||
	addChildrenFromNode := func(node *layerNode) (bool, error) {
 | 
			
		||||
		foundChildren := false
 | 
			
		||||
		for _, childImage := range node.images {
 | 
			
		||||
			isChild, err := checkParent(childImage)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return foundChildren, err
 | 
			
		||||
			}
 | 
			
		||||
			if isChild {
 | 
			
		||||
				foundChildren = true
 | 
			
		||||
				children = append(children, childImage.ID())
 | 
			
		||||
				if all {
 | 
			
		||||
					return foundChildren, nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return foundChildren, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First check images where parent's top layer is also the parent
 | 
			
		||||
	// layer.
 | 
			
		||||
	for _, childNode := range parentNode.children {
 | 
			
		||||
		found, err := addChildrenFromNode(childNode)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if found && all {
 | 
			
		||||
			return children, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now check images with the same top layer.
 | 
			
		||||
	if _, err := addChildrenFromNode(parentNode); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return children, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parent returns the parent image or nil if no parent image could be found.
 | 
			
		||||
func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) {
 | 
			
		||||
	if child.TopLayer() == "" {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	node, exists := t.nodes[child.TopLayer()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return nil, errors.Errorf("layer not found in layer tree: %q", child.TopLayer())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	childOCI, err := t.toOCI(ctx, child)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check images from the parent node (i.e., parent layer) and images
 | 
			
		||||
	// with the same layer (i.e., same top layer).
 | 
			
		||||
	childID := child.ID()
 | 
			
		||||
	images := node.images
 | 
			
		||||
	if node.parent != nil {
 | 
			
		||||
		images = append(images, node.parent.images...)
 | 
			
		||||
	}
 | 
			
		||||
	for _, parent := range images {
 | 
			
		||||
		if parent.ID() == childID {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		parentOCI, err := t.toOCI(ctx, parent)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// History check.
 | 
			
		||||
		if areParentAndChild(parentOCI, childOCI) {
 | 
			
		||||
			return parent, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hasChildrenAndParent returns true if the specified image has children and a
 | 
			
		||||
// parent.
 | 
			
		||||
func (t *layerTree) hasChildrenAndParent(ctx context.Context, i *Image) (bool, error) {
 | 
			
		||||
	children, err := t.children(ctx, i, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(children) == 0 {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	parent, err := t.parent(ctx, i)
 | 
			
		||||
	return parent != nil, err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +66,12 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree, err := ir.layerTree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, i := range allImages {
 | 
			
		||||
		// filter the images based on this.
 | 
			
		||||
		for _, filterFunc := range filterFuncs {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,8 +91,9 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//skip the cache or intermediate images
 | 
			
		||||
		intermediate, err := i.Intermediate(ctx)
 | 
			
		||||
		// skip the cache (i.e., with parent) and intermediate (i.e.,
 | 
			
		||||
		// with children) images
 | 
			
		||||
		intermediate, err := tree.hasChildrenAndParent(ctx, i)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,20 +102,14 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
 | 
			
		|||
	if query.All {
 | 
			
		||||
		return images, nil
 | 
			
		||||
	}
 | 
			
		||||
	returnImages := []*image.Image{}
 | 
			
		||||
	for _, img := range images {
 | 
			
		||||
		if len(img.Names()) == 0 {
 | 
			
		||||
			parent, err := img.IsParent(r.Context())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			if parent {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		returnImages = append(returnImages, img)
 | 
			
		||||
 | 
			
		||||
	filter, err := runtime.ImageRuntime().IntermediateFilter(r.Context(), images)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return returnImages, nil
 | 
			
		||||
	images = image.FilterImages(images, []image.ResultFilter{filter})
 | 
			
		||||
 | 
			
		||||
	return images, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetImage(r *http.Request, name string) (*image.Image, error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,14 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !opts.All {
 | 
			
		||||
		filter, err := ir.Libpod.ImageRuntime().IntermediateFilter(ctx, images)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		images = libpodImage.FilterImages(images, []libpodImage.ResultFilter{filter})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	summaries := []*entities.ImageSummary{}
 | 
			
		||||
	for _, img := range images {
 | 
			
		||||
		var repoTags []string
 | 
			
		||||
| 
						 | 
				
			
			@ -32,15 +40,6 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
 | 
			
		|||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			if len(img.Names()) == 0 {
 | 
			
		||||
				parent, err := img.IsParent(ctx)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
				if parent {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		digests := make([]string, len(img.Digests()))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue