github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/libpod/image/layer_tree.go (about)

     1  package image
     2  
     3  import (
     4  	"context"
     5  
     6  	ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
     7  	"github.com/pkg/errors"
     8  	"github.com/sirupsen/logrus"
     9  )
    10  
    11  // layerTree is an internal representation of local layers.
    12  type layerTree struct {
    13  	// nodes is the actual layer tree with layer IDs being keys.
    14  	nodes map[string]*layerNode
    15  	// ociCache is a cache for Image.ID -> OCI Image. Translations are done
    16  	// on-demand.
    17  	ociCache map[string]*ociv1.Image
    18  }
    19  
    20  // node returns a layerNode for the specified layerID.
    21  func (t *layerTree) node(layerID string) *layerNode {
    22  	node, exists := t.nodes[layerID]
    23  	if !exists {
    24  		node = &layerNode{}
    25  		t.nodes[layerID] = node
    26  	}
    27  	return node
    28  }
    29  
    30  // toOCI returns an OCI image for the specified image.
    31  func (t *layerTree) toOCI(ctx context.Context, i *Image) (*ociv1.Image, error) {
    32  	var err error
    33  	oci, exists := t.ociCache[i.ID()]
    34  	if !exists {
    35  		oci, err = i.ociv1Image(ctx)
    36  		if err != nil {
    37  			logrus.Errorf("%v, ignoring the error", err)
    38  			return nil, nil
    39  		}
    40  		t.ociCache[i.ID()] = oci
    41  	}
    42  	return oci, err
    43  }
    44  
    45  // layerNode is a node in a layerTree.  It's ID is the key in a layerTree.
    46  type layerNode struct {
    47  	children []*layerNode
    48  	images   []*Image
    49  	parent   *layerNode
    50  }
    51  
    52  // layerTree extracts a layerTree from the layers in the local storage and
    53  // relates them to the specified images.
    54  func (ir *Runtime) layerTree() (*layerTree, error) {
    55  	layers, err := ir.store.Layers()
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	images, err := ir.GetImages()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	tree := layerTree{
    66  		nodes:    make(map[string]*layerNode),
    67  		ociCache: make(map[string]*ociv1.Image),
    68  	}
    69  
    70  	// First build a tree purely based on layer information.
    71  	for _, layer := range layers {
    72  		node := tree.node(layer.ID)
    73  		if layer.Parent == "" {
    74  			continue
    75  		}
    76  		parent := tree.node(layer.Parent)
    77  		node.parent = parent
    78  		parent.children = append(parent.children, node)
    79  	}
    80  
    81  	// Now assign the images to each (top) layer.
    82  	for i := range images {
    83  		img := images[i] // do not leak loop variable outside the scope
    84  		topLayer := img.TopLayer()
    85  		if topLayer == "" {
    86  			continue
    87  		}
    88  		node, exists := tree.nodes[topLayer]
    89  		if !exists {
    90  			return nil, errors.Errorf("top layer %s of image %s not found in layer tree", img.TopLayer(), img.ID())
    91  		}
    92  		node.images = append(node.images, img)
    93  	}
    94  
    95  	return &tree, nil
    96  }
    97  
    98  // children returns the image IDs of children . Child images are images
    99  // with either the same top layer as parent or parent being the true parent
   100  // layer.  Furthermore, the history of the parent and child images must match
   101  // with the parent having one history item less.
   102  // If all is true, all images are returned.  Otherwise, the first image is
   103  // returned.
   104  func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]string, error) {
   105  	if parent.TopLayer() == "" {
   106  		return nil, nil
   107  	}
   108  
   109  	var children []string
   110  
   111  	parentNode, exists := t.nodes[parent.TopLayer()]
   112  	if !exists {
   113  		return nil, errors.Errorf("layer not found in layer tree: %q", parent.TopLayer())
   114  	}
   115  
   116  	parentID := parent.ID()
   117  	parentOCI, err := t.toOCI(ctx, parent)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// checkParent returns true if child and parent are in such a relation.
   123  	checkParent := func(child *Image) (bool, error) {
   124  		if parentID == child.ID() {
   125  			return false, nil
   126  		}
   127  		childOCI, err := t.toOCI(ctx, child)
   128  		if err != nil {
   129  			return false, err
   130  		}
   131  		// History check.
   132  		return areParentAndChild(parentOCI, childOCI), nil
   133  	}
   134  
   135  	// addChildrenFrom adds child images of parent to children.  Returns
   136  	// true if any image is a child of parent.
   137  	addChildrenFromNode := func(node *layerNode) (bool, error) {
   138  		foundChildren := false
   139  		for _, childImage := range node.images {
   140  			isChild, err := checkParent(childImage)
   141  			if err != nil {
   142  				return foundChildren, err
   143  			}
   144  			if isChild {
   145  				foundChildren = true
   146  				children = append(children, childImage.ID())
   147  				if all {
   148  					return foundChildren, nil
   149  				}
   150  			}
   151  		}
   152  		return foundChildren, nil
   153  	}
   154  
   155  	// First check images where parent's top layer is also the parent
   156  	// layer.
   157  	for _, childNode := range parentNode.children {
   158  		found, err := addChildrenFromNode(childNode)
   159  		if err != nil {
   160  			return nil, err
   161  		}
   162  		if found && all {
   163  			return children, nil
   164  		}
   165  	}
   166  
   167  	// Now check images with the same top layer.
   168  	if _, err := addChildrenFromNode(parentNode); err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	return children, nil
   173  }
   174  
   175  // parent returns the parent image or nil if no parent image could be found.
   176  func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) {
   177  	if child.TopLayer() == "" {
   178  		return nil, nil
   179  	}
   180  
   181  	node, exists := t.nodes[child.TopLayer()]
   182  	if !exists {
   183  		return nil, errors.Errorf("layer not found in layer tree: %q", child.TopLayer())
   184  	}
   185  
   186  	childOCI, err := t.toOCI(ctx, child)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// Check images from the parent node (i.e., parent layer) and images
   192  	// with the same layer (i.e., same top layer).
   193  	childID := child.ID()
   194  	images := node.images
   195  	if node.parent != nil {
   196  		images = append(images, node.parent.images...)
   197  	}
   198  	for _, parent := range images {
   199  		if parent.ID() == childID {
   200  			continue
   201  		}
   202  		parentOCI, err := t.toOCI(ctx, parent)
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		// History check.
   207  		if areParentAndChild(parentOCI, childOCI) {
   208  			return parent, nil
   209  		}
   210  	}
   211  
   212  	return nil, nil
   213  }
   214  
   215  // hasChildrenAndParent returns true if the specified image has children and a
   216  // parent.
   217  func (t *layerTree) hasChildrenAndParent(ctx context.Context, i *Image) (bool, error) {
   218  	children, err := t.children(ctx, i, false)
   219  	if err != nil {
   220  		return false, err
   221  	}
   222  	if len(children) == 0 {
   223  		return false, nil
   224  	}
   225  	parent, err := t.parent(ctx, i)
   226  	return parent != nil, err
   227  }