summaryrefslogtreecommitdiff
path: root/libpod/image/tree.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/image/tree.go')
-rw-r--r--libpod/image/tree.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/libpod/image/tree.go b/libpod/image/tree.go
new file mode 100644
index 000000000..c7c69462f
--- /dev/null
+++ b/libpod/image/tree.go
@@ -0,0 +1,138 @@
+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)
+ }
+}