mirror of
https://github.com/containers/podman.git
synced 2025-11-03 07:47:19 +08:00
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>
121 lines
3.1 KiB
Go
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
|
|
}
|