mirror of
https://github.com/containers/podman.git
synced 2025-06-07 07:45:34 +08:00
139 lines
4.0 KiB
Go
139 lines
4.0 KiB
Go
package image
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/docker/go-units"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
middleItem = "├── "
|
|
continueItem = "│ "
|
|
lastItem = "└── "
|
|
)
|
|
|
|
type tree struct {
|
|
img *Image
|
|
imageInfo *InfoImage
|
|
layerInfo map[string]*LayerInfo
|
|
sb *strings.Builder
|
|
}
|
|
|
|
// GenerateTree creates an image tree string representation for displaying it
|
|
// to the user.
|
|
func (i *Image) GenerateTree(whatRequires bool) (string, error) {
|
|
// Fetch map of image-layers, which is used for printing output.
|
|
layerInfo, err := GetLayersMapWithImageInfo(i.imageruntime)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "error while retrieving layers of image %q", i.InputName)
|
|
}
|
|
|
|
// Create an imageInfo and fill the image and layer info
|
|
imageInfo := &InfoImage{
|
|
ID: i.ID(),
|
|
Tags: i.Names(),
|
|
}
|
|
|
|
if err := BuildImageHierarchyMap(imageInfo, layerInfo, i.TopLayer()); err != nil {
|
|
return "", err
|
|
}
|
|
sb := &strings.Builder{}
|
|
tree := &tree{i, imageInfo, layerInfo, sb}
|
|
if err := tree.print(whatRequires); err != nil {
|
|
return "", err
|
|
}
|
|
return tree.string(), nil
|
|
}
|
|
|
|
func (t *tree) string() string {
|
|
return t.sb.String()
|
|
}
|
|
|
|
func (t *tree) print(whatRequires bool) error {
|
|
size, err := t.img.Size(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintf(t.sb, "Image ID: %s\n", t.imageInfo.ID[:12])
|
|
fmt.Fprintf(t.sb, "Tags: %s\n", t.imageInfo.Tags)
|
|
fmt.Fprintf(t.sb, "Size: %v\n", units.HumanSizeWithPrecision(float64(*size), 4))
|
|
if t.img.TopLayer() != "" {
|
|
fmt.Fprintf(t.sb, "Image Layers\n")
|
|
} else {
|
|
fmt.Fprintf(t.sb, "No Image Layers\n")
|
|
}
|
|
|
|
if !whatRequires {
|
|
// fill imageInfo with layers associated with image.
|
|
// the layers will be filled such that
|
|
// (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
|
|
// Build output from imageInfo into buffer
|
|
t.printImageHierarchy(t.imageInfo)
|
|
} else {
|
|
// fill imageInfo with layers associated with image.
|
|
// the layers will be filled such that
|
|
// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End)
|
|
// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End)
|
|
return t.printImageChildren(t.layerInfo, t.img.TopLayer(), "", true)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Stores all children layers which are created using given Image.
|
|
// Layers are stored as follows
|
|
// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End)
|
|
// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End)
|
|
func (t *tree) printImageChildren(layerMap map[string]*LayerInfo, layerID string, prefix string, last bool) error {
|
|
if layerID == "" {
|
|
return nil
|
|
}
|
|
ll, ok := layerMap[layerID]
|
|
if !ok {
|
|
return fmt.Errorf("lookup error: layerid %s, not found", layerID)
|
|
}
|
|
fmt.Fprint(t.sb, prefix)
|
|
|
|
//initialize intend with middleItem to reduce middleItem checks.
|
|
intend := middleItem
|
|
if !last {
|
|
// add continueItem i.e. '|' for next iteration prefix
|
|
prefix += continueItem
|
|
} else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 {
|
|
// The above condition ensure, alignment happens for node, which has more then 1 children.
|
|
// If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├──
|
|
intend = lastItem
|
|
prefix += " "
|
|
}
|
|
|
|
var tags string
|
|
if len(ll.RepoTags) > 0 {
|
|
tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags)
|
|
}
|
|
fmt.Fprintf(t.sb, "%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags)
|
|
for count, childID := range ll.ChildID {
|
|
if err := t.printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// prints the layers info of image
|
|
func (t *tree) printImageHierarchy(imageInfo *InfoImage) {
|
|
for count, l := range imageInfo.Layers {
|
|
var tags string
|
|
intend := middleItem
|
|
if len(l.RepoTags) > 0 {
|
|
tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags)
|
|
}
|
|
if count == len(imageInfo.Layers)-1 {
|
|
intend = lastItem
|
|
}
|
|
fmt.Fprintf(t.sb, "%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags)
|
|
}
|
|
}
|