summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/cliconfig/config.go5
-rw-r--r--cmd/podman/common.go2
-rw-r--r--cmd/podman/container.go19
-rw-r--r--cmd/podman/image.go19
-rw-r--r--cmd/podman/inspect.go38
-rw-r--r--cmd/podman/main.go2
-rw-r--r--cmd/podman/tree.go190
7 files changed, 261 insertions, 14 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/common.go b/cmd/podman/common.go
index 1d96f40c8..43dfdb837 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -226,7 +226,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
)
createFlags.String(
"detach-keys", "",
- "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`",
+ "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
)
createFlags.StringSlice(
"device", []string{},
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index 8ad8d7a44..ce6ad8883 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -19,6 +19,20 @@ var (
},
}
+ contInspectSubCommand cliconfig.InspectValues
+ _contInspectSubCommand = &cobra.Command{
+ Use: strings.Replace(_inspectCommand.Use, "| IMAGE", "", 1),
+ Short: "Display the configuration of a container",
+ Long: `Displays the low-level information on a container identified by name or ID.`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ contInspectSubCommand.InputArgs = args
+ contInspectSubCommand.GlobalFlags = MainGlobalOpts
+ return inspectCmd(&contInspectSubCommand)
+ },
+ Example: `podman container inspect myCtr
+ podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
+ }
+
listSubCommand cliconfig.PsValues
_listSubCommand = &cobra.Command{
Use: strings.Replace(_psCommand.Use, "ps", "list", 1),
@@ -37,12 +51,15 @@ var (
// Commands that are universally implemented.
containerCommands = []*cobra.Command{
_containerExistsCommand,
- _inspectCommand,
+ _contInspectSubCommand,
_listSubCommand,
}
)
func init() {
+ contInspectSubCommand.Command = _contInspectSubCommand
+ inspectInit(&contInspectSubCommand)
+
listSubCommand.Command = _listSubCommand
psInit(&listSubCommand)
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index 52bac6ecb..66c141686 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -31,6 +31,19 @@ var (
Example: strings.Replace(_imagesCommand.Example, "podman images", "podman image list", -1),
}
+ inspectSubCommand cliconfig.InspectValues
+ _inspectSubCommand = &cobra.Command{
+ Use: strings.Replace(_inspectCommand.Use, "CONTAINER | ", "", 1),
+ Short: "Display the configuration of an image",
+ Long: `Displays the low-level information on an image identified by name or ID.`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ inspectSubCommand.InputArgs = args
+ inspectSubCommand.GlobalFlags = MainGlobalOpts
+ return inspectCmd(&inspectSubCommand)
+ },
+ Example: `podman image inspect alpine`,
+ }
+
rmSubCommand cliconfig.RmiValues
_rmSubCommand = &cobra.Command{
Use: strings.Replace(_rmiCommand.Use, "rmi", "rm", 1),
@@ -52,7 +65,7 @@ var imageSubCommands = []*cobra.Command{
_imagesSubCommand,
_imageExistsCommand,
_importCommand,
- _inspectCommand,
+ _inspectSubCommand,
_loadCommand,
_pruneImagesCommand,
_pullCommand,
@@ -60,6 +73,7 @@ var imageSubCommands = []*cobra.Command{
_rmSubCommand,
_saveCommand,
_tagCommand,
+ _treeCommand,
}
func init() {
@@ -69,6 +83,9 @@ func init() {
imagesSubCommand.Command = _imagesSubCommand
imagesInit(&imagesSubCommand)
+ inspectSubCommand.Command = _inspectSubCommand
+ inspectInit(&inspectSubCommand)
+
imageCommand.SetUsageTemplate(UsageTemplate())
imageCommand.AddCommand(imageSubCommands...)
imageCommand.AddCommand(getImageSubCommands()...)
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index e14f25c24..3d6fd07e0 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -27,7 +27,7 @@ var (
inspectDescription = `This displays the low-level information on containers and images identified by name or ID.
If given a name that matches both a container and an image, this command inspects the container. By default, this will render all results in a JSON array.`
- _inspectCommand = &cobra.Command{
+ _inspectCommand = cobra.Command{
Use: "inspect [flags] CONTAINER | IMAGE",
Short: "Display the configuration of a container or image",
Long: inspectDescription,
@@ -42,16 +42,34 @@ var (
}
)
+func inspectInit(command *cliconfig.InspectValues) {
+ command.SetHelpTemplate(HelpTemplate())
+ command.SetUsageTemplate(UsageTemplate())
+ flags := command.Flags()
+ flags.StringVarP(&command.Format, "format", "f", "", "Change the output format to a Go template")
+
+ // -t flag applicable only to 'podman inspect', not 'image/container inspect'
+ ambiguous := strings.Contains(command.Use, "|")
+ if ambiguous {
+ flags.StringVarP(&command.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (image or container)")
+ }
+
+ if strings.Contains(command.Use, "CONTAINER") {
+ containers_only := " (containers only)"
+ if !ambiguous {
+ containers_only = ""
+ command.TypeObject = inspectTypeContainer
+ }
+ flags.BoolVarP(&command.Latest, "latest", "l", false, "Act on the latest container podman is aware of"+containers_only)
+ flags.BoolVarP(&command.Size, "size", "s", false, "Display total file size"+containers_only)
+ markFlagHiddenForRemoteClient("latest", flags)
+ } else {
+ command.TypeObject = inspectTypeImage
+ }
+}
func init() {
- inspectCommand.Command = _inspectCommand
- inspectCommand.SetHelpTemplate(HelpTemplate())
- inspectCommand.SetUsageTemplate(UsageTemplate())
- flags := inspectCommand.Flags()
- flags.StringVarP(&inspectCommand.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (e.g image, container or task)")
- flags.StringVarP(&inspectCommand.Format, "format", "f", "", "Change the output format to a Go template")
- flags.BoolVarP(&inspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of if the type is a container")
- flags.BoolVarP(&inspectCommand.Size, "size", "s", false, "Display total file size if the type is container")
- markFlagHiddenForRemoteClient("latest", flags)
+ inspectCommand.Command = &_inspectCommand
+ inspectInit(&inspectCommand)
}
func inspectCmd(c *cliconfig.InspectValues) error {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 5e8b9745a..1717e0624 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -42,7 +42,7 @@ var mainCommands = []*cobra.Command{
&_imagesCommand,
_importCommand,
_infoCommand,
- _inspectCommand,
+ &_inspectCommand,
_killCommand,
_loadCommand,
podCommand.Command,
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)
+ }
+}