mirror of
				https://github.com/containers/podman.git
				synced 2025-10-26 02:35:43 +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)
 | |
| 	}
 | |
| }
 | 
