summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-03-14 09:53:49 -0700
committerGitHub <noreply@github.com>2019-03-14 09:53:49 -0700
commitfc5951ad55b5d9c4e2cc9ca0188c1adf8a12a3bb (patch)
tree06c269da2e787b6ed77cb9be5e0d835d336109bb /cmd/podman
parent38d2b952fb5e285183795e2b2533e83bfdf96615 (diff)
parenta4b3b9ffbb9bf4cac1863ac8c3b5dbf7748f9fdd (diff)
downloadpodman-fc5951ad55b5d9c4e2cc9ca0188c1adf8a12a3bb.tar.gz
podman-fc5951ad55b5d9c4e2cc9ca0188c1adf8a12a3bb.tar.bz2
podman-fc5951ad55b5d9c4e2cc9ca0188c1adf8a12a3bb.zip
Merge pull request #1642 from kunalkushwaha/image-tree
Tree implementation for podman images
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/cliconfig/config.go5
-rw-r--r--cmd/podman/image.go1
-rw-r--r--cmd/podman/tree.go190
3 files changed, 196 insertions, 0 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index ec08eedb5..cb9d9a338 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -66,6 +66,11 @@ type TagValues struct {
PodmanCommand
}
+type TreeValues struct {
+ PodmanCommand
+ WhatRequires bool
+}
+
type WaitValues struct {
PodmanCommand
Interval uint
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index 0e980d4d3..66c141686 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -73,6 +73,7 @@ var imageSubCommands = []*cobra.Command{
_rmSubCommand,
_saveCommand,
_tagCommand,
+ _treeCommand,
}
func init() {
diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
new file mode 100644
index 000000000..ebda18cdb
--- /dev/null
+++ b/cmd/podman/tree.go
@@ -0,0 +1,190 @@
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/image"
+ units "github.com/docker/go-units"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+const (
+ middleItem = "├── "
+ continueItem = "│ "
+ lastItem = "└── "
+)
+
+var (
+ treeCommand cliconfig.TreeValues
+
+ treeDescription = "Prints layer hierarchy of an image in a tree format"
+ _treeCommand = &cobra.Command{
+ Use: "tree",
+ Short: treeDescription,
+ Long: treeDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ treeCommand.InputArgs = args
+ treeCommand.GlobalFlags = MainGlobalOpts
+ return treeCmd(&treeCommand)
+ },
+ Example: "podman image tree alpine:latest",
+ }
+)
+
+func init() {
+ treeCommand.Command = _treeCommand
+ treeCommand.SetUsageTemplate(UsageTemplate())
+ treeCommand.Flags().BoolVar(&treeCommand.WhatRequires, "whatrequires", false, "Show all child images and layers of the specified image")
+}
+
+// infoImage keep information of Image along with all associated layers
+type infoImage struct {
+ // id of image
+ id string
+ // tags of image
+ tags []string
+ // layers stores all layers of image.
+ layers []image.LayerInfo
+}
+
+func treeCmd(c *cliconfig.TreeValues) error {
+ args := c.InputArgs
+ if len(args) == 0 {
+ return errors.Errorf("an image name must be specified")
+ }
+ if len(args) > 1 {
+ return errors.Errorf("you must provide at most 1 argument")
+ }
+
+ runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ img, err := runtime.ImageRuntime().NewFromLocal(args[0])
+ if err != nil {
+ return err
+ }
+
+ // Fetch map of image-layers, which is used for printing output.
+ layerInfoMap, err := image.GetLayersMapWithImageInfo(runtime.ImageRuntime())
+ if err != nil {
+ return errors.Wrapf(err, "error while retriving layers of image %q", img.InputName)
+ }
+
+ // Create an imageInfo and fill the image and layer info
+ imageInfo := &infoImage{
+ id: img.ID(),
+ tags: img.Names(),
+ }
+
+ size, err := img.Size(context.Background())
+ if err != nil {
+ return errors.Wrapf(err, "error while retriving image size")
+ }
+ fmt.Printf("Image ID: %s\n", imageInfo.id[:12])
+ fmt.Printf("Tags:\t %s\n", imageInfo.tags)
+ fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4))
+ fmt.Printf(fmt.Sprintf("Image Layers\n"))
+
+ if !c.WhatRequires {
+ // fill imageInfo with layers associated with image.
+ // the layers will be filled such that
+ // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
+ err := buildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer())
+ if err != nil {
+ return err
+ }
+ // Build output from imageInfo into buffer
+ printImageHierarchy(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)
+ err := printImageChildren(layerInfoMap, img.TopLayer(), "", true)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Stores hierarchy of images such that all parent layers using which image is built are stored in imageInfo
+// Layers are added such that (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End)
+func buildImageHierarchyMap(imageInfo *infoImage, layerMap map[string]*image.LayerInfo, layerID string) error {
+ if layerID == "" {
+ return nil
+ }
+ ll, ok := layerMap[layerID]
+ if !ok {
+ return fmt.Errorf("lookup error: layerid %s not found", layerID)
+ }
+ if err := buildImageHierarchyMap(imageInfo, layerMap, ll.ParentID); err != nil {
+ return err
+ }
+
+ imageInfo.layers = append(imageInfo.layers, *ll)
+ 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 printImageChildren(layerMap map[string]*image.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.Printf(prefix)
+
+ //initialize intend with middleItem to reduce middleItem checks.
+ intend := middleItem
+ if !last {
+ // add continueItem i.e. '|' for next iteration prefix
+ 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 childern.
+ // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├──
+ intend = lastItem
+ prefix = prefix + " "
+ }
+
+ var tags string
+ if len(ll.RepoTags) > 0 {
+ tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags)
+ }
+ fmt.Printf("%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 := printImageChildren(layerMap, childID, prefix, (count == len(ll.ChildID)-1)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// prints the layers info of image
+func 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.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags)
+ }
+}