diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2020-07-06 15:55:15 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-06 15:55:15 -0400 |
commit | e3e2b1e65930bfc40c5b60802c4d6ea175169732 (patch) | |
tree | de9fbf65281b2d9e69eaa90be2bdee96d3047d5c | |
parent | b8ad7f241aba622b23b2cfab6fb5284cd26125e3 (diff) | |
parent | 2fb9bb20df49f665dbc7420a80dc2da57530278f (diff) | |
download | podman-e3e2b1e65930bfc40c5b60802c4d6ea175169732.tar.gz podman-e3e2b1e65930bfc40c5b60802c4d6ea175169732.tar.bz2 podman-e3e2b1e65930bfc40c5b60802c4d6ea175169732.zip |
Merge pull request #6871 from mheon/202_backports
Backports for v2.0.2
102 files changed, 939 insertions, 565 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 44b64f977..c843bd091 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,24 @@ # Release Notes +## 2.0.2 +### Bugfixes +- Fixed a bug where the `podman ps` command would not truncate long container commands, resulting in display issues as the column could become extremely wide (the `--no-trunc` flag can be used to print the full command). +- Fixed a bug where `podman pod` commands operationg on multiple containers (e.g. `podman pod stop` and `podman pod kill`) would not print errors from individual containers, but only a warning that some containers had failed. +- Fixed a bug where the `podman system service` command would panic if a connection to the Events endpoint hung up early ([#6805](https://github.com/containers/libpod/issues/6805)). +- Fixed a bug where rootless Podman would create anonymous and named volumes with the wrong owner for containers run with the `--user` directive. +- Fixed a bug where the `TMPDIR` environment variable was not being defaulted (if unset) to `/var/tmp`. +- Fixed a bug where the `--publish` flag to `podman create` and `podman run` required that a host port be specified if an IP address was given ([#6806](https://github.com/containers/libpod/issues/6806)). +- Fixed a bug where in `podman-remote` commands performing an attach (`podman run`, `podman attach`, `podman start --attach`, `podman exec`) did not properly configure the terminal on Windows. +- Fixed a bug where the `--remote` flag to Podman required an argument, despite being a boolean ([#6704](https://github.com/containers/libpod/issues/6704)). +- Fixed a bug where the `podman generate systemd --new` command could generate incorrect unit files for a pod if a container in the pod was created using the `--pod=...` flag (with an =, instead of a space, before the pod ID) ([#6766](https://github.com/containers/libpod/issues/6766)). +- Fixed a bug where `NPROC` and `NOFILE` rlimits could be improperly set for rootless Podman containers + +### API +- Fixed a bug where the timestamp format for Libpod image list endpoint was incorrect - the format has been switched to Unix time. +- Fixed a bug where the compatability Create endpoint did not handle empty entrypoints properly. +- Fixed a bug where the compatibility network remove endpoint would improperly handle errors where the network was not found +- Fixed a bug where containers would be created with improper permissions because of a umask issue ([#6787](https://github.com/containers/libpod/issues/6787)) + ## 2.0.1 ### Changes - The `podman system connection` command was mistakenly omitted from the 2.0 release, and has been included here. diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go index 7a67f3d18..a12eeb6cb 100644 --- a/cmd/podman/auto-update.go +++ b/cmd/podman/auto-update.go @@ -16,6 +16,8 @@ var ( autoUpdateDescription = `Auto update containers according to their auto-update policy. Auto-update policies are specified with the "io.containers.autoupdate" label. + Containers are expected to run in systemd units created with "podman-generate-systemd --new", + or similar units that create new containers in order to run the updated images. Note that this command is experimental. Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ Use: "auto-update [flags]", diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go index a3ce6198c..e21e349d9 100644 --- a/cmd/podman/common/util.go +++ b/cmd/podman/common/util.go @@ -184,22 +184,24 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) } if hostPort != nil { if *hostPort == "" { - return newPort, errors.Errorf("must provide a non-empty container host port to publish") - } - hostStart, hostLen, err := parseAndValidateRange(*hostPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") - } - if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + // Set 0 as a placeholder. The server side of Specgen + // will find a random, open, unused port to use. + newPort.HostPort = 0 + } else { + hostStart, hostLen, err := parseAndValidateRange(*hostPort) + if err != nil { + return newPort, errors.Wrapf(err, "error parsing host port") + } + if hostLen != ctrLen { + return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + } + newPort.HostPort = hostStart } - newPort.HostPort = hostStart + } else { + newPort.HostPort = newPort.ContainerPort } hport := newPort.HostPort - if hport == 0 { - hport = newPort.ContainerPort - } logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) return newPort, nil diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index 6835d4e32..eca9e0787 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -44,10 +44,6 @@ func attachFlags(flags *pflag.FlagSet) { flags.StringVar(&attachOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") - flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -55,22 +51,24 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: attachCommand, }) - flags := attachCommand.Flags() - attachFlags(flags) + attachFlags(attachCommand.Flags()) + validate.AddLatestFlag(attachCommand, &attachOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerAttachCommand, Parent: containerCmd, }) - containerAttachFlags := containerAttachCommand.Flags() - attachFlags(containerAttachFlags) + attachFlags(containerAttachCommand.Flags()) + validate.AddLatestFlag(containerAttachCommand, &attachOpts.Latest) + } func attach(cmd *cobra.Command, args []string) error { if len(args) > 1 || (len(args) == 0 && !attachOpts.Latest) { return errors.Errorf("attach requires the name or id of one running container or the latest flag") } + var name string if len(args) > 0 { name = args[0] diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index 6aa4304af..683437000 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/rootless" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: checkpointDescription, RunE: checkpoint, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container checkpoint --keep ctrID podman container checkpoint --all @@ -48,12 +48,9 @@ func init() { flags.BoolVarP(&checkpointOptions.LeaveRunning, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk") flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers") - flags.BoolVarP(&checkpointOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVarP(&checkpointOptions.Export, "export", "e", "", "Export the checkpoint image to a tar.gz") flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) } func checkpoint(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go index f36dcf6d4..5dea77b7a 100644 --- a/cmd/podman/containers/cleanup.go +++ b/cmd/podman/containers/cleanup.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -24,7 +24,7 @@ var ( Long: cleanupDescription, RunE: cleanup, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container cleanup --latest podman container cleanup ctrID1 ctrID2 ctrID3 @@ -44,11 +44,10 @@ func init() { }) flags := cleanupCommand.Flags() flags.BoolVarP(&cleanupOptions.All, "all", "a", false, "Cleans up all containers") - flags.BoolVarP(&cleanupOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&cleanupOptions.Exec, "exec", "", "Clean up the given exec session instead of the container") flags.BoolVar(&cleanupOptions.Remove, "rm", false, "After cleanup, remove the container entirely") flags.BoolVar(&cleanupOptions.RemoveImage, "rmi", false, "After cleanup, remove the image entirely") - + validate.AddLatestFlag(cleanupCommand, &cleanupOptions.Latest) } func cleanup(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index dffa9d7fe..cebf0fa4b 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -59,6 +59,7 @@ func createFlags(flags *pflag.FlagSet) { flags.AddFlagSet(common.GetCreateFlags(&cliVals)) flags.AddFlagSet(common.GetNetFlags()) flags.SetNormalizeFunc(common.AliasFlags) + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index d61f92bb7..f39b22ede 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -14,8 +14,8 @@ var ( diffCmd = &cobra.Command{ Use: "diff [flags] CONTAINER", Args: validate.IDOrLatestArgs, - Short: "Inspect changes on container's file systems", - Long: `Displays changes on a container filesystem. The container will be compared to its parent layer.`, + Short: "Inspect changes to the container's file systems", + Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`, RunE: diff, Example: `podman container diff myCtr podman container diff -l --format json myCtr`, @@ -35,10 +35,7 @@ func init() { flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") - - if !registry.IsRemote() { - flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index 4b2f0e9a2..e0fc740b5 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -6,6 +6,7 @@ import ( "os" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" envLib "github.com/containers/libpod/v2/pkg/env" @@ -53,14 +54,13 @@ func execFlags(flags *pflag.FlagSet) { flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables") flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables") flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") - flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("preserve-fds") } } @@ -70,20 +70,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: execCommand, }) - flags := execCommand.Flags() - execFlags(flags) + execFlags(execCommand.Flags()) + validate.AddLatestFlag(execCommand, &execOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerExecCommand, Parent: containerCmd, }) - - containerExecFlags := containerExecCommand.Flags() - execFlags(containerExecFlags) + execFlags(containerExecCommand.Flags()) + validate.AddLatestFlag(containerExecCommand, &execOpts.Latest) } -func exec(cmd *cobra.Command, args []string) error { +func exec(_ *cobra.Command, args []string) error { var nameOrID string if len(args) == 0 && !execOpts.Latest { diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go index c1f166d51..98f69fa4b 100644 --- a/cmd/podman/containers/init.go +++ b/cmd/podman/containers/init.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -20,7 +20,7 @@ var ( Long: initDescription, RunE: initContainer, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman init --latest podman init 3c45ef19d893 @@ -45,10 +45,6 @@ var ( func initFlags(flags *pflag.FlagSet) { flags.BoolVarP(&initOptions.All, "all", "a", false, "Initialize all containers") - flags.BoolVarP(&initOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -58,15 +54,16 @@ func init() { }) flags := initCommand.Flags() initFlags(flags) + validate.AddLatestFlag(initCommand, &initOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Parent: containerCmd, Command: containerInitCommand, }) - containerInitFlags := containerInitCommand.Flags() initFlags(containerInitFlags) + validate.AddLatestFlag(containerInitCommand, &initOptions.Latest) } func initContainer(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index bea5cefd7..9ef3c2c4a 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -3,6 +3,7 @@ package containers import ( "github.com/containers/libpod/v2/cmd/podman/inspect" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -30,7 +31,7 @@ func init() { flags := inspectCmd.Flags() flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size") flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format the output to a Go template or json") - flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") + validate.AddLatestFlag(inspectCmd, &inspectOpts.Latest) } func inspectExec(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index 22f7383b1..da60fcf52 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/signal" "github.com/spf13/cobra" @@ -22,7 +22,7 @@ var ( Long: killDescription, RunE: kill, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman kill mywebserver podman kill 860a4b23 @@ -31,7 +31,7 @@ var ( containerKillCommand = &cobra.Command{ Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Use: killCommand.Use, Short: killCommand.Short, @@ -50,10 +50,6 @@ var ( func killFlags(flags *pflag.FlagSet) { flags.BoolVarP(&killOptions.All, "all", "a", false, "Signal all running containers") flags.StringVarP(&killOptions.Signal, "signal", "s", "KILL", "Signal to send to the container") - flags.BoolVarP(&killOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -61,20 +57,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: killCommand, }) - flags := killCommand.Flags() - killFlags(flags) + killFlags(killCommand.Flags()) + validate.AddLatestFlag(killCommand, &killOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerKillCommand, Parent: containerCmd, }) - - containerKillFlags := containerKillCommand.Flags() - killFlags(containerKillFlags) + killFlags(containerKillCommand.Flags()) + validate.AddLatestFlag(containerKillCommand, &killOptions.Latest) } -func kill(cmd *cobra.Command, args []string) error { +func kill(_ *cobra.Command, args []string) error { var ( err error errs utils.OutputErrors diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go index e2688623d..2d107d51d 100644 --- a/cmd/podman/containers/list.go +++ b/cmd/podman/containers/list.go @@ -29,4 +29,5 @@ func init() { Parent: containerCmd, }) listFlagSet(listCmd.Flags()) + validate.AddLatestFlag(listCmd, &listOpts.Latest) } diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index 85d3262da..850cb2e1f 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -4,6 +4,7 @@ import ( "os" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -68,9 +69,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: logsCommand, }) - - flags := logsCommand.Flags() - logsFlags(flags) + logsFlags(logsCommand.Flags()) + validate.AddLatestFlag(logsCommand, &logsOptions.Latest) // container logs registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -78,15 +78,13 @@ func init() { Command: containerLogsCommand, Parent: containerCmd, }) - - containerLogsFlags := containerLogsCommand.Flags() - logsFlags(containerLogsFlags) + logsFlags(containerLogsCommand.Flags()) + validate.AddLatestFlag(containerLogsCommand, &logsOptions.Latest) } func logsFlags(flags *pflag.FlagSet) { flags.BoolVar(&logsOptions.Details, "details", false, "Show extra details provided to the logs") flags.BoolVarP(&logsOptions.Follow, "follow", "f", false, "Follow log output. The default is false") - flags.BoolVarP(&logsOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&logsOptions.SinceRaw, "since", "", "Show logs since TIMESTAMP") flags.Int64Var(&logsOptions.Tail, "tail", -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines") flags.BoolVarP(&logsOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") @@ -95,7 +93,7 @@ func logsFlags(flags *pflag.FlagSet) { _ = flags.MarkHidden("details") } -func logs(cmd *cobra.Command, args []string) error { +func logs(_ *cobra.Command, args []string) error { if logsOptions.SinceRaw != "" { // parse time, error out if something is wrong since, err := util.ParseInputTime(logsOptions.SinceRaw) diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index f2b66a2cd..44af27801 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -6,9 +6,9 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -28,7 +28,7 @@ var ( Long: mountDescription, RunE: mount, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, } @@ -47,7 +47,6 @@ var ( func mountFlags(flags *pflag.FlagSet) { flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers") flags.StringVar(&mountOpts.Format, "format", "", "Change the output format to Go template") - flags.BoolVarP(&mountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output") } @@ -56,19 +55,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: mountCommand, }) - flags := mountCommand.Flags() - mountFlags(flags) + mountFlags(mountCommand.Flags()) + validate.AddLatestFlag(mountCommand, &mountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, Command: containerMountCommmand, Parent: containerCmd, }) - containerMountFlags := containerMountCommmand.Flags() - mountFlags(containerMountFlags) + mountFlags(containerMountCommmand.Flags()) + validate.AddLatestFlag(containerMountCommmand, &mountOpts.Latest) } -func mount(cmd *cobra.Command, args []string) error { +func mount(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index 83a3824cf..33d6ff06f 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -66,6 +66,7 @@ func pause(cmd *cobra.Command, args []string) error { if rootless.IsRootless() && !registry.IsRemote() { return errors.New("pause is not supported for rootless containers") } + if len(args) < 1 && !pauseOpts.All { return errors.Errorf("you must provide at least one container name or id") } diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index 3ef8f0fde..7e548b6c9 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -5,8 +5,8 @@ import ( "strconv" "strings" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" @@ -23,7 +23,7 @@ var ( Long: portDescription, RunE: port, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman port --all podman port ctrID 80/tcp @@ -36,7 +36,7 @@ var ( Long: portDescription, RunE: portCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container port --all podman container port --latest 80`, @@ -49,10 +49,6 @@ var ( func portFlags(flags *pflag.FlagSet) { flags.BoolVarP(&portOpts.All, "all", "a", false, "Display port information for all containers") - flags.BoolVarP(&portOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -60,22 +56,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: portCommand, }) - - flags := portCommand.Flags() - portFlags(flags) + portFlags(portCommand.Flags()) + validate.AddLatestFlag(portCommand, &portOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerPortCommand, Parent: containerCmd, }) - - containerPortflags := containerPortCommand.Flags() - portFlags(containerPortflags) - + portFlags(containerPortCommand.Flags()) + validate.AddLatestFlag(containerPortCommand, &portOpts.Latest) } -func port(cmd *cobra.Command, args []string) error { +func port(_ *cobra.Command, args []string) error { var ( container string err error diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 5cd7f40ac..7c84cbae1 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -50,6 +50,7 @@ func init() { Command: psCommand, }) listFlagSet(psCommand.Flags()) + validate.AddLatestFlag(psCommand, &listOpts.Latest) } func listFlagSet(flags *pflag.FlagSet) { @@ -57,7 +58,6 @@ func listFlagSet(flags *pflag.FlagSet) { flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template") flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)") - flags.BoolVarP(&listOpts.Latest, "latest", "l", false, "Show the latest container created (all states)") flags.BoolVar(&listOpts.Namespace, "namespace", false, "Display namespace information") flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information") flags.BoolVar(&noTrunc, "no-trunc", false, "Display the extended information") @@ -69,10 +69,6 @@ func listFlagSet(flags *pflag.FlagSet) { sort := validate.Value(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) - - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func checkFlags(c *cobra.Command) error { // latest, and last are mutually exclusive. @@ -313,7 +309,13 @@ func (l psReporter) Status() string { // Command returns the container command in string format func (l psReporter) Command() string { - return strings.Join(l.ListContainer.Command, " ") + command := strings.Join(l.ListContainer.Command, " ") + if !noTrunc { + if len(command) > 17 { + return command[0:17] + "..." + } + } + return command } // Size returns the rootfs and virtual sizes in human duration in diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index d3e90c160..393b003a9 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: restartDescription, RunE: restart, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman restart ctrID podman restart --latest @@ -50,12 +50,9 @@ var ( func restartFlags(flags *pflag.FlagSet) { flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all non-running containers") - flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&restartOptions.Running, "running", false, "Restart only running containers when --all is used") flags.UintVarP(&restartTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + flags.SetNormalizeFunc(utils.AliasFlags) } @@ -64,17 +61,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restartCommand, }) - flags := restartCommand.Flags() - restartFlags(flags) + restartFlags(restartCommand.Flags()) + validate.AddLatestFlag(restartCommand, &restartOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRestartCommand, Parent: containerCmd, }) - - containerRestartFlags := containerRestartCommand.Flags() - restartFlags(containerRestartFlags) + restartFlags(containerRestartCommand.Flags()) + validate.AddLatestFlag(containerRestartCommand, &restartOptions.Latest) } func restart(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 975087a97..e9e0ad6fc 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/rootless" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: restoreDescription, RunE: restore, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container restore ctrID podman container restore --latest @@ -46,19 +46,16 @@ func init() { flags := restoreCommand.Flags() flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files") - flags.BoolVarP(&restoreOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections") flags.StringVarP(&restoreOptions.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)") flags.StringVarP(&restoreOptions.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint") flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest) } -func restore(cmd *cobra.Command, args []string) error { +func restore(_ *cobra.Command, args []string) error { var errs utils.OutputErrors if rootless.IsRootless() { return errors.New("restoring a container requires root") diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 3afda4c5d..427e1e72c 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -26,7 +26,7 @@ var ( Long: rmDescription, RunE: rm, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman rm imageID podman rm mywebserver myflaskserver 860a4b23 @@ -40,7 +40,7 @@ var ( Long: rmCommand.Long, RunE: rmCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman container rm imageID podman container rm mywebserver myflaskserver 860a4b23 @@ -57,12 +57,11 @@ func rmFlags(flags *pflag.FlagSet) { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") - flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&rmOptions.Storage, "storage", false, "Remove container from storage library") flags.BoolVarP(&rmOptions.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") flags.StringArrayVarP(&rmOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") _ = flags.MarkHidden("cidfile") _ = flags.MarkHidden("storage") @@ -75,18 +74,18 @@ func init() { Command: rmCommand, }) rmFlags(rmCommand.Flags()) + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRmCommand, Parent: containerCmd, }) - - containerRmFlags := containerRmCommand.Flags() - rmFlags(containerRmFlags) + rmFlags(containerRmCommand.Flags()) + validate.AddLatestFlag(containerRmCommand, &rmOptions.Latest) } -func rm(cmd *cobra.Command, args []string) error { +func rm(_ *cobra.Command, args []string) error { return removeContainers(args, rmOptions, true) } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index c1af2c1d3..638b1c96e 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -61,6 +61,7 @@ func runFlags(flags *pflag.FlagSet) { flags.SetNormalizeFunc(common.AliasFlags) flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 3c3bffaba..941588137 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -44,10 +45,9 @@ func startFlags(flags *pflag.FlagSet) { flags.BoolVarP(&startOptions.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") flags.StringVar(&startOptions.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") - flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("sig-proxy") } } @@ -56,17 +56,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCommand, }) - flags := startCommand.Flags() - startFlags(flags) + startFlags(startCommand.Flags()) + validate.AddLatestFlag(startCommand, &startOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStartCommand, Parent: containerCmd, }) + startFlags(containerStartCommand.Flags()) + validate.AddLatestFlag(containerStartCommand, &startOptions.Latest) - containerStartFlags := containerStartCommand.Flags() - startFlags(containerStartFlags) } func start(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 9b877b3af..86674cfc9 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -10,6 +10,7 @@ import ( tm "github.com/buger/goterm" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/cgroups" "github.com/containers/libpod/v2/pkg/domain/entities" @@ -56,12 +57,8 @@ var ( func statFlags(flags *pflag.FlagSet) { flags.BoolVarP(&statsOptions.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false") flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") - flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -69,17 +66,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: statsCommand, }) - flags := statsCommand.Flags() - statFlags(flags) + statFlags(statsCommand.Flags()) + validate.AddLatestFlag(statsCommand, &statsOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStatsCommand, Parent: containerCmd, }) - - containerStatsFlags := containerStatsCommand.Flags() - statFlags(containerStatsFlags) + statFlags(containerStatsCommand.Flags()) + validate.AddLatestFlag(containerStatsCommand, &statsOptions.Latest) } // stats is different in that it will assume running containers if diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 7e741d161..7959918a8 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -22,7 +22,7 @@ var ( Long: stopDescription, RunE: stop, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman stop ctrID podman stop --latest @@ -35,7 +35,7 @@ var ( Long: stopCommand.Long, RunE: stopCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman container stop ctrID podman container stop --latest @@ -52,11 +52,9 @@ func stopFlags(flags *pflag.FlagSet) { flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running containers") flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.StringArrayVarP(&stopOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") - flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVarP(&stopTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("cidfile") _ = flags.MarkHidden("ignore") } @@ -68,8 +66,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCommand, }) - flags := stopCommand.Flags() - stopFlags(flags) + stopFlags(stopCommand.Flags()) + validate.AddLatestFlag(stopCommand, &stopOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -77,8 +75,8 @@ func init() { Parent: containerCmd, }) - containerStopFlags := containerStopCommand.Flags() - stopFlags(containerStopFlags) + stopFlags(containerStopCommand.Flags()) + validate.AddLatestFlag(containerStopCommand, &stopOptions.Latest) } func stop(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index adeba5ee8..079a0ca1e 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -51,11 +52,7 @@ podman container top ctrID -eo user,pid,comm`, func topFlags(flags *pflag.FlagSet) { flags.SetInterspersed(false) flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "") - flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") _ = flags.MarkHidden("list-descriptors") // meant only for bash completion - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -63,8 +60,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: topCommand, }) - flags := topCommand.Flags() - topFlags(flags) + topFlags(topCommand.Flags()) + validate.AddLatestFlag(topCommand, &topOptions.Latest) descriptors, err := util.GetContainerPidInformationDescriptors() if err == nil { @@ -77,8 +74,8 @@ func init() { Command: containerTopCommand, Parent: containerCmd, }) - containerTopFlags := containerTopCommand.Flags() - topFlags(containerTopFlags) + topFlags(containerTopCommand.Flags()) + validate.AddLatestFlag(containerTopCommand, &topOptions.Latest) } func top(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go index 11889e8b0..c40c2be7e 100644 --- a/cmd/podman/containers/unmount.go +++ b/cmd/podman/containers/unmount.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -25,7 +25,7 @@ var ( Long: description, RunE: unmount, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman umount ctrID podman umount ctrID1 ctrID2 ctrID3 @@ -38,7 +38,7 @@ var ( Long: umountCommand.Long, RunE: umountCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container umount ctrID podman container umount ctrID1 ctrID2 ctrID3 @@ -53,7 +53,6 @@ var ( func umountFlags(flags *pflag.FlagSet) { flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Umount all of the currently mounted containers") flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers") - flags.BoolVarP(&unmountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") } func init() { @@ -61,17 +60,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: umountCommand, }) - flags := umountCommand.Flags() - umountFlags(flags) + umountFlags(umountCommand.Flags()) + validate.AddLatestFlag(umountCommand, &unmountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, Command: containerUnmountCommand, Parent: containerCmd, }) - - containerUmountFlags := containerUnmountCommand.Flags() - umountFlags(containerUmountFlags) + umountFlags(containerUnmountCommand.Flags()) + validate.AddLatestFlag(containerUnmountCommand, &unmountOpts.Latest) } func unmount(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 82bfb2020..c30bfe405 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -47,9 +47,6 @@ var ( func waitFlags(flags *pflag.FlagSet) { flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") - if !registry.IsRemote() { - flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } } func init() { @@ -57,17 +54,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: waitCommand, }) - flags := waitCommand.Flags() - waitFlags(flags) + waitFlags(waitCommand.Flags()) + validate.AddLatestFlag(waitCommand, &waitOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerWaitCommand, Parent: containerCmd, }) + waitFlags(containerWaitCommand.Flags()) + validate.AddLatestFlag(containerWaitCommand, &waitOptions.Latest) - containerWaitFlags := containerWaitCommand.Flags() - waitFlags(containerWaitFlags) } func wait(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 14a4529d5..cdd908cf1 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -17,12 +17,11 @@ var ( // Command: podman _diff_ Object_ID diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` diffCmd = &cobra.Command{ - Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", - Args: validate.IDOrLatestArgs, - Short: "Display the changes of object's file system", - Long: diffDescription, - TraverseChildren: true, - RunE: diff, + Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", + Args: validate.IDOrLatestArgs, + Short: "Display the changes to the object's file system", + Long: diffDescription, + RunE: diff, Example: `podman diff imageID podman diff ctrID podman diff --format json redis:alpine`, @@ -40,10 +39,7 @@ func init() { flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") - - if !registry.IsRemote() { - flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go index 3b3cb238d..d85d93347 100644 --- a/cmd/podman/generate/generate.go +++ b/cmd/podman/generate/generate.go @@ -11,11 +11,10 @@ import ( var ( // Command: podman _generate_ generateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate structured data based on containers and pods.", - Long: "Generate structured data (e.g., Kubernetes yaml or systemd units) based on containers and pods.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "generate", + Short: "Generate structured data based on containers and pods.", + Long: "Generate structured data (e.g., Kubernetes yaml or systemd units) based on containers and pods.", + RunE: validate.SubCommandExists, } containerConfig = util.DefaultContainerConfig() ) diff --git a/cmd/podman/healthcheck/healthcheck.go b/cmd/podman/healthcheck/healthcheck.go index 8b5d7bf80..fdf40e872 100644 --- a/cmd/podman/healthcheck/healthcheck.go +++ b/cmd/podman/healthcheck/healthcheck.go @@ -10,11 +10,10 @@ import ( var ( // Command: healthcheck healthCmd = &cobra.Command{ - Use: "healthcheck", - Short: "Manage health checks on containers", - Long: "Run health checks on containers", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "healthcheck", + Short: "Manage health checks on containers", + Long: "Run health checks on containers", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 40a9a8e7f..3ea74b4af 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -40,11 +40,11 @@ var ( // Command: podman _diff_ Object_ID buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." buildCmd = &cobra.Command{ - Use: "build [flags] [CONTEXT]", - Short: "Build an image using instructions from Containerfiles", - Long: buildDescription, - TraverseChildren: true, - RunE: build, + Use: "build [flags] [CONTEXT]", + Short: "Build an image using instructions from Containerfiles", + Long: buildDescription, + Args: cobra.MaximumNArgs(1), + RunE: build, Example: `podman build . podman build --creds=username:password -t imageName -f Containerfile.simple . podman build --layers --force-rm --tag imageName .`, diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index cd674ee9c..10a1c06ec 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -14,8 +14,8 @@ var ( diffCmd = &cobra.Command{ Use: "diff [flags] IMAGE", Args: cobra.ExactArgs(1), - Short: "Inspect changes on image's file systems", - Long: `Displays changes on a image's filesystem. The image will be compared to its parent layer.`, + Short: "Inspect changes to the image's file systems", + Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`, RunE: diff, Example: `podman image diff myImage podman image diff --format json redis:alpine`, diff --git a/cmd/podman/images/image.go b/cmd/podman/images/image.go index 85c1afc0e..89badd035 100644 --- a/cmd/podman/images/image.go +++ b/cmd/podman/images/image.go @@ -13,11 +13,10 @@ var ( // Command: podman _image_ imageCmd = &cobra.Command{ - Use: "image", - Short: "Manage images", - Long: "Manage images", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "image", + Short: "Manage images", + Long: "Manage images", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 7c5bd890d..f1a2cda96 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -86,6 +86,7 @@ func searchFlags(flags *pflag.FlagSet) { flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output") flags.StringVar(&searchOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("tls-verify") diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 667271aaf..4dd6b4a9a 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", - Short: "Display the configuration of object denoted by ID", - Long: "Displays the low-level information on an object identified by name or ID", - TraverseChildren: true, - RunE: inspectExec, + Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", + Short: "Display the configuration of object denoted by ID", + Long: "Displays the low-level information on an object identified by name or ID", + RunE: inspectExec, Example: `podman inspect fedora podman inspect --type image fedora podman inspect CtrID ImgID diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index ce51bc1e9..6fcca597b 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -8,6 +8,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -32,8 +33,8 @@ func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions { flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size") flags.StringVarP(&opts.Format, "format", "f", "json", "Format the output to a Go template or json") flags.StringVarP(&opts.Type, "type", "t", AllType, fmt.Sprintf("Specify inspect-oject type (%q, %q or %q)", ImageType, ContainerType, AllType)) - flags.BoolVarP(&opts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") + validate.AddLatestFlag(cmd, &opts.Latest) return &opts } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 77cadc842..5f740a006 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -16,7 +16,9 @@ import ( _ "github.com/containers/libpod/v2/cmd/podman/system" _ "github.com/containers/libpod/v2/cmd/podman/volumes" "github.com/containers/libpod/v2/pkg/rootless" + "github.com/containers/libpod/v2/pkg/terminal" "github.com/containers/storage/pkg/reexec" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -27,6 +29,11 @@ func main() { return } + // Hard code TMPDIR functions to use /var/tmp, if user did not override + if _, ok := os.LookupEnv("TMPDIR"); !ok { + os.Setenv("TMPDIR", "/var/tmp") + } + cfg := registry.PodmanConfig() for _, c := range registry.Commands { for _, m := range c.Mode { @@ -53,6 +60,10 @@ func main() { } } } + if err := terminal.SetConsole(); err != nil { + logrus.Error(err) + os.Exit(1) + } Execute() os.Exit(0) diff --git a/cmd/podman/manifest/manifest.go b/cmd/podman/manifest/manifest.go index 2bdbec662..f992705c1 100644 --- a/cmd/podman/manifest/manifest.go +++ b/cmd/podman/manifest/manifest.go @@ -10,11 +10,10 @@ import ( var ( manifestDescription = "Creates, modifies, and pushes manifest lists and image indexes." manifestCmd = &cobra.Command{ - Use: "manifest", - Short: "Manipulate manifest lists and image indexes", - Long: manifestDescription, - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "manifest", + Short: "Manipulate manifest lists and image indexes", + Long: manifestDescription, + RunE: validate.SubCommandExists, Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 podman manifest create localhost/list podman manifest inspect localhost/list diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index 226dc716c..d1eb37ae5 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -49,6 +49,7 @@ func init() { flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`") flags.BoolVar(&manifestPushOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go index aa319f15e..953e4678c 100644 --- a/cmd/podman/networks/network.go +++ b/cmd/podman/networks/network.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _network_ networkCmd = &cobra.Command{ - Use: "network", - Short: "Manage networks", - Long: "Manage networks", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "network", + Short: "Manage networks", + Long: "Manage networks", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/parse/common.go b/cmd/podman/parse/common.go deleted file mode 100644 index b3aa88da2..000000000 --- a/cmd/podman/parse/common.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -// TODO: the two functions here are almost identical. It may be worth looking -// into generalizing the two a bit more and share code but time is scarce and -// we only live once. - -// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. -// If cidfile is set, also check for the --cidfile flag. -func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { - argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !cidfile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("cidfile") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") - } - } - - specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") - specifiedCIDFile := false - if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { - specifiedCIDFile = true - } - - if specifiedCIDFile && (specifiedAll || specifiedLatest) { - return errors.Errorf("--all, --latest and --cidfile cannot be used together") - } else if specifiedAll && specifiedLatest { - return errors.Errorf("--all and --latest cannot be used together") - } - - if (argLen > 0) && specifiedAll { - return errors.Errorf("no arguments are needed with --all") - } - - if ignoreArgLen { - return nil - } - - if argLen > 0 { - if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") - } else if cidfile && (specifiedLatest || specifiedCIDFile) { - return errors.Errorf("no arguments are needed with --latest or --cidfile") - } - } - - if specifiedCIDFile { - return nil - } - - if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { - return errors.Errorf("you must provide at least one name or id") - } - return nil -} - -// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. -// If withIDFile is set, also check for the --pod-id-file flag. -func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { - argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !withIDFile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("pod-id-file") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") - } - } - - specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") - specifiedPodIDFile := false - if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { - specifiedPodIDFile = true - } - - if specifiedPodIDFile && (specifiedAll || specifiedLatest) { - return errors.Errorf("--all, --latest and --pod-id-file cannot be used together") - } else if specifiedAll && specifiedLatest { - return errors.Errorf("--all and --latest cannot be used together") - } - - if (argLen > 0) && specifiedAll { - return errors.Errorf("no arguments are needed with --all") - } - - if ignoreArgLen { - return nil - } - - if argLen > 0 { - if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") - } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { - return errors.Errorf("no arguments are needed with --latest or --pod-id-file") - } - } - - if specifiedPodIDFile { - return nil - } - - if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile { - return errors.Errorf("you must provide at least one name or id") - } - return nil -} diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 3567295bf..9bd5c10db 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -61,7 +61,6 @@ func init() { flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") } - _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/play/play.go b/cmd/podman/play/play.go index f7018eafb..eb8873595 100644 --- a/cmd/podman/play/play.go +++ b/cmd/podman/play/play.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _play_ playCmd = &cobra.Command{ - Use: "play", - Short: "Play a pod and its containers from a structured file.", - Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "play", + Short: "Play a pod and its containers from a structured file.", + Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 7d31385de..e21c7a74b 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -6,6 +6,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -36,11 +37,8 @@ func init() { Parent: podCmd, }) flags := inspectCmd.Flags() - flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.StringVarP(&inspectOptions.Format, "format", "f", "json", "Format the output to a Go template or json") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(inspectCmd, &inspectOptions.Latest) } func inspect(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go index 2076d55e9..2f4930897 100644 --- a/cmd/podman/pods/kill.go +++ b/cmd/podman/pods/kill.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podKillDescription, RunE: kill, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod kill podID podman pod kill --signal TERM mywebserver @@ -41,14 +41,11 @@ func init() { }) flags := killCommand.Flags() flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods") - flags.BoolVarP(&killOpts.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } - + validate.AddLatestFlag(killCommand, &killOpts.Latest) } -func kill(cmd *cobra.Command, args []string) error { + +func kill(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go index d495db601..f79d88973 100644 --- a/cmd/podman/pods/pause.go +++ b/cmd/podman/pods/pause.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podPauseDescription, RunE: pause, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod pause podID1 podID2 podman pod pause --latest @@ -41,12 +41,9 @@ func init() { }) flags := pauseCommand.Flags() flags.BoolVarP(&pauseOptions.All, "all", "a", false, "Pause all running pods") - flags.BoolVarP(&pauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(pauseCommand, &pauseOptions.Latest) } -func pause(cmd *cobra.Command, args []string) error { +func pause(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/pods/pod.go b/cmd/podman/pods/pod.go index 6eccade22..fff5ea615 100644 --- a/cmd/podman/pods/pod.go +++ b/cmd/podman/pods/pod.go @@ -14,11 +14,10 @@ var ( // Command: podman _pod_ podCmd = &cobra.Command{ - Use: "pod", - Short: "Manage pods", - Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "pod", + Short: "Manage pods", + Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.", + RunE: validate.SubCommandExists, } containerConfig = util.DefaultContainerConfig() ) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 49407d39b..7a1221480 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -53,18 +53,15 @@ func init() { // TODO should we make this a [] ? flags.StringSliceVarP(&inputFilters, "filter", "f", []string{}, "Filter output based on conditions given") flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template") - flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") flags.BoolVar(&psInput.Namespace, "ns", false, "Display namespace information of the pod") flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate pod and container IDs") flags.BoolVarP(&psInput.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only") flags.StringVar(&psInput.Sort, "sort", "created", "Sort output by created, id, name, or number") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(psCmd, &psInput.Latest) } -func pods(cmd *cobra.Command, args []string) error { +func pods(cmd *cobra.Command, _ []string) error { var ( w io.Writer = os.Stdout row string diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go index 7ca6b3cac..77d9f62d4 100644 --- a/cmd/podman/pods/restart.go +++ b/cmd/podman/pods/restart.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podRestartDescription, RunE: restart, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod restart podID1 podID2 podman pod restart --latest @@ -42,10 +42,7 @@ func init() { flags := restartCommand.Flags() flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all running pods") - flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(restartCommand, &restartOptions.Latest) } func restart(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 60972da76..3e07b31e9 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -30,7 +30,7 @@ var ( Long: podRmDescription, RunE: rm, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 @@ -49,15 +49,15 @@ func init() { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all running pods") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") - flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") flags.StringArrayVarP(&rmOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") } } -func rm(cmd *cobra.Command, args []string) error { +func rm(_ *cobra.Command, args []string) error { ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles) if err != nil { return err diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index db09f1bce..586737fb2 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -29,7 +29,7 @@ var ( Long: podStartDescription, RunE: start, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod start podID podman pod start --latest @@ -50,11 +50,8 @@ func init() { flags := startCommand.Flags() flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") - flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") flags.StringArrayVarP(&startOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(startCommand, &startOptions.Latest) } func start(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 19ff750aa..930a6d15c 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -13,6 +13,7 @@ import ( "github.com/buger/goterm" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util/camelcase" "github.com/spf13/cobra" @@ -55,13 +56,9 @@ func init() { flags := statsCmd.Flags() flags.BoolVarP(&statsOptions.All, "all", "a", false, "Provide stats for all pods") flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") - flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Provide stats on the latest pod Podman is aware of") flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result") - - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(statsCmd, &statsOptions.Latest) } func stats(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index afe3b6eae..84190fd08 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -34,7 +34,7 @@ var ( Long: podStopDescription, RunE: stop, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod stop mywebserverpod podman pod stop --latest @@ -51,13 +51,14 @@ func init() { flags := stopCommand.Flags() flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods") flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") - flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") flags.UintVarP(&stopOptions.TimeoutCLI, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") flags.StringArrayVarP(&stopOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + validate.AddLatestFlag(stopCommand, &stopOptions.Latest) + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") } + flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 551eeccac..840d2da0a 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -50,15 +51,11 @@ func init() { flags := topCommand.Flags() flags.SetInterspersed(false) flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "") - flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - _ = flags.MarkHidden("list-descriptors") // meant only for bash completion - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(topCommand, &topOptions.Latest) } -func top(cmd *cobra.Command, args []string) error { +func top(_ *cobra.Command, args []string) error { if topOptions.ListDescriptors { descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go index 09f2e472d..f96f3481c 100644 --- a/cmd/podman/pods/unpause.go +++ b/cmd/podman/pods/unpause.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podUnpauseDescription, RunE: unpause, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod unpause podID1 podID2 podman pod unpause --all @@ -41,12 +41,10 @@ func init() { }) flags := unpauseCommand.Flags() flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods") - flags.BoolVarP(&unpauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(unpauseCommand, &unpauseOptions.Latest) } -func unpause(cmd *cobra.Command, args []string) error { + +func unpause(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go index 85bf5f944..75e67b35d 100644 --- a/cmd/podman/registry/config.go +++ b/cmd/podman/registry/config.go @@ -5,7 +5,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "sync" "github.com/containers/common/pkg/config" @@ -45,7 +44,7 @@ func newPodmanConfig() { case "linux": // Some linux clients might only be compiled without ABI // support (e.g., podman-remote). - if abiSupport { + if abiSupport && !remoteOverride { mode = entities.ABIMode } else { mode = entities.TunnelMode @@ -55,19 +54,6 @@ func newPodmanConfig() { os.Exit(1) } - // Check if need to fallback to the tunnel mode if --remote is used. - if abiSupport && mode == entities.ABIMode { - // cobra.Execute() may not be called yet, so we peek at os.Args. - for _, v := range os.Args { - // Prefix checking works because of how default EngineMode's - // have been defined. - if strings.HasPrefix(v, "--remote") { - mode = entities.TunnelMode - break - } - } - } - cfg, err := config.NewConfig("") if err != nil { fmt.Fprint(os.Stderr, "Failed to obtain podman configuration: "+err.Error()) diff --git a/cmd/podman/registry/remote.go b/cmd/podman/registry/remote.go index 3040c4c2a..006a1b900 100644 --- a/cmd/podman/registry/remote.go +++ b/cmd/podman/registry/remote.go @@ -1,9 +1,26 @@ package registry import ( + "os" + "sync" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Was --remote given on command line + remoteOverride bool + remoteSync sync.Once ) +// IsRemote returns true if podman was built to run remote +// Use in init() functions as a initialization check func IsRemote() bool { - return podmanOptions.EngineMode == entities.TunnelMode + remoteSync.Do(func() { + remote := &cobra.Command{} + remote.Flags().BoolVarP(&remoteOverride, "remote", "r", false, "") + _ = remote.ParseFlags(os.Args) + }) + return podmanOptions.EngineMode == entities.TunnelMode || remoteOverride } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 1ca358e71..7c54da91a 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -6,6 +6,7 @@ import ( "path" "runtime" "runtime/pprof" + "strconv" "strings" "github.com/containers/common/pkg/config" @@ -20,7 +21,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // HelpTemplate is the help template for podman commands @@ -79,7 +79,7 @@ func init() { syslogHook, ) - rootFlags(registry.PodmanConfig(), rootCmd.PersistentFlags()) + rootFlags(rootCmd, registry.PodmanConfig()) // "version" is a local flag to avoid collisions with sub-commands that use "-v" var dummyVersion bool @@ -111,6 +111,15 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { cfg := registry.PodmanConfig() + // Validate --remote and --latest not given on same command + latest := cmd.Flags().Lookup("latest") + if latest != nil { + value, _ := strconv.ParseBool(latest.Value.String()) + if cfg.Remote && value { + return errors.Errorf("For %s \"--remote\" and \"--latest\", are mutually exclusive flags", cmd.CommandPath()) + } + } + // Prep the engines if _, err := registry.NewImageEngine(cmd, args); err != nil { return err @@ -193,7 +202,7 @@ func loggingHook() { } } if !found { - fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, strings.Join(logLevels, ", ")) + fmt.Fprintf(os.Stderr, "Log Level %q is not supported, choose from: %s\n", logLevel, strings.Join(logLevels, ", ")) os.Exit(1) } @@ -209,44 +218,45 @@ func loggingHook() { } } -func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { - // V2 flags - flags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") +func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { + cfg := opts.Config + lFlags := cmd.Flags() custom, _ := config.ReadCustomConfig() defaultURI := custom.Engine.RemoteURI if defaultURI == "" { defaultURI = registry.DefaultAPIAddress() } - flags.StringVar(&opts.URI, "url", defaultURI, "URL to access Podman service (CONTAINER_HOST)") - flags.StringVar(&opts.Identity, "identity", custom.Engine.RemoteIdentity, "path to SSH identity file, (CONTAINER_SSHKEY)") - - cfg := opts.Config - flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") - flags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") - flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") - flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") - flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") - flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") - flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) - flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") - flags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations") - flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") - flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") - flags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") - flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") - flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") + lFlags.StringVar(&opts.URI, "url", defaultURI, "URL to access Podman service (CONTAINER_HOST)") + lFlags.StringVar(&opts.Identity, "identity", custom.Engine.RemoteIdentity, "path to SSH identity file, (CONTAINER_SSHKEY)") + + pFlags := cmd.PersistentFlags() + pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") + pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") + pFlags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") + pFlags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") + pFlags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") + pFlags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") + pFlags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) + pFlags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") + pFlags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations") + pFlags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") + pFlags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") + pFlags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") + pFlags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") + pFlags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") // -s is deprecated due to conflict with -s on subcommands - flags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") - flags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + pFlags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + pFlags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") - flags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") - flags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)") + pFlags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") + pFlags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)") // Override default --help information of `--help` global flag var dummyHelp bool - flags.BoolVar(&dummyHelp, "help", false, "Help for podman") - flags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(logLevels, ", "))) + pFlags.BoolVar(&dummyHelp, "help", false, "Help for podman") + pFlags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(logLevels, ", "))) // Hide these flags for both ABI and Tunneling for _, f := range []string{ @@ -256,13 +266,13 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { "registries-conf", "trace", } { - if err := flags.MarkHidden(f); err != nil { + if err := pFlags.MarkHidden(f); err != nil { logrus.Warnf("unable to mark %s flag as hidden: %s", f, err.Error()) } } // Only create these flags for ABI connections if !registry.IsRemote() { - flags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") + pFlags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") } } diff --git a/cmd/podman/system/connection.go b/cmd/podman/system/connection.go index cb22522aa..bdb113ea3 100644 --- a/cmd/podman/system/connection.go +++ b/cmd/podman/system/connection.go @@ -60,7 +60,6 @@ func init() { }) flags := connectionCmd.Flags() - flags.StringVar(&cOpts.Identity, "identity", "", "path to ssh identity file") flags.IntVarP(&cOpts.Port, "port", "p", 22, "port number for destination") flags.StringVar(&cOpts.UDSPath, "socket-path", "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)") } diff --git a/cmd/podman/system/system.go b/cmd/podman/system/system.go index 6c4b26955..2dd6cf774 100644 --- a/cmd/podman/system/system.go +++ b/cmd/podman/system/system.go @@ -13,11 +13,10 @@ var ( // Command: podman _system_ systemCmd = &cobra.Command{ - Use: "system", - Short: "Manage podman", - Long: "Manage podman", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "system", + Short: "Manage podman", + Long: "Manage podman", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index 69240798f..a33f47959 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -2,6 +2,7 @@ package validate import ( "fmt" + "strconv" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -25,8 +26,122 @@ func SubCommandExists(cmd *cobra.Command, args []string) error { // IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag func IDOrLatestArgs(cmd *cobra.Command, args []string) error { - if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) { - return fmt.Errorf("`%s` requires a name, id or the \"--latest\" flag", cmd.CommandPath()) + if len(args) > 1 { + return fmt.Errorf("`%s` accepts at most one argument", cmd.CommandPath()) + } + + latest := cmd.Flag("latest") + if latest != nil { + given, _ := strconv.ParseBool(cmd.Flag("latest").Value.String()) + if len(args) == 0 && !given { + return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) + } + } + return nil +} + +// TODO: the two functions CheckAllLatestAndCIDFile and CheckAllLatestAndPodIDFile are almost identical. +// It may be worth looking into generalizing the two a bit more and share code but time is scarce and +// we only live once. + +// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. +// If cidfile is set, also check for the --cidfile flag. +func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { + argLen := len(args) + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !cidfile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("cidfile") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedCIDFile := false + if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { + specifiedCIDFile = true + } + + if specifiedCIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --cidfile cannot be used together") + } else if specifiedAll && specifiedLatest { + return errors.Errorf("--all and --latest cannot be used together") + } + + if (argLen > 0) && specifiedAll { + return errors.Errorf("no arguments are needed with --all") + } + + if ignoreArgLen { + return nil + } + + if argLen > 0 { + if specifiedLatest { + return errors.Errorf("no arguments are needed with --latest") + } else if cidfile && (specifiedLatest || specifiedCIDFile) { + return errors.Errorf("no arguments are needed with --latest or --cidfile") + } + } + + if specifiedCIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { + return errors.Errorf("you must provide at least one name or id") + } + return nil +} + +// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. +// If withIDFile is set, also check for the --pod-id-file flag. +func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { + argLen := len(args) + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !withIDFile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("pod-id-file") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedPodIDFile := false + if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { + specifiedPodIDFile = true + } + + if specifiedPodIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --pod-id-file cannot be used together") + } else if specifiedAll && specifiedLatest { + return errors.Errorf("--all and --latest cannot be used together") + } + + if (argLen > 0) && specifiedAll { + return errors.Errorf("no arguments are needed with --all") + } + + if ignoreArgLen { + return nil + } + + if argLen > 0 { + if specifiedLatest { + return errors.Errorf("no arguments are needed with --latest") + } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { + return errors.Errorf("no arguments are needed with --latest or --pod-id-file") + } + } + + if specifiedPodIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile { + return errors.Errorf("you must provide at least one name or id") } return nil } diff --git a/cmd/podman/validate/latest.go b/cmd/podman/validate/latest.go new file mode 100644 index 000000000..0ebed7227 --- /dev/null +++ b/cmd/podman/validate/latest.go @@ -0,0 +1,15 @@ +package validate + +import ( + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/spf13/cobra" +) + +func AddLatestFlag(cmd *cobra.Command, b *bool) { + // Initialization flag verification + cmd.Flags().BoolVarP(b, "latest", "l", false, + "Act on the latest container podman is aware of\nNot supported with the \"--remote\" flag") + if registry.IsRemote() { + _ = cmd.Flags().MarkHidden("latest") + } +} diff --git a/cmd/podman/volumes/volume.go b/cmd/podman/volumes/volume.go index 4514be88a..3ef20a027 100644 --- a/cmd/podman/volumes/volume.go +++ b/cmd/podman/volumes/volume.go @@ -13,11 +13,10 @@ var ( // Command: podman _volume_ volumeCmd = &cobra.Command{ - Use: "volume", - Short: "Manage volumes", - Long: "Volumes are created in and can be shared between containers", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "volume", + Short: "Manage volumes", + Long: "Volumes are created in and can be shared between containers", + RunE: validate.SubCommandExists, } ) diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md index 90e581e42..73d75be1f 100644 --- a/docs/source/markdown/podman-auto-update.1.md +++ b/docs/source/markdown/podman-auto-update.1.md @@ -23,6 +23,9 @@ Note that `podman auto-update` relies on systemd and requires a fully-qualified This enforcement is necessary to know which image to actually check and pull. If an image ID was used, Podman would not know which image to check/pull anymore. +Moreover, the systemd units are expected to be generated with `podman-generate-systemd --new`, or similar units that create new containers in order to run the updated images. +Systemd units that start and stop a container cannot run a new image. + ## OPTIONS **--authfile**=*path* diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 3ec91a3ad..7c2903e33 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -623,6 +623,8 @@ When specifying ranges for both, the number of container ports in the range must (e.g., `podman run -p 1234-1236:1222-1224 --name thisWorks -t busybox` but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`) With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage` +Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`). +If it is not, the container port will be randomly assigned a port on the host. Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT` **--publish-all**, **-P**=*true|false* diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 7e91a06a3..6b6ab03f6 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -632,6 +632,9 @@ Both hostPort and containerPort can be specified as a range of ports. When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. +Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`). +If it is not, the container port will be randomly assigned a port on the host. + Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**. **--publish-all**, **-P**=**true**|**false** diff --git a/docs/source/markdown/podman-system-service.1.md b/docs/source/markdown/podman-system-service.1.md index 3ae414f7a..7d18a6832 100644 --- a/docs/source/markdown/podman-system-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -13,6 +13,10 @@ If no endpoint is provided, defaults will be used. The default endpoint for a r service is *unix:/run/podman/podman.sock* and rootless is *unix:/$XDG_RUNTIME_DIR/podman/podman.sock* (for example *unix:/run/user/1000/podman/podman.sock*) +The REST API provided by **podman system service** is split into two parts: a compatibility layer offering support for the Docker v1.40 API, and a Podman-native Libpod layer. +Documentation for the latter is available at *https://docs.podman.io/en/latest/_static/api.html*. +Both APIs are versioned, but the server will not reject requests with an unsupported version set. + ## OPTIONS **--time**, **-t** diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 934fe220f..7a547e565 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1015,6 +1015,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { return err } + for _, v := range c.config.NamedVolumes { + if err := c.chownVolume(v.Name); err != nil { + return err + } + } + // With the spec complete, do an OCI create if err := c.ociRuntime.CreateContainer(c, nil); err != nil { // Fedora 31 is carrying a patch to display improved error @@ -1508,6 +1514,48 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) return vol, nil } +// Chown the specified volume if necessary. +func (c *Container) chownVolume(volumeName string) error { + vol, err := c.runtime.state.Volume(volumeName) + if err != nil { + return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID()) + } + + uid := int(c.config.Spec.Process.User.UID) + gid := int(c.config.Spec.Process.User.GID) + + vol.lock.Lock() + defer vol.lock.Unlock() + + // The volume may need a copy-up. Check the state. + if err := vol.update(); err != nil { + return err + } + + if vol.state.NeedsChown { + vol.state.NeedsChown = false + vol.state.UIDChowned = uid + vol.state.GIDChowned = gid + + if err := vol.save(); err != nil { + return err + } + err := filepath.Walk(vol.MountPoint(), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if err := os.Chown(path, uid, gid); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + return nil +} + // cleanupStorage unmounts and cleans up the container's root filesystem func (c *Container) cleanupStorage() error { if !c.state.Mounted { @@ -1854,8 +1902,8 @@ func (c *Container) unmount(force bool) error { // this should be from chrootarchive. // Container MUST be mounted before calling. func (c *Container) copyWithTarFromImage(source, dest string) error { - a := archive.NewDefaultArchiver() - + mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) + a := archive.NewArchiver(mappings) if err := c.copyOwnerAndPerms(source, dest); err != nil { return err } diff --git a/libpod/define/errors.go b/libpod/define/errors.go index d41e00648..1e9179353 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -70,6 +70,10 @@ var ( // ErrInternal indicates an internal library error ErrInternal = errors.New("internal libpod error") + // ErrPodPartialFail indicates that a pod operation was only partially + // successful, and some containers within the pod failed. + ErrPodPartialFail = errors.New("some containers failed") + // ErrDetach indicates that an attach session was manually detached by // the user. ErrDetach = utils.ErrDetach diff --git a/libpod/events.go b/libpod/events.go index 74c910004..7560940a5 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -1,6 +1,7 @@ package libpod import ( + "context" "fmt" "github.com/containers/libpod/v2/libpod/events" @@ -75,16 +76,16 @@ func (v *Volume) newVolumeEvent(status events.Status) { // Events is a wrapper function for everyone to begin tailing the events log // with options -func (r *Runtime) Events(options events.ReadOptions) error { +func (r *Runtime) Events(ctx context.Context, options events.ReadOptions) error { eventer, err := r.newEventer() if err != nil { return err } - return eventer.Read(options) + return eventer.Read(ctx, options) } // GetEvents reads the event log and returns events based on input filters -func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { +func (r *Runtime) GetEvents(ctx context.Context, filters []string) ([]*events.Event, error) { var readErr error eventChannel := make(chan *events.Event) options := events.ReadOptions{ @@ -98,7 +99,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { return nil, err } go func() { - readErr = eventer.Read(options) + readErr = eventer.Read(ctx, options) }() if readErr != nil { return nil, readErr @@ -112,7 +113,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { // GetLastContainerEvent takes a container name or ID and an event status and returns // the last occurrence of the container event -func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.Status) (*events.Event, error) { +func (r *Runtime) GetLastContainerEvent(ctx context.Context, nameOrID string, containerEvent events.Status) (*events.Event, error) { // check to make sure the event.Status is valid if _, err := events.StringToStatus(containerEvent.String()); err != nil { return nil, err @@ -122,7 +123,7 @@ func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.S fmt.Sprintf("event=%s", containerEvent), "type=container", } - containerEvents, err := r.GetEvents(filters) + containerEvents, err := r.GetEvents(ctx, filters) if err != nil { return nil, err } diff --git a/libpod/events/config.go b/libpod/events/config.go index 8fe551c5d..c34408e63 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -1,6 +1,7 @@ package events import ( + "context" "time" "github.com/pkg/errors" @@ -52,7 +53,7 @@ type Eventer interface { // Write an event to a backend Write(event Event) error // Read an event from the backend - Read(options ReadOptions) error + Read(ctx context.Context, options ReadOptions) error // String returns the type of event logger String() string } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 482435038..d341ca7b5 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -3,6 +3,7 @@ package events import ( + "context" "fmt" "strconv" "time" @@ -53,7 +54,7 @@ func (e EventJournalD) Write(ee Event) error { } // Read reads events from the journal and sends qualified events to the event channel -func (e EventJournalD) Read(options ReadOptions) error { +func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { defer close(options.EventChannel) eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until) if err != nil { diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go index 93e6fa3c9..28d0dc07e 100644 --- a/libpod/events/logfile.go +++ b/libpod/events/logfile.go @@ -1,6 +1,7 @@ package events import ( + "context" "fmt" "os" @@ -40,7 +41,7 @@ func (e EventLogFile) Write(ee Event) error { } // Reads from the log file -func (e EventLogFile) Read(options ReadOptions) error { +func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error { defer close(options.EventChannel) eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until) if err != nil { @@ -50,6 +51,17 @@ func (e EventLogFile) Read(options ReadOptions) error { if err != nil { return err } + funcDone := make(chan bool) + copy := true + go func() { + select { + case <-funcDone: + // Do nothing + case <-ctx.Done(): + copy = false + t.Kill(errors.New("hangup by client")) + } + }() for line := range t.Lines { event, err := newEventFromJSONString(line.Text) if err != nil { @@ -65,10 +77,11 @@ func (e EventLogFile) Read(options ReadOptions) error { for _, filter := range eventOptions { include = include && filter(event) } - if include { + if include && copy { options.EventChannel <- event } } + funcDone <- true return nil } diff --git a/libpod/events/nullout.go b/libpod/events/nullout.go index f3b36e609..3eca9e8db 100644 --- a/libpod/events/nullout.go +++ b/libpod/events/nullout.go @@ -1,5 +1,9 @@ package events +import ( + "context" +) + // EventToNull is an eventer type that only performs write operations // and only writes to /dev/null. It is meant for unittests only type EventToNull struct{} @@ -10,7 +14,7 @@ func (e EventToNull) Write(ee Event) error { } // Read does nothing. Do not use it. -func (e EventToNull) Read(options ReadOptions) error { +func (e EventToNull) Read(ctx context.Context, options ReadOptions) error { return nil } diff --git a/libpod/options.go b/libpod/options.go index 573806308..3120a35d7 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1485,6 +1485,19 @@ func WithVolumeGID(gid int) VolumeCreateOption { } } +// WithVolumeNeedsChown sets the NeedsChown flag for the volume. +func WithVolumeNeedsChown() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.state.NeedsChown = true + + return nil + } +} + // withSetAnon sets a bool notifying libpod that this volume is anonymous and // should be removed when containers using it are removed and volumes are // specified for removal. diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 14e6647be..a02b171e1 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -59,7 +59,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error starting some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error starting some containers") } defer p.newPodEvent(events.Start) return nil, nil @@ -139,7 +139,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers") } defer p.newPodEvent(events.Stop) return nil, nil @@ -208,7 +208,7 @@ func (p *Pod) Pause() (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error pausing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error pausing some containers") } defer p.newPodEvent(events.Pause) return nil, nil @@ -267,7 +267,7 @@ func (p *Pod) Unpause() (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error unpausing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error unpausing some containers") } defer p.newPodEvent(events.Unpause) @@ -321,7 +321,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers") } p.newPodEvent(events.Stop) p.newPodEvent(events.Start) @@ -387,7 +387,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error killing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error killing some containers") } defer p.newPodEvent(events.Kill) return nil, nil diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 4010c9bea..c073aabb5 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -309,7 +309,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai logrus.Debugf("Creating new volume %s for container", vol.Name) // The volume does not exist, so we need to create it. - volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())} + volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()), WithVolumeNeedsChown()} if isAnonymous { volOptions = append(volOptions, withSetAnon()) } diff --git a/libpod/volume.go b/libpod/volume.go index 72b080d1a..438957086 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -64,6 +64,14 @@ type VolumeState struct { // create time, then cleared after the copy up is done and never set // again. NeedsCopyUp bool `json:"notYetMounted,omitempty"` + // NeedsChown indicates that the next time the volume is mounted into + // a container, the container will chown the volume to the container process + // UID/GID. + NeedsChown bool `json:"notYetChowned,omitempty"` + // UIDChowned is the UID the volume was chowned to. + UIDChowned int `json:"uidChowned,omitempty"` + // GIDChowned is the GID the volume was chowned to. + GIDChowned int `json:"gidChowned,omitempty"` } // Name retrieves the volume's name @@ -113,13 +121,33 @@ func (v *Volume) Anonymous() bool { } // UID returns the UID the volume will be created as. -func (v *Volume) UID() int { - return v.config.UID +func (v *Volume) UID() (int, error) { + v.lock.Lock() + defer v.lock.Unlock() + + if !v.valid { + return -1, define.ErrVolumeRemoved + } + + if v.state.UIDChowned > 0 { + return v.state.UIDChowned, nil + } + return v.config.UID, nil } // GID returns the GID the volume will be created as. -func (v *Volume) GID() int { - return v.config.GID +func (v *Volume) GID() (int, error) { + v.lock.Lock() + defer v.lock.Unlock() + + if !v.valid { + return -1, define.ErrVolumeRemoved + } + + if v.state.GIDChowned > 0 { + return v.state.GIDChowned, nil + } + return v.config.GID, nil } // CreatedTime returns the time the volume was created at. It was not tracked diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go index 5258792eb..85848f84f 100644 --- a/libpod/volume_inspect.go +++ b/libpod/volume_inspect.go @@ -65,8 +65,15 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) { for k, v := range v.config.Options { data.Options[k] = v } - data.UID = v.config.UID - data.GID = v.config.GID + var err error + data.UID, err = v.UID() + if err != nil { + return nil, err + } + data.GID, err = v.GID() + if err != nil { + return nil, err + } data.Anonymous = v.config.IsAnon return data, nil diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 215d7c972..5acc94153 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -1,6 +1,7 @@ package compat import ( + "context" "fmt" "net/http" @@ -45,13 +46,15 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { fromStart = true } + eventCtx, eventCancel := context.WithCancel(r.Context()) eventChannel := make(chan *events.Event) go func() { readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until} - eventsError = runtime.Events(readOpts) + eventsError = runtime.Events(eventCtx, readOpts) }() if eventsError != nil { utils.InternalServerError(w, eventsError) + eventCancel() close(eventChannel) return } @@ -59,6 +62,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { // If client disappears we need to stop listening for events go func(done <-chan struct{}) { <-done + eventCancel() if _, ok := <-eventChannel; ok { close(eventChannel) } diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index fe7d8888e..2e11c0edb 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -285,7 +285,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { return } if !exists { - utils.Error(w, "network not found", http.StatusNotFound, err) + utils.Error(w, "network not found", http.StatusNotFound, network.ErrNetworkNotFound) return } if err := network.RemoveNetwork(config, name); err != nil { diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index f9a651c5d..0d83218e3 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -86,6 +86,17 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } + var uid, gid int + uid, err = vol.UID() + if err != nil { + utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) + return + } + gid, err = vol.GID() + if err != nil { + utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + return + } volResponse := entities.VolumeConfigResponse{ Name: vol.Name(), Driver: vol.Driver(), @@ -94,8 +105,8 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { Labels: vol.Labels(), Scope: vol.Scope(), Options: vol.Options(), - UID: vol.UID(), - GID: vol.GID(), + UID: uid, + GID: gid, } utils.WriteResponse(w, http.StatusOK, volResponse) } @@ -130,6 +141,17 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) + return + } + gid, err = v.GID() + if err != nil { + utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + return + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -138,8 +160,8 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config}) } diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index d68f6893a..8af6d3186 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -173,6 +173,10 @@ func (s *APIServer) Serve() error { }() } + // Before we start serving, ensure umask is properly set for container + // creation. + _ = syscall.Umask(0022) + go func() { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index d5dce0b0f..596fc2cc1 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -741,7 +741,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = define.ExecErrorCodeNotFound @@ -871,7 +871,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) report.ExitCode = define.ExecErrorCodeNotFound diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go index 50d7727ce..7a8185445 100644 --- a/pkg/domain/infra/abi/events.go +++ b/pkg/domain/infra/abi/events.go @@ -9,5 +9,5 @@ import ( func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { readOpts := events.ReadOptions{FromStart: opts.FromStart, Stream: opts.Stream, Filters: opts.Filter, EventChannel: opts.EventChan, Since: opts.Since, Until: opts.Until} - return ic.Libpod.Events(readOpts) + return ic.Libpod.Events(ctx, readOpts) } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index e8d55989f..d1f465362 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -67,14 +67,14 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt for _, p := range pods { report := entities.PodKillReport{Id: p.ID()} conErrs, err := p.Kill(uint(sig)) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(conErrs) > 0 { - for _, err := range conErrs { - report.Errs = append(report.Errs, err) + for id, err := range conErrs { + report.Errs = append(report.Errs, errors.Wrapf(err, "error killing container %s", id)) } reports = append(reports, &report) continue @@ -93,13 +93,13 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op for _, p := range pods { report := entities.PodPauseReport{Id: p.ID()} errs, err := p.Pause() - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error pausing container %s", id)) } reports = append(reports, &report) continue @@ -118,13 +118,13 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, for _, p := range pods { report := entities.PodUnpauseReport{Id: p.ID()} errs, err := p.Unpause() - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error unpausing container %s", id)) } reports = append(reports, &report) continue @@ -143,13 +143,13 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt for _, p := range pods { report := entities.PodStopReport{Id: p.ID()} errs, err := p.StopWithTimeout(ctx, false, options.Timeout) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error stopping container %s", id)) } reports = append(reports, &report) continue @@ -168,14 +168,14 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, for _, p := range pods { report := entities.PodRestartReport{Id: p.ID()} errs, err := p.Restart(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error restarting container %s", id)) } reports = append(reports, &report) continue @@ -195,14 +195,14 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op for _, p := range pods { report := entities.PodStartReport{Id: p.ID()} errs, err := p.Start(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error starting container %s", id)) } reports = append(reports, &report) continue diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 861617eb6..8db89899e 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -95,6 +95,15 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports := make([]*entities.VolumeInspectReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + return nil, err + } + gid, err = v.GID() + if err != nil { + return nil, err + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -103,8 +112,8 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } @@ -141,6 +150,15 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL } reports := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + return nil, err + } + gid, err = v.GID() + if err != nil { + return nil, err + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -149,8 +167,8 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config}) } diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index b1d6be016..879c66895 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -287,10 +287,11 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l options = append(options, libpod.WithCommand(c.UserCommand)) } - // Add entrypoint unconditionally - // If it's empty it's because it was explicitly set to "" or the image - // does not have one - options = append(options, libpod.WithEntrypoint(c.Entrypoint)) + // Add entrypoint if it was set + // If it's empty it's because it was explicitly set to "" + if c.Entrypoint != nil { + options = append(options, libpod.WithEntrypoint(c.Entrypoint)) + } // TODO: MNT, USER, CGROUP options = append(options, libpod.WithStopSignal(c.StopSignal)) diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 3732d5431..0a485e7cd 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -52,10 +52,14 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) } - current = rlimit.Cur - max = rlimit.Max + if rlimit.Cur < current { + current = rlimit.Cur + } + if rlimit.Max < max { + max = rlimit.Max + } } - g.AddProcessRlimits("RLIMIT_NOFILE", current, max) + g.AddProcessRlimits("RLIMIT_NOFILE", max, current) } if !nprocSet { max := kernelMax @@ -65,10 +69,14 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) } - current = rlimit.Cur - max = rlimit.Max + if rlimit.Cur < current { + current = rlimit.Cur + } + if rlimit.Max < max { + max = rlimit.Max + } } - g.AddProcessRlimits("RLIMIT_NPROC", current, max) + g.AddProcessRlimits("RLIMIT_NPROC", max, current) } return nil diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index b529fd4cd..9412ecfbf 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -43,6 +43,8 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, containerPortValidate[proto] = make(map[string]map[uint16]uint16) } + postAssignHostPort := false + // Iterate through all port mappings, generating OCICNI PortMapping // structs and validating there is no overlap. for _, port := range portMappings { @@ -71,9 +73,6 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, return nil, nil, nil, errors.Errorf("container port number must be non-0") } hostPort := port.HostPort - if hostPort == 0 { - hostPort = containerPort - } if uint32(len-1)+uint32(containerPort) > 65535 { return nil, nil, nil, errors.Errorf("container port range exceeds maximum allowable port number") } @@ -105,26 +104,42 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, cPort := containerPort + index hPort := hostPort + index - if cPort == 0 || hPort == 0 { - return nil, nil, nil, errors.Errorf("host and container ports cannot be 0") - } - - testCPort := ctrPortMap[cPort] - if testCPort != 0 && testCPort != hPort { - // This is an attempt to redefine a port - return nil, nil, nil, errors.Errorf("conflicting port mappings for container port %d (protocol %s)", cPort, p) + if cPort == 0 { + return nil, nil, nil, errors.Errorf("container port cannot be 0") } - ctrPortMap[cPort] = hPort - testHPort := hostPortMap[hPort] - if testHPort != 0 && testHPort != cPort { - return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p) - } - hostPortMap[hPort] = cPort - - // If we have an exact duplicate, just continue - if testCPort == hPort && testHPort == cPort { - continue + // Host port is allowed to be 0. If it is, we + // select a random port on the host. + // This will happen *after* all other ports are + // placed, to ensure we don't accidentally + // select a port that a later mapping wanted. + if hPort == 0 { + // If we already have a host port + // assigned to their container port - + // just use that. + if ctrPortMap[cPort] != 0 { + hPort = ctrPortMap[cPort] + } else { + postAssignHostPort = true + } + } else { + testCPort := ctrPortMap[cPort] + if testCPort != 0 && testCPort != hPort { + // This is an attempt to redefine a port + return nil, nil, nil, errors.Errorf("conflicting port mappings for container port %d (protocol %s)", cPort, p) + } + ctrPortMap[cPort] = hPort + + testHPort := hostPortMap[hPort] + if testHPort != 0 && testHPort != cPort { + return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p) + } + hostPortMap[hPort] = cPort + + // If we have an exact duplicate, just continue + if testCPort == hPort && testHPort == cPort { + continue + } } // We appear to be clear. Make an OCICNI port @@ -142,6 +157,61 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, } } + // Handle any 0 host ports now by setting random container ports. + if postAssignHostPort { + remadeMappings := make([]ocicni.PortMapping, 0, len(finalMappings)) + + // Iterate over all + for _, p := range finalMappings { + if p.HostPort != 0 { + remadeMappings = append(remadeMappings, p) + continue + } + + hostIPMap := hostPortValidate[p.Protocol] + ctrIPMap := containerPortValidate[p.Protocol] + + hostPortMap, ok := hostIPMap[p.HostIP] + if !ok { + hostPortMap = make(map[uint16]uint16) + hostIPMap[p.HostIP] = hostPortMap + } + ctrPortMap, ok := ctrIPMap[p.HostIP] + if !ok { + ctrPortMap = make(map[uint16]uint16) + ctrIPMap[p.HostIP] = ctrPortMap + } + + // See if container port has been used elsewhere + if ctrPortMap[uint16(p.ContainerPort)] != 0 { + // Duplicate definition. Let's not bother + // including it. + continue + } + + // Max retries to ensure we don't loop forever. + for i := 0; i < 15; i++ { + candidate, err := getRandomPort() + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort) + } + + if hostPortMap[uint16(candidate)] == 0 { + logrus.Debugf("Successfully assigned container port %d to host port %d (IP %s Protocol %s)", p.ContainerPort, candidate, p.HostIP, p.Protocol) + hostPortMap[uint16(candidate)] = uint16(p.ContainerPort) + ctrPortMap[uint16(p.ContainerPort)] = uint16(candidate) + p.HostPort = int32(candidate) + break + } + } + if p.HostPort == 0 { + return nil, nil, nil, errors.Errorf("could not find open host port to map container port %d to", p.ContainerPort) + } + remadeMappings = append(remadeMappings, p) + } + return remadeMappings, containerPortValidate, hostPortValidate, nil + } + return finalMappings, containerPortValidate, hostPortValidate, nil } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 03e840ab4..327c15c5a 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -430,7 +430,8 @@ type PortMapping struct { ContainerPort uint16 `json:"container_port"` // HostPort is the port number that will be forwarded from the host into // the container. - // If omitted, will be assumed to be identical to + // If omitted, a random port on the host (guaranteed to be over 1024) + // will be assigned. HostPort uint16 `json:"host_port,omitempty"` // Range is the number of ports that will be forwarded, starting at // HostPort and ContainerPort and counting up. diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index fe56dc874..d6d18a810 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -1,6 +1,8 @@ package generate import ( + "strings" + "github.com/pkg/errors" ) @@ -44,6 +46,9 @@ func filterPodFlags(command []string) []string { i++ continue } + if strings.HasPrefix(s, "--pod=") || strings.HasPrefix(s, "--pod-id-file=") { + continue + } processed = append(processed, s) } return processed diff --git a/pkg/systemd/generate/common_test.go b/pkg/systemd/generate/common_test.go index f53bb7828..389c30f59 100644 --- a/pkg/systemd/generate/common_test.go +++ b/pkg/systemd/generate/common_test.go @@ -1,6 +1,7 @@ package generate import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -14,12 +15,16 @@ func TestFilterPodFlags(t *testing.T) { {[]string{"podman", "pod", "create"}}, {[]string{"podman", "pod", "create", "--name", "foo"}}, {[]string{"podman", "pod", "create", "--pod-id-file", "foo"}}, + {[]string{"podman", "pod", "create", "--pod-id-file=foo"}}, {[]string{"podman", "run", "--pod", "foo"}}, + {[]string{"podman", "run", "--pod=foo"}}, } for _, test := range tests { processed := filterPodFlags(test.input) - assert.NotContains(t, processed, "--pod-id-file") - assert.NotContains(t, processed, "--pod") + for _, s := range processed { + assert.False(t, strings.HasPrefix(s, "--pod-id-file")) + assert.False(t, strings.HasPrefix(s, "--pod")) + } } } diff --git a/pkg/terminal/console_unix.go b/pkg/terminal/console_unix.go new file mode 100644 index 000000000..6eee6aa2f --- /dev/null +++ b/pkg/terminal/console_unix.go @@ -0,0 +1,8 @@ +// +build !windows + +package terminal + +// SetConsole for non-windows environments is a no-op +func SetConsole() error { + return nil +} diff --git a/pkg/terminal/console_windows.go b/pkg/terminal/console_windows.go new file mode 100644 index 000000000..c7691857c --- /dev/null +++ b/pkg/terminal/console_windows.go @@ -0,0 +1,37 @@ +// +build windows + +package terminal + +import ( + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +// SetConsole switches the windows terminal mode to be able to handle colors, etc +func SetConsole() error { + if err := setConsoleMode(windows.Stdout, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + if err := setConsoleMode(windows.Stderr, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + if err := setConsoleMode(windows.Stdin, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + return nil +} + +func setConsoleMode(handle windows.Handle, flags uint32) error { + var mode uint32 + err := windows.GetConsoleMode(handle, &mode) + if err != nil { + return err + } + if err := windows.SetConsoleMode(handle, mode|flags); err != nil { + // In similar code, it is not considered an error if we cannot set the + // console mode. Following same line of thinking here. + logrus.WithError(err).Error("Failed to set console mode for cli") + } + + return nil +} diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 731d89b8f..8acf2a1b6 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -4,6 +4,7 @@ package varlinkapi import ( "bufio" + "context" "io" "github.com/containers/libpod/v2/libpod" @@ -89,7 +90,7 @@ func (i *VarlinkAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys s if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := i.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := i.Runtime.GetLastContainerEvent(context.Background(), ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = define.ExecErrorCodeNotFound diff --git a/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go index 2e468b706..910b64a57 100644 --- a/pkg/varlinkapi/events.go +++ b/pkg/varlinkapi/events.go @@ -3,6 +3,7 @@ package varlinkapi import ( + "context" "time" "github.com/containers/libpod/v2/libpod/events" @@ -27,7 +28,7 @@ func (i *VarlinkAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since eventChannel := make(chan *events.Event) go func() { readOpts := events.ReadOptions{FromStart: fromStart, Stream: stream, Filters: filter, EventChannel: eventChannel} - eventsError = i.Runtime.Events(readOpts) + eventsError = i.Runtime.Events(context.Background(), readOpts) }() if eventsError != nil { return call.ReplyErrorOccurred(eventsError.Error()) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 87c476ecc..6633f3a53 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -143,6 +143,12 @@ var _ = SynchronizedBeforeSuite(func() []byte { fmt.Println(err) os.Exit(1) } + + // If running remote, we need to stop the associated podman system service + if podman.RemoteTest { + podman.StopRemoteService() + } + return []byte(path) }, func(data []byte) { LockTmpDir = string(data) @@ -173,6 +179,10 @@ var _ = SynchronizedAfterSuite(func() {}, fmt.Printf("%q\n", err) } + // If running remote, we need to stop the associated podman system service + if podmanTest.RemoteTest { + podmanTest.StopRemoteService() + } // for localized tests, this removes the image cache dir and for remote tests // this is a no-op removeCache() diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 3d583bb8c..152c85704 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -466,4 +466,15 @@ var _ = Describe("Podman ps", func() { Expect(ps.ExitCode()).To(Equal(0)) Expect(ps.OutputToString()).To(ContainSubstring("0.0.0.0:8080->80/tcp")) }) + + It("podman ps truncate long create commad", func() { + session := podmanTest.Podman([]string{"run", ALPINE, "echo", "very", "long", "create", "command"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"ps", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring("echo very long cr...")) + }) + }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index dd018b910..6c049c5c1 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -184,6 +184,30 @@ var _ = Describe("Podman run networking", func() { Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) + It("podman run -p 127.0.0.1::8080/udp", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1::8080/udp", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports["8080/udp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostPort).To(Not(Equal("8080"))) + Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostIP).To(Equal("127.0.0.1")) + }) + + It("podman run -p :8080", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", ":8080", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080"))) + Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) + }) + It("podman run network expose host port 80 to container port 8000", func() { SkipIfRootless() session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"}) diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index e48a427d2..c0d98f7b1 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -245,4 +245,31 @@ var _ = Describe("Podman UserNS support", func() { ok, _ := session.GrepString("4998") Expect(ok).To(BeTrue()) }) + + It("podman --user with volume", func() { + tests := []struct { + uid, gid, arg, vol string + }{ + {"0", "0", "0:0", "vol-0"}, + {"1000", "0", "1000", "vol-1"}, + {"1000", "1000", "1000:1000", "vol-2"}, + } + + for _, tt := range tests { + session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol}) + inspectUID.WaitWithDefaultTimeout() + Expect(inspectUID.ExitCode()).To(Equal(0)) + Expect(inspectUID.OutputToString()).To(Equal(tt.uid)) + + // Make sure we're defaulting to 0. + inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol}) + inspectGID.WaitWithDefaultTimeout() + Expect(inspectGID.ExitCode()).To(Equal(0)) + Expect(inspectGID.OutputToString()).To(Equal(tt.gid)) + } + }) }) |