aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/completion.go181
-rw-r--r--cmd/podman/common/completion_test.go72
-rw-r--r--cmd/podman/containers/create.go3
-rw-r--r--cmd/podman/images/mount.go15
-rw-r--r--cmd/podman/images/save.go2
-rw-r--r--cmd/podman/images/search.go3
-rw-r--r--cmd/podman/images/trust_set.go2
-rw-r--r--cmd/podman/pods/create.go15
-rw-r--r--cmd/podman/pods/ps.go2
-rw-r--r--cmd/podman/root.go2
-rw-r--r--cmd/podman/volumes/create.go4
11 files changed, 226 insertions, 75 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 58dff3578..285cab1b9 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -5,8 +5,11 @@ import (
"fmt"
"os"
"reflect"
+ "strconv"
"strings"
+ "unicode"
+ libimageDefine "github.com/containers/common/libimage/define"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/pkg/sysregistriesv2"
@@ -15,6 +18,7 @@ import (
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
+ "github.com/containers/podman/v4/pkg/signal"
systemdDefine "github.com/containers/podman/v4/pkg/systemd/define"
"github.com/containers/podman/v4/pkg/util"
"github.com/spf13/cobra"
@@ -495,6 +499,11 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([
return getImages(cmd, toComplete)
}
+// AutocompleteImageSearchFilters - Autocomplate `search --filter`.
+func AutocompleteImageSearchFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ return libimageDefine.SearchFilters, cobra.ShellCompDirectiveNoFileComp
+}
+
// AutocompletePodExitPolicy - Autocomplete pod exit policy.
func AutocompletePodExitPolicy(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return config.PodExitPolicies, cobra.ShellCompDirectiveNoFileComp
@@ -594,7 +603,9 @@ func AutocompleteRunlabelCommand(cmd *cobra.Command, args []string, toComplete s
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
- // FIXME: What labels can we recommend here?
+ // This is unfortunate because the argument order is label followed by image.
+ // If it would be the other way around we could inspect the first arg and get
+ // all labels from it to suggest them.
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 1 {
@@ -799,8 +810,7 @@ func AutocompleteLogDriver(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteLogOpt - Autocomplete log-opt options.
// -> "path=", "tag="
func AutocompleteLogOpt(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- // FIXME: are these the only one? the man page states these but in the current shell completion they are more options
- logOptions := []string{"path=", "tag="}
+ logOptions := []string{"path=", "tag=", "max-size="}
if strings.HasPrefix(toComplete, "path=") {
return nil, cobra.ShellCompDirectiveDefault
}
@@ -839,10 +849,26 @@ func AutocompleteSecurityOption(cmd *cobra.Command, args []string, toComplete st
}
// AutocompleteStopSignal - Autocomplete stop signal options.
-// -> "SIGHUP", "SIGINT", "SIGKILL", "SIGTERM"
+// Autocompletes signals both lower or uppercase depending on the user input.
func AutocompleteStopSignal(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- // FIXME: add more/different signals?
- stopSignals := []string{"SIGHUP", "SIGINT", "SIGKILL", "SIGTERM"}
+ // convertCase will convert a string to lowercase only if the user input is lowercase
+ convertCase := func(s string) string { return s }
+ if len(toComplete) > 0 && unicode.IsLower(rune(toComplete[0])) {
+ convertCase = strings.ToLower
+ }
+
+ prefix := ""
+ // if input starts with "SI" we have to add SIG in front
+ // since the signal map does not have this prefix but the option
+ // allows signals with and without SIG prefix
+ if strings.HasPrefix(toComplete, convertCase("SI")) {
+ prefix = "SIG"
+ }
+
+ stopSignals := make([]string, 0, len(signal.SignalMap))
+ for sig := range signal.SignalMap {
+ stopSignals = append(stopSignals, convertCase(prefix+sig))
+ }
return stopSignals, cobra.ShellCompDirectiveNoFileComp
}
@@ -963,9 +989,22 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin
return append(networks, suggestions...), dir
}
+type formatSuggestion struct {
+ fieldname string
+ suffix string
+}
+
+func convertFormatSuggestions(suggestions []formatSuggestion) []string {
+ completions := make([]string, 0, len(suggestions))
+ for _, f := range suggestions {
+ completions = append(completions, f.fieldname+f.suffix)
+ }
+ return completions
+}
+
// AutocompleteFormat - Autocomplete json or a given struct to use for a go template.
// The input can be nil, In this case only json will be autocompleted.
-// This function will only work for structs other types are not supported.
+// This function will only work for pointer to structs other types are not supported.
// When "{{." is typed the field and method names of the given struct will be completed.
// This also works recursive for nested structs.
func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@@ -994,6 +1033,12 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
// split this into it struct field names
fields := strings.Split(field[len(field)-1], ".")
f := reflect.ValueOf(o)
+ if f.Kind() != reflect.Ptr {
+ // We panic here to make sure that all callers pass the value by reference.
+ // If someone passes a by value then all podman commands will panic since
+ // this function is run at init time.
+ panic("AutocompleteFormat: passed value must be a pointer to a struct")
+ }
for i := 1; i < len(fields); i++ {
// last field get all names to suggest
if i == len(fields)-1 {
@@ -1002,61 +1047,83 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
toCompArr := strings.Split(toComplete, ".")
toCompArr[len(toCompArr)-1] = ""
toComplete = strings.Join(toCompArr, ".")
- return prefixSlice(toComplete, suggestions), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
+ return prefixSlice(toComplete, convertFormatSuggestions(suggestions)), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
- val := getActualStructType(f)
- if val == nil {
- // no struct return nothing to complete
+ // first follow pointer and create element when it is nil
+ f = actualReflectValue(f)
+ switch f.Kind() {
+ case reflect.Struct:
+ for j := 0; j < f.NumField(); j++ {
+ field := f.Type().Field(j)
+ // ok this is a bit weird but when we have an embedded nil struct
+ // calling FieldByName on a name which is present on this struct will panic
+ // Therefore we have to init them (non nil ptr), https://github.com/containers/podman/issues/14223
+ if field.Anonymous && f.Field(j).Type().Kind() == reflect.Ptr {
+ f.Field(j).Set(reflect.New(f.Field(j).Type().Elem()))
+ }
+ }
+ // set the next struct field
+ f = f.FieldByName(fields[i])
+ case reflect.Map:
+ rtype := f.Type().Elem()
+ if rtype.Kind() == reflect.Ptr {
+ rtype = rtype.Elem()
+ }
+ f = reflect.New(rtype)
+ case reflect.Func:
+ if f.Type().NumOut() != 1 {
+ // unsupported type return nothing
+ return nil, cobra.ShellCompDirectiveNoFileComp
+ }
+ f = reflect.New(f.Type().Out(0))
+ default:
+ // unsupported type return nothing
return nil, cobra.ShellCompDirectiveNoFileComp
}
- f = *val
-
- // set the next struct field
- f = f.FieldByName(fields[i])
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
-// getActualStructType take the value and check if it is a struct,
+// actualReflectValue takes the value,
// if it is pointer it will dereference it and when it is nil,
-// it will create a new value from it to get the actual struct
-// returns nil when type is not a struct
-func getActualStructType(f reflect.Value) *reflect.Value {
+// it will create a new value from it
+func actualReflectValue(f reflect.Value) reflect.Value {
// follow the pointer first
if f.Kind() == reflect.Ptr {
// if the pointer is nil we create a new value from the elements type
- // this allows us to follow nil pointers and get the actual struct fields
+ // this allows us to follow nil pointers and get the actual type
if f.IsNil() {
f = reflect.New(f.Type().Elem())
}
f = f.Elem()
}
- // we only support structs
- if f.Kind() != reflect.Struct {
- return nil
- }
- return &f
+ return f
}
// getStructFields reads all struct field names and method names and returns them.
-func getStructFields(f reflect.Value, prefix string) []string {
- var suggestions []string
+func getStructFields(f reflect.Value, prefix string) []formatSuggestion {
+ var suggestions []formatSuggestion
if f.IsValid() {
suggestions = append(suggestions, getMethodNames(f, prefix)...)
}
- val := getActualStructType(f)
- if val == nil {
- // no struct return nothing to complete
+ f = actualReflectValue(f)
+ // we only support structs
+ if f.Kind() != reflect.Struct {
return suggestions
}
- f = *val
+ var anonymous []formatSuggestion
// loop over all field names
for j := 0; j < f.NumField(); j++ {
field := f.Type().Field(j)
+ // check if struct field is not exported, templates only use exported fields
+ // PkgPath is always empty for exported fields
+ if field.PkgPath != "" {
+ continue
+ }
fname := field.Name
suffix := "}}"
kind := field.Type.Kind()
@@ -1065,27 +1132,63 @@ func getStructFields(f reflect.Value, prefix string) []string {
kind = field.Type.Elem().Kind()
}
// when we have a nested struct do not append braces instead append a dot
- if kind == reflect.Struct {
+ if kind == reflect.Struct || kind == reflect.Map {
suffix = "."
}
// if field is anonymous add the child fields as well
if field.Anonymous {
- suggestions = append(suggestions, getStructFields(f.Field(j), prefix)...)
- } else if strings.HasPrefix(fname, prefix) {
+ anonymous = append(anonymous, getStructFields(f.Field(j), prefix)...)
+ }
+ if strings.HasPrefix(fname, prefix) {
// add field name with suffix
- suggestions = append(suggestions, fname+suffix)
+ suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix})
+ }
+ }
+outer:
+ for _, ano := range anonymous {
+ // we should only add anonymous child fields if they are not already present.
+ for _, sug := range suggestions {
+ if ano.fieldname == sug.fieldname {
+ continue outer
+ }
}
+ suggestions = append(suggestions, ano)
}
return suggestions
}
-func getMethodNames(f reflect.Value, prefix string) []string {
- suggestions := make([]string, 0, f.NumMethod())
+func getMethodNames(f reflect.Value, prefix string) []formatSuggestion {
+ suggestions := make([]formatSuggestion, 0, f.NumMethod())
for j := 0; j < f.NumMethod(); j++ {
- fname := f.Type().Method(j).Name
+ method := f.Type().Method(j)
+ // in a template we can only run functions with one return value
+ if method.Func.Type().NumOut() != 1 {
+ continue
+ }
+ // when we have a nested struct do not append braces instead append a dot
+ kind := method.Func.Type().Out(0).Kind()
+ suffix := "}}"
+ if kind == reflect.Struct || kind == reflect.Map {
+ suffix = "."
+ }
+ // From a template users POV it is not importent when the use a struct field or method.
+ // They only notice the difference when the function requires arguments.
+ // So lets be nice and let the user know that this method requires arguments via the help text.
+ // Note since this is actually a method on a type the first argument is always fix so we should skip it.
+ num := method.Func.Type().NumIn() - 1
+ if num > 0 {
+ // everything after tab will the completion scripts show as help when enabled
+ // overwrite the suffix because it expects the args
+ suffix = "\tThis is a function and requires " + strconv.Itoa(num) + " argument"
+ if num > 1 {
+ // add plural s
+ suffix += "s"
+ }
+ }
+ fname := method.Name
if strings.HasPrefix(fname, prefix) {
// add method name with closing braces
- suggestions = append(suggestions, fname+"}}")
+ suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix})
}
}
return suggestions
diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go
index 13f45a662..ae23b02e2 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -14,11 +14,29 @@ type Car struct {
HP *int
Displacement int
}
- Extras map[string]string
+ Extras map[string]Extra
+ // also ensure it will work with pointers
+ Extras2 map[string]*Extra
+}
+
+type Extra struct {
+ Name1 string
+ Name2 string
}
type Anonymous struct {
Hello string
+ // The name should match the testStruct Name below. This is used to make
+ // sure the logic uses the actual struct fields before the embedded ones.
+ Name struct {
+ Suffix string
+ Prefix string
+ }
+}
+
+// The name should match the testStruct Age name below.
+func (a Anonymous) Age() int {
+ return 0
}
func (c Car) Type() string {
@@ -31,6 +49,20 @@ func (c *Car) Color() string {
return ""
}
+// This is for reflect testing required.
+// nolint:unused
+func (c Car) internal() int {
+ return 0
+}
+
+func (c Car) TwoOut() (string, string) {
+ return "", ""
+}
+
+func (c Car) Struct() Car {
+ return Car{}
+}
+
func TestAutocompleteFormat(t *testing.T) {
testStruct := struct {
Name string
@@ -38,10 +70,10 @@ func TestAutocompleteFormat(t *testing.T) {
Car *Car
Car2 *Car
*Anonymous
+ private int
}{}
testStruct.Car = &Car{}
- testStruct.Car.Extras = map[string]string{"test": "1"}
tests := []struct {
name string
@@ -76,17 +108,17 @@ func TestAutocompleteFormat(t *testing.T) {
{
"invalid completion",
"{{ ..",
- nil,
+ []string{},
},
{
"fist level struct field name",
"{{.",
- []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Hello}}"},
+ []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Anonymous.", "{{.Hello}}"},
},
{
"fist level struct field name",
"{{ .",
- []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Hello}}"},
+ []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Anonymous.", "{{ .Hello}}"},
},
{
"fist level struct field name",
@@ -96,7 +128,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level struct field name",
"{{ .Car.",
- []string{"{{ .Car.Color}}", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}"},
+ []string{"{{ .Car.Color}}", "{{ .Car.Struct.", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras.", "{{ .Car.Extras2."},
},
{
"second level struct field name",
@@ -106,7 +138,7 @@ func TestAutocompleteFormat(t *testing.T) {
{
"second level nil struct field name",
"{{ .Car2.",
- []string{"{{ .Car2.Color}}", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}"},
+ []string{"{{ .Car2.Color}}", "{{ .Car2.Struct.", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras.", "{{ .Car2.Extras2."},
},
{
"three level struct field name",
@@ -126,28 +158,44 @@ func TestAutocompleteFormat(t *testing.T) {
{
"invalid field name",
"{{ .Ca.B",
- nil,
+ []string{},
},
{
"map key names don't work",
"{{ .Car.Extras.",
- nil,
+ []string{},
+ },
+ {
+ "map values work",
+ "{{ .Car.Extras.somekey.",
+ []string{"{{ .Car.Extras.somekey.Name1}}", "{{ .Car.Extras.somekey.Name2}}"},
+ },
+ {
+ "map values work with ptr",
+ "{{ .Car.Extras2.somekey.",
+ []string{"{{ .Car.Extras2.somekey.Name1}}", "{{ .Car.Extras2.somekey.Name2}}"},
},
{
"two variables struct field name",
"{{ .Car.Brand }} {{ .Car.",
- []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}", "{{ .Car.Brand }} {{ .Car.Brand}}",
- "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}"},
+ []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Struct.", "{{ .Car.Brand }} {{ .Car.Type}}",
+ "{{ .Car.Brand }} {{ .Car.Brand}}", "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras.",
+ "{{ .Car.Brand }} {{ .Car.Extras2."},
},
{
"only dot without variable",
".",
nil,
},
+ {
+ "access embedded nil struct field",
+ "{{.Hello.",
+ []string{},
+ },
}
for _, test := range tests {
- completion, directive := common.AutocompleteFormat(testStruct)(nil, nil, test.toComplete)
+ completion, directive := common.AutocompleteFormat(&testStruct)(nil, nil, test.toComplete)
// directive should always be greater than ShellCompDirectiveNoFileComp
assert.GreaterOrEqual(t, directive, cobra.ShellCompDirectiveNoFileComp, "unexpected ShellCompDirective")
assert.Equal(t, test.expected, completion, test.name)
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 29e138e30..c62ddd6eb 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
+ cutil "github.com/containers/common/pkg/util"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/cmd/podman/common"
@@ -116,7 +117,7 @@ func create(cmd *cobra.Command, args []string) error {
if !cmd.Flags().Changed("pod") {
return errors.New("must specify pod value with init-ctr")
}
- if !util.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
+ if !cutil.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
return errors.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer)
}
cliVals.InitContainerType = initctr
diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go
index d5ab3d274..532d96196 100644
--- a/cmd/podman/images/mount.go
+++ b/cmd/podman/images/mount.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -71,16 +70,12 @@ func mount(cmd *cobra.Command, args []string) error {
return err
}
- if len(args) > 0 || mountOpts.All {
- var errs utils.OutputErrors
- for _, r := range reports {
- if r.Err == nil {
- fmt.Println(r.Path)
- continue
- }
- errs = append(errs, r.Err)
+ if len(args) == 1 && mountOpts.Format == "" && !mountOpts.All {
+ if len(reports) != 1 {
+ return fmt.Errorf("internal error: expected 1 report but got %d", len(reports))
}
- return errs.PrintErrors()
+ fmt.Println(reports[0].Path)
+ return nil
}
switch {
diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go
index 3394c2e99..d85d688ee 100644
--- a/cmd/podman/images/save.go
+++ b/cmd/podman/images/save.go
@@ -6,12 +6,12 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/containers/podman/v4/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/term"
diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go
index 335ea2b5a..a18f7a11d 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -83,8 +83,7 @@ func searchFlags(cmd *cobra.Command) {
filterFlagName := "filter"
flags.StringSliceVarP(&searchOptions.Filters, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])")
- // TODO add custom filter function
- _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteImageSearchFilters)
formatFlagName := "format"
flags.StringVar(&searchOptions.Format, formatFlagName, "", "Change the output format to JSON or a Go template")
diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go
index fff035d12..f4ff0cffc 100644
--- a/cmd/podman/images/trust_set.go
+++ b/cmd/podman/images/trust_set.go
@@ -5,10 +5,10 @@ import (
"regexp"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
- "github.com/containers/podman/v4/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 62f820790..eaf6dd9d5 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -16,7 +16,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/containers"
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
@@ -36,12 +35,14 @@ var (
You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
createCommand = &cobra.Command{
- Use: "create [options]",
- Args: validate.NoArgs,
+ Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new empty pod",
Long: podCreateDescription,
RunE: create,
ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman pod create
+ podman pod create --label foo=bar mypod`,
}
)
@@ -115,6 +116,12 @@ func create(cmd *cobra.Command, args []string) error {
rawImageName string
podName string
)
+ if len(args) > 0 {
+ if len(createOptions.Name) > 0 {
+ return fmt.Errorf("cannot specify --name and NAME at the same time")
+ }
+ createOptions.Name = args[0]
+ }
labelFile = infraOptions.LabelFile
labels = infraOptions.Label
createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
@@ -128,7 +135,7 @@ func create(cmd *cobra.Command, args []string) error {
img := imageName
if !createOptions.Infra {
if cmd.Flag("no-hosts").Changed {
- return fmt.Errorf("cannot specify no-hosts without an infra container")
+ return fmt.Errorf("cannot specify --no-hosts without an infra container")
}
flags := cmd.Flags()
createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags)
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 1275e65dc..a89448275 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -24,7 +24,7 @@ var (
// Command: podman pod _ps_
psCmd = &cobra.Command{
- Use: "ps [options]",
+ Use: "ps [options]",
Aliases: []string{"ls", "list"},
Short: "List pods",
Long: psDescription,
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 2bd4fa723..1892ff9f7 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -423,7 +423,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
// -s is deprecated due to conflict with -s on subcommands
storageDriverFlagName := "storage-driver"
pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers")
- _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here?
+ _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone)
tmpdirFlagName := "tmpdir"
pFlags.StringVar(&opts.Engine.TmpDir, tmpdirFlagName, "", "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")
diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go
index 1668c72de..b47ae16ce 100644
--- a/cmd/podman/volumes/create.go
+++ b/cmd/podman/volumes/create.go
@@ -17,6 +17,7 @@ var (
createCommand = &cobra.Command{
Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new volume",
Long: createDescription,
RunE: create,
@@ -59,9 +60,6 @@ func create(cmd *cobra.Command, args []string) error {
var (
err error
)
- if len(args) > 1 {
- return errors.Errorf("too many arguments, create takes at most 1 argument")
- }
if len(args) > 0 {
createOpts.Name = args[0]
}