Files
Urvashi Mohnani 7c8c945496 Vendor in latest c/common
Pull in updates made to the filters code for
images. Filters now perform an AND operation
except for th reference filter which does an
OR operation for positive case but an AND operation
for negative cases.

Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
2024-01-25 11:10:41 -05:00

121 lines
3.1 KiB
Go

//go:build !remote
package libimage
import (
"context"
"fmt"
"strings"
"github.com/disiqueira/gotree/v3"
"github.com/docker/go-units"
)
// Tree generates a tree for the specified image and its layers. Use
// `traverseChildren` to traverse the layers of all children. By default, only
// layers of the image are printed.
func (i *Image) Tree(traverseChildren bool) (string, error) {
// NOTE: a string builder prevents us from copying to much data around
// and compile the string when and where needed.
sb := &strings.Builder{}
// First print the pretty header for the target image.
size, err := i.Size()
if err != nil {
return "", err
}
repoTags, err := i.RepoTags()
if err != nil {
return "", err
}
fmt.Fprintf(sb, "Image ID: %s\n", i.ID()[:12])
fmt.Fprintf(sb, "Tags: %s\n", repoTags)
fmt.Fprintf(sb, "Size: %v\n", units.HumanSizeWithPrecision(float64(size), 4))
if i.TopLayer() != "" {
fmt.Fprintf(sb, "Image Layers")
} else {
fmt.Fprintf(sb, "No Image Layers")
}
layerTree, err := i.runtime.layerTree(context.Background(), nil)
if err != nil {
return "", err
}
imageNode := layerTree.node(i.TopLayer())
// Traverse the entire tree down to all children.
if traverseChildren {
tree := gotree.New(sb.String())
if err := imageTreeTraverseChildren(imageNode, tree); err != nil {
return "", err
}
return tree.Print(), nil
}
// Walk all layers of the image and assemble their data. Note that the
// tree is constructed in reverse order to remain backwards compatible
// with Podman.
contents := []string{}
for parentNode := imageNode; parentNode != nil; parentNode = parentNode.parent {
if parentNode.layer == nil {
break // we're done
}
var tags string
repoTags, err := parentNode.repoTags()
if err != nil {
return "", err
}
if len(repoTags) > 0 {
tags = fmt.Sprintf(" Top Layer of: %s", repoTags)
}
content := fmt.Sprintf("ID: %s Size: %7v%s", parentNode.layer.ID[:12], units.HumanSizeWithPrecision(float64(parentNode.layer.UncompressedSize), 4), tags)
contents = append(contents, content)
}
contents = append(contents, sb.String())
tree := gotree.New(contents[len(contents)-1])
for i := len(contents) - 2; i >= 0; i-- {
tree.Add(contents[i])
}
return tree.Print(), nil
}
func imageTreeTraverseChildren(node *layerNode, parent gotree.Tree) error {
if node.layer == nil {
return nil
}
var tags string
repoTags, err := node.repoTags()
if err != nil {
return err
}
if len(repoTags) > 0 {
tags = fmt.Sprintf(" Top Layer of: %s", repoTags)
}
content := fmt.Sprintf("ID: %s Size: %7v%s", node.layer.ID[:12], units.HumanSizeWithPrecision(float64(node.layer.UncompressedSize), 4), tags)
var newTree gotree.Tree
if node.parent == nil || len(node.parent.children) <= 1 {
// No parent or no siblings, so we can go linear.
parent.Add(content)
newTree = parent
} else {
// Each siblings gets a new tree, so we can branch.
newTree = gotree.New(content)
parent.AddTree(newTree)
}
for i := range node.children {
child := node.children[i]
if err := imageTreeTraverseChildren(child, newTree); err != nil {
return err
}
}
return nil
}