diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/attach.go | 2 | ||||
-rw-r--r-- | cmd/podman/build.go | 2 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 5 | ||||
-rw-r--r-- | cmd/podman/common.go | 6 | ||||
-rw-r--r-- | cmd/podman/container.go | 19 | ||||
-rw-r--r-- | cmd/podman/events.go | 3 | ||||
-rw-r--r-- | cmd/podman/export.go | 7 | ||||
-rw-r--r-- | cmd/podman/image.go | 19 | ||||
-rw-r--r-- | cmd/podman/inspect.go | 38 | ||||
-rw-r--r-- | cmd/podman/load.go | 7 | ||||
-rw-r--r-- | cmd/podman/login.go | 2 | ||||
-rw-r--r-- | cmd/podman/main.go | 4 | ||||
-rw-r--r-- | cmd/podman/play_kube.go | 2 | ||||
-rw-r--r-- | cmd/podman/pull.go | 2 | ||||
-rw-r--r-- | cmd/podman/push.go | 2 | ||||
-rw-r--r-- | cmd/podman/run.go | 6 | ||||
-rw-r--r-- | cmd/podman/runlabel.go | 2 | ||||
-rw-r--r-- | cmd/podman/save.go | 8 | ||||
-rw-r--r-- | cmd/podman/search.go | 2 | ||||
-rw-r--r-- | cmd/podman/start.go | 10 | ||||
-rw-r--r-- | cmd/podman/tree.go | 190 |
21 files changed, 295 insertions, 43 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index 08976cdaa..86e89cfd7 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -35,7 +35,7 @@ func init() { flags := attachCommand.Flags() flags.StringVar(&attachCommand.DetachKeys, "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 _") flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") - flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)") + flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 72d78aff9..f0a67791a 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -52,7 +52,7 @@ func init() { buildCommand.SetHelpTemplate(HelpTemplate()) buildCommand.SetUsageTemplate(UsageTemplate()) flags := buildCommand.Flags() - flags.SetInterspersed(false) + flags.SetInterspersed(true) budFlags := buildahcli.GetBudFlags(&budFlagsValues) flag := budFlags.Lookup("pull") diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 38a68f94f..1461c9f03 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 79b7495ab..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{}, @@ -313,7 +313,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "image-volume", "bind", - "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')", + "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore'", ) createFlags.Bool( "init", false, @@ -374,7 +374,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.Int64( "memory-swappiness", -1, - "Tune container memory swappiness (0 to 100) (default -1)", + "Tune container memory swappiness (0 to 100, or -1 for system default)", ) createFlags.String( "name", "", 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/events.go b/cmd/podman/events.go index dda9a03f9..f6c20e8ff 100644 --- a/cmd/podman/events.go +++ b/cmd/podman/events.go @@ -11,7 +11,8 @@ var ( eventsCommand cliconfig.EventValues eventsDescription = "Monitor podman events" _eventsCommand = &cobra.Command{ - Use: "events [flags]", + Use: "events", + Args: noSubArgs, Short: "show podman events", Long: eventsDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/export.go b/cmd/podman/export.go index e5dc410a7..92633facd 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -36,7 +36,7 @@ func init() { exportCommand.SetHelpTemplate(HelpTemplate()) exportCommand.SetUsageTemplate(UsageTemplate()) flags := exportCommand.Flags() - flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + flags.StringVarP(&exportCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") } // exportCmd saves a container to a tarball on disk @@ -60,15 +60,16 @@ func exportCmd(c *cliconfig.ExportValues) error { } output := c.Output - if runtime.Remote && (output == "/dev/stdout" || len(output) == 0) { + if runtime.Remote && len(output) == 0 { return errors.New("remote client usage must specify an output file (-o)") } - if output == "/dev/stdout" { + if len(output) == 0 { file := os.Stdout if logrus.IsTerminal(file) { return errors.Errorf("refusing to export to terminal. Use -o flag or redirect") } + output = "/dev/stdout" } if err := parse.ValidateFileName(output); err != nil { 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/load.go b/cmd/podman/load.go index 303c23bc7..46add699e 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -34,7 +34,7 @@ func init() { loadCommand.SetHelpTemplate(HelpTemplate()) loadCommand.SetUsageTemplate(UsageTemplate()) flags := loadCommand.Flags() - flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN") + flags.StringVarP(&loadCommand.Input, "input", "i", "", "Read from specified archive file (default: stdin)") flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output") flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)") @@ -64,7 +64,10 @@ func loadCmd(c *cliconfig.LoadValues) error { if runtime.Remote && len(input) == 0 { return errors.New("the remote client requires you to load via -i and a tarball") } - if input == "/dev/stdin" { + if len(input) == 0 { + input = "/dev/stdin" + c.Input = input + fi, err := os.Stdin.Stat() if err != nil { return err diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 43a7d246e..4e96b43cb 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -45,7 +45,7 @@ func init() { flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry") flags.BoolVar(&loginCommand.GetLogin, "get-login", true, "Return the current login user for the registry") flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry") - flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry") flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 669860341..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, @@ -115,7 +115,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error (default), fatal or panic") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") rootCmd.PersistentFlags().MarkHidden("max-workers") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index 44aa4776b..eeb1aad64 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -59,7 +59,7 @@ func init() { flags.StringVar(&playKubeCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error { diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 7986d5530..8888c5e28 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -52,7 +52,7 @@ func init() { flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } diff --git a/cmd/podman/push.go b/cmd/podman/push.go index afc385527..a1dac24ae 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -54,7 +54,7 @@ func init() { flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") - flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func pushCmd(c *cliconfig.PushValues) error { diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 130c5a32c..32e7b3510 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -44,7 +44,7 @@ func init() { runCommand.SetUsageTemplate(UsageTemplate()) flags := runCommand.Flags() flags.SetInterspersed(false) - flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)") + flags.Bool("sig-proxy", true, "Proxy received signals to the process") getCreateFlags(&runCommand.PodmanCommand) } @@ -166,6 +166,10 @@ func runCmd(c *cliconfig.RunValues) error { exitCode = int(ecode) } + if c.IsSet("rm") { + runtime.RemoveContainer(ctx, ctr, false, true) + } + return nil } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index 5ab55949b..f79aa8b0e 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -60,7 +60,7 @@ func init() { flags.BoolP("pull", "p", false, "Pull the image if it does not exist locally prior to executing the label contents") flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images") flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.MarkDeprecated("pull", "podman will pull if not found in local storage") } diff --git a/cmd/podman/save.go b/cmd/podman/save.go index df016b069..c10679740 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -58,7 +58,7 @@ func init() { flags := saveCommand.Flags() flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&saveCommand.Format, "format", v2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)") - flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + flags.StringVarP(&saveCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output") } @@ -79,14 +79,14 @@ func saveCmd(c *cliconfig.SaveValues) error { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") } - output := c.Output - if output == "/dev/stdout" { + if len(c.Output) == 0 { fi := os.Stdout if logrus.IsTerminal(fi) { return errors.Errorf("refusing to save to terminal. Use -o flag or redirect") } + c.Output = "/dev/stdout" } - if err := parse.ValidateFileName(output); err != nil { + if err := parse.ValidateFileName(c.Output); err != nil { return err } return runtime.SaveImage(getContext(), c) diff --git a/cmd/podman/search.go b/cmd/podman/search.go index 25f5a98b7..5997e144a 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -46,7 +46,7 @@ func init() { flags.StringVar(&searchCommand.Format, "format", "", "Change the output format to a Go template") flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results") flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") - flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func searchCmd(c *cliconfig.SearchValues) error { diff --git a/cmd/podman/start.go b/cmd/podman/start.go index e942c1ccd..cf406cf66 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -41,7 +41,7 @@ func init() { flags.StringVar(&startCommand.DetachKeys, "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 _") flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)") + flags.BoolVar(&startCommand.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") markFlagHiddenForRemoteClient("latest", flags) } @@ -62,14 +62,10 @@ func startCmd(c *cliconfig.StartValues) error { return errors.Errorf("you cannot start and attach multiple containers at once") } - sigProxy := c.SigProxy + sigProxy := c.SigProxy || attach if sigProxy && !attach { - if c.Flag("sig-proxy").Changed { - return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") - } else { - sigProxy = false - } + return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") } runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) 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) + } +} |