summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/containers/commit.go6
-rw-r--r--cmd/podman/containers/export.go2
-rw-r--r--cmd/podman/containers/inspect.go2
-rw-r--r--cmd/podman/containers/mount.go2
-rw-r--r--cmd/podman/containers/port.go3
-rw-r--r--cmd/podman/containers/top.go2
-rw-r--r--cmd/podman/generate/systemd.go2
-rw-r--r--cmd/podman/images/build.go1
-rw-r--r--cmd/podman/images/inspect.go2
-rw-r--r--cmd/podman/images/list.go2
-rw-r--r--cmd/podman/images/push.go19
-rw-r--r--cmd/podman/images/save.go4
-rw-r--r--cmd/podman/images/trust_show.go1
-rw-r--r--cmd/podman/inspect.go2
-rw-r--r--cmd/podman/manifest/create.go2
-rw-r--r--cmd/podman/networks/create.go12
-rw-r--r--cmd/podman/networks/inspect.go2
-rw-r--r--cmd/podman/pods/top.go2
-rw-r--r--cmd/podman/system/connection.go6
-rw-r--r--cmd/podman/system/unshare.go2
-rw-r--r--cmd/podman/validate/args.go7
-rw-r--r--libpod/container_internal.go20
-rw-r--r--pkg/api/server/register_networks.go10
-rw-r--r--pkg/api/server/register_volumes.go4
-rw-r--r--pkg/bindings/connection.go13
-rw-r--r--test/e2e/run_userns_test.go7
-rw-r--r--test/system/015-help.bats63
-rw-r--r--test/system/120-load.bats6
28 files changed, 134 insertions, 72 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_networks.go b/pkg/api/server/register_networks.go
index 2c60b2b27..3ea16f81a 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -32,7 +32,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/networks/{name}"), s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
r.HandleFunc("/networks/{name}", s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
- // swagger:operation GET /networks/{name}/json compat compatInspectNetwork
+ // swagger:operation GET /networks/{name} compat compatInspectNetwork
// ---
// tags:
// - networks (compat)
@@ -53,9 +53,9 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchNetwork"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/networks/{name}/json"), s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
- r.HandleFunc("/networks/{name}/json", s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
- // swagger:operation GET /networks/json compat compatListNetwork
+ r.HandleFunc(VersionedPath("/networks/{name}"), s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
+ r.HandleFunc("/networks/{name}", s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
+ // swagger:operation GET /networks compat compatListNetwork
// ---
// tags:
// - networks (compat)
@@ -68,7 +68,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/CompatNetworkList"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/networks/json"), s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
+ r.HandleFunc(VersionedPath("/networks"), s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
r.HandleFunc("/networks", s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
// swagger:operation POST /networks/create compat compatCreateNetwork
// ---
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