diff options
27 files changed, 129 insertions, 67 deletions
diff --git a/cmd/podman/containers/commit.go b/cmd/podman/containers/commit.go index b3c3d7626..79e2a32a7 100644 --- a/cmd/podman/containers/commit.go +++ b/cmd/podman/containers/commit.go @@ -22,7 +22,7 @@ var ( Short: "Create new image based on the changed container", Long: commitDescription, RunE: commit, - Args: cobra.MinimumNArgs(1), + Args: cobra.RangeArgs(1, 2), Example: `podman commit -q --message "committing container to image" reverent_golick image-committed podman commit -q --author "firstName lastName" reverent_golick image-committed podman commit -q --pause=false containerID image-committed @@ -30,7 +30,7 @@ var ( } containerCommitCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), + Args: commitCommand.Args, Use: commitCommand.Use, Short: commitCommand.Short, Long: commitCommand.Long, @@ -82,7 +82,7 @@ func init() { func commit(cmd *cobra.Command, args []string) error { container := args[0] - if len(args) > 1 { + if len(args) == 2 { commitOptions.ImageName = args[1] } if !commitOptions.Quiet { diff --git a/cmd/podman/containers/export.go b/cmd/podman/containers/export.go index bbb6a6bc9..66e768ccb 100644 --- a/cmd/podman/containers/export.go +++ b/cmd/podman/containers/export.go @@ -28,7 +28,7 @@ var ( } containerExportCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), + Args: cobra.ExactArgs(1), Use: exportCommand.Use, Short: exportCommand.Short, Long: exportCommand.Long, diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index 8556ebe83..e49fcc2e0 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -10,7 +10,7 @@ import ( var ( // podman container _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] CONTAINER", + Use: "inspect [flags] CONTAINER [CONTAINER...]", Short: "Display the configuration of a container", Long: `Displays the low-level information on a container identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index 7f15616de..5c73cceaa 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -23,7 +23,7 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [flags] [CONTAINER]", + Use: "mount [flags] [CONTAINER...]", Short: "Mount a working container's root filesystem", Long: mountDescription, RunE: mount, diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index d058a6aaf..115adc2a7 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -89,6 +89,9 @@ func port(cmd *cobra.Command, args []string) error { container = args[0] } port := "" + if len(args) > 2 { + return errors.Errorf("`port` accepts at most 2 arguments") + } if len(args) > 1 && !portOpts.Latest { port = args[1] } diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index d2b11ec77..afab12a14 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -25,7 +25,7 @@ var ( topOptions = entities.TopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS]", + Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of a container", Long: topDescription, RunE: top, diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index e4fdd8690..b4ab0f9df 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -20,7 +20,7 @@ var ( Short: "Generate systemd units.", Long: systemdDescription, RunE: systemd, - Args: cobra.MinimumNArgs(1), + Args: cobra.ExactArgs(1), Example: `podman generate systemd CTR podman generate systemd --new --time 10 CTR podman generate systemd --files --name POD`, diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 23bfcab79..dfde896a1 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -45,6 +45,7 @@ var ( Long: buildDescription, TraverseChildren: true, RunE: build, + Args: cobra.MaximumNArgs(1), 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/inspect.go b/cmd/podman/images/inspect.go index f6a10ba44..a1a9e91eb 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -10,7 +10,7 @@ import ( var ( // Command: podman image _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [flags] IMAGE", + Use: "inspect [flags] IMAGE [IMAGE...]", Short: "Display the configuration of an image", Long: `Displays the low-level information of an image identified by name or ID.`, RunE: inspectExec, diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index b7a8b8911..de7cca40d 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -32,7 +32,7 @@ type listFlagType struct { var ( // Command: podman image _list_ listCmd = &cobra.Command{ - Use: "list [FLAGS] [IMAGE]", + Use: "list [flags] [IMAGE]", Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), Short: "List images in local storage", diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index a1614dc7a..7af2d9343 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -29,10 +29,11 @@ var ( // Command: podman push pushCmd = &cobra.Command{ - Use: "push [flags] SOURCE DESTINATION", + Use: "push [flags] SOURCE [DESTINATION]", Short: "Push an image to a specified destination", Long: pushDescription, RunE: imagePush, + Args: cobra.RangeArgs(1, 2), Example: `podman push imageID docker://registry.example.com/repository:tag podman push imageID oci-archive:/path/to/layout:image:tag`, } @@ -45,6 +46,7 @@ var ( Short: pushCmd.Short, Long: pushCmd.Long, RunE: pushCmd.RunE, + Args: pushCmd.Args, Example: `podman image push imageID docker://registry.example.com/repository:tag podman image push imageID oci-archive:/path/to/layout:image:tag`, } @@ -96,19 +98,8 @@ func pushFlags(flags *pflag.FlagSet) { // imagePush is implement the command for pushing images. func imagePush(cmd *cobra.Command, args []string) error { - var source, destination string - switch len(args) { - case 1: - source = args[0] - destination = args[0] - case 2: - source = args[0] - destination = args[1] - case 0: - fallthrough - default: - return errors.New("push requires at least one image name, or optionally a second to specify a different destination") - } + source := args[0] + destination := args[len(args)-1] // TLS verification in c/image is controlled via a `types.OptionalBool` // which allows for distinguishing among set-true, set-false, unspecified diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index 56953e41c..9b03c1383 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -23,8 +23,8 @@ var ( saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.` saveCommand = &cobra.Command{ - Use: "save [flags] IMAGE", - Short: "Save image to an archive", + Use: "save [flags] IMAGE [IMAGE...]", + Short: "Save image(s) to an archive", Long: saveDescription, RunE: save, Args: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index 23ee6c709..dbaa800a4 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -18,6 +18,7 @@ var ( Short: "Display trust policy for the system", Long: showTrustDescription, RunE: showTrust, + Args: cobra.MaximumNArgs(1), Example: "", } ) diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index a5fdaedc2..6c4607d88 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -10,7 +10,7 @@ import ( var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", + 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, diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go index 9c0097b90..2ab1fccea 100644 --- a/cmd/podman/manifest/create.go +++ b/cmd/podman/manifest/create.go @@ -20,7 +20,7 @@ var ( Example: `podman manifest create mylist:v1.11 podman manifest create mylist:v1.11 arch-specific-image-to-add podman manifest create --all mylist:v1.11 transport:tagged-image-to-add`, - Args: cobra.MinimumNArgs(1), + Args: cobra.RangeArgs(1, 2), } ) diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 5d28c7140..2d29beddd 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -8,7 +8,6 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/network" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -20,6 +19,7 @@ var ( Short: "network create", Long: networkCreateDescription, RunE: networkCreate, + Args: cobra.MaximumNArgs(1), Example: `podman network create podman1`, Annotations: map[string]string{ registry.ParentNSRequired: "", @@ -62,14 +62,10 @@ func networkCreate(cmd *cobra.Command, args []string) error { if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil { return err } - if len(args) > 1 { - return errors.Errorf("only one network can be created at a time") - } - if len(args) > 0 && !define.NameRegex.MatchString(args[0]) { - return define.RegexError - } - if len(args) > 0 { + if !define.NameRegex.MatchString(args[0]) { + return define.RegexError + } name = args[0] } response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions) diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index 1b2e89909..31269e836 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -16,7 +16,7 @@ import ( var ( networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ - Use: "inspect NETWORK [NETWORK...] [flags] ", + Use: "inspect [flags] NETWORK [NETWORK...]", Short: "network inspect", Long: networkinspectDescription, RunE: networkInspect, diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index ba1efb638..8df00f92a 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -22,7 +22,7 @@ var ( topOptions = entities.PodTopOptions{} topCommand = &cobra.Command{ - Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS]", + Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS...]", Short: "Display the running processes of containers in a pod", Long: topDescription, RunE: top, diff --git a/cmd/podman/system/connection.go b/cmd/podman/system/connection.go index d8c709d6e..9f80a454b 100644 --- a/cmd/podman/system/connection.go +++ b/cmd/podman/system/connection.go @@ -29,7 +29,7 @@ var ( return nil } connectionCmd = &cobra.Command{ - Use: "connection [flags] destination", + Use: "connection [flags] DESTINATION", Args: cobra.ExactArgs(1), Long: `Store ssh destination information in podman configuration. "destination" is of the form [user@]hostname or @@ -42,7 +42,7 @@ var ( RunE: connection, Example: `podman system connection server.fubar.com podman system connection --identity ~/.ssh/dev_rsa ssh://root@server.fubar.com:2222 - podman system connection --identity ~/.ssh/dev_rsa -port 22 root@server.fubar.com`, + podman system connection --identity ~/.ssh/dev_rsa --port 22 root@server.fubar.com`, } cOpts = struct { @@ -202,7 +202,7 @@ func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) { return "", errors.Wrapf(err, "failed to parse 'podman info' results") } - if info.Host.RemoteSocket == nil || !info.Host.RemoteSocket.Exists { + if info.Host.RemoteSocket == nil || len(info.Host.RemoteSocket.Path) == 0 { return "", fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host) } return info.Host.RemoteSocket.Path, nil diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go index 7db5d36d2..e5d41e06d 100644 --- a/cmd/podman/system/unshare.go +++ b/cmd/podman/system/unshare.go @@ -13,7 +13,7 @@ import ( var ( unshareDescription = "Runs a command in a modified user namespace." unshareCommand = &cobra.Command{ - Use: "unshare [flags] [COMMAND [ARG]]", + Use: "unshare [flags] [COMMAND [ARG ...]]", Short: "Run a command in a modified user namespace", Long: unshareDescription, RunE: unshare, diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index 69240798f..d170447ee 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -25,8 +25,11 @@ 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()) + } + if len(args) == 0 && !cmd.Flag("latest").Changed { + return fmt.Errorf("`%s` requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) } return nil } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 73e0b2118..db64f5eeb 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -22,6 +22,7 @@ import ( "github.com/containers/libpod/pkg/selinux" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/mount" securejoin "github.com/cyphar/filepath-securejoin" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -360,6 +361,25 @@ func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) { } dest.AutoUserNsOpts.InitialSize = initialSize + 1 } + } else if c.config.Spec.Linux != nil { + dest.UIDMap = nil + for _, r := range c.config.Spec.Linux.UIDMappings { + u := idtools.IDMap{ + ContainerID: int(r.ContainerID), + HostID: int(r.HostID), + Size: int(r.Size), + } + dest.UIDMap = append(dest.UIDMap, u) + } + dest.GIDMap = nil + for _, r := range c.config.Spec.Linux.GIDMappings { + g := idtools.IDMap{ + ContainerID: int(r.ContainerID), + HostID: int(r.HostID), + Size: int(r.Size), + } + dest.GIDMap = append(dest.GIDMap, g) + } } } diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go index 93b972b6b..1d5abd830 100644 --- a/pkg/api/server/register_volumes.go +++ b/pkg/api/server/register_volumes.go @@ -28,7 +28,7 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // swagger:operation GET /libpod/volumes/json volumes listVolumes // --- // summary: List volumes - // description: Returns a list of networks + // description: Returns a list of volumes // produces: // - application/json // parameters: @@ -36,7 +36,7 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // name: filters // type: string // description: | - // JSON encoded value of the filters (a map[string][]string) to process on the networks list. Available filters: + // JSON encoded value of the filters (a map[string][]string) to process on the volumes list. Available filters: // - driver=<volume-driver-name> Matches volumes based on their driver. // - label=<key> or label=<key>:<value> Matches volumes based on the presence of a label alone or a label and a value. // - name=<volume-name> Matches all of volume name. diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 584aa55c1..c02d55e31 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -181,12 +181,15 @@ func pingNewConnection(ctx context.Context) error { func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) { authMethods := []ssh.AuthMethod{} - auth, err := terminal.PublicKey(identity, []byte(passPhrase)) - if err != nil { - return Connection{}, errors.Wrapf(err, "failed to parse identity %q", identity) + + if len(identity) > 0 { + auth, err := terminal.PublicKey(identity, []byte(passPhrase)) + if err != nil { + return Connection{}, errors.Wrapf(err, "failed to parse identity %q", identity) + } + logrus.Debugf("public key signer enabled for identity %q", identity) + authMethods = append(authMethods, auth) } - logrus.Debugf("public key signer enabled for identity %q", identity) - authMethods = append(authMethods, auth) if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock) diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index 5b9a99daa..be0981408 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -89,6 +89,13 @@ var _ = Describe("Podman UserNS support", func() { Expect(ok).To(BeTrue()) }) + It("podman --userns=keep-id root owns /usr", func() { + session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0")) + }) + It("podman --userns=keep-id --user root:root", func() { session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"}) session.WaitWithDefaultTimeout() diff --git a/test/system/015-help.bats b/test/system/015-help.bats index 42d3bd3ec..14af8e1a4 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -25,7 +25,7 @@ function podman_commands() { function check_help() { local count=0 - local subcommands_found=0 + local -A found for cmd in $(podman_commands "$@"); do # Human-readable podman command string, with multiple spaces collapsed @@ -44,24 +44,37 @@ function check_help() { # If usage ends in '[command]', recurse into subcommands if expr "$usage" : '.*\[command\]$' >/dev/null; then - subcommands_found=$(expr $subcommands_found + 1) + found[subcommands]=1 check_help "$@" $cmd continue fi - # If usage ends in '[flag]', command takes no more arguments. - # Confirm that by running with 'invalid-arg' and expecting failure. - if expr "$usage" : '.*\[flags\]$' >/dev/null; then + # We had someone write upper-case '[FLAGS]' once. Prevent it. + if expr "$usage" : '.*\[FLAG' >/dev/null; then + die "'flags' string must be lower-case in usage: $usage" + fi + + # We had someone do 'podman foo ARG [flags]' one time. Yeah, no. + if expr "$usage" : '.*[A-Z].*\[flag' >/dev/null; then + die "'flags' must precede arguments in usage: $usage" + fi + + # If usage lists no arguments (strings in ALL CAPS), confirm + # by running with 'invalid-arg' and expecting failure. + if ! expr "$usage" : '.*[A-Z]' >/dev/null; then if [ "$cmd" != "help" ]; then dprint "$command_string invalid-arg" run_podman 125 "$@" $cmd invalid-arg is "$output" "Error: .* takes no arguments" \ "'$command_string' with extra (invalid) arguments" fi + found[takes_no_args]=1 fi - # If usage has required arguments, try running without them - if expr "$usage" : '.*\[flags\] [A-Z]' >/dev/null; then + # If usage has required arguments, try running without them. + # The expression here is 'first capital letter is not in [BRACKETS]'. + # It is intended to handle 'podman foo [flags] ARG' but not ' [ARG]'. + if expr "$usage" : '[^A-Z]\+ [A-Z]' >/dev/null; then # Exceptions: these commands don't work rootless if is_rootless; then # "pause is not supported for rootless containers" @@ -80,6 +93,31 @@ function check_help() { run_podman 125 "$@" $cmd </dev/null is "$output" "Error:.* \(require\|specif\|must\|provide\|need\|choose\|accepts\)" \ "'$command_string' without required arg" + + found[required_args]=1 + fi + + # Commands with fixed number of arguments (i.e. no ellipsis): count + # the required args, then invoke with one extra. We should get a + # usage error. + if ! expr "$usage" : ".*\.\.\."; then + # "podman help" can take infinite args, so skip that one + if [ "$cmd" != "help" ]; then + # Get the args part of the command line; this should be + # everything from the first CAPITAL LETTER onward. We + # don't actually care about the letter itself, so just + # make it 'X'. And we don't care about [OPTIONAL] brackets + # either. What we do care about is stuff like 'IMAGE | CTR' + # which is actually one argument; convert to 'IMAGE-or-CTR' + local rhs=$(sed -e 's/^[^A-Z]\+[A-Z]/X/' -e 's/ | /-or-/g' <<<"$usage") + local n_args=$(wc -w <<<"$rhs") + + run_podman 125 "$@" $cmd $(seq --format='x%g' 0 $n_args) + is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \ + "'$command_string' with >$n_args arguments" + + found[fixed_args]=1 + fi fi count=$(expr $count + 1) @@ -101,9 +139,14 @@ function check_help() { [ $count -gt 0 ] || \ die "Internal error: no commands found in 'podman help $@' list" - # At least the top level must have some subcommands - if [ -z "$*" -a $subcommands_found -eq 0 ]; then - die "Internal error: did not find any podman subcommands" + # Sanity check: make sure the special loops above triggered at least once. + # (We've had situations where a typo makes the conditional never run) + if [ -z "$*" ]; then + for i in subcommands required_args takes_no_args fixed_args; do + if [[ -z ${found[$i]} ]]; then + die "Internal error: '$i' subtest did not trigger" + fi + done fi } diff --git a/test/system/120-load.bats b/test/system/120-load.bats index f290c1888..afa5ab473 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -107,10 +107,4 @@ verify_iid_and_name() { "Diagnostic from 'podman load' without redirection or -i" } -@test "podman load - at most 1 arg(s)" { - run_podman 125 load 1 2 3 - is "$output" \ - "Error: accepts at most 1 arg(s), received 3" -} - # vim: filetype=sh |