From b5d1d89a377e38762a75c2042dcc50a370ba6707 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Sun, 31 May 2020 17:35:59 +0200 Subject: Add shell completion with cobra Allow automatic generation for shell completion scripts with the internal cobra functions (requires v1.0.0+). This should replace the handwritten completion scripts and even adds support for fish. With this approach it is less likley that completions and code are out of sync. We can now create the scripts with - podman completion bash - podman completion zsh - podman completion fish To test the completion run: source <(podman completion bash) The same works for podman-remote and podman --remote and it will complete your remote containers/images with the correct endpoints values from --url/--connection. The completion logic is written in go and provided by the cobra library. The completion functions lives in `cmd/podman/completion/completion.go`. The unit test at cmd/podman/shell_completion_test.go checks if each command and flag has an autocompletion function set. This prevents that commands and flags have no shell completion set. This commit does not replace the current autocompletion scripts. Closes #6440 Signed-off-by: Paul Holzinger --- cmd/podman/auto-update.go | 15 +- cmd/podman/common/completion.go | 638 ++++++++++++++++++++++++++++ cmd/podman/common/create.go | 446 +++++++++++++++---- cmd/podman/common/netflags.go | 53 ++- cmd/podman/completion/completion.go | 93 ++++ cmd/podman/containers/attach.go | 37 +- cmd/podman/containers/checkpoint.go | 9 +- cmd/podman/containers/cleanup.go | 9 +- cmd/podman/containers/commit.go | 63 +-- cmd/podman/containers/cp.go | 37 +- cmd/podman/containers/create.go | 40 +- cmd/podman/containers/diff.go | 17 +- cmd/podman/containers/exec.go | 44 +- cmd/podman/containers/exists.go | 2 + cmd/podman/containers/export.go | 40 +- cmd/podman/containers/init.go | 13 +- cmd/podman/containers/inspect.go | 16 +- cmd/podman/containers/kill.go | 25 +- cmd/podman/containers/list.go | 16 +- cmd/podman/containers/logs.go | 36 +- cmd/podman/containers/mount.go | 30 +- cmd/podman/containers/pause.go | 19 +- cmd/podman/containers/port.go | 3 + cmd/podman/containers/prune.go | 18 +- cmd/podman/containers/ps.go | 47 +- cmd/podman/containers/restart.go | 26 +- cmd/podman/containers/restore.go | 14 +- cmd/podman/containers/rm.go | 18 +- cmd/podman/containers/run.go | 49 ++- cmd/podman/containers/runlabel.go | 35 +- cmd/podman/containers/start.go | 35 +- cmd/podman/containers/stats.go | 38 +- cmd/podman/containers/stop.go | 24 +- cmd/podman/containers/top.go | 21 +- cmd/podman/containers/unmount.go | 3 + cmd/podman/containers/unpause.go | 19 +- cmd/podman/containers/wait.go | 40 +- cmd/podman/diff.go | 18 +- cmd/podman/generate/kube.go | 19 +- cmd/podman/generate/systemd.go | 44 +- cmd/podman/healthcheck/run.go | 2 + cmd/podman/images/build.go | 39 +- cmd/podman/images/diff.go | 16 +- cmd/podman/images/exists.go | 12 +- cmd/podman/images/history.go | 41 +- cmd/podman/images/images.go | 3 +- cmd/podman/images/import.go | 42 +- cmd/podman/images/inspect.go | 15 +- cmd/podman/images/list.go | 40 +- cmd/podman/images/load.go | 37 +- cmd/podman/images/mount.go | 22 +- cmd/podman/images/prune.go | 20 +- cmd/podman/images/pull.go | 69 +-- cmd/podman/images/push.go | 69 ++- cmd/podman/images/rm.go | 10 +- cmd/podman/images/rmi.go | 13 +- cmd/podman/images/save.go | 34 +- cmd/podman/images/search.go | 60 ++- cmd/podman/images/sign.go | 27 +- cmd/podman/images/tag.go | 3 + cmd/podman/images/tree.go | 14 +- cmd/podman/images/trust_set.go | 25 +- cmd/podman/images/trust_show.go | 14 +- cmd/podman/images/unmount.go | 12 +- cmd/podman/images/untag.go | 3 + cmd/podman/inspect.go | 12 +- cmd/podman/inspect/inspect.go | 12 +- cmd/podman/login.go | 16 +- cmd/podman/logout.go | 17 +- cmd/podman/main.go | 11 +- cmd/podman/manifest/add.go | 58 ++- cmd/podman/manifest/annotate.go | 50 ++- cmd/podman/manifest/create.go | 10 +- cmd/podman/manifest/inspect.go | 2 + cmd/podman/manifest/push.go | 47 +- cmd/podman/manifest/remove.go | 2 + cmd/podman/networks/create.go | 51 ++- cmd/podman/networks/inspect.go | 19 +- cmd/podman/networks/list.go | 28 +- cmd/podman/networks/rm.go | 14 +- cmd/podman/play/kube.go | 51 ++- cmd/podman/pods/create.go | 68 ++- cmd/podman/pods/exists.go | 12 +- cmd/podman/pods/inspect.go | 18 +- cmd/podman/pods/kill.go | 8 +- cmd/podman/pods/pause.go | 2 + cmd/podman/pods/prune.go | 14 +- cmd/podman/pods/ps.go | 33 +- cmd/podman/pods/restart.go | 2 + cmd/podman/pods/rm.go | 8 +- cmd/podman/pods/start.go | 8 +- cmd/podman/pods/stats.go | 16 +- cmd/podman/pods/stop.go | 13 +- cmd/podman/pods/top.go | 12 +- cmd/podman/pods/unpause.go | 4 + cmd/podman/registry/remote.go | 11 +- cmd/podman/root.go | 139 ++++-- cmd/podman/shell_completion_test.go | 71 ++++ cmd/podman/system/connection/add.go | 20 +- cmd/podman/system/connection/default.go | 2 + cmd/podman/system/connection/list.go | 6 +- cmd/podman/system/connection/remove.go | 2 + cmd/podman/system/connection/rename.go | 2 + cmd/podman/system/df.go | 18 +- cmd/podman/system/events.go | 35 +- cmd/podman/system/info.go | 42 +- cmd/podman/system/migrate.go | 17 +- cmd/podman/system/prune.go | 14 +- cmd/podman/system/renumber.go | 2 + cmd/podman/system/reset.go | 12 +- cmd/podman/system/service.go | 20 +- cmd/podman/system/unshare.go | 2 + cmd/podman/system/varlink.go | 22 +- cmd/podman/system/version.go | 16 +- cmd/podman/volumes/create.go | 25 +- cmd/podman/volumes/inspect.go | 15 +- cmd/podman/volumes/list.go | 26 +- cmd/podman/volumes/prune.go | 12 +- cmd/podman/volumes/rm.go | 12 +- docs/source/markdown/podman-completion.1.md | 56 +++ docs/source/markdown/podman.1.md | 1 + hack/podman-commands.sh | 6 + hack/xref-helpmsgs-manpages | 3 + pkg/systemd/generate/common.go | 6 +- 124 files changed, 3078 insertions(+), 966 deletions(-) create mode 100644 cmd/podman/common/completion.go create mode 100644 cmd/podman/completion/completion.go create mode 100644 cmd/podman/shell_completion_test.go create mode 100644 docs/source/markdown/podman-completion.1.md diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go index 76bff0c70..cf806900f 100644 --- a/cmd/podman/auto-update.go +++ b/cmd/podman/auto-update.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" @@ -20,10 +21,11 @@ var ( or similar units that create new containers in order to run the updated images. Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ - Use: "auto-update [options]", - Short: "Auto update containers according to their auto-update policy", - Long: autoUpdateDescription, - RunE: autoUpdate, + Use: "auto-update [options]", + Short: "Auto update containers according to their auto-update policy", + Long: autoUpdateDescription, + RunE: autoUpdate, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman auto-update podman auto-update --authfile ~/authfile.json`, } @@ -36,7 +38,10 @@ func init() { }) flags := autoUpdateCommand.Flags() - flags.StringVar(&autoUpdateOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path to the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + + authfileFlagName := "authfile" + flags.StringVar(&autoUpdateOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path to the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = autoUpdateCommand.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) } func autoUpdate(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go new file mode 100644 index 000000000..4d66b4e2b --- /dev/null +++ b/cmd/podman/common/completion.go @@ -0,0 +1,638 @@ +package common + +import ( + "bufio" + "os" + "strings" + + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/registry" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/registries" + systemdGen "github.com/containers/podman/v2/pkg/systemd/generate" + "github.com/spf13/cobra" +) + +var ( + // ChangeCmds is the list of valid Change commands to passed to the Commit call + ChangeCmds = []string{"CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR"} + // LogLevels supported by podman + LogLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} +) + +func getContainers(status string, toComplete string) ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + listOpts := entities.ContainerListOptions{ + Filters: make(map[string][]string), + } + listOpts.All = true + + if status != "all" { + listOpts.Filters = map[string][]string{"status": {status}} + } + + containers, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for _, container := range containers { + // include ids in suggestions if more then 2 chars are typed + if len(toComplete) > 1 { + suggestions = append(suggestions, container.ID[0:12]) + } + // include name in suggestions + suggestions = append(suggestions, container.Names...) + } + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +func getPods(status string, toComplete string) ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + listOpts := entities.PodPSOptions{ + Filters: make(map[string][]string), + } + + if status != "all" { + listOpts.Filters = map[string][]string{"status": {status}} + } + + pods, err := registry.ContainerEngine().PodPs(registry.GetContext(), listOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for _, pod := range pods { + // include ids in suggestions if more then 2 chars are typed + if len(toComplete) > 1 { + suggestions = append(suggestions, pod.Id[0:12]) + } + // include name in suggestions + suggestions = append(suggestions, pod.Name) + } + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +func getVolumes() ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + lsOpts := entities.VolumeListOptions{} + + volumes, err := registry.ContainerEngine().VolumeList(registry.GetContext(), lsOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for _, volume := range volumes { + suggestions = append(suggestions, volume.Name) + } + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +func getImages(toComplete string) ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + listOptions := entities.ImageListOptions{} + + images, err := registry.ImageEngine().List(registry.GetContext(), listOptions) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for _, image := range images { + // FIXME: need ux testing + // discuss when image ids should be completed + // include ids in suggestions if more then 2 chars are typed + if len(toComplete) > 1 { + suggestions = append(suggestions, image.ID[0:12]) + } + for _, repo := range image.RepoTags { + if toComplete == "" { + // suggest only full repo path if no input is given + suggestions = append(suggestions, repo) + } else { + // suggested "registry.fedoraproject.org/f29/httpd:latest" as + // - "registry.fedoraproject.org/f29/httpd:latest" + // - "registry.fedoraproject.org/f29/httpd" + // - "f29/httpd:latest" + // - "f29/httpd" + // - "httpd:latest" + // - "httpd" + paths := strings.Split(repo, "/") + for i := range paths { + suggestionWithTag := strings.Join(paths[i:], "/") + suggestionWithoutTag := strings.SplitN(strings.SplitN(suggestionWithTag, ":", 2)[0], "@", 2)[0] + suggestions = append(suggestions, suggestionWithTag, suggestionWithoutTag) + } + } + } + } + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +func getRegistries() ([]string, cobra.ShellCompDirective) { + regs, err := registries.GetRegistries() + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + return regs, cobra.ShellCompDirectiveNoFileComp +} + +func getNetworks() ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + networkListOptions := entities.NetworkListOptions{} + + networks, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for _, network := range networks { + suggestions = append(suggestions, network.Name) + } + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +/* Autocomplete Functions for cobra ValidArgsFunction */ + +// AutocompleteContainers - Autocomplete all container names. +func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getContainers("all", toComplete) +} + +// AutocompleteContainersCreated - Autocomplete only created container names. +func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getContainers("created", toComplete) +} + +// AutocompleteContainersExited - Autocomplete only exited container names. +func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getContainers("exited", toComplete) +} + +// AutocompleteContainersPaused - Autocomplete only paused container names. +func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getContainers("paused", toComplete) +} + +// AutocompleteContainersRunning - Autocomplete only running container names. +func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getContainers("running", toComplete) +} + +// AutocompleteContainersStartable - Autocomplete only created and exited container names. +func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + containersCreated, _ := getContainers("created", toComplete) + containersExited, _ := getContainers("exited", toComplete) + return append(containersCreated, containersExited...), cobra.ShellCompDirectiveNoFileComp +} + +// AutocompletePods - Autocomplete all pod names. +func AutocompletePods(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getPods("all", toComplete) +} + +// AutocompletePodsRunning - Autocomplete only running pod names. +func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getPods("running", toComplete) +} + +// AutocompleteContainersAndPods - Autocomplete container names and pod names. +func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + containers, _ := getContainers("all", toComplete) + pods, _ := getPods("all", toComplete) + return append(containers, pods...), cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteContainersAndImages - Autocomplete container names and pod names. +func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + containers, _ := getContainers("all", toComplete) + images, _ := getImages(toComplete) + return append(containers, images...), cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteVolumes - Autocomplete volumes. +func AutocompleteVolumes(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getVolumes() +} + +// AutocompleteImages - Autocomplete images. +func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getImages(toComplete) +} + +// AutocompleteCreateRun - Autocomplete only the fist argument as image and then do file completion. +func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) < 1 { + return getImages(toComplete) + } + // TODO: add path completion for files in the image + return nil, cobra.ShellCompDirectiveDefault +} + +// AutocompleteRegistries - Autocomplete registries. +func AutocompleteRegistries(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getRegistries() +} + +// AutocompleteNetworks - Autocomplete networks. +func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return getNetworks() +} + +// AutocompleteCpCommand - Autocomplete podman cp command args. +func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) < 2 { + containers, _ := getContainers("all", toComplete) + for _, container := range containers { + // TODO: Add path completion for inside the container if possible + if strings.HasPrefix(container, toComplete) { + return containers, cobra.ShellCompDirectiveNoSpace + } + } + // else complete paths + return nil, cobra.ShellCompDirectiveDefault + } + // don't complete more than 2 args + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteSystemConnections - Autocomplete system connections. +func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + suggestions := []string{} + cfg, err := config.ReadCustomConfig() + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveError + } + + for k, v := range cfg.Engine.ServiceDestinations { + // the URI will be show as description in shells like zsh + suggestions = append(suggestions, k+"\t"+v.URI) + } + + return suggestions, cobra.ShellCompDirectiveNoFileComp +} + +/* -------------- Flags ----------------- */ + +// AutocompleteDetachKeys - Autocomplete detach-keys options. +// -> "ctrl-" +func AutocompleteDetachKeys(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if strings.HasSuffix(toComplete, ",") { + return []string{toComplete + "ctrl-"}, cobra.ShellCompDirectiveNoSpace + } + return []string{"ctrl-"}, cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteChangeInstructions - Autocomplete change instructions options for commit and import. +// -> "CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR" +func AutocompleteChangeInstructions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return ChangeCmds, cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteImageFormat - Autocomplete image format options. +// -> "oci", "docker" +func AutocompleteImageFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ImageFormat := []string{"oci", "docker"} + return ImageFormat, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteCreateAttach - Autocomplete create --attach options. +// -> "stdin", "stdout", "stderr" +func AutocompleteCreateAttach(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"stdin", "stdout", "stderr"}, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteNamespace - Autocomplete namespace options. +// -> host,container:[name],ns:[path],private +func AutocompleteNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + namespacesOptions := []string{"host", "container:", "ns:", "private"} + + switch { + case strings.HasPrefix(toComplete, "container:"): + // Complete containers after colon + containers, _ := getContainers("all", toComplete[10:]) //trim "container:" + + // add "container:" in front of the suggestions + var suggestions []string + for _, container := range containers { + suggestions = append(suggestions, "container:"+container) + } + + return suggestions, cobra.ShellCompDirectiveNoFileComp + + case strings.HasPrefix(toComplete, "ns:"): + // Complete path after colon + return nil, cobra.ShellCompDirectiveDefault + + case strings.HasPrefix(toComplete, "c") || strings.HasPrefix(toComplete, "n"): + // don't insert space for container: and ns: + return []string{"container:", "ns:"}, cobra.ShellCompDirectiveNoSpace + } + return namespacesOptions, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteUserNamespace - Autocomplete namespace options. +// -> same as AutocompleteNamespace with "auto", "keep-id" added +func AutocompleteUserNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + results, directive := AutocompleteNamespace(cmd, args, toComplete) + if directive == cobra.ShellCompDirectiveNoFileComp { + // add the auto and keep-id options + results = append(results, "auto", "keep-id") + } + return results, directive +} + +// AutocompleteCgroupMode - Autocomplete cgroup mode options. +// -> "enabled", "disabled", "no-conmon", "split" +func AutocompleteCgroupMode(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + cgroupModes := []string{"enabled", "disabled", "no-conmon", "split"} + return cgroupModes, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteImageVolume - Autocomplete image volume options. +// -> "bind", "tmpfs", "ignore" +func AutocompleteImageVolume(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + imageVolumes := []string{"bind", "tmpfs", "ignore"} + return imageVolumes, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteLogDriver - Autocomplete log-driver options. +// -> "journald", "none", "k8s-file" +func AutocompleteLogDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // don't show json-file + logDrivers := []string{define.JournaldLogging, define.NoLogging, define.KubernetesLogging} + return logDrivers, cobra.ShellCompDirectiveNoFileComp +} + +// 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="} + if strings.HasPrefix(toComplete, "path=") { + return nil, cobra.ShellCompDirectiveDefault + } + return logOptions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace +} + +// AutocompletePullOption - Autocomplete pull options for create and run command. +// -> "always", "missing", "never" +func AutocompletePullOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + pullOptions := []string{"always", "missing", "never"} + return pullOptions, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteRestartOption - Autocomplete restart options for create and run command. +// -> "always", "no", "on-failure", "unless-stopped" +func AutocompleteRestartOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + restartOptions := []string{libpod.RestartPolicyAlways, libpod.RestartPolicyNo, + libpod.RestartPolicyOnFailure, libpod.RestartPolicyUnlessStopped} + return restartOptions, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteSecurityOption - Autocomplete security options options. +func AutocompleteSecurityOption(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + SecurityOptions := []string{"apparmor=", "no-new-privileges", "seccomp=", "label="} + switch { + case strings.HasPrefix(toComplete, "apparmor=u"): + // add space after unconfined + return []string{"apparmor=unconfined"}, cobra.ShellCompDirectiveNoFileComp + + case strings.HasPrefix(toComplete, "label=d"): + // add space after disable + return []string{"label=disable"}, cobra.ShellCompDirectiveNoFileComp + + case strings.HasPrefix(toComplete, "label="): + return []string{"label=user:", "label=role:", "label=type:", "label=level:", "label=filetype:", "label=disable"}, + cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace + + case strings.HasPrefix(toComplete, "seccomp="): + // complete files + return nil, cobra.ShellCompDirectiveDefault + + case strings.HasPrefix(toComplete, "n"): + // add space if no-new-privileges + return []string{"no-new-privileges"}, cobra.ShellCompDirectiveNoFileComp + } + return SecurityOptions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteStopSignal - Autocomplete stop signal options. +// -> "SIGHUP", "SIGINT", "SIGKILL", "SIGTERM" +func AutocompleteStopSignal(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // FIXME: add more/different signals? + stopSignals := []string{"SIGHUP", "SIGINT", "SIGKILL", "SIGTERM"} + return stopSignals, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteSystemdFlag - Autocomplete systemd flag options. +// -> "true", "false", "always" +func AutocompleteSystemdFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + systemd := []string{"true", "false", "always"} + return systemd, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteUserFlag - Autocomplete user flag based on the names and groups (includes ids after first char) in /etc/passwd and /etc/group files. +// -> user:group +func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if strings.Contains(toComplete, ":") { + // It would be nice to read the file in the image + // but at this point we don't know the image. + file, err := os.Open("/etc/group") + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + defer file.Close() + + var groups []string + scanner := bufio.NewScanner(file) + user := strings.SplitN(toComplete, ":", 2)[0] + for scanner.Scan() { + entries := strings.SplitN(scanner.Text(), ":", 4) + groups = append(groups, user+":"+entries[0]) + // complete ids after at least one char is given + if len(user)+1 < len(toComplete) { + groups = append(groups, user+":"+entries[2]) + } + } + if err = scanner.Err(); err != nil { + return nil, cobra.ShellCompDirectiveError + } + return groups, cobra.ShellCompDirectiveNoFileComp + } + + // It would be nice to read the file in the image + // but at this point we don't know the image. + file, err := os.Open("/etc/passwd") + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + defer file.Close() + + var users []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + entries := strings.SplitN(scanner.Text(), ":", 7) + users = append(users, entries[0]+":") + // complete ids after at least one char is given + if len(toComplete) > 0 { + users = append(users, entries[2]+":") + } + } + if err = scanner.Err(); err != nil { + return nil, cobra.ShellCompDirectiveError + } + return users, cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteMountFlag - Autocomplete mount flag options. +// -> "type=bind,", "type=volume,", "type=tmpfs," +func AutocompleteMountFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"type=bind,", "type=volume,", "type=tmpfs,"} + // TODO: Add support for all different options + return types, cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteVolumeFlag - Autocomplete volume flag options. +// -> volumes and paths +func AutocompleteVolumeFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + result := []string{} + volumes, _ := getVolumes() + for _, volume := range volumes { + // If we don't filter on "toComplete", zsh and fish will not do file completion + // even if the prefix typed by the user does not match the returned completions + if strings.HasPrefix(volume, toComplete) { + result = append(result, volume) + } + } + directive := cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveDefault + if strings.Contains(toComplete, ":") { + // add space after second path + directive = cobra.ShellCompDirectiveDefault + } + return result, directive +} + +// AutocompleteJSONFormat - Autocomplete format flag option. +// -> "json" +func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"json"}, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteEventFilter - Autocomplete event filter flag options. +// -> "container=", "event=", "image=", "pod=", "volume=", "type=" +func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + filters := []string{"container=", "event=", "image=", "pod=", "volume=", "type="} + return filters, cobra.ShellCompDirectiveNoSpace +} + +// AutocompleteSystemdRestartOptions - Autocomplete systemd restart options. +// -> "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always" +func AutocompleteSystemdRestartOptions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return systemdGen.RestartPolicies, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteTrustType - Autocomplete trust type options. +// -> "signedBy", "accept", "reject" +func AutocompleteTrustType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"signedBy", "accept", "reject"} + return types, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteImageSort - Autocomplete images sort options. +// -> "created", "id", "repository", "size", "tag" +func AutocompleteImageSort(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + sortBy := []string{"created", "id", "repository", "size", "tag"} + return sortBy, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteInspectType - Autocomplete inspect type options. +// -> "container", "image", "all" +func AutocompleteInspectType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"container", "image", "all"} + return types, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteManifestFormat - Autocomplete manifest format options. +// -> "oci", "v2s2" +func AutocompleteManifestFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"oci", "v2s2"} + return types, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteNetworkDriver - Autocomplete network driver option. +// -> "bridge" +func AutocompleteNetworkDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + drivers := []string{"bridge"} + return drivers, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompletePodShareNamespace - Autocomplete pod create --share flag option. +// -> "ipc", "net", "pid", "user", "uts", "cgroup", "none" +func AutocompletePodShareNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + namespaces := []string{"ipc", "net", "pid", "user", "uts", "cgroup", "none"} + return namespaces, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompletePodPsSort - Autocomplete images sort options. +// -> "created", "id", "name", "status", "number" +func AutocompletePodPsSort(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + sortBy := []string{"created", "id", "name", "status", "number"} + return sortBy, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompletePsSort - Autocomplete images sort options. +// -> "command", "created", "id", "image", "names", "runningfor", "size", "status" +func AutocompletePsSort(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + sortBy := []string{"command", "created", "id", "image", "names", "runningfor", "size", "status"} + return sortBy, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteImageSaveFormat - Autocomplete image save format options. +// -> "oci-archive", "oci-dir", "docker-dir" +func AutocompleteImageSaveFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + formats := []string{"oci-archive", "oci-dir", "docker-dir"} + return formats, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteWaitCondition - Autocomplete wait condition options. +// -> "unknown", "configured", "created", "running", "stopped", "paused", "exited", "removing" +func AutocompleteWaitCondition(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + states := []string{"unknown", "configured", "created", "running", "stopped", "paused", "exited", "removing"} + return states, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteCgroupManager - Autocomplete cgroup manager options. +// -> "cgroupfs", "systemd" +func AutocompleteCgroupManager(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"cgroupfs", "systemd"} + return types, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteEventBackend - Autocomplete event backend options. +// -> "file", "journald", "none" +func AutocompleteEventBackend(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"file", "journald", "none"} + return types, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteLogLevel - Autocomplete log level options. +// -> "debug", "info", "warn", "error", "fatal", "panic" +func AutocompleteLogLevel(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return LogLevels, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteSDNotify - Autocomplete sdnotify options. +// -> "container", "conmon", "ignore" +func AutocompleteSDNotify(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"container", "conmon", "ignore"} + return types, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 60f4e526c..ab3a984f0 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -5,296 +5,457 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/libpod/define" - "github.com/spf13/pflag" + "github.com/spf13/cobra" ) const sizeWithUnitFormat = "(format: `[]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))" var containerConfig = registry.PodmanConfig() -func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { - createFlags := pflag.FlagSet{} +func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { + createFlags := cmd.Flags() + + annotationFlagName := "annotation" createFlags.StringSliceVar( &cf.Annotation, - "annotation", []string{}, + annotationFlagName, []string{}, "Add annotations to container (key:value)", ) + _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + + attachFlagName := "attach" createFlags.StringSliceVarP( &cf.Attach, - "attach", "a", []string{}, + attachFlagName, "a", []string{}, "Attach to STDIN, STDOUT or STDERR", ) + _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) + + authfileFlagName := "authfile" createFlags.StringVar( &cf.Authfile, - "authfile", auth.GetDefaultAuthFile(), + authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", ) + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + + blkioWeightFlagName := "blkio-weight" createFlags.StringVar( &cf.BlkIOWeight, - "blkio-weight", "", + blkioWeightFlagName, "", "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) + + blkioWeightDeviceFlagName := "blkio-weight-device" createFlags.StringSliceVar( &cf.BlkIOWeightDevice, - "blkio-weight-device", []string{}, + blkioWeightDeviceFlagName, []string{}, "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) + + capAddFlagName := "cap-add" createFlags.StringSliceVar( &cf.CapAdd, - "cap-add", []string{}, + capAddFlagName, []string{}, "Add capabilities to the container", ) + _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) + + capDropFlagName := "cap-drop" createFlags.StringSliceVar( &cf.CapDrop, - "cap-drop", []string{}, + capDropFlagName, []string{}, "Drop capabilities from the container", ) + _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) + + cgroupnsFlagName := "cgroupns" createFlags.String( - "cgroupns", "", + cgroupnsFlagName, "", "cgroup namespace to use", ) + _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) + + cgroupsFlagName := "cgroups" createFlags.StringVar( &cf.CGroupsMode, - "cgroups", containerConfig.Cgroups(), + cgroupsFlagName, containerConfig.Cgroups(), `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, ) + _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) + + cgroupParentFlagName := "cgroup-parent" createFlags.StringVar( &cf.CGroupParent, - "cgroup-parent", "", + cgroupParentFlagName, "", "Optional parent cgroup for the container", ) + _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) + + cidfileFlagName := "cidfile" createFlags.StringVar( &cf.CIDFile, - "cidfile", "", + cidfileFlagName, "", "Write the container ID to the file", ) + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + + conmonPidfileFlagName := "conmon-pidfile" createFlags.StringVar( &cf.ConmonPIDFile, - "conmon-pidfile", "", + conmonPidfileFlagName, "", "Path to the file that will receive the PID of conmon", ) + _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) + + cpuPeriodFlagName := "cpu-period" createFlags.Uint64Var( &cf.CPUPeriod, - "cpu-period", 0, + cpuPeriodFlagName, 0, "Limit the CPU CFS (Completely Fair Scheduler) period", ) + _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) + + cpuQuotaFlagName := "cpu-quota" createFlags.Int64Var( &cf.CPUQuota, - "cpu-quota", 0, + cpuQuotaFlagName, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota", ) + _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) + + cpuRtPeriodFlagName := "cpu-rt-period" createFlags.Uint64Var( &cf.CPURTPeriod, - "cpu-rt-period", 0, + cpuRtPeriodFlagName, 0, "Limit the CPU real-time period in microseconds", ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) + + cpuRtRuntimeFlagName := "cpu-rt-runtime" createFlags.Int64Var( &cf.CPURTRuntime, - "cpu-rt-runtime", 0, + cpuRtRuntimeFlagName, 0, "Limit the CPU real-time runtime in microseconds", ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) + + cpuSharesFlagName := "cpu-shares" createFlags.Uint64Var( &cf.CPUShares, - "cpu-shares", 0, + cpuSharesFlagName, 0, "CPU shares (relative weight)", ) + _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + + cpusFlagName := "cpus" createFlags.Float64Var( &cf.CPUS, - "cpus", 0, + cpusFlagName, 0, "Number of CPUs. The default is 0.000 which means no limit", ) + _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + + cpusetCpusFlagName := "cpuset-cpus" createFlags.StringVar( &cf.CPUSetCPUs, - "cpuset-cpus", "", + cpusetCpusFlagName, "", "CPUs in which to allow execution (0-3, 0,1)", ) + _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + + cpusetMemsFlagName := "cpuset-mems" createFlags.StringVar( &cf.CPUSetMems, - "cpuset-mems", "", + cpusetMemsFlagName, "", "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", ) + _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) + + deviceFlagName := "device" createFlags.StringSliceVar( &cf.Devices, - "device", containerConfig.Devices(), + deviceFlagName, containerConfig.Devices(), fmt.Sprintf("Add a host device to the container"), ) + _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) + + deviceCgroupRuleFlagName := "device-cgroup-rule" createFlags.StringSliceVar( &cf.DeviceCGroupRule, - "device-cgroup-rule", []string{}, + deviceCgroupRuleFlagName, []string{}, "Add a rule to the cgroup allowed devices list", ) + _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) + + deviceReadBpsFlagName := "device-read-bps" createFlags.StringSliceVar( &cf.DeviceReadBPs, - "device-read-bps", []string{}, + deviceReadBpsFlagName, []string{}, "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) + + deviceReadIopsFlagName := "device-read-iops" createFlags.StringSliceVar( &cf.DeviceReadIOPs, - "device-read-iops", []string{}, + deviceReadIopsFlagName, []string{}, "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) + + deviceWriteBpsFlagName := "device-write-bps" createFlags.StringSliceVar( &cf.DeviceWriteBPs, - "device-write-bps", []string{}, + deviceWriteBpsFlagName, []string{}, "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) + + deviceWriteIopsFlagName := "device-write-iops" createFlags.StringSliceVar( &cf.DeviceWriteIOPs, - "device-write-iops", []string{}, + deviceWriteIopsFlagName, []string{}, "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) + createFlags.Bool( "disable-content-trust", false, "This is a Docker specific option and is a NOOP", ) - createFlags.String("entrypoint", "", + + entrypointFlagName := "entrypoint" + createFlags.String(entrypointFlagName, "", "Overwrite the default ENTRYPOINT of the image", ) + _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) + + envFlagName := "env" createFlags.StringArrayP( - "env", "e", containerConfig.Env(), + envFlagName, "e", containerConfig.Env(), "Set environment variables in container", ) + _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + if !registry.IsRemote() { createFlags.BoolVar( &cf.EnvHost, "env-host", false, "Use all current host environment variables in container", ) } + + envFileFlagName := "env-file" createFlags.StringSliceVar( &cf.EnvFile, - "env-file", []string{}, + envFileFlagName, []string{}, "Read in a file of environment variables", ) + _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) + + exposeFlagName := "expose" createFlags.StringSliceVar( &cf.Expose, - "expose", []string{}, + exposeFlagName, []string{}, "Expose a port or a range of ports", ) + _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) + + gidmapFlagName := "gidmap" createFlags.StringSliceVar( &cf.GIDMap, - "gidmap", []string{}, + gidmapFlagName, []string{}, "GID map to use for the user namespace", ) + _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) + + groupAddFlagName := "group-add" createFlags.StringSliceVar( &cf.GroupAdd, - "group-add", []string{}, + groupAddFlagName, []string{}, "Add additional groups to join", ) + _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) + createFlags.Bool( "help", false, "", ) + + healthCmdFlagName := "health-cmd" createFlags.StringVar( &cf.HealthCmd, - "health-cmd", "", + healthCmdFlagName, "", "set a healthcheck command for the container ('none' disables the existing healthcheck)", ) + _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) + + healthIntervalFlagName := "health-interval" createFlags.StringVar( &cf.HealthInterval, - "health-interval", DefaultHealthCheckInterval, + healthIntervalFlagName, DefaultHealthCheckInterval, "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", ) + _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) + + healthRetriesFlagName := "health-retries" createFlags.UintVar( &cf.HealthRetries, - "health-retries", DefaultHealthCheckRetries, + healthRetriesFlagName, DefaultHealthCheckRetries, "the number of retries allowed before a healthcheck is considered to be unhealthy", ) + _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) + + healthStartPeriodFlagName := "health-start-period" createFlags.StringVar( &cf.HealthStartPeriod, - "health-start-period", DefaultHealthCheckStartPeriod, + healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, "the initialization time needed for a container to bootstrap", ) + _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) + + healthTimeoutFlagName := "health-timeout" createFlags.StringVar( &cf.HealthTimeout, - "health-timeout", DefaultHealthCheckTimeout, + healthTimeoutFlagName, DefaultHealthCheckTimeout, "the maximum time allowed to complete the healthcheck before an interval is considered failed", ) + _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) + + hostnameFlagName := "hostname" createFlags.StringVarP( &cf.Hostname, - "hostname", "h", "", + hostnameFlagName, "h", "", "Set container hostname", ) + _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + createFlags.BoolVar( &cf.HTTPProxy, "http-proxy", true, "Set proxy environment variables in the container based on the host proxy vars", ) + + imageVolumeFlagName := "image-volume" createFlags.StringVar( &cf.ImageVolume, - "image-volume", DefaultImageVolume, + imageVolumeFlagName, DefaultImageVolume, `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, ) + _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) + createFlags.BoolVar( &cf.Init, "init", false, "Run an init binary inside the container that forwards signals and reaps processes", ) + + initPathFlagName := "init-path" createFlags.StringVar( &cf.InitPath, - "init-path", containerConfig.InitPath(), + initPathFlagName, containerConfig.InitPath(), // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) fmt.Sprintf("Path to the container-init binary"), ) + _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) + createFlags.BoolVarP( &cf.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached", ) + + ipcFlagName := "ipc" createFlags.String( - "ipc", "", + ipcFlagName, "", "IPC namespace to use", ) + _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) + + kernelMemoryFlagName := "kernel-memory" createFlags.StringVar( &cf.KernelMemory, - "kernel-memory", "", + kernelMemoryFlagName, "", "Kernel memory limit "+sizeWithUnitFormat, ) + _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + + labelFlagName := "label" createFlags.StringArrayVarP( &cf.Label, - "label", "l", []string{}, + labelFlagName, "l", []string{}, "Set metadata on container", ) + _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + + labelFileFlagName := "label-file" createFlags.StringSliceVar( &cf.LabelFile, - "label-file", []string{}, + labelFileFlagName, []string{}, "Read in a line delimited file of labels", ) + _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) + + logDriverFlagName := "log-driver" createFlags.StringVar( &cf.LogDriver, - "log-driver", "", + logDriverFlagName, "", "Logging driver for the container", ) + _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) + + logOptFlagName := "log-opt" createFlags.StringSliceVar( &cf.LogOptions, - "log-opt", []string{}, + logOptFlagName, []string{}, "Logging driver options", ) + _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) + + memoryFlagName := "memory" createFlags.StringVarP( &cf.Memory, - "memory", "m", "", + memoryFlagName, "m", "", "Memory limit "+sizeWithUnitFormat, ) + _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) + + memoryReservationFlagName := "memory-reservation" createFlags.StringVar( &cf.MemoryReservation, - "memory-reservation", "", + memoryReservationFlagName, "", "Memory soft limit "+sizeWithUnitFormat, ) + _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) + + memorySwapFlagName := "memory-swap" createFlags.StringVar( &cf.MemorySwap, - "memory-swap", "", + memorySwapFlagName, "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", ) + _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) + + memorySwappinessFlagName := "memory-swappiness" createFlags.Int64Var( &cf.MemorySwappiness, - "memory-swappiness", -1, + memorySwappinessFlagName, -1, "Tune container memory swappiness (0 to 100, or -1 for system default)", ) + _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) + + nameFlagName := "name" createFlags.StringVar( &cf.Name, - "name", "", + nameFlagName, "", "Assign a name to the container", ) + _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + createFlags.BoolVar( &cf.NoHealthCheck, "no-healthcheck", false, @@ -305,44 +466,69 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "oom-kill-disable", false, "Disable OOM Killer", ) + + oomScoreAdjFlagName := "oom-score-adj" createFlags.IntVar( &cf.OOMScoreAdj, - "oom-score-adj", 0, + oomScoreAdjFlagName, 0, "Tune the host's OOM preferences (-1000 to 1000)", ) + _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) + + overrideArchFlagName := "override-arch" createFlags.StringVar( &cf.OverrideArch, - "override-arch", "", + overrideArchFlagName, "", "use `ARCH` instead of the architecture of the machine for choosing images", ) + _ = cmd.RegisterFlagCompletionFunc(overrideArchFlagName, completion.AutocompleteNone) + + overrideOSFlagName := "override-os" createFlags.StringVar( &cf.OverrideOS, - "override-os", "", + overrideOSFlagName, "", "use `OS` instead of the running OS for choosing images", ) + _ = cmd.RegisterFlagCompletionFunc(overrideOSFlagName, completion.AutocompleteNone) + + overrideVariantFlagName := "override-variant" createFlags.StringVar( &cf.OverrideVariant, - "override-variant", "", + overrideVariantFlagName, "", "Use _VARIANT_ instead of the running architecture variant for choosing images", ) + _ = cmd.RegisterFlagCompletionFunc(overrideVariantFlagName, completion.AutocompleteNone) + + pidFlagName := "pid" createFlags.String( - "pid", "", + pidFlagName, "", "PID namespace to use", ) + _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) + + pidsLimitFlagName := "pids-limit" createFlags.Int64( - "pids-limit", containerConfig.PidsLimit(), + pidsLimitFlagName, containerConfig.PidsLimit(), "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", ) + _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) + + podFlagName := "pod" createFlags.StringVar( &cf.Pod, - "pod", "", + podFlagName, "", "Run container in an existing pod", ) + _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) + + podIDFileFlagName := "pod-id-file" createFlags.StringVar( &cf.PodIDFile, - "pod-id-file", "", + podIDFileFlagName, "", "Read the pod ID from the file", ) + _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + createFlags.BoolVar( &cf.Privileged, "privileged", false, @@ -353,11 +539,15 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "publish-all", "P", false, "Publish all exposed ports to random ports on the host interface", ) + + pullFlagName := "pull" createFlags.StringVar( &cf.Pull, - "pull", containerConfig.Engine.PullPolicy, + pullFlagName, containerConfig.Engine.PullPolicy, `Pull image before creating ("always"|"missing"|"never")`, ) + _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) + createFlags.BoolVarP( &cf.Quiet, "quiet", "q", false, @@ -378,11 +568,15 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "replace", false, `If a container with the same name exists, replace it`, ) + + restartFlagName := "restart" createFlags.StringVar( &cf.Restart, - "restart", "", + restartFlagName, "", `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, ) + _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) + createFlags.BoolVar( &cf.Rm, "rm", false, @@ -393,20 +587,31 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "rootfs", false, "The first argument is not an image but the rootfs to the exploded container", ) + + sdnotifyFlagName := "sdnotify" createFlags.StringVar( &cf.SdNotifyMode, - "sdnotify", define.SdNotifyModeContainer, + sdnotifyFlagName, define.SdNotifyModeContainer, `control sd-notify behavior ("container"|"conmon"|"ignore")`, ) + _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) + + securityOptFlagName := "security-opt" createFlags.StringArrayVar( &cf.SecurityOpt, - "security-opt", []string{}, + securityOptFlagName, []string{}, "Security Options", ) + _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) + + shmSizeFlagName := "shm-size" createFlags.String( - "shm-size", containerConfig.ShmSize(), + shmSizeFlagName, containerConfig.ShmSize(), "Size of /dev/shm "+sizeWithUnitFormat, ) + _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) + + stopSignalFlagName := "stop-signal" createFlags.StringVar( &cf.SignaturePolicy, "signature-policy", "", @@ -414,112 +619,175 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { ) createFlags.StringVar( &cf.StopSignal, - "stop-signal", "", + stopSignalFlagName, "", "Signal to stop a container. Default is SIGTERM", ) + _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) + + stopTimeoutFlagName := "stop-timeout" createFlags.UintVar( &cf.StopTimeout, - "stop-timeout", containerConfig.Engine.StopTimeout, + stopTimeoutFlagName, containerConfig.Engine.StopTimeout, "Timeout (in seconds) to stop a container. Default is 10", ) + _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) + + storageOptFlagName := "storage-opt" createFlags.StringSliceVar( &cf.StoreageOpt, - "storage-opt", []string{}, + storageOptFlagName, []string{}, "Storage driver options per container", ) + //FIXME: What should we suggest here? The flag is not in the man page. + _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone) + + subgidnameFlagName := "subgidname" createFlags.StringVar( &cf.SubUIDName, - "subgidname", "", + subgidnameFlagName, "", "Name of range listed in /etc/subgid for use in user namespace", ) + _ = cmd.RegisterFlagCompletionFunc(subgidnameFlagName, completion.AutocompleteSubgidName) + + subuidnameFlagName := "subuidname" createFlags.StringVar( &cf.SubGIDName, - "subuidname", "", + subuidnameFlagName, "", "Name of range listed in /etc/subuid for use in user namespace", ) + _ = cmd.RegisterFlagCompletionFunc(subuidnameFlagName, completion.AutocompleteSubuidName) + sysctlFlagName := "sysctl" createFlags.StringSliceVar( &cf.Sysctl, - "sysctl", []string{}, + sysctlFlagName, []string{}, "Sysctl options", ) + //TODO: Add function for systctl completion. + _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) + + systemdFlagName := "systemd" createFlags.StringVar( &cf.Systemd, - "systemd", "true", + systemdFlagName, "true", `Run container in systemd mode ("true"|"false"|"always")`, ) + _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) + + tmpfsFlagName := "tmpfs" createFlags.StringArrayVar( &cf.TmpFS, - "tmpfs", []string{}, + tmpfsFlagName, []string{}, "Mount a temporary filesystem (`tmpfs`) into a container", ) + _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) + createFlags.BoolVarP( &cf.TTY, "tty", "t", false, "Allocate a pseudo-TTY for container", ) + + timezonezFlagName := "tz" createFlags.StringVar( &cf.Timezone, - "tz", containerConfig.TZ(), + timezonezFlagName, containerConfig.TZ(), "Set timezone in container", ) + _ = cmd.RegisterFlagCompletionFunc(timezonezFlagName, completion.AutocompleteNone) //TODO: add timezone completion + + umaskFlagName := "umask" createFlags.StringVar( &cf.Umask, - "umask", containerConfig.Umask(), + umaskFlagName, containerConfig.Umask(), "Set umask in container", ) + _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) + + uidmapFlagName := "uidmap" createFlags.StringSliceVar( &cf.UIDMap, - "uidmap", []string{}, + uidmapFlagName, []string{}, "UID map to use for the user namespace", ) + _ = cmd.RegisterFlagCompletionFunc(uidmapFlagName, completion.AutocompleteNone) + + ulimitFlagName := "ulimit" createFlags.StringSliceVar( &cf.Ulimit, - "ulimit", containerConfig.Ulimits(), + ulimitFlagName, containerConfig.Ulimits(), "Ulimit options", ) + _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) + + userFlagName := "user" createFlags.StringVarP( &cf.User, - "user", "u", "", + userFlagName, "u", "", "Username or UID (format: [:])", ) + _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) + + usernsFlagName := "userns" createFlags.String( - "userns", os.Getenv("PODMAN_USERNS"), + usernsFlagName, os.Getenv("PODMAN_USERNS"), "User namespace to use", ) + _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) + + utsFlagName := "uts" createFlags.String( - "uts", "", + utsFlagName, "", "UTS namespace to use", ) + _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) + + mountFlagName := "mount" createFlags.StringArrayVar( &cf.Mount, - "mount", []string{}, + mountFlagName, []string{}, "Attach a filesystem mount to the container", ) + _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) + + volumeFlagName := "volume" createFlags.StringArrayVarP( &cf.Volume, - "volume", "v", containerConfig.Volumes(), + volumeFlagName, "v", containerConfig.Volumes(), "Bind mount a volume into the container", ) + _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) + + volumesFromFlagName := "volumes-from" createFlags.StringArrayVar( &cf.VolumesFrom, - "volumes-from", []string{}, + volumesFromFlagName, []string{}, "Mount volumes from the specified container(s)", ) + _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) + + workdirFlagName := "workdir" createFlags.StringVarP( &cf.Workdir, - "workdir", "w", "", + workdirFlagName, "w", "", "Working directory inside the container", ) + _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) + + seccompPolicyFlagName := "seccomp-policy" createFlags.StringVar( &cf.SeccompPolicy, - "seccomp-policy", "default", + seccompPolicyFlagName, "default", "Policy for selecting a seccomp profile (experimental)", ) + _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) + + cgroupConfFlagName := "cgroup-conf" createFlags.StringSliceVar( &cf.CgroupConf, - "cgroup-conf", []string{}, + cgroupConfFlagName, []string{}, "Configure cgroup v2 (key=value)", ) - return &createFlags + _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) + } diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index 935a5f7b9..cae52ccaa 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -4,58 +4,85 @@ import ( "net" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/specgen" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) -func GetNetFlags() *pflag.FlagSet { - netFlags := pflag.FlagSet{} +func DefineNetFlags(cmd *cobra.Command) { + netFlags := cmd.Flags() + + addHostFlagName := "add-host" netFlags.StringSlice( - "add-host", []string{}, + addHostFlagName, []string{}, "Add a custom host-to-IP mapping (host:ip) (default [])", ) + _ = cmd.RegisterFlagCompletionFunc(addHostFlagName, completion.AutocompleteNone) + + dnsFlagName := "dns" netFlags.StringSlice( - "dns", containerConfig.DNSServers(), + dnsFlagName, containerConfig.DNSServers(), "Set custom DNS servers", ) + _ = cmd.RegisterFlagCompletionFunc(dnsFlagName, completion.AutocompleteNone) + + dnsOptFlagName := "dns-opt" netFlags.StringSlice( - "dns-opt", containerConfig.DNSOptions(), + dnsOptFlagName, containerConfig.DNSOptions(), "Set custom DNS options", ) + _ = cmd.RegisterFlagCompletionFunc(dnsOptFlagName, completion.AutocompleteNone) + + dnsSearchFlagName := "dns-search" netFlags.StringSlice( - "dns-search", containerConfig.DNSSearches(), + dnsSearchFlagName, containerConfig.DNSSearches(), "Set custom DNS search domains", ) + _ = cmd.RegisterFlagCompletionFunc(dnsSearchFlagName, completion.AutocompleteNone) + + ipFlagName := "ip" netFlags.String( - "ip", "", + ipFlagName, "", "Specify a static IPv4 address for the container", ) + _ = cmd.RegisterFlagCompletionFunc(ipFlagName, completion.AutocompleteNone) + + macAddressFlagName := "mac-address" netFlags.String( - "mac-address", "", + macAddressFlagName, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)", ) + _ = cmd.RegisterFlagCompletionFunc(macAddressFlagName, completion.AutocompleteNone) + + networkFlagName := "network" netFlags.String( - "network", containerConfig.NetNS(), + networkFlagName, containerConfig.NetNS(), "Connect a container to a network", ) + _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworks) + + networkAliasFlagName := "network-alias" netFlags.StringSlice( - "network-alias", []string{}, + networkAliasFlagName, []string{}, "Add network-scoped alias for the container", ) + _ = cmd.RegisterFlagCompletionFunc(networkAliasFlagName, completion.AutocompleteNone) + + publishFlagName := "publish" netFlags.StringSliceP( - "publish", "p", []string{}, + publishFlagName, "p", []string{}, "Publish a container's port, or a range of ports, to the host (default [])", ) + _ = cmd.RegisterFlagCompletionFunc(publishFlagName, completion.AutocompleteNone) + netFlags.Bool( "no-hosts", false, "Do not create /etc/hosts within the container, instead use the version from the image", ) - return &netFlags } func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { diff --git a/cmd/podman/completion/completion.go b/cmd/podman/completion/completion.go new file mode 100644 index 000000000..ead8d1f05 --- /dev/null +++ b/cmd/podman/completion/completion.go @@ -0,0 +1,93 @@ +package completion + +import ( + "fmt" + "io" + "os" + "strings" + + commonComp "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/registry" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +const ( + completionDescription = `Generate shell autocompletions. + Valid arguments are bash, zsh, and fish. + Please refer to the man page to see how you can load these completions.` +) + +var ( + file string + noDesc bool + shells = []string{"bash", "zsh", "fish"} + completionCmd = &cobra.Command{ + Use: fmt.Sprintf("completion [options] {%s}", strings.Join(shells, "|")), + Short: "Generate shell autocompletions", + Long: completionDescription, + ValidArgs: shells, + Args: cobra.ExactValidArgs(1), + RunE: completion, + Example: `podman completion bash + podman completion zsh -f _podman + podman completion fish --no-desc`, + //dont show this command to users + Hidden: true, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: completionCmd, + }) + flags := completionCmd.Flags() + fileFlagName := "file" + flags.StringVarP(&file, fileFlagName, "f", "", "Output the completion to file rather than stdout.") + _ = completionCmd.RegisterFlagCompletionFunc(fileFlagName, commonComp.AutocompleteDefault) + + flags.BoolVar(&noDesc, "no-desc", false, "Don't include descriptions in the completion output.") +} + +func completion(cmd *cobra.Command, args []string) error { + var w io.Writer + + if file != "" { + file, err := os.Create(file) + if err != nil { + return err + } + defer file.Close() + w = file + } else { + w = os.Stdout + } + + var err error + switch args[0] { + case "bash": + if noDesc { + err = cmd.Root().GenBashCompletion(w) + } else { + err = cmd.Root().GenBashCompletionWithDesc(w) + } + case "zsh": + if noDesc { + err = cmd.Root().GenZshCompletionNoDesc(w) + } else { + err = cmd.Root().GenZshCompletion(w) + } + case "fish": + err = cmd.Root().GenFishCompletion(w, !noDesc) + } + + if err != nil { + return err + } + + _, err = io.WriteString(w, fmt.Sprintf( + "\n# This file is generated with %q; see: podman-completion(1)\n", cmd.CommandPath(), + )) + return err +} diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index 0db7c22d0..f00c5378b 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -3,33 +3,35 @@ package containers import ( "os" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively." attachCommand = &cobra.Command{ - Use: "attach [options] CONTAINER", - Short: "Attach to a running container", - Long: attachDescription, - RunE: attach, - Args: validate.IDOrLatestArgs, + Use: "attach [options] CONTAINER", + Short: "Attach to a running container", + Long: attachDescription, + RunE: attach, + Args: validate.IDOrLatestArgs, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman attach ctrID podman attach 1234 podman attach --no-stdin foobar`, } containerAttachCommand = &cobra.Command{ - Use: attachCommand.Use, - Short: attachCommand.Short, - Long: attachCommand.Long, - RunE: attachCommand.RunE, - Args: validate.IDOrLatestArgs, + Use: attachCommand.Use, + Short: attachCommand.Short, + Long: attachCommand.Long, + RunE: attachCommand.RunE, + Args: validate.IDOrLatestArgs, + ValidArgsFunction: attachCommand.ValidArgsFunction, Example: `podman container attach ctrID podman container attach 1234 podman container attach --no-stdin foobar`, @@ -40,8 +42,13 @@ var ( attachOpts entities.AttachOptions ) -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-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") +func attachFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + detachKeysFlagName := "detach-keys" + flags.StringVar(&attachOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) + 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") } @@ -51,7 +58,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: attachCommand, }) - attachFlags(attachCommand.Flags()) + attachFlags(attachCommand) validate.AddLatestFlag(attachCommand, &attachOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -59,7 +66,7 @@ func init() { Command: containerAttachCommand, Parent: containerCmd, }) - attachFlags(containerAttachCommand.Flags()) + attachFlags(containerAttachCommand) validate.AddLatestFlag(containerAttachCommand, &attachOpts.Latest) } diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index 2606f62c5..b6dc21348 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -27,6 +29,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman container checkpoint --keep ctrID podman container checkpoint --all podman container checkpoint --leave-running --latest`, @@ -48,7 +51,11 @@ 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.StringVarP(&checkpointOptions.Export, "export", "e", "", "Export the checkpoint image to a tar.gz") + + exportFlagName := "export" + flags.StringVarP(&checkpointOptions.Export, exportFlagName, "e", "", "Export the checkpoint image to a tar.gz") + _ = checkpointCommand.RegisterFlagCompletionFunc(exportFlagName, completion.AutocompleteDefault) + flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting") validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) } diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go index 54bc64a60..593aaad3d 100644 --- a/cmd/podman/containers/cleanup.go +++ b/cmd/podman/containers/cleanup.go @@ -3,6 +3,8 @@ package containers import ( "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -26,6 +28,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainersExited, Example: `podman container cleanup --latest podman container cleanup ctrID1 ctrID2 ctrID3 podman container cleanup --all`, @@ -44,7 +47,11 @@ func init() { }) flags := cleanupCommand.Flags() flags.BoolVarP(&cleanupOptions.All, "all", "a", false, "Cleans up all containers") - flags.StringVar(&cleanupOptions.Exec, "exec", "", "Clean up the given exec session instead of the container") + + execFlagName := "exec" + flags.StringVar(&cleanupOptions.Exec, execFlagName, "", "Clean up the given exec session instead of the container") + _ = cleanupCommand.RegisterFlagCompletionFunc(execFlagName, completion.AutocompleteNone) + 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) diff --git a/cmd/podman/containers/commit.go b/cmd/podman/containers/commit.go index 412dbf7a8..c5c7673b2 100644 --- a/cmd/podman/containers/commit.go +++ b/cmd/podman/containers/commit.go @@ -7,22 +7,24 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.` commitCommand = &cobra.Command{ - Use: "commit [options] CONTAINER [IMAGE]", - Short: "Create new image based on the changed container", - Long: commitDescription, - RunE: commit, - Args: cobra.RangeArgs(1, 2), + Use: "commit [options] CONTAINER [IMAGE]", + Short: "Create new image based on the changed container", + Long: commitDescription, + RunE: commit, + Args: cobra.RangeArgs(1, 2), + ValidArgsFunction: common.AutocompleteContainers, 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,19 +32,17 @@ var ( } containerCommitCommand = &cobra.Command{ - Args: commitCommand.Args, - Use: commitCommand.Use, - Short: commitCommand.Short, - Long: commitCommand.Long, - RunE: commitCommand.RunE, + Args: commitCommand.Args, + Use: commitCommand.Use, + Short: commitCommand.Short, + Long: commitCommand.Long, + RunE: commitCommand.RunE, + ValidArgsFunction: commitCommand.ValidArgsFunction, Example: `podman container commit -q --message "committing container to image" reverent_golick image-committed podman container commit -q --author "firstName lastName" reverent_golick image-committed podman container commit -q --pause=false containerID image-committed podman container commit containerID`, } - - // ChangeCmds is the list of valid Changes commands to passed to the Commit call - ChangeCmds = []string{"CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR"} ) var ( @@ -52,12 +52,29 @@ var ( iidFile string ) -func commitFlags(flags *pflag.FlagSet) { - flags.StringArrayVarP(&commitOptions.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): "+strings.Join(ChangeCmds, " | ")) - flags.StringVarP(&commitOptions.Format, "format", "f", "oci", "`Format` of the image manifest and metadata") - flags.StringVarP(&iidFile, "iidfile", "", "", "`file` to write the image ID to") - flags.StringVarP(&commitOptions.Message, "message", "m", "", "Set commit message for imported image") - flags.StringVarP(&commitOptions.Author, "author", "a", "", "Set the author for the image committed") +func commitFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + changeFlagName := "change" + flags.StringArrayVarP(&commitOptions.Changes, changeFlagName, "c", []string{}, "Apply the following possible instructions to the created image (default []): "+strings.Join(common.ChangeCmds, " | ")) + _ = cmd.RegisterFlagCompletionFunc(changeFlagName, common.AutocompleteChangeInstructions) + + formatFlagName := "format" + flags.StringVarP(&commitOptions.Format, formatFlagName, "f", "oci", "`Format` of the image manifest and metadata") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteImageFormat) + + iidFileFlagName := "iidfile" + flags.StringVarP(&iidFile, iidFileFlagName, "", "", "`file` to write the image ID to") + _ = cmd.RegisterFlagCompletionFunc(iidFileFlagName, completion.AutocompleteDefault) + + messageFlagName := "message" + flags.StringVarP(&commitOptions.Message, messageFlagName, "m", "", "Set commit message for imported image") + _ = cmd.RegisterFlagCompletionFunc(messageFlagName, completion.AutocompleteNone) + + authorFlagName := "author" + flags.StringVarP(&commitOptions.Author, authorFlagName, "a", "", "Set the author for the image committed") + _ = cmd.RegisterFlagCompletionFunc(authorFlagName, completion.AutocompleteNone) + flags.BoolVarP(&commitOptions.Pause, "pause", "p", false, "Pause container during commit") flags.BoolVarP(&commitOptions.Quiet, "quiet", "q", false, "Suppress output") flags.BoolVar(&commitOptions.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes") @@ -68,16 +85,14 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: commitCommand, }) - flags := commitCommand.Flags() - commitFlags(flags) + commitFlags(commitCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCommitCommand, Parent: containerCmd, }) - containerCommitFlags := containerCommitCommand.Flags() - commitFlags(containerCommitFlags) + commitFlags(containerCommitCommand) } func commit(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 5dfe43ded..a74ea89d2 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -1,13 +1,13 @@ package containers import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -16,21 +16,23 @@ var ( You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or directory. ` cpCommand = &cobra.Command{ - Use: "cp [options] SRC_PATH DEST_PATH", - Short: "Copy files/folders between a container and the local filesystem", - Long: cpDescription, - Args: cobra.ExactArgs(2), - RunE: cp, - Example: "podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + Use: "cp [options] SRC_PATH DEST_PATH", + Short: "Copy files/folders between a container and the local filesystem", + Long: cpDescription, + Args: cobra.ExactArgs(2), + RunE: cp, + ValidArgsFunction: common.AutocompleteCpCommand, + Example: "podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", } containerCpCommand = &cobra.Command{ - Use: cpCommand.Use, - Short: cpCommand.Short, - Long: cpCommand.Long, - Args: cpCommand.Args, - RunE: cpCommand.RunE, - Example: "podman container cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + Use: cpCommand.Use, + Short: cpCommand.Short, + Long: cpCommand.Long, + Args: cpCommand.Args, + RunE: cpCommand.RunE, + ValidArgsFunction: cpCommand.ValidArgsFunction, + Example: "podman container cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", } ) @@ -38,7 +40,8 @@ var ( cpOpts entities.ContainerCpOptions ) -func cpFlags(flags *pflag.FlagSet) { +func cpFlags(cmd *cobra.Command) { + flags := cmd.Flags() flags.BoolVar(&cpOpts.Extract, "extract", false, "Extract the tar file into the destination directory.") flags.BoolVar(&cpOpts.Pause, "pause", copyPause(), "Pause the container while copying") } @@ -48,16 +51,14 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: cpCommand, }) - flags := cpCommand.Flags() - cpFlags(flags) + cpFlags(cpCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, Command: containerCpCommand, Parent: containerCmd, }) - containerCpFlags := containerCpCommand.Flags() - cpFlags(containerCpFlags) + cpFlags(containerCpCommand) } func cp(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index b7b2a364f..db920554e 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -19,7 +19,6 @@ import ( "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -27,22 +26,24 @@ var ( The container ID is then printed to stdout. You can then start it at any time with the podman start command. The container will be created with the initial state 'created'.` createCommand = &cobra.Command{ - Use: "create [options] IMAGE [COMMAND [ARG...]]", - Short: "Create but do not start a container", - Long: createDescription, - RunE: create, - Args: cobra.MinimumNArgs(1), + Use: "create [options] IMAGE [COMMAND [ARG...]]", + Short: "Create but do not start a container", + Long: createDescription, + RunE: create, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteCreateRun, Example: `podman create alpine ls podman create --annotation HELLO=WORLD alpine ls podman create -t -i --name myctr alpine ls`, } containerCreateCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), - Use: createCommand.Use, - Short: createCommand.Short, - Long: createCommand.Long, - RunE: createCommand.RunE, + Args: cobra.MinimumNArgs(1), + Use: createCommand.Use, + Short: createCommand.Short, + Long: createCommand.Long, + RunE: createCommand.RunE, + ValidArgsFunction: createCommand.ValidArgsFunction, Example: `podman container create alpine ls podman container create --annotation HELLO=WORLD alpine ls podman container create -t -i --name myctr alpine ls`, @@ -53,10 +54,13 @@ var ( cliVals common.ContainerCLIOpts ) -func createFlags(flags *pflag.FlagSet) { +func createFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.SetInterspersed(false) - flags.AddFlagSet(common.GetCreateFlags(&cliVals)) - flags.AddFlagSet(common.GetNetFlags()) + common.DefineCreateFlags(cmd, &cliVals) + common.DefineNetFlags(cmd) + flags.SetNormalizeFunc(utils.AliasFlags) _ = flags.MarkHidden("signature-policy") @@ -70,18 +74,14 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCommand, }) - // common.GetCreateFlags(createCommand) - flags := createCommand.Flags() - createFlags(flags) + createFlags(createCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCreateCommand, Parent: containerCmd, }) - - containerCreateFlags := containerCreateCommand.Flags() - createFlags(containerCreateFlags) + createFlags(containerCreateCommand) } func create(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index caf7e9955..4533b7159 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -13,11 +13,12 @@ import ( var ( // podman container _diff_ diffCmd = &cobra.Command{ - Use: "diff [options] CONTAINER", - Args: validate.IDOrLatestArgs, - 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, + Use: "diff [options] CONTAINER", + Args: validate.IDOrLatestArgs, + 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, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman container diff myCtr podman container diff -l --format json myCtr`, } @@ -35,7 +36,11 @@ func init() { flags := diffCmd.Flags() 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") + + formatFlagName := "format" + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index 88851f619..306bae58e 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -5,6 +5,8 @@ import ( "fmt" "os" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -13,7 +15,6 @@ import ( "github.com/containers/podman/v2/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -25,6 +26,7 @@ var ( Long: execDescription, RunE: exec, DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman exec -it ctrID ls podman exec -it -w /tmp myCtr pwd podman exec --user root ctrID ls`, @@ -36,6 +38,7 @@ var ( Long: execCommand.Long, RunE: execCommand.RunE, DisableFlagsInUseLine: true, + ValidArgsFunction: execCommand.ValidArgsFunction, Example: `podman container exec -it ctrID ls podman container exec -it -w /tmp myCtr pwd podman container exec --user root ctrID ls`, @@ -48,18 +51,39 @@ var ( execDetach bool ) -func execFlags(flags *pflag.FlagSet) { +func execFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.SetInterspersed(false) flags.BoolVarP(&execDetach, "detach", "d", false, "Run the exec session in detached mode (backgrounded)") - flags.StringVar(&execOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl- where is one of: a-z, @, ^, [, , or _") - flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables") - flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables") + + detachKeysFlagName := "detach-keys" + flags.StringVar(&execOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl- where is one of: a-z, @, ^, [, , or _") + _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) + + envFlagName := "env" + flags.StringArrayVarP(&envInput, envFlagName, "e", []string{}, "Set environment variables") + _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + + envFileFlagName := "env-file" + flags.StringSliceVar(&envFile, envFileFlagName, []string{}, "Read in a file of environment variables") + _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) + flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") 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") + + userFlagName := "user" + flags.StringVarP(&execOpts.User, userFlagName, "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") + _ = cmd.RegisterFlagCompletionFunc(userFlagName, common.AutocompleteUserFlag) + + preserveFdsFlagName := "preserve-fds" + flags.UintVar(&execOpts.PreserveFDs, preserveFdsFlagName, 0, "Pass N additional file descriptors to the container") + _ = cmd.RegisterFlagCompletionFunc(preserveFdsFlagName, completion.AutocompleteNone) + + workdirFlagName := "workdir" + flags.StringVarP(&execOpts.WorkDir, workdirFlagName, "w", "", "Working directory inside the container") + _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) if registry.IsRemote() { _ = flags.MarkHidden("preserve-fds") @@ -71,7 +95,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: execCommand, }) - execFlags(execCommand.Flags()) + execFlags(execCommand) validate.AddLatestFlag(execCommand, &execOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -79,7 +103,7 @@ func init() { Command: containerExecCommand, Parent: containerCmd, }) - execFlags(containerExecCommand.Flags()) + execFlags(containerExecCommand) validate.AddLatestFlag(containerExecCommand, &execOpts.Latest) } diff --git a/cmd/podman/containers/exists.go b/cmd/podman/containers/exists.go index 70b8af159..607a03023 100644 --- a/cmd/podman/containers/exists.go +++ b/cmd/podman/containers/exists.go @@ -3,6 +3,7 @@ package containers import ( "context" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -20,6 +21,7 @@ var ( RunE: exists, Args: cobra.ExactArgs(1), DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteContainers, } ) diff --git a/cmd/podman/containers/export.go b/cmd/podman/containers/export.go index f5e02d134..fe2da0f3a 100644 --- a/cmd/podman/containers/export.go +++ b/cmd/podman/containers/export.go @@ -4,12 +4,13 @@ import ( "context" "os" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" ) @@ -18,21 +19,23 @@ var ( " and saves it on the local machine." exportCommand = &cobra.Command{ - Use: "export [options] CONTAINER", - Short: "Export container's filesystem contents as a tar archive", - Long: exportDescription, - RunE: export, - Args: cobra.ExactArgs(1), + Use: "export [options] CONTAINER", + Short: "Export container's filesystem contents as a tar archive", + Long: exportDescription, + RunE: export, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainers, Example: `podman export ctrID > myCtr.tar podman export --output="myCtr.tar" ctrID`, } containerExportCommand = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: exportCommand.Use, - Short: exportCommand.Short, - Long: exportCommand.Long, - RunE: exportCommand.RunE, + Args: cobra.ExactArgs(1), + Use: exportCommand.Use, + Short: exportCommand.Short, + Long: exportCommand.Long, + RunE: exportCommand.RunE, + ValidArgsFunction: exportCommand.ValidArgsFunction, Example: `podman container export ctrID > myCtr.tar podman container export --output="myCtr.tar" ctrID`, } @@ -42,8 +45,12 @@ var ( exportOpts entities.ContainerExportOptions ) -func exportFlags(flags *pflag.FlagSet) { - flags.StringVarP(&exportOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") +func exportFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + outputFlagName := "output" + flags.StringVarP(&exportOpts.Output, outputFlagName, "o", "", "Write to a specified file (default: stdout, which must be redirected)") + _ = cmd.RegisterFlagCompletionFunc(outputFlagName, completion.AutocompleteDefault) } func init() { @@ -51,17 +58,14 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: exportCommand, }) - flags := exportCommand.Flags() - exportFlags(flags) + exportFlags(exportCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerExportCommand, Parent: containerCmd, }) - - containerExportFlags := containerExportCommand.Flags() - exportFlags(containerExportFlags) + exportFlags(containerExportCommand) } func export(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go index 983c0e4e8..99a32bc24 100644 --- a/cmd/podman/containers/init.go +++ b/cmd/podman/containers/init.go @@ -3,6 +3,7 @@ package containers import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -22,17 +23,19 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainersCreated, Example: `podman init --latest podman init 3c45ef19d893 podman init test1`, } containerInitCommand = &cobra.Command{ - Use: initCommand.Use, - Short: initCommand.Short, - Long: initCommand.Long, - RunE: initCommand.RunE, - Args: initCommand.Args, + Use: initCommand.Use, + Short: initCommand.Short, + Long: initCommand.Long, + RunE: initCommand.RunE, + Args: initCommand.Args, + ValidArgsFunction: initCommand.ValidArgsFunction, Example: `podman container init --latest podman container init 3c45ef19d893 podman container init test1`, diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index b4e1feccb..e438bca95 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -1,6 +1,7 @@ package containers import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -11,10 +12,11 @@ import ( var ( // podman container _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [options] 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, + Use: "inspect [options] 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, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman container inspect myCtr podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`, } @@ -30,7 +32,11 @@ func init() { inspectOpts = new(entities.InspectOptions) 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") + + formatFlagName := "format" + flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + validate.AddLatestFlag(inspectCmd, &inspectOpts.Latest) } diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index 1bb071b6d..4640229a9 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -5,13 +5,13 @@ import ( "errors" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/signal" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -24,6 +24,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman kill mywebserver podman kill 860a4b23 podman kill --signal TERM ctrID`, @@ -33,10 +34,11 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, - Use: killCommand.Use, - Short: killCommand.Short, - Long: killCommand.Long, - RunE: killCommand.RunE, + Use: killCommand.Use, + Short: killCommand.Short, + Long: killCommand.Long, + RunE: killCommand.RunE, + ValidArgsFunction: killCommand.ValidArgsFunction, Example: `podman container kill mywebserver podman container kill 860a4b23 podman container kill --signal TERM ctrID`, @@ -47,9 +49,14 @@ var ( killOptions = entities.KillOptions{} ) -func killFlags(flags *pflag.FlagSet) { +func killFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&killOptions.All, "all", "a", false, "Signal all running containers") - flags.StringVarP(&killOptions.Signal, "signal", "s", "KILL", "Signal to send to the container") + + signalFlagName := "signal" + flags.StringVarP(&killOptions.Signal, signalFlagName, "s", "KILL", "Signal to send to the container") + _ = cmd.RegisterFlagCompletionFunc(signalFlagName, common.AutocompleteStopSignal) } func init() { @@ -57,7 +64,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: killCommand, }) - killFlags(killCommand.Flags()) + killFlags(killCommand) validate.AddLatestFlag(killCommand, &killOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -65,7 +72,7 @@ func init() { Command: containerKillCommand, Parent: containerCmd, }) - killFlags(containerKillCommand.Flags()) + killFlags(containerKillCommand) validate.AddLatestFlag(containerKillCommand, &killOptions.Latest) } diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go index 78a15559f..834413c2c 100644 --- a/cmd/podman/containers/list.go +++ b/cmd/podman/containers/list.go @@ -1,6 +1,7 @@ package containers import ( + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -10,12 +11,13 @@ import ( var ( // podman container _list_ listCmd = &cobra.Command{ - Use: "list [options]", - Aliases: []string{"ls"}, - Args: validate.NoArgs, - Short: "List containers", - Long: "Prints out information about the containers", - RunE: ps, + Use: "list [options]", + Aliases: []string{"ls"}, + Args: validate.NoArgs, + Short: "List containers", + Long: "Prints out information about the containers", + RunE: ps, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman container list -a podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" podman container list --size --sort names`, @@ -28,6 +30,6 @@ func init() { Command: listCmd, Parent: containerCmd, }) - listFlagSet(listCmd.Flags()) + listFlagSet(listCmd) validate.AddLatestFlag(listCmd, &listOpts.Latest) } diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index 8ad2d7e16..1fa4ac11f 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -3,13 +3,14 @@ package containers import ( "os" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // logsOptionsWrapper wraps entities.LogsOptions and prevents leaking @@ -43,7 +44,8 @@ var ( } return nil }, - RunE: logs, + RunE: logs, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman logs ctrID podman logs --names ctrID1 ctrID2 podman logs --tail 2 mywebserver @@ -52,11 +54,12 @@ var ( } containerLogsCommand = &cobra.Command{ - Use: logsCommand.Use, - Short: logsCommand.Short, - Long: logsCommand.Long, - Args: logsCommand.Args, - RunE: logsCommand.RunE, + Use: logsCommand.Use, + Short: logsCommand.Short, + Long: logsCommand.Long, + Args: logsCommand.Args, + RunE: logsCommand.RunE, + ValidArgsFunction: logsCommand.ValidArgsFunction, Example: `podman container logs ctrID podman container logs --names ctrID1 ctrID2 podman container logs --tail 2 mywebserver @@ -71,7 +74,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: logsCommand, }) - logsFlags(logsCommand.Flags()) + logsFlags(logsCommand) validate.AddLatestFlag(logsCommand, &logsOptions.Latest) // container logs @@ -80,15 +83,24 @@ func init() { Command: containerLogsCommand, Parent: containerCmd, }) - logsFlags(containerLogsCommand.Flags()) + logsFlags(containerLogsCommand) validate.AddLatestFlag(containerLogsCommand, &logsOptions.Latest) } -func logsFlags(flags *pflag.FlagSet) { +func logsFlags(cmd *cobra.Command) { + flags := cmd.Flags() + 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.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") + + sinceFlagName := "since" + flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) + + tailFlagName := "tail" + flags.Int64Var(&logsOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines") + _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone) + flags.BoolVarP(&logsOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") flags.BoolVarP(&logsOptions.Names, "names", "n", false, "Output the container name in the log") flags.SetInterspersed(false) diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index 335367e18..fb2101d64 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -7,13 +7,13 @@ import ( "text/template" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -36,15 +36,17 @@ var ( registry.UnshareNSRequired: "", registry.ParentNSRequired: "", }, + ValidArgsFunction: common.AutocompleteContainers, } containerMountCommmand = &cobra.Command{ - Use: mountCommand.Use, - Short: mountCommand.Short, - Long: mountCommand.Long, - RunE: mountCommand.RunE, - Args: mountCommand.Args, - Annotations: mountCommand.Annotations, + Use: mountCommand.Use, + Short: mountCommand.Short, + Long: mountCommand.Long, + RunE: mountCommand.RunE, + Args: mountCommand.Args, + Annotations: mountCommand.Annotations, + ValidArgsFunction: mountCommand.ValidArgsFunction, } ) @@ -52,9 +54,15 @@ var ( mountOpts entities.ContainerMountOptions ) -func mountFlags(flags *pflag.FlagSet) { +func mountFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers") - flags.StringVar(&mountOpts.Format, "format", "", "Print the mounted containers in specified format (json)") + + formatFlagName := "format" + flags.StringVar(&mountOpts.Format, formatFlagName, "", "Print the mounted containers in specified format (json)") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output") } @@ -63,7 +71,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: mountCommand, }) - mountFlags(mountCommand.Flags()) + mountFlags(mountCommand) validate.AddLatestFlag(mountCommand, &mountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -71,7 +79,7 @@ func init() { Command: containerMountCommmand, Parent: containerCmd, }) - mountFlags(containerMountCommmand.Flags()) + mountFlags(containerMountCommmand) validate.AddLatestFlag(containerMountCommmand, &mountOpts.Latest) } diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index 89a76ab25..18b37adfd 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/cgroups" @@ -17,20 +18,22 @@ import ( var ( pauseDescription = `Pauses one or more running containers. The container name or ID can be used.` pauseCommand = &cobra.Command{ - Use: "pause [options] CONTAINER [CONTAINER...]", - Short: "Pause all the processes in one or more containers", - Long: pauseDescription, - RunE: pause, + Use: "pause [options] CONTAINER [CONTAINER...]", + Short: "Pause all the processes in one or more containers", + Long: pauseDescription, + RunE: pause, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman pause mywebserver podman pause 860a4b23 podman pause -a`, } containerPauseCommand = &cobra.Command{ - Use: pauseCommand.Use, - Short: pauseCommand.Short, - Long: pauseCommand.Long, - RunE: pauseCommand.RunE, + Use: pauseCommand.Use, + Short: pauseCommand.Short, + Long: pauseCommand.Long, + RunE: pauseCommand.RunE, + ValidArgsFunction: pauseCommand.ValidArgsFunction, Example: `podman container pause mywebserver podman container pause 860a4b23 podman container pause -a`, diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index a895f24b1..ac31e158e 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -25,6 +26,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman port --all podman port ctrID 80/tcp podman port --latest 80`, @@ -38,6 +40,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, + ValidArgsFunction: portCommand.ValidArgsFunction, Example: `podman container port --all podman container port --latest 80`, } diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index bfdace086..e8debd3ad 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -8,6 +8,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -21,12 +22,13 @@ var ( Removes all non running containers`) pruneCommand = &cobra.Command{ - Use: "prune [options]", - Short: "Remove all non running containers", - Long: pruneDescription, - RunE: prune, - Example: `podman container prune`, - Args: validate.NoArgs, + Use: "prune [options]", + Short: "Remove all non running containers", + Long: pruneDescription, + RunE: prune, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman container prune`, + Args: validate.NoArgs, } force bool filter = []string{} @@ -40,7 +42,9 @@ func init() { }) flags := pruneCommand.Flags() flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation. The default is false") - flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label==')") + filterFlagName := "filter" + flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label==')") + _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) } func prune(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 7da430bc6..a9e2d2e35 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -11,7 +11,9 @@ import ( "time" tm "github.com/buger/goterm" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -21,17 +23,17 @@ import ( "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( psDescription = "Prints out information about the containers" psCommand = &cobra.Command{ - Use: "ps [options]", - Args: validate.NoArgs, - Short: "List containers", - Long: psDescription, - RunE: ps, + Use: "ps [options]", + Args: validate.NoArgs, + Short: "List containers", + Long: psDescription, + RunE: ps, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman ps -a podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" podman ps --size --sort names`, @@ -50,26 +52,45 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: psCommand, }) - listFlagSet(psCommand.Flags()) + listFlagSet(psCommand) validate.AddLatestFlag(psCommand, &listOpts.Latest) } -func listFlagSet(flags *pflag.FlagSet) { +func listFlagSet(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers") - flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") flags.BoolVar(&listOpts.Storage, "external", false, "Show containers in storage not controlled by Podman") - 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)") + + filterFlagName := "filter" + flags.StringSliceVarP(&filters, filterFlagName, "f", []string{}, "Filter output based on conditions given") + //TODO add custom filter function + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + + formatFlagName := "format" + flags.StringVar(&listOpts.Format, formatFlagName, "", "Pretty-print containers to JSON or using a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + + lastFlagName := "last" + flags.IntVarP(&listOpts.Last, lastFlagName, "n", -1, "Print the n last created containers (all states)") + _ = cmd.RegisterFlagCompletionFunc(lastFlagName, completion.AutocompleteNone) + flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information") flags.BoolVar(&noTrunc, "no-trunc", false, "Display the extended information") flags.BoolVarP(&listOpts.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with") flags.BoolVarP(&listOpts.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only") flags.BoolVarP(&listOpts.Size, "size", "s", false, "Display the total file sizes") flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime") - flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds") + + watchFlagName := "watch" + flags.UintVarP(&listOpts.Watch, watchFlagName, "w", 0, "Watch the ps output on an interval in seconds") + _ = cmd.RegisterFlagCompletionFunc(watchFlagName, completion.AutocompleteNone) sort := validate.Value(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") - flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) + sortFlagName := "sort" + flags.Var(sort, sortFlagName, "Sort output by: "+sort.Choices()) + _ = cmd.RegisterFlagCompletionFunc(sortFlagName, common.AutocompletePsSort) + flags.SetNormalizeFunc(utils.AliasFlags) } func checkFlags(c *cobra.Command) error { diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index 1cc28c20d..a51355194 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -11,7 +13,6 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -27,16 +28,18 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman restart ctrID podman restart --latest podman restart ctrID1 ctrID2`, } containerRestartCommand = &cobra.Command{ - Use: restartCommand.Use, - Short: restartCommand.Short, - Long: restartCommand.Long, - RunE: restartCommand.RunE, + Use: restartCommand.Use, + Short: restartCommand.Short, + Long: restartCommand.Long, + RunE: restartCommand.RunE, + ValidArgsFunction: restartCommand.ValidArgsFunction, Example: `podman container restart ctrID podman container restart --latest podman container restart ctrID1 ctrID2`, @@ -48,10 +51,15 @@ var ( restartTimeout uint ) -func restartFlags(flags *pflag.FlagSet) { +func restartFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all non-running containers") 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") + + timeFlagName := "time" + flags.UintVarP(&restartTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") + _ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) flags.SetNormalizeFunc(utils.AliasFlags) } @@ -61,7 +69,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restartCommand, }) - restartFlags(restartCommand.Flags()) + restartFlags(restartCommand) validate.AddLatestFlag(restartCommand, &restartOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -69,7 +77,7 @@ func init() { Command: containerRestartCommand, Parent: containerCmd, }) - restartFlags(containerRestartCommand.Flags()) + restartFlags(containerRestartCommand) validate.AddLatestFlag(containerRestartCommand, &restartOptions.Latest) } diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 314bf7564..6a1d2b319 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -27,6 +29,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman container restore ctrID podman container restore --latest podman container restore --all`, @@ -47,8 +50,15 @@ func init() { flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files") 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)") + + importFlagName := "import" + flags.StringVarP(&restoreOptions.Import, importFlagName, "i", "", "Restore from exported checkpoint archive (tar.gz)") + _ = restoreCommand.RegisterFlagCompletionFunc(importFlagName, completion.AutocompleteDefault) + + nameFlagName := "name" + flags.StringVarP(&restoreOptions.Name, nameFlagName, "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") + _ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + 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") diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index ccdd2ef05..ee9dc4c78 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -13,7 +15,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -28,6 +29,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman rm imageID podman rm mywebserver myflaskserver 860a4b23 podman rm --force --all @@ -42,6 +44,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, + ValidArgsFunction: rmCommand.ValidArgsFunction, Example: `podman container rm imageID podman container rm mywebserver myflaskserver 860a4b23 podman container rm --force --all @@ -53,12 +56,17 @@ var ( rmOptions = entities.RmOptions{} ) -func rmFlags(flags *pflag.FlagSet) { +func rmFlags(cmd *cobra.Command) { + flags := cmd.Flags() + 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.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") - flags.StringArrayVarP(&rmOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") + + cidfileFlagName := "cidfile" + flags.StringArrayVarP(&rmOptions.CIDFiles, cidfileFlagName, "", nil, "Read the container ID from the file") + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) if !registry.IsRemote() { // This option is deprecated, but needs to still exists for backwards compatibility @@ -72,7 +80,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCommand, }) - rmFlags(rmCommand.Flags()) + rmFlags(rmCommand) validate.AddLatestFlag(rmCommand, &rmOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -80,7 +88,7 @@ func init() { Command: containerRmCommand, Parent: containerCmd, }) - rmFlags(containerRmCommand.Flags()) + rmFlags(containerRmCommand) validate.AddLatestFlag(containerRmCommand, &rmOptions.Latest) } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 780cd0c0d..b17c13f46 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -18,28 +19,29 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( runDescription = "Runs a command in a new container from the given image" runCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), - Use: "run [options] IMAGE [COMMAND [ARG...]]", - Short: "Run a command in a new container", - Long: runDescription, - RunE: run, + Args: cobra.MinimumNArgs(1), + Use: "run [options] IMAGE [COMMAND [ARG...]]", + Short: "Run a command in a new container", + Long: runDescription, + RunE: run, + ValidArgsFunction: common.AutocompleteCreateRun, Example: `podman run imageID ls -alF /etc podman run --network=host imageID dnf -y install java podman run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`, } containerRunCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), - Use: runCommand.Use, - Short: runCommand.Short, - Long: runCommand.Long, - RunE: runCommand.RunE, + Args: cobra.MinimumNArgs(1), + Use: runCommand.Use, + Short: runCommand.Short, + Long: runCommand.Long, + RunE: runCommand.RunE, + ValidArgsFunction: runCommand.ValidArgsFunction, Example: `podman container run imageID ls -alF /etc podman container run --network=host imageID dnf -y install java podman container run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`, @@ -55,16 +57,26 @@ var ( runRmi bool ) -func runFlags(flags *pflag.FlagSet) { +func runFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.SetInterspersed(false) - flags.AddFlagSet(common.GetCreateFlags(&cliVals)) - flags.AddFlagSet(common.GetNetFlags()) + common.DefineCreateFlags(cmd, &cliVals) + common.DefineNetFlags(cmd) + flags.SetNormalizeFunc(utils.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") + + preserveFdsFlagName := "preserve-fds" flags.UintVar(&runOpts.PreserveFDs, "preserve-fds", 0, "Pass a number of additional file descriptors into the container") + _ = cmd.RegisterFlagCompletionFunc(preserveFdsFlagName, completion.AutocompleteNone) + flags.BoolVarP(&runOpts.Detach, "detach", "d", false, "Run container in background and print container ID") - flags.StringVar(&runOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + + detachKeysFlagName := "detach-keys" + flags.StringVar(&runOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) _ = flags.MarkHidden("signature-policy") if registry.IsRemote() { @@ -77,8 +89,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: runCommand, }) - flags := runCommand.Flags() - runFlags(flags) + + runFlags(runCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -86,8 +98,7 @@ func init() { Parent: containerCmd, }) - containerRunFlags := containerRunCommand.Flags() - runFlags(containerRunFlags) + runFlags(containerRunCommand) } func run(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/runlabel.go b/cmd/podman/containers/runlabel.go index 92581c26f..2f6d2eb05 100644 --- a/cmd/podman/containers/runlabel.go +++ b/cmd/podman/containers/runlabel.go @@ -5,7 +5,9 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/sirupsen/logrus" @@ -23,11 +25,12 @@ var ( runlabelOptions = runlabelOptionsWrapper{} runlabelDescription = "Executes a command as described by a container image label." runlabelCommand = &cobra.Command{ - Use: "runlabel [options] LABEL IMAGE [ARG...]", - Short: "Execute the command described by an image label", - Long: runlabelDescription, - RunE: runlabel, - Args: cobra.MinimumNArgs(2), + Use: "runlabel [options] LABEL IMAGE [ARG...]", + Short: "Execute the command described by an image label", + Long: runlabelDescription, + RunE: runlabel, + Args: cobra.MinimumNArgs(2), + ValidArgsFunction: common.AutocompleteImages, Example: `podman container runlabel run imageID podman container runlabel install imageID arg1 arg2 podman container runlabel --display run myImage`, @@ -42,11 +45,25 @@ func init() { }) flags := runlabelCommand.Flags() - flags.StringVar(&runlabelOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&runlabelOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.StringVar(&runlabelOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + + authfileflagName := "authfile" + flags.StringVar(&runlabelOptions.Authfile, authfileflagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = runlabelCommand.RegisterFlagCompletionFunc(authfileflagName, completion.AutocompleteDefault) + + certDirFlagName := "cert-dir" + flags.StringVar(&runlabelOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") + _ = runlabelCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + + credsFlagName := "creds" + flags.StringVar(&runlabelOptions.Credentials, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + _ = runlabelCommand.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + flags.BoolVar(&runlabelOptions.Display, "display", false, "Preview the command that the label would run") - flags.StringVarP(&runlabelOptions.Name, "name", "n", "", "Assign a name to the container") + + nameFlagName := "name" + flags.StringVarP(&runlabelOptions.Name, nameFlagName, "n", "", "Assign a name to the container") + _ = runlabelCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + flags.StringVar(&runlabelOptions.Optional1, "opt1", "", "Optional parameter to pass for install") flags.StringVar(&runlabelOptions.Optional2, "opt2", "", "Optional parameter to pass for install") flags.StringVar(&runlabelOptions.Optional3, "opt3", "", "Optional parameter to pass for install") diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 21f31d360..dba2c3c3e 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -11,26 +12,27 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( startDescription = `Starts one or more containers. The container name or ID can be used.` startCommand = &cobra.Command{ - Use: "start [options] CONTAINER [CONTAINER...]", - Short: "Start one or more containers", - Long: startDescription, - RunE: start, + Use: "start [options] CONTAINER [CONTAINER...]", + Short: "Start one or more containers", + Long: startDescription, + RunE: start, + ValidArgsFunction: common.AutocompleteContainersStartable, Example: `podman start --latest podman start 860a4b231279 5421ab43b45 podman start --interactive --attach imageID`, } containerStartCommand = &cobra.Command{ - Use: startCommand.Use, - Short: startCommand.Short, - Long: startCommand.Long, - RunE: startCommand.RunE, + Use: startCommand.Use, + Short: startCommand.Short, + Long: startCommand.Long, + RunE: startCommand.RunE, + ValidArgsFunction: startCommand.ValidArgsFunction, Example: `podman container start --latest podman container start 860a4b231279 5421ab43b45 podman container start --interactive --attach imageID`, @@ -41,9 +43,15 @@ var ( startOptions entities.ContainerStartOptions ) -func startFlags(flags *pflag.FlagSet) { +func startFlags(cmd *cobra.Command) { + flags := cmd.Flags() + 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-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + + detachKeysFlagName := "detach-keys" + flags.StringVar(&startOptions.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) + flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") @@ -56,7 +64,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCommand, }) - startFlags(startCommand.Flags()) + startFlags(startCommand) validate.AddLatestFlag(startCommand, &startOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -64,9 +72,8 @@ func init() { Command: containerStartCommand, Parent: containerCmd, }) - startFlags(containerStartCommand.Flags()) + startFlags(containerStartCommand) validate.AddLatestFlag(containerStartCommand, &startOptions.Latest) - } func start(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index bfab469ca..ca5c0fdb8 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -8,6 +8,7 @@ import ( tm "github.com/buger/goterm" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -20,28 +21,29 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers." statsCommand = &cobra.Command{ - Use: "stats [options] [CONTAINER...]", - Short: "Display a live stream of container resource usage statistics", - Long: statsDescription, - RunE: stats, - Args: checkStatOptions, + Use: "stats [options] [CONTAINER...]", + Short: "Display a live stream of container resource usage statistics", + Long: statsDescription, + RunE: stats, + Args: checkStatOptions, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman stats --all --no-stream podman stats ctrID podman stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`, } containerStatsCommand = &cobra.Command{ - Use: statsCommand.Use, - Short: statsCommand.Short, - Long: statsCommand.Long, - RunE: statsCommand.RunE, - Args: checkStatOptions, + Use: statsCommand.Use, + Short: statsCommand.Short, + Long: statsCommand.Long, + RunE: statsCommand.RunE, + Args: checkStatOptions, + ValidArgsFunction: statsCommand.ValidArgsFunction, Example: `podman container stats --all --no-stream podman container stats ctrID podman container stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`, @@ -62,9 +64,15 @@ var ( statsOptions statsOptionsCLI ) -func statFlags(flags *pflag.FlagSet) { +func statFlags(cmd *cobra.Command) { + flags := cmd.Flags() + 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") + + formatFlagName := "format" + flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + 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") } @@ -74,7 +82,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: statsCommand, }) - statFlags(statsCommand.Flags()) + statFlags(statsCommand) validate.AddLatestFlag(statsCommand, &statsOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -82,7 +90,7 @@ func init() { Command: containerStatsCommand, Parent: containerCmd, }) - statFlags(containerStatsCommand.Flags()) + statFlags(containerStatsCommand) validate.AddLatestFlag(containerStatsCommand, &statsOptions.Latest) } diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 7c8c1b50e..3a4211357 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -4,12 +4,13 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -24,6 +25,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman stop ctrID podman stop --latest podman stop --time 2 mywebserver 6e534f14da9d`, @@ -37,6 +39,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, + ValidArgsFunction: stopCommand.ValidArgsFunction, Example: `podman container stop ctrID podman container stop --latest podman container stop --time 2 mywebserver 6e534f14da9d`, @@ -48,11 +51,19 @@ var ( stopTimeout uint ) -func stopFlags(flags *pflag.FlagSet) { +func stopFlags(cmd *cobra.Command) { + flags := cmd.Flags() + 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.UintVarP(&stopTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") + + cidfileFlagName := "cidfile" + flags.StringArrayVarP(&stopOptions.CIDFiles, cidfileFlagName, "", nil, "Read the container ID from the file") + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + + timeFlagName := "time" + flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") + _ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) if registry.IsRemote() { _ = flags.MarkHidden("cidfile") @@ -66,7 +77,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCommand, }) - stopFlags(stopCommand.Flags()) + stopFlags(stopCommand) validate.AddLatestFlag(stopCommand, &stopOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -74,8 +85,7 @@ func init() { Command: containerStopCommand, Parent: containerCmd, }) - - stopFlags(containerStopCommand.Flags()) + stopFlags(containerStopCommand) validate.AddLatestFlag(containerStopCommand, &stopOptions.Latest) } diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index 361d30516..3eb6d2af2 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -7,6 +7,7 @@ import ( "strings" "text/tabwriter" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -26,11 +27,12 @@ var ( topOptions = entities.TopOptions{} topCommand = &cobra.Command{ - Use: "top [options] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", - Short: "Display the running processes of a container", - Long: topDescription, - RunE: top, - Args: cobra.ArbitraryArgs, + Use: "top [options] CONTAINER [FORMAT-DESCRIPTORS|ARGS...]", + Short: "Display the running processes of a container", + Long: topDescription, + RunE: top, + Args: cobra.ArbitraryArgs, + ValidArgsFunction: common.AutocompleteContainersRunning, Example: `podman top ctrID podman top --latest podman top ctrID pid seccomp args %C @@ -38,10 +40,11 @@ podman top ctrID -eo user,pid,comm`, } containerTopCommand = &cobra.Command{ - Use: topCommand.Use, - Short: topCommand.Short, - Long: topCommand.Long, - RunE: topCommand.RunE, + Use: topCommand.Use, + Short: topCommand.Short, + Long: topCommand.Long, + RunE: topCommand.RunE, + ValidArgsFunction: topCommand.ValidArgsFunction, Example: `podman container top ctrID podman container top --latest podman container top ctrID pid seccomp args %C diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go index c3159cfed..22e0768e3 100644 --- a/cmd/podman/containers/unmount.go +++ b/cmd/podman/containers/unmount.go @@ -3,6 +3,7 @@ package containers import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -27,6 +28,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman unmount ctrID podman unmount ctrID1 ctrID2 ctrID3 podman unmount --all`, @@ -41,6 +43,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman container unmount ctrID podman container unmount ctrID1 ctrID2 ctrID3 podman container unmount --all`, diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go index 8927fc426..0e8b59192 100644 --- a/cmd/podman/containers/unpause.go +++ b/cmd/podman/containers/unpause.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/cgroups" @@ -17,20 +18,22 @@ import ( var ( unpauseDescription = `Unpauses one or more previously paused containers. The container name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [options] CONTAINER [CONTAINER...]", - Short: "Unpause the processes in one or more containers", - Long: unpauseDescription, - RunE: unpause, + Use: "unpause [options] CONTAINER [CONTAINER...]", + Short: "Unpause the processes in one or more containers", + Long: unpauseDescription, + RunE: unpause, + ValidArgsFunction: common.AutocompleteContainersPaused, Example: `podman unpause ctrID podman unpause --all`, } unPauseOptions = entities.PauseUnPauseOptions{} containerUnpauseCommand = &cobra.Command{ - Use: unpauseCommand.Use, - Short: unpauseCommand.Short, - Long: unpauseCommand.Long, - RunE: unpauseCommand.RunE, + Use: unpauseCommand.Use, + Short: unpauseCommand.Short, + Long: unpauseCommand.Long, + RunE: unpauseCommand.RunE, + ValidArgsFunction: unpauseCommand.ValidArgsFunction, Example: `podman container unpause ctrID podman container unpause --all`, } diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index b4986143b..2bbfbccc9 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -12,26 +14,27 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( waitDescription = `Block until one or more containers stop and then print their exit codes. ` waitCommand = &cobra.Command{ - Use: "wait [options] CONTAINER [CONTAINER...]", - Short: "Block on one or more containers", - Long: waitDescription, - RunE: wait, + Use: "wait [options] CONTAINER [CONTAINER...]", + Short: "Block on one or more containers", + Long: waitDescription, + RunE: wait, + ValidArgsFunction: common.AutocompleteContainers, Example: `podman wait --interval 5s ctrID podman wait ctrID1 ctrID2`, } containerWaitCommand = &cobra.Command{ - Use: waitCommand.Use, - Short: waitCommand.Short, - Long: waitCommand.Long, - RunE: waitCommand.RunE, + Use: waitCommand.Use, + Short: waitCommand.Short, + Long: waitCommand.Long, + RunE: waitCommand.RunE, + ValidArgsFunction: waitCommand.ValidArgsFunction, Example: `podman container wait --interval 5s ctrID podman container wait ctrID1 ctrID2`, } @@ -43,9 +46,17 @@ var ( waitInterval string ) -func waitFlags(flags *pflag.FlagSet) { - flags.StringVarP(&waitInterval, "interval", "i", "250ns", "Time Interval to wait before polling for completion") - flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") +func waitFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + intervalFlagName := "interval" + flags.StringVarP(&waitInterval, intervalFlagName, "i", "250ns", "Time Interval to wait before polling for completion") + _ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone) + + conditionFlagName := "condition" + flags.StringVar(&waitCondition, conditionFlagName, "stopped", "Condition to wait on") + _ = cmd.RegisterFlagCompletionFunc(conditionFlagName, common.AutocompleteWaitCondition) + } func init() { @@ -53,7 +64,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: waitCommand, }) - waitFlags(waitCommand.Flags()) + waitFlags(waitCommand) validate.AddLatestFlag(waitCommand, &waitOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -61,9 +72,8 @@ func init() { Command: containerWaitCommand, Parent: containerCmd, }) - waitFlags(containerWaitCommand.Flags()) + waitFlags(containerWaitCommand) validate.AddLatestFlag(containerWaitCommand, &waitOptions.Latest) - } func wait(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index bad31a4a2..5e6abe243 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/containers" "github.com/containers/podman/v2/cmd/podman/images" "github.com/containers/podman/v2/cmd/podman/registry" @@ -17,11 +18,12 @@ 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 [options] {CONTAINER_ID | IMAGE_ID}", - Args: validate.IDOrLatestArgs, - Short: "Display the changes to the object's file system", - Long: diffDescription, - RunE: diff, + Use: "diff [options] {CONTAINER_ID | IMAGE_ID}", + Args: validate.IDOrLatestArgs, + Short: "Display the changes to the object's file system", + Long: diffDescription, + RunE: diff, + ValidArgsFunction: common.AutocompleteContainersAndImages, Example: `podman diff imageID podman diff ctrID podman diff --format json redis:alpine`, @@ -38,7 +40,11 @@ func init() { flags := diffCmd.Flags() 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") + + formatFlagName := "format" + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go index 87f7501e3..e47bd35b5 100644 --- a/cmd/podman/generate/kube.go +++ b/cmd/podman/generate/kube.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "os" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -20,11 +22,12 @@ var ( Whether the input is for a container or pod, Podman will always generate the specification as a pod.` kubeCmd = &cobra.Command{ - Use: "kube [options] CONTAINER | POD", - Short: "Generate Kubernetes YAML from a container or pod.", - Long: kubeDescription, - RunE: kube, - Args: cobra.ExactArgs(1), + Use: "kube [options] CONTAINER | POD", + Short: "Generate Kubernetes YAML from a container or pod.", + Long: kubeDescription, + RunE: kube, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainersAndPods, Example: `podman generate kube ctrID podman generate kube podID podman generate kube --service podID`, @@ -39,7 +42,11 @@ func init() { }) flags := kubeCmd.Flags() flags.BoolVarP(&kubeOptions.Service, "service", "s", false, "Generate YAML for a Kubernetes service object") - flags.StringVarP(&kubeFile, "filename", "f", "", "Write output to the specified path") + + filenameFlagName := "filename" + flags.StringVarP(&kubeFile, filenameFlagName, "f", "", "Write output to the specified path") + _ = kubeCmd.RegisterFlagCompletionFunc(filenameFlagName, completion.AutocompleteDefault) + flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 8e937fa90..e9cf76aae 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -6,7 +6,9 @@ import ( "os" "path/filepath" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -24,11 +26,12 @@ var ( The generated units can later be controlled via systemctl(1).` systemdCmd = &cobra.Command{ - Use: "systemd [options] CTR|POD", - Short: "Generate systemd units.", - Long: systemdDescription, - RunE: systemd, - Args: cobra.ExactArgs(1), + Use: "systemd [options] CTR|POD", + Short: "Generate systemd units.", + Long: systemdDescription, + RunE: systemd, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainersAndPods, Example: `podman generate systemd CTR podman generate systemd --new --time 10 CTR podman generate systemd --files --name POD`, @@ -44,13 +47,32 @@ func init() { flags := systemdCmd.Flags() flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs") flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout") - flags.UintVarP(&systemdTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Stop timeout override") - flags.StringVar(&systemdOptions.RestartPolicy, "restart-policy", "on-failure", "Systemd restart-policy") + + timeFlagName := "time" + flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override") + _ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one") - flags.StringVar(&systemdOptions.ContainerPrefix, "container-prefix", "container", "Systemd unit name prefix for containers") - flags.StringVar(&systemdOptions.PodPrefix, "pod-prefix", "pod", "Systemd unit name prefix for pods") - flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name separator between name/id and prefix") - flags.StringVar(&format, "format", "", "Print the created units in specified format (json)") + + containerPrefixFlagName := "container-prefix" + flags.StringVar(&systemdOptions.ContainerPrefix, containerPrefixFlagName, "container", "Systemd unit name prefix for containers") + _ = systemdCmd.RegisterFlagCompletionFunc(containerPrefixFlagName, completion.AutocompleteNone) + + podPrefixFlagName := "pod-prefix" + flags.StringVar(&systemdOptions.PodPrefix, podPrefixFlagName, "pod", "Systemd unit name prefix for pods") + _ = systemdCmd.RegisterFlagCompletionFunc(podPrefixFlagName, completion.AutocompleteNone) + + separatorFlagName := "separator" + flags.StringVar(&systemdOptions.Separator, separatorFlagName, "-", "Systemd unit name separator between name/id and prefix") + _ = systemdCmd.RegisterFlagCompletionFunc(separatorFlagName, completion.AutocompleteNone) + + restartPolicyFlagName := "restart-policy" + flags.StringVar(&systemdOptions.RestartPolicy, restartPolicyFlagName, "on-failure", "Systemd restart-policy") + _ = systemdCmd.RegisterFlagCompletionFunc(restartPolicyFlagName, common.AutocompleteSystemdRestartOptions) + + formatFlagName := "format" + flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)") + _ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/healthcheck/run.go b/cmd/podman/healthcheck/run.go index 6d7f0b548..b4331d04e 100644 --- a/cmd/podman/healthcheck/run.go +++ b/cmd/podman/healthcheck/run.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -18,6 +19,7 @@ var ( Example: `podman healthcheck run mywebapp`, RunE: run, Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainersRunning, DisableFlagsInUseLine: true, } ) diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index f48d1cd94..b9aabe64e 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -9,6 +9,7 @@ import ( "github.com/containers/buildah/imagebuildah" buildahCLI "github.com/containers/buildah/pkg/cli" "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -18,7 +19,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // buildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal @@ -40,22 +40,24 @@ 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 [options] [CONTEXT]", - Short: "Build an image using instructions from Containerfiles", - Long: buildDescription, - Args: cobra.MaximumNArgs(1), - RunE: build, + Use: "build [options] [CONTEXT]", + Short: "Build an image using instructions from Containerfiles", + Long: buildDescription, + Args: cobra.MaximumNArgs(1), + RunE: build, + ValidArgsFunction: completion.AutocompleteDefault, Example: `podman build . podman build --creds=username:password -t imageName -f Containerfile.simple . podman build --layers --force-rm --tag imageName .`, } imageBuildCmd = &cobra.Command{ - Args: buildCmd.Args, - Use: buildCmd.Use, - Short: buildCmd.Short, - Long: buildCmd.Long, - RunE: buildCmd.RunE, + Args: buildCmd.Args, + Use: buildCmd.Use, + Short: buildCmd.Short, + Long: buildCmd.Long, + RunE: buildCmd.RunE, + ValidArgsFunction: buildCmd.ValidArgsFunction, Example: `podman image build . podman image build --creds=username:password -t imageName -f Containerfile.simple . podman image build --layers --force-rm --tag imageName .`, @@ -79,22 +81,25 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: buildCmd, }) - buildFlags(buildCmd.Flags()) + buildFlags(buildCmd) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageBuildCmd, Parent: imageCmd, }) - buildFlags(imageBuildCmd.Flags()) + buildFlags(imageBuildCmd) } -func buildFlags(flags *pflag.FlagSet) { +func buildFlags(cmd *cobra.Command) { + flags := cmd.Flags() + // Podman flags flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer") // Bud flags budFlags := buildahCLI.GetBudFlags(&buildOpts.BudResults) + // --pull flag flag := budFlags.Lookup("pull") if err := flag.Value.Set("true"); err != nil { @@ -102,6 +107,9 @@ func buildFlags(flags *pflag.FlagSet) { } flag.DefValue = "true" flags.AddFlagSet(&budFlags) + // Add the completion functions + budCompletions := buildahCLI.GetBudFlagsCompletions() + completion.CompleteCommandFlags(cmd, budCompletions) // Layer flags layerFlags := buildahCLI.GetLayerFlags(&buildOpts.LayerResults) @@ -127,6 +135,9 @@ func buildFlags(flags *pflag.FlagSet) { os.Exit(1) } flags.AddFlagSet(&fromAndBudFlags) + // Add the completion functions + fromAndBudFlagsCompletions := buildahCLI.GetFromAndBudFlagsCompletions() + completion.CompleteCommandFlags(cmd, fromAndBudFlagsCompletions) _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index b7722e5e5..71793c707 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -13,11 +13,12 @@ import ( var ( // podman container _inspect_ diffCmd = &cobra.Command{ - Use: "diff [options] IMAGE", - Args: cobra.ExactArgs(1), - 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, + Use: "diff [options] IMAGE", + Args: cobra.ExactArgs(1), + 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, + ValidArgsFunction: common.AutocompleteImages, Example: `podman image diff myImage podman image diff --format json redis:alpine`, } @@ -37,7 +38,10 @@ func diffFlags(flags *pflag.FlagSet) { diffOpts = &entities.DiffOptions{} flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.") - flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") + + formatFlagName := "format" + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/exists.go b/cmd/podman/images/exists.go index 31bdc791e..cb1ca0295 100644 --- a/cmd/podman/images/exists.go +++ b/cmd/podman/images/exists.go @@ -1,6 +1,7 @@ package images import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -8,11 +9,12 @@ import ( var ( existsCmd = &cobra.Command{ - Use: "exists IMAGE", - Short: "Check if an image exists in local storage", - Long: `If the named image exists in local storage, podman image exists exits with 0, otherwise the exit code will be 1.`, - Args: cobra.ExactArgs(1), - RunE: exists, + Use: "exists IMAGE", + Short: "Check if an image exists in local storage", + Long: `If the named image exists in local storage, podman image exists exits with 0, otherwise the exit code will be 1.`, + Args: cobra.ExactArgs(1), + RunE: exists, + ValidArgsFunction: common.AutocompleteImages, Example: `podman image exists ID podman image exists IMAGE && podman pull IMAGE`, DisableFlagsInUseLine: true, diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index e9751b365..964c7a975 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -11,13 +11,13 @@ import ( "unicode" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -27,21 +27,23 @@ var ( // podman _history_ historyCmd = &cobra.Command{ - Use: "history [options] IMAGE", - Short: "Show history of a specified image", - Long: long, - Args: cobra.ExactArgs(1), - RunE: history, - Example: "podman history quay.io/fedora/fedora", + Use: "history [options] IMAGE", + Short: "Show history of a specified image", + Long: long, + Args: cobra.ExactArgs(1), + RunE: history, + ValidArgsFunction: common.AutocompleteImages, + Example: "podman history quay.io/fedora/fedora", } imageHistoryCmd = &cobra.Command{ - Args: historyCmd.Args, - Use: historyCmd.Use, - Short: historyCmd.Short, - Long: historyCmd.Long, - RunE: historyCmd.RunE, - Example: `podman image history quay.io/fedora/fedora`, + Args: historyCmd.Args, + Use: historyCmd.Use, + Short: historyCmd.Short, + Long: historyCmd.Long, + ValidArgsFunction: historyCmd.ValidArgsFunction, + RunE: historyCmd.RunE, + Example: `podman image history quay.io/fedora/fedora`, } opts = struct { @@ -57,18 +59,23 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: historyCmd, }) - historyFlags(historyCmd.Flags()) + historyFlags(historyCmd) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageHistoryCmd, Parent: imageCmd, }) - historyFlags(imageHistoryCmd.Flags()) + historyFlags(imageHistoryCmd) } -func historyFlags(flags *pflag.FlagSet) { - flags.StringVar(&opts.format, "format", "", "Change the output to JSON or a Go template") +func historyFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + formatFlagName := "format" + flags.StringVar(&opts.format, formatFlagName, "", "Change the output to JSON or a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVarP(&opts.human, "human", "H", true, "Display sizes and dates in human readable format") flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output") flags.BoolVar(&opts.noTrunc, "notruncate", false, "Do not truncate the output") diff --git a/cmd/podman/images/images.go b/cmd/podman/images/images.go index 14ea01047..499bfd2ad 100644 --- a/cmd/podman/images/images.go +++ b/cmd/podman/images/images.go @@ -16,6 +16,7 @@ var ( Short: listCmd.Short, Long: listCmd.Long, RunE: listCmd.RunE, + ValidArgsFunction: listCmd.ValidArgsFunction, Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), DisableFlagsInUseLine: true, } @@ -27,5 +28,5 @@ func init() { Command: imagesCmd, }) - imageListFlagSet(imagesCmd.Flags()) + imageListFlagSet(imagesCmd) } diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index e3545da69..f38ab3b19 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -3,14 +3,16 @@ package images import ( "context" "fmt" + "strings" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -19,21 +21,23 @@ var ( Note remote tar balls can be specified, via web address. Optionally tag the image. You can specify the instructions using the --change option.` importCommand = &cobra.Command{ - Use: "import [options] PATH [REFERENCE]", - Short: "Import a tarball to create a filesystem image", - Long: importDescription, - RunE: importCon, + Use: "import [options] PATH [REFERENCE]", + Short: "Import a tarball to create a filesystem image", + Long: importDescription, + RunE: importCon, + ValidArgsFunction: completion.AutocompleteDefault, Example: `podman import http://example.com/ctr.tar url-image cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman import -`, } imageImportCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), - Use: importCommand.Use, - Short: importCommand.Short, - Long: importCommand.Long, - RunE: importCommand.RunE, + Args: cobra.MinimumNArgs(1), + Use: importCommand.Use, + Short: importCommand.Short, + Long: importCommand.Long, + RunE: importCommand.RunE, + ValidArgsFunction: importCommand.ValidArgsFunction, Example: `podman image import http://example.com/ctr.tar url-image cat ctr.tar | podman -q image import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman image import -`, @@ -49,19 +53,27 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: importCommand, }) - importFlags(importCommand.Flags()) + importFlags(importCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageImportCommand, Parent: imageCmd, }) - importFlags(imageImportCommand.Flags()) + importFlags(imageImportCommand) } -func importFlags(flags *pflag.FlagSet) { - flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") - flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image") +func importFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + changeFlagName := "change" + flags.StringArrayVarP(&importOpts.Changes, changeFlagName, "c", []string{}, "Apply the following possible instructions to the created image (default []): "+strings.Join(common.ChangeCmds, " | ")) + _ = cmd.RegisterFlagCompletionFunc(changeFlagName, common.AutocompleteChangeInstructions) + + messageFlagName := "message" + flags.StringVarP(&importOpts.Message, messageFlagName, "m", "", "Set commit message for imported image") + _ = cmd.RegisterFlagCompletionFunc(messageFlagName, completion.AutocompleteNone) + flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output") flags.StringVar(&importOpts.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file") _ = flags.MarkHidden("signature-policy") diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 8f005553d..488f03760 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -1,6 +1,7 @@ package images import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" @@ -10,10 +11,11 @@ import ( var ( // Command: podman image _inspect_ inspectCmd = &cobra.Command{ - Use: "inspect [options] 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, + Use: "inspect [options] 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, + ValidArgsFunction: common.AutocompleteImages, Example: `podman inspect alpine podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, @@ -29,7 +31,10 @@ func init() { }) inspectOpts = new(entities.InspectOptions) flags := inspectCmd.Flags() - flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format the output to a Go template or json") + + formatFlagName := "format" + flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func inspectExec(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index e24631b24..4692699f2 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -10,15 +10,16 @@ import ( "time" "unicode" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/image/v5/docker/reference" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) type listFlagType struct { @@ -35,12 +36,13 @@ type listFlagType struct { var ( // Command: podman image _list_ listCmd = &cobra.Command{ - Use: "list [options] [IMAGE]", - Aliases: []string{"ls"}, - Args: cobra.MaximumNArgs(1), - Short: "List images in local storage", - Long: "Lists images previously pulled to the system or created on the system.", - RunE: images, + Use: "list [options] [IMAGE]", + Aliases: []string{"ls"}, + Args: cobra.MaximumNArgs(1), + Short: "List images in local storage", + Long: "Lists images previously pulled to the system or created on the system.", + RunE: images, + ValidArgsFunction: common.AutocompleteImages, Example: `podman image list --format json podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" podman image list --filter dangling=true`, @@ -67,18 +69,32 @@ func init() { Command: listCmd, Parent: imageCmd, }) - imageListFlagSet(listCmd.Flags()) + imageListFlagSet(listCmd) } -func imageListFlagSet(flags *pflag.FlagSet) { +func imageListFlagSet(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&listOptions.All, "all", "a", false, "Show all images (default hides intermediate images)") - flags.StringSliceVarP(&listOptions.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") - flags.StringVar(&listFlag.format, "format", "", "Change the output format to JSON or a Go template") + + filterFlagName := "filter" + flags.StringSliceVarP(&listOptions.Filter, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])") + // TODO: add completion function for filters + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + + formatFlagName := "format" + flags.StringVar(&listFlag.format, formatFlagName, "", "Change the output format to JSON or a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVar(&listFlag.digests, "digests", false, "Show digests") flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print column headings") flags.BoolVar(&listFlag.noTrunc, "no-trunc", false, "Do not truncate output") flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Display only image IDs") - flags.StringVar(&listFlag.sort, "sort", "created", "Sort by "+sortFields.String()) + + sortFlagName := "sort" + flags.StringVar(&listFlag.sort, sortFlagName, "created", "Sort by "+sortFields.String()) + _ = cmd.RegisterFlagCompletionFunc(sortFlagName, completion.AutocompleteNone) + flags.BoolVarP(&listFlag.history, "history", "", false, "Display the image name history") } diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index 02f1b3b39..a7884f4c5 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -8,6 +8,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/docker/reference" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" @@ -15,26 +16,27 @@ import ( "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" ) var ( loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." loadCommand = &cobra.Command{ - Use: "load [options] [NAME[:TAG]]", - Short: "Load an image from container archive", - Long: loadDescription, - RunE: load, - Args: cobra.MaximumNArgs(1), + Use: "load [options] [NAME[:TAG]]", + Short: "Load an image from container archive", + Long: loadDescription, + RunE: load, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: completion.AutocompleteNone, } imageLoadCommand = &cobra.Command{ - Args: loadCommand.Args, - Use: loadCommand.Use, - Short: loadCommand.Short, - Long: loadCommand.Long, - RunE: loadCommand.RunE, + Args: loadCommand.Args, + Use: loadCommand.Use, + Short: loadCommand.Short, + Long: loadCommand.Long, + ValidArgsFunction: loadCommand.ValidArgsFunction, + RunE: loadCommand.RunE, } ) @@ -47,17 +49,22 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: loadCommand, }) - loadFlags(loadCommand.Flags()) + loadFlags(loadCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageLoadCommand, Parent: imageCmd, }) - loadFlags(imageLoadCommand.Flags()) + loadFlags(imageLoadCommand) } -func loadFlags(flags *pflag.FlagSet) { - flags.StringVarP(&loadOpts.Input, "input", "i", "", "Read from specified archive file (default: stdin)") +func loadFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + inputFlagName := "input" + flags.StringVarP(&loadOpts.Input, inputFlagName, "i", "", "Read from specified archive file (default: stdin)") + _ = cmd.RegisterFlagCompletionFunc(inputFlagName, completion.AutocompleteDefault) + flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output") flags.StringVar(&loadOpts.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file") _ = flags.MarkHidden("signature-policy") diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 28e9264ee..1eac59ef9 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -7,12 +7,12 @@ import ( "text/template" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -24,10 +24,11 @@ var ( ` mountCommand = &cobra.Command{ - Use: "mount [options] [IMAGE...]", - Short: "Mount an image's root filesystem", - Long: mountDescription, - RunE: mount, + Use: "mount [options] [IMAGE...]", + Short: "Mount an image's root filesystem", + Long: mountDescription, + RunE: mount, + ValidArgsFunction: common.AutocompleteImages, Example: `podman image mount imgID podman image mount imgID1 imgID2 imgID3 podman image mount @@ -43,9 +44,14 @@ var ( mountOpts entities.ImageMountOptions ) -func mountFlags(flags *pflag.FlagSet) { +func mountFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all images") - flags.StringVar(&mountOpts.Format, "format", "", "Print the mounted images in specified format (json)") + + formatFlagName := "format" + flags.StringVar(&mountOpts.Format, formatFlagName, "", "Print the mounted images in specified format (json)") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func init() { @@ -54,7 +60,7 @@ func init() { Command: mountCommand, Parent: imageCmd, }) - mountFlags(mountCommand.Flags()) + mountFlags(mountCommand) } func mount(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index b6e6b9562..3af56b015 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -19,12 +20,13 @@ var ( If an image is not being used by a container, it will be removed from the system.` pruneCmd = &cobra.Command{ - Use: "prune [options]", - Args: validate.NoArgs, - Short: "Remove unused images", - Long: pruneDescription, - RunE: prune, - Example: `podman image prune`, + Use: "prune [options]", + Args: validate.NoArgs, + Short: "Remove unused images", + Long: pruneDescription, + RunE: prune, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman image prune`, } pruneOpts = entities.ImagePruneOptions{} @@ -42,7 +44,11 @@ func init() { flags := pruneCmd.Flags() flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all unused images, not just dangling ones") flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation") - flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label==')") + + filterFlagName := "filter" + flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label==')") + //TODO: add completion for filters + _ = pruneCmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) } diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index ab3b0a197..a6f41688c 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -5,12 +5,13 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // pullOptionsWrapper wraps entities.ImagePullOptions and prevents leaking @@ -29,11 +30,12 @@ var ( // Command: podman pull pullCmd = &cobra.Command{ - Use: "pull [options] IMAGE", - Args: cobra.ExactArgs(1), - Short: "Pull an image from a registry", - Long: pullDescription, - RunE: imagePull, + Use: "pull [options] IMAGE", + Args: cobra.ExactArgs(1), + Short: "Pull an image from a registry", + Long: pullDescription, + RunE: imagePull, + ValidArgsFunction: common.AutocompleteImages, Example: `podman pull imageName podman pull fedora:latest`, } @@ -42,11 +44,12 @@ var ( // It's basically a clone of `pullCmd` with the exception of being a // child of the images command. imagesPullCmd = &cobra.Command{ - Use: pullCmd.Use, - Args: pullCmd.Args, - Short: pullCmd.Short, - Long: pullCmd.Long, - RunE: pullCmd.RunE, + Use: pullCmd.Use, + Args: pullCmd.Args, + Short: pullCmd.Short, + Long: pullCmd.Long, + RunE: pullCmd.RunE, + ValidArgsFunction: pullCmd.ValidArgsFunction, Example: `podman image pull imageName podman image pull fedora:latest`, } @@ -58,9 +61,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pullCmd, }) - - flags := pullCmd.Flags() - pullFlags(flags) + pullFlags(pullCmd) // images pull registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -68,26 +69,46 @@ func init() { Command: imagesPullCmd, Parent: imageCmd, }) - - imagesPullFlags := imagesPullCmd.Flags() - pullFlags(imagesPullFlags) + pullFlags(imagesPullCmd) } // pullFlags set the flags for the pull command. -func pullFlags(flags *pflag.FlagSet) { +func pullFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") - flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") - flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images") - flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images") - flags.StringVar(&pullOptions.OverrideVariant, "override-variant", "", " use VARIANT instead of the running architecture variant for choosing images") + + credsFlagName := "creds" + flags.StringVar(&pullOptions.CredentialsCLI, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + _ = cmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + + overrideArchFlagName := "override-arch" + flags.StringVar(&pullOptions.OverrideArch, overrideArchFlagName, "", "Use `ARCH` instead of the architecture of the machine for choosing images") + _ = cmd.RegisterFlagCompletionFunc(overrideArchFlagName, completion.AutocompleteNone) + + overrideOsFlagName := "override-os" + flags.StringVar(&pullOptions.OverrideOS, overrideOsFlagName, "", "Use `OS` instead of the running OS for choosing images") + _ = cmd.RegisterFlagCompletionFunc(overrideOsFlagName, completion.AutocompleteNone) + + overrideVariantFlagName := "override-variant" + flags.StringVar(&pullOptions.OverrideVariant, overrideVariantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images") + _ = cmd.RegisterFlagCompletionFunc(overrideVariantFlagName, completion.AutocompleteNone) + flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + + authfileFlagName := "authfile" + flags.StringVar(&pullOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) if !registry.IsRemote() { - flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + + certDirFlagName := "cert-dir" + flags.StringVar(&pullOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") + _ = cmd.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index dd45a790f..447b02fbe 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -4,12 +4,13 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // pushOptionsWrapper wraps entities.ImagepushOptions and prevents leaking @@ -28,11 +29,12 @@ var ( // Command: podman push pushCmd = &cobra.Command{ - Use: "push [options] SOURCE [DESTINATION]", - Short: "Push an image to a specified destination", - Long: pushDescription, - RunE: imagePush, - Args: cobra.RangeArgs(1, 2), + Use: "push [options] SOURCE [DESTINATION]", + Short: "Push an image to a specified destination", + Long: pushDescription, + RunE: imagePush, + Args: cobra.RangeArgs(1, 2), + ValidArgsFunction: common.AutocompleteImages, Example: `podman push imageID docker://registry.example.com/repository:tag podman push imageID oci-archive:/path/to/layout:image:tag`, } @@ -41,11 +43,12 @@ var ( // It's basically a clone of `pushCmd` with the exception of being a // child of the images command. imagePushCmd = &cobra.Command{ - Use: pushCmd.Use, - Short: pushCmd.Short, - Long: pushCmd.Long, - RunE: pushCmd.RunE, - Args: pushCmd.Args, + Use: pushCmd.Use, + Short: pushCmd.Short, + Long: pushCmd.Long, + RunE: pushCmd.RunE, + Args: pushCmd.Args, + ValidArgsFunction: pushCmd.ValidArgsFunction, Example: `podman image push imageID docker://registry.example.com/repository:tag podman image push imageID oci-archive:/path/to/layout:image:tag`, } @@ -57,9 +60,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pushCmd, }) - - flags := pushCmd.Flags() - pushFlags(flags) + pushFlags(pushCmd) // images push registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -67,23 +68,45 @@ func init() { Command: imagePushCmd, Parent: imageCmd, }) - - pushFlags(imagePushCmd.Flags()) + pushFlags(imagePushCmd) } // pushFlags set the flags for the push command. -func pushFlags(flags *pflag.FlagSet) { - flags.StringVar(&pushOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys") +func pushFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + authfileFlagName := "authfile" + flags.StringVar(&pushOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + + certDirFlagName := "cert-dir" + flags.StringVar(&pushOptions.CertDir, certDirFlagName, "", "Path to a directory containing TLS certificates and keys") + _ = cmd.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") - flags.StringVar(&pushOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") - flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file") + + credsFlagName := "creds" + flags.StringVar(&pushOptions.CredentialsCLI, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + _ = cmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") - flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)") + + digestfileFlagName := "digestfile" + flags.StringVar(&pushOptions.DigestFile, digestfileFlagName, "", "Write the digest of the pushed image to the specified file") + _ = cmd.RegisterFlagCompletionFunc(digestfileFlagName, completion.AutocompleteDefault) + + formatFlagName := "format" + flags.StringVarP(&pushOptions.Format, formatFlagName, "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteManifestFormat) + flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images") flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") flags.StringVar(&pushOptions.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file") - flags.StringVar(&pushOptions.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") + + signByFlagName := "sign-by" + flags.StringVar(&pushOptions.SignBy, signByFlagName, "", "Add a signature at the destination using the specified key") + _ = cmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone) + flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") if registry.IsRemote() { diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go index 9dddef48f..587f08c29 100644 --- a/cmd/podman/images/rm.go +++ b/cmd/podman/images/rm.go @@ -3,6 +3,7 @@ package images import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" @@ -14,10 +15,11 @@ import ( var ( rmDescription = "Removes one or more previously pulled or locally created images." rmCmd = &cobra.Command{ - Use: "rm [options] IMAGE [IMAGE...]", - Short: "Removes one or more images from local storage", - Long: rmDescription, - RunE: rm, + Use: "rm [options] IMAGE [IMAGE...]", + Short: "Removes one or more images from local storage", + Long: rmDescription, + RunE: rm, + ValidArgsFunction: common.AutocompleteImages, Example: `podman image rm imageID podman image rm --force alpine podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`, diff --git a/cmd/podman/images/rmi.go b/cmd/podman/images/rmi.go index 520847890..ab174d750 100644 --- a/cmd/podman/images/rmi.go +++ b/cmd/podman/images/rmi.go @@ -10,12 +10,13 @@ import ( var ( rmiCmd = &cobra.Command{ - Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1), - Args: rmCmd.Args, - Short: rmCmd.Short, - Long: rmCmd.Long, - RunE: rmCmd.RunE, - Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1), + Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1), + Args: rmCmd.Args, + Short: rmCmd.Short, + Long: rmCmd.Long, + RunE: rmCmd.RunE, + ValidArgsFunction: rmCmd.ValidArgsFunction, + Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1), } ) diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index db1fa7159..9ef2d0c91 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -5,6 +5,8 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/libpod/define" @@ -12,7 +14,6 @@ import ( "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" ) @@ -42,16 +43,18 @@ var ( } return nil }, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman save --quiet -o myimage.tar imageID podman save --format docker-dir -o ubuntu-dir ubuntu podman save > alpine-all.tar alpine:latest`, } imageSaveCommand = &cobra.Command{ - Args: saveCommand.Args, - Use: saveCommand.Use, - Short: saveCommand.Short, - Long: saveCommand.Long, - RunE: saveCommand.RunE, + Args: saveCommand.Args, + Use: saveCommand.Use, + Short: saveCommand.Short, + Long: saveCommand.Long, + RunE: saveCommand.RunE, + ValidArgsFunction: saveCommand.ValidArgsFunction, Example: `podman image save --quiet -o myimage.tar imageID podman image save --format docker-dir -o ubuntu-dir ubuntu podman image save > alpine-all.tar alpine:latest`, @@ -67,20 +70,29 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: saveCommand, }) - saveFlags(saveCommand.Flags()) + saveFlags(saveCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageSaveCommand, Parent: imageCmd, }) - saveFlags(imageSaveCommand.Flags()) + saveFlags(imageSaveCommand) } -func saveFlags(flags *pflag.FlagSet) { +func saveFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVar(&saveOpts.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") - flags.StringVar(&saveOpts.Format, "format", define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)") - flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") + + formatFlagName := "format" + flags.StringVar(&saveOpts.Format, formatFlagName, define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteImageSaveFormat) + + outputFlagName := "output" + flags.StringVarP(&saveOpts.Output, outputFlagName, "o", "", "Write to a specified file (default: stdout, which must be redirected)") + _ = cmd.RegisterFlagCompletionFunc(outputFlagName, completion.AutocompleteDefault) + flags.BoolVarP(&saveOpts.Quiet, "quiet", "q", false, "Suppress the output") flags.BoolVarP(&saveOpts.MultiImageArchive, "multi-image-archive", "m", containerConfig.Engine.MultiImageArchive, "Interpret additional arguments as images not tags and create a multi-image-archive (only for docker-archive)") } diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 7de6a7316..c2ef7d767 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -7,6 +7,7 @@ import ( "text/template" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/cmd/podman/parse" @@ -14,7 +15,6 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // searchOptionsWrapper wraps entities.ImagePullOptions and prevents leaking @@ -34,11 +34,12 @@ var ( // Command: podman search searchCmd = &cobra.Command{ - Use: "search [options] TERM", - Short: "Search registry for image", - Long: searchDescription, - RunE: imageSearch, - Args: cobra.ExactArgs(1), + Use: "search [options] TERM", + Short: "Search registry for image", + Long: searchDescription, + RunE: imageSearch, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.AutocompleteNone, Example: `podman search --filter=is-official --limit 3 alpine podman search registry.fedoraproject.org/ # only works with v2 registries podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`, @@ -46,12 +47,13 @@ var ( // Command: podman image search imageSearchCmd = &cobra.Command{ - Use: searchCmd.Use, - Short: searchCmd.Short, - Long: searchCmd.Long, - RunE: searchCmd.RunE, - Args: searchCmd.Args, - Annotations: searchCmd.Annotations, + Use: searchCmd.Use, + Short: searchCmd.Short, + Long: searchCmd.Long, + RunE: searchCmd.RunE, + Args: searchCmd.Args, + Annotations: searchCmd.Annotations, + ValidArgsFunction: searchCmd.ValidArgsFunction, Example: `podman image search --filter=is-official --limit 3 alpine podman image search registry.fedoraproject.org/ # only works with v2 registries podman image search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`, @@ -64,9 +66,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: searchCmd, }) - - flags := searchCmd.Flags() - searchFlags(flags) + searchFlags(searchCmd) // images search registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -74,18 +74,32 @@ func init() { Command: imageSearchCmd, Parent: imageCmd, }) - - imageSearchFlags := imageSearchCmd.Flags() - searchFlags(imageSearchFlags) + searchFlags(imageSearchCmd) } // searchFlags set the flags for the pull command. -func searchFlags(flags *pflag.FlagSet) { - flags.StringSliceVarP(&searchOptions.Filters, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") - flags.StringVar(&searchOptions.Format, "format", "", "Change the output format to JSON or a Go template") - flags.IntVar(&searchOptions.Limit, "limit", 0, "Limit the number of results") +func searchFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + 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) + + formatFlagName := "format" + flags.StringVar(&searchOptions.Format, formatFlagName, "", "Change the output format to JSON or a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) + + limitFlagName := "limit" + flags.IntVar(&searchOptions.Limit, limitFlagName, 0, "Limit the number of results") + _ = cmd.RegisterFlagCompletionFunc(limitFlagName, completion.AutocompleteNone) + 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") + + authfileFlagName := "authfile" + flags.StringVar(&searchOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.BoolVar(&searchOptions.ListTags, "list-tags", false, "List the tags of the input registry") } diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go index f6c1f9856..529fb3d92 100644 --- a/cmd/podman/images/sign.go +++ b/cmd/podman/images/sign.go @@ -3,6 +3,8 @@ package images import ( "os" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -12,11 +14,12 @@ import ( var ( signDescription = "Create a signature file that can be used later to verify the image." signCommand = &cobra.Command{ - Use: "sign [options] IMAGE [IMAGE...]", - Short: "Sign an image", - Long: signDescription, - RunE: sign, - Args: cobra.MinimumNArgs(1), + Use: "sign [options] IMAGE [IMAGE...]", + Short: "Sign an image", + Long: signDescription, + RunE: sign, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteImages, Example: `podman image sign --sign-by mykey imageID podman image sign --sign-by mykey --directory ./mykeydir imageID`, } @@ -33,9 +36,17 @@ func init() { Parent: imageCmd, }) flags := signCommand.Flags() - flags.StringVarP(&signOptions.Directory, "directory", "d", "", "Define an alternate directory to store signatures") - flags.StringVar(&signOptions.SignBy, "sign-by", "", "Name of the signing key") - flags.StringVar(&signOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + directoryFlagName := "directory" + flags.StringVarP(&signOptions.Directory, directoryFlagName, "d", "", "Define an alternate directory to store signatures") + _ = signCommand.RegisterFlagCompletionFunc(directoryFlagName, completion.AutocompleteDefault) + + signByFlagName := "sign-by" + flags.StringVar(&signOptions.SignBy, signByFlagName, "", "Name of the signing key") + _ = signCommand.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone) + + certDirFlagName := "cert-dir" + flags.StringVar(&signOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") + _ = signCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) } func sign(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/tag.go b/cmd/podman/images/tag.go index be7f84588..157db49ca 100644 --- a/cmd/podman/images/tag.go +++ b/cmd/podman/images/tag.go @@ -1,6 +1,7 @@ package images import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -15,6 +16,7 @@ var ( RunE: tag, Args: cobra.MinimumNArgs(2), DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteImages, Example: `podman tag 0e3bbc2 fedora:latest podman tag imageID:latest myNewImage:newTag podman tag httpd myregistryhost:5000/fedora/httpd:v2`, @@ -27,6 +29,7 @@ var ( Short: tagCommand.Short, Long: tagCommand.Long, RunE: tagCommand.RunE, + ValidArgsFunction: tagCommand.ValidArgsFunction, Example: `podman image tag 0e3bbc2 fedora:latest podman image tag imageID:latest myNewImage:newTag podman image tag httpd myregistryhost:5000/fedora/httpd:v2`, diff --git a/cmd/podman/images/tree.go b/cmd/podman/images/tree.go index 237a2ab91..76c69b37e 100644 --- a/cmd/podman/images/tree.go +++ b/cmd/podman/images/tree.go @@ -3,6 +3,7 @@ package images import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -11,12 +12,13 @@ import ( var ( treeDescription = "Prints layer hierarchy of an image in a tree format" treeCmd = &cobra.Command{ - Use: "tree [options] IMAGE", - Args: cobra.ExactArgs(1), - Short: treeDescription, - Long: treeDescription, - RunE: tree, - Example: "podman image tree alpine:latest", + Use: "tree [options] IMAGE", + Args: cobra.ExactArgs(1), + Short: treeDescription, + Long: treeDescription, + RunE: tree, + ValidArgsFunction: common.AutocompleteImages, + Example: "podman image tree alpine:latest", } treeOpts entities.ImageTreeOptions ) diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go index 2e4b4fe17..f0399b110 100644 --- a/cmd/podman/images/trust_set.go +++ b/cmd/podman/images/trust_set.go @@ -1,6 +1,8 @@ package images import ( + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" @@ -12,12 +14,13 @@ import ( var ( setTrustDescription = "Set default trust policy or add a new trust policy for a registry" setTrustCommand = &cobra.Command{ - Use: "set [options] REGISTRY", - Short: "Set default trust policy or a new trust policy for a registry", - Long: setTrustDescription, - Example: "", - RunE: setTrust, - Args: cobra.ExactArgs(1), + Use: "set [options] REGISTRY", + Short: "Set default trust policy or a new trust policy for a registry", + Long: setTrustDescription, + Example: "", + RunE: setTrust, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteRegistries, } ) @@ -34,11 +37,17 @@ func init() { setFlags := setTrustCommand.Flags() setFlags.StringVar(&setOptions.PolicyPath, "policypath", "", "") _ = setFlags.MarkHidden("policypath") - setFlags.StringSliceVarP(&setOptions.PubKeysFile, "pubkeysfile", "f", []string{}, `Path of installed public key(s) to trust for TARGET. + + pubkeysfileFlagName := "pubkeysfile" + setFlags.StringSliceVarP(&setOptions.PubKeysFile, pubkeysfileFlagName, "f", []string{}, `Path of installed public key(s) to trust for TARGET. Absolute path to keys is added to policy.json. May used multiple times to define multiple public keys. File(s) must exist before using this command`) - setFlags.StringVarP(&setOptions.Type, "type", "t", "signedBy", "Trust type, accept values: signedBy(default), accept, reject") + _ = setTrustCommand.RegisterFlagCompletionFunc(pubkeysfileFlagName, completion.AutocompleteDefault) + + typeFlagName := "type" + setFlags.StringVarP(&setOptions.Type, typeFlagName, "t", "signedBy", "Trust type, accept values: signedBy(default), accept, reject") + _ = setTrustCommand.RegisterFlagCompletionFunc(typeFlagName, common.AutocompleteTrustType) } func setTrust(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index ba3b4e7fb..dc35dc6a1 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -6,6 +6,7 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -14,12 +15,13 @@ import ( var ( showTrustDescription = "Display trust policy for the system" showTrustCommand = &cobra.Command{ - Use: "show [options] [REGISTRY]", - Short: "Display trust policy for the system", - Long: showTrustDescription, - RunE: showTrust, - Args: cobra.MaximumNArgs(1), - Example: "", + Use: "show [options] [REGISTRY]", + Short: "Display trust policy for the system", + Long: showTrustDescription, + RunE: showTrust, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: common.AutocompleteRegistries, + Example: "", } ) diff --git a/cmd/podman/images/unmount.go b/cmd/podman/images/unmount.go index 50dc972e8..3af959b9f 100644 --- a/cmd/podman/images/unmount.go +++ b/cmd/podman/images/unmount.go @@ -3,6 +3,7 @@ package images import ( "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -19,11 +20,12 @@ var ( An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [options] IMAGE [IMAGE...]", - Aliases: []string{"umount"}, - Short: "Unmount an image's root filesystem", - Long: description, - RunE: unmount, + Use: "unmount [options] IMAGE [IMAGE...]", + Aliases: []string{"umount"}, + Short: "Unmount an image's root filesystem", + Long: description, + RunE: unmount, + ValidArgsFunction: common.AutocompleteImages, Example: `podman unmount imgID podman unmount imgID1 imgID2 imgID3 podman unmount --all`, diff --git a/cmd/podman/images/untag.go b/cmd/podman/images/untag.go index da749c8a5..17dc21203 100644 --- a/cmd/podman/images/untag.go +++ b/cmd/podman/images/untag.go @@ -1,6 +1,7 @@ package images import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -14,6 +15,7 @@ var ( RunE: untag, Args: cobra.MinimumNArgs(1), DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteImages, Example: `podman untag 0e3bbc2 podman untag imageID:latest otherImageName:latest podman untag httpd myregistryhost:5000/fedora/httpd:v2`, @@ -26,6 +28,7 @@ var ( Short: untagCommand.Short, Long: untagCommand.Long, RunE: untagCommand.RunE, + ValidArgsFunction: untagCommand.ValidArgsFunction, Example: `podman image untag 0e3bbc2 podman image untag imageID:latest otherImageName:latest podman image untag httpd myregistryhost:5000/fedora/httpd:v2`, diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index f1d673a21..f62abe931 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -1,6 +1,7 @@ package main import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" @@ -19,11 +20,12 @@ var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [options] {CONTAINER_ID | IMAGE_ID} [...]", - Short: "Display the configuration of object denoted by ID", - RunE: inspectExec, - Long: inspectDescription, - TraverseChildren: true, + Use: "inspect [options] {CONTAINER_ID | IMAGE_ID} [...]", + Short: "Display the configuration of object denoted by ID", + RunE: inspectExec, + Long: inspectDescription, + TraverseChildren: true, + ValidArgsFunction: common.AutocompleteContainersAndImages, 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 9c400d506..f9bd75c93 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -9,7 +9,9 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -44,8 +46,14 @@ func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions { flags := cmd.Flags() 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)) + + formatFlagName := "format" + flags.StringVarP(&opts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) + + typeFlagName := "type" + flags.StringVarP(&opts.Type, typeFlagName, "t", AllType, fmt.Sprintf("Specify inspect-oject type (%q, %q or %q)", ImageType, ContainerType, AllType)) + _ = cmd.RegisterFlagCompletionFunc(typeFlagName, common.AutocompleteInspectType) validate.AddLatestFlag(cmd, &opts.Latest) return &opts diff --git a/cmd/podman/login.go b/cmd/podman/login.go index a789cef33..7c8fe03dc 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -5,7 +5,9 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/registries" @@ -20,11 +22,12 @@ type loginOptionsWrapper struct { var ( loginOptions = loginOptionsWrapper{} loginCommand = &cobra.Command{ - Use: "login [options] [REGISTRY]", - Short: "Login to a container registry", - Long: "Login to a container registry on a specified server.", - RunE: login, - Args: cobra.MaximumNArgs(1), + Use: "login [options] [REGISTRY]", + Short: "Login to a container registry", + Long: "Login to a container registry on a specified server.", + RunE: login, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: common.AutocompleteRegistries, Example: `podman login quay.io podman login --username ... --password ... quay.io podman login --authfile dir/auth.json quay.io`, @@ -44,6 +47,9 @@ func init() { // Flags from the auth package. flags.AddFlagSet(auth.GetLoginFlags(&loginOptions.LoginOptions)) + // Add flag completion + completion.CompleteCommandFlags(loginCommand, auth.GetLoginFlagsCompletions()) + // Podman flags. flags.BoolVarP(&loginOptions.tlsVerify, "tls-verify", "", false, "Require HTTPS and verify certificates when contacting registries") loginOptions.Stdin = os.Stdin diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 7b5615d30..3cb11071b 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -4,7 +4,9 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/registries" @@ -14,11 +16,12 @@ import ( var ( logoutOptions = auth.LogoutOptions{} logoutCommand = &cobra.Command{ - Use: "logout [options] [REGISTRY]", - Short: "Logout of a container registry", - Long: "Remove the cached username and password for the registry.", - RunE: logout, - Args: cobra.MaximumNArgs(1), + Use: "logout [options] [REGISTRY]", + Short: "Logout of a container registry", + Long: "Remove the cached username and password for the registry.", + RunE: logout, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: common.AutocompleteRegistries, Example: `podman logout quay.io podman logout --authfile dir/auth.json quay.io podman logout --all`, @@ -37,6 +40,10 @@ func init() { // Flags from the auth package. flags.AddFlagSet(auth.GetLogoutFlags(&logoutOptions)) + + // Add flag completion + completion.CompleteCommandFlags(logoutCommand, auth.GetLogoutFlagsCompletions()) + logoutOptions.Stdout = os.Stdout logoutOptions.AcceptUnspecifiedRegistry = true } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 9747769c7..c3aaf84a8 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + _ "github.com/containers/podman/v2/cmd/podman/completion" _ "github.com/containers/podman/v2/cmd/podman/containers" _ "github.com/containers/podman/v2/cmd/podman/generate" _ "github.com/containers/podman/v2/cmd/podman/healthcheck" @@ -35,6 +36,13 @@ func main() { os.Setenv("TMPDIR", "/var/tmp") } + rootCmd = parseCommands() + + Execute() + os.Exit(0) +} + +func parseCommands() *cobra.Command { cfg := registry.PodmanConfig() for _, c := range registry.Commands { for _, m := range c.Mode { @@ -75,6 +83,5 @@ func main() { os.Exit(1) } - Execute() - os.Exit(0) + return rootCmd } diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go index 4b85f4c2a..91bd423b8 100644 --- a/cmd/podman/manifest/add.go +++ b/cmd/podman/manifest/add.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" @@ -25,10 +27,11 @@ type manifestAddOptsWrapper struct { var ( manifestAddOpts = manifestAddOptsWrapper{} addCmd = &cobra.Command{ - Use: "add [options] LIST LIST", - Short: "Add images to a manifest list or image index", - Long: "Adds an image to a manifest list or image index.", - RunE: add, + Use: "add [options] LIST LIST", + Short: "Add images to a manifest list or image index", + Long: "Adds an image to a manifest list or image index.", + RunE: add, + ValidArgsFunction: common.AutocompleteImages, Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 podman manifest add mylist:v1.11 transport:imageName`, Args: cobra.ExactArgs(2), @@ -43,17 +46,44 @@ func init() { }) flags := addCmd.Flags() flags.BoolVar(&manifestAddOpts.All, "all", false, "add all of the list's images if the image is a list") - flags.StringSliceVar(&manifestAddOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image") - flags.StringVar(&manifestAddOpts.Arch, "arch", "", "override the `architecture` of the specified image") - flags.StringVar(&manifestAddOpts.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&manifestAddOpts.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") - flags.StringVar(&manifestAddOpts.CredentialsCLI, "creds", "", "use `[username[:password]]` for accessing the registry") - - flags.StringSliceVar(&manifestAddOpts.Features, "features", nil, "override the `features` of the specified image") - flags.StringVar(&manifestAddOpts.OS, "os", "", "override the `OS` of the specified image") - flags.StringVar(&manifestAddOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image") + + annotationFlagName := "annotation" + flags.StringSliceVar(&manifestAddOpts.Annotation, annotationFlagName, nil, "set an `annotation` for the specified image") + _ = addCmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + + archFlagName := "arch" + flags.StringVar(&manifestAddOpts.Arch, archFlagName, "", "override the `architecture` of the specified image") + _ = addCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteNone) + + authfileFlagName := "authfile" + flags.StringVar(&manifestAddOpts.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = addCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + + certDirFlagName := "cert-dir" + flags.StringVar(&manifestAddOpts.CertDir, certDirFlagName, "", "use certificates at the specified path to access the registry") + _ = addCmd.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + + credsFlagName := "creds" + flags.StringVar(&manifestAddOpts.CredentialsCLI, credsFlagName, "", "use `[username[:password]]` for accessing the registry") + _ = addCmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + + featuresFlagName := "features" + flags.StringSliceVar(&manifestAddOpts.Features, featuresFlagName, nil, "override the `features` of the specified image") + _ = addCmd.RegisterFlagCompletionFunc(featuresFlagName, completion.AutocompleteNone) + + osFlagName := "os" + flags.StringVar(&manifestAddOpts.OS, osFlagName, "", "override the `OS` of the specified image") + _ = addCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteNone) + + osVersionFlagName := "os-version" + flags.StringVar(&manifestAddOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image") + _ = addCmd.RegisterFlagCompletionFunc(osVersionFlagName, completion.AutocompleteNone) + flags.BoolVar(&manifestAddOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") - flags.StringVar(&manifestAddOpts.Variant, "variant", "", "override the `Variant` of the specified image") + + variantFlagName := "variant" + flags.StringVar(&manifestAddOpts.Variant, variantFlagName, "", "override the `Variant` of the specified image") + _ = addCmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) if registry.IsRemote() { _ = flags.MarkHidden("cert-dir") diff --git a/cmd/podman/manifest/annotate.go b/cmd/podman/manifest/annotate.go index 861e94034..dab8c4da6 100644 --- a/cmd/podman/manifest/annotate.go +++ b/cmd/podman/manifest/annotate.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -13,12 +15,13 @@ import ( var ( manifestAnnotateOpts = entities.ManifestAnnotateOptions{} annotateCmd = &cobra.Command{ - Use: "annotate [options] LIST IMAGE", - Short: "Add or update information about an entry in a manifest list or image index", - Long: "Adds or updates information about an entry in a manifest list or image index.", - RunE: annotate, - Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`, - Args: cobra.ExactArgs(2), + Use: "annotate [options] LIST IMAGE", + Short: "Add or update information about an entry in a manifest list or image index", + Long: "Adds or updates information about an entry in a manifest list or image index.", + RunE: annotate, + Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`, + Args: cobra.ExactArgs(2), + ValidArgsFunction: common.AutocompleteImages, } ) @@ -29,13 +32,34 @@ func init() { Parent: manifestCmd, }) flags := annotateCmd.Flags() - flags.StringSliceVar(&manifestAnnotateOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image") - flags.StringVar(&manifestAnnotateOpts.Arch, "arch", "", "override the `architecture` of the specified image") - flags.StringSliceVar(&manifestAnnotateOpts.Features, "features", nil, "override the `features` of the specified image") - flags.StringVar(&manifestAnnotateOpts.OS, "os", "", "override the `OS` of the specified image") - flags.StringSliceVar(&manifestAnnotateOpts.OSFeatures, "os-features", nil, "override the OS `features` of the specified image") - flags.StringVar(&manifestAnnotateOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image") - flags.StringVar(&manifestAnnotateOpts.Variant, "variant", "", "override the `variant` of the specified image") + + annotationFlagName := "annotation" + flags.StringSliceVar(&manifestAnnotateOpts.Annotation, annotationFlagName, nil, "set an `annotation` for the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + + archFlagName := "arch" + flags.StringVar(&manifestAnnotateOpts.Arch, archFlagName, "", "override the `architecture` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteNone) + + featuresFlagName := "features" + flags.StringSliceVar(&manifestAnnotateOpts.Features, featuresFlagName, nil, "override the `features` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(featuresFlagName, completion.AutocompleteNone) + + osFlagName := "os" + flags.StringVar(&manifestAnnotateOpts.OS, osFlagName, "", "override the `OS` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteNone) + + osFeaturesFlagName := "os-features" + flags.StringSliceVar(&manifestAnnotateOpts.OSFeatures, osFeaturesFlagName, nil, "override the OS `features` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(osFeaturesFlagName, completion.AutocompleteNone) + + osVersionFlagName := "os-version" + flags.StringVar(&manifestAnnotateOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(osVersionFlagName, completion.AutocompleteNone) + + variantFlagName := "variant" + flags.StringVar(&manifestAnnotateOpts.Variant, variantFlagName, "", "override the `Variant` of the specified image") + _ = annotateCmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) } func annotate(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go index 956946f9d..c903c6fa8 100644 --- a/cmd/podman/manifest/create.go +++ b/cmd/podman/manifest/create.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -13,10 +14,11 @@ import ( var ( manifestCreateOpts = entities.ManifestCreateOptions{} createCmd = &cobra.Command{ - Use: "create [options] LIST [IMAGE]", - Short: "Create manifest list or image index", - Long: "Creates manifest lists or image indexes.", - RunE: create, + Use: "create [options] LIST [IMAGE]", + Short: "Create manifest list or image index", + Long: "Creates manifest lists or image indexes.", + RunE: create, + ValidArgsFunction: common.AutocompleteImages, 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`, diff --git a/cmd/podman/manifest/inspect.go b/cmd/podman/manifest/inspect.go index 19396ceb3..17c94aaba 100644 --- a/cmd/podman/manifest/inspect.go +++ b/cmd/podman/manifest/inspect.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -16,6 +17,7 @@ var ( Short: "Display the contents of a manifest list or image index", Long: "Display the contents of a manifest list or image index.", RunE: inspect, + ValidArgsFunction: common.AutocompleteImages, Example: "podman manifest inspect localhost/list", Args: cobra.ExactArgs(1), DisableFlagsInUseLine: true, diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index 91881c1b3..9d0977834 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -2,7 +2,9 @@ package manifest import ( "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/util" @@ -22,12 +24,13 @@ type manifestPushOptsWrapper struct { var ( manifestPushOpts = manifestPushOptsWrapper{} pushCmd = &cobra.Command{ - Use: "push [options] SOURCE DESTINATION", - Short: "Push a manifest list or image index to a registry", - Long: "Pushes manifest lists and image indexes to registries.", - RunE: push, - Example: `podman manifest push mylist:v1.11 quay.io/myimagelist`, - Args: cobra.ExactArgs(2), + Use: "push [options] SOURCE DESTINATION", + Short: "Push a manifest list or image index to a registry", + Long: "Pushes manifest lists and image indexes to registries.", + RunE: push, + Example: `podman manifest push mylist:v1.11 quay.io/myimagelist`, + Args: cobra.ExactArgs(2), + ValidArgsFunction: common.AutocompleteImages, } ) @@ -40,13 +43,33 @@ func init() { flags := pushCmd.Flags() flags.BoolVar(&manifestPushOpts.Purge, "purge", false, "remove the manifest list if push succeeds") flags.BoolVar(&manifestPushOpts.All, "all", false, "also push the images in the list") - flags.StringVar(&manifestPushOpts.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") - flags.StringVar(&manifestPushOpts.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") - flags.StringVar(&manifestPushOpts.CredentialsCLI, "creds", "", "use `[username[:password]]` for accessing the registry") - flags.StringVar(&manifestPushOpts.DigestFile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file") - flags.StringVarP(&manifestPushOpts.Format, "format", "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)") + + authfileFlagName := "authfile" + flags.StringVar(&manifestPushOpts.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = pushCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + + certDirFlagName := "cert-dir" + flags.StringVar(&manifestPushOpts.CertDir, certDirFlagName, "", "use certificates at the specified path to access the registry") + _ = pushCmd.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + + credsFlagName := "creds" + flags.StringVar(&manifestPushOpts.CredentialsCLI, credsFlagName, "", "use `[username[:password]]` for accessing the registry") + _ = pushCmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + + digestfileFlagName := "digestfile" + flags.StringVar(&manifestPushOpts.DigestFile, digestfileFlagName, "", "after copying the image, write the digest of the resulting digest to the file") + _ = pushCmd.RegisterFlagCompletionFunc(digestfileFlagName, completion.AutocompleteDefault) + + formatFlagName := "format" + flags.StringVarP(&manifestPushOpts.Format, formatFlagName, "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)") + _ = pushCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteManifestFormat) + flags.BoolVarP(&manifestPushOpts.RemoveSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images") - flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`") + + signByFlagName := "sign-by" + flags.StringVar(&manifestPushOpts.SignBy, signByFlagName, "", "sign the image using a GPG key with the specified `FINGERPRINT`") + _ = pushCmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone) + 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") diff --git a/cmd/podman/manifest/remove.go b/cmd/podman/manifest/remove.go index 677d45484..170e68d3f 100644 --- a/cmd/podman/manifest/remove.go +++ b/cmd/podman/manifest/remove.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -16,6 +17,7 @@ var ( Short: "Remove an entry from a manifest list or image index", Long: "Removes an image from a manifest list or image index.", RunE: remove, + ValidArgsFunction: common.AutocompleteImages, Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, Args: cobra.ExactArgs(2), DisableFlagsInUseLine: true, diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index c06011ce9..17de2c95d 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -4,22 +4,24 @@ import ( "fmt" "net" + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( networkCreateDescription = `create CNI networks for containers and pods` networkCreateCommand = &cobra.Command{ - Use: "create [options] [NETWORK]", - Short: "network create", - Long: networkCreateDescription, - RunE: networkCreate, - Args: cobra.MaximumNArgs(1), - Example: `podman network create podman1`, + Use: "create [options] [NETWORK]", + Short: "network create", + Long: networkCreateDescription, + RunE: networkCreate, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman network create podman1`, } ) @@ -27,16 +29,36 @@ var ( networkCreateOptions entities.NetworkCreateOptions ) -func networkCreateFlags(flags *pflag.FlagSet) { - flags.StringVarP(&networkCreateOptions.Driver, "driver", "d", "bridge", "driver to manage the network") - flags.IPVar(&networkCreateOptions.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet") +func networkCreateFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + driverFlagName := "driver" + flags.StringVarP(&networkCreateOptions.Driver, driverFlagName, "d", "bridge", "driver to manage the network") + _ = cmd.RegisterFlagCompletionFunc(driverFlagName, common.AutocompleteNetworkDriver) + + gatewayFlagName := "gateway" + flags.IPVar(&networkCreateOptions.Gateway, gatewayFlagName, nil, "IPv4 or IPv6 gateway for the subnet") + _ = cmd.RegisterFlagCompletionFunc(gatewayFlagName, completion.AutocompleteNone) + flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network") - flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range") - flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") + + ipRangeFlagName := "ip-range" + flags.IPNetVar(&networkCreateOptions.Range, ipRangeFlagName, net.IPNet{}, "allocate container IP from range") + _ = cmd.RegisterFlagCompletionFunc(ipRangeFlagName, completion.AutocompleteNone) + + macvlanFlagName := "macvlan" + flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device") + _ = cmd.RegisterFlagCompletionFunc(macvlanFlagName, completion.AutocompleteNone) + // TODO not supported yet // flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") + flags.BoolVar(&networkCreateOptions.IPv6, "ipv6", false, "enable IPv6 networking") - flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format") + + subnetFlagName := "subnet" + flags.IPNetVar(&networkCreateOptions.Subnet, subnetFlagName, net.IPNet{}, "subnet in CIDR format") + _ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone) + flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") } func init() { @@ -45,8 +67,7 @@ func init() { Command: networkCreateCommand, Parent: networkCmd, }) - flags := networkCreateCommand.Flags() - networkCreateFlags(flags) + networkCreateFlags(networkCreateCommand) } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index 25ee7e574..671b0265f 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -1,6 +1,7 @@ package network import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" @@ -10,12 +11,13 @@ import ( var ( networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ - Use: "inspect [options] NETWORK [NETWORK...]", - Short: "network inspect", - Long: networkinspectDescription, - RunE: networkInspect, - Example: `podman network inspect podman`, - Args: cobra.MinimumNArgs(1), + Use: "inspect [options] NETWORK [NETWORK...]", + Short: "network inspect", + Long: networkinspectDescription, + RunE: networkInspect, + Example: `podman network inspect podman`, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteNetworks, } inspectOpts *entities.InspectOptions ) @@ -28,7 +30,10 @@ func init() { }) inspectOpts = new(entities.InspectOptions) flags := networkinspectCommand.Flags() - flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") + + formatFlagName := "format" + flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "", "Pretty-print network to JSON or using a Go template") + _ = networkinspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func networkInspect(_ *cobra.Command, args []string) error { diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index f68e4ed75..bab6b45ea 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -8,7 +8,9 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -21,12 +23,13 @@ import ( var ( networklistDescription = `List networks` networklistCommand = &cobra.Command{ - Use: "ls [options]", - Args: validate.NoArgs, - Short: "network list", - Long: networklistDescription, - RunE: networkList, - Example: `podman network list`, + Use: "ls [options]", + Args: validate.NoArgs, + Short: "network list", + Long: networklistDescription, + RunE: networkList, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman network list`, } ) @@ -35,11 +38,16 @@ var ( ) func networkListFlags(flags *pflag.FlagSet) { - // TODO enable filters based on something - // flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") - flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print networks to JSON or using a Go template") + formatFlagName := "format" + flags.StringVarP(&networkListOptions.Format, formatFlagName, "f", "", "Pretty-print networks to JSON or using a Go template") + _ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") - flags.StringVarP(&networkListOptions.Filter, "filter", "", "", "Provide filter values (e.g. 'name=podman')") + + filterFlagName := "filter" + flags.StringVarP(&networkListOptions.Filter, filterFlagName, "", "", "Provide filter values (e.g. 'name=podman')") + _ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + } func init() { diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 3d7db941a..34e756a3a 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/libpod/define" @@ -16,12 +17,13 @@ import ( var ( networkrmDescription = `Remove networks` networkrmCommand = &cobra.Command{ - Use: "rm [options] NETWORK [NETWORK...]", - Short: "network rm", - Long: networkrmDescription, - RunE: networkRm, - Example: `podman network rm podman`, - Args: cobra.MinimumNArgs(1), + Use: "rm [options] NETWORK [NETWORK...]", + Short: "network rm", + Long: networkrmDescription, + RunE: networkRm, + Example: `podman network rm podman`, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteNetworks, } ) diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 4f34b2b76..a9e91bd68 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -5,7 +5,9 @@ import ( "os" "github.com/containers/common/pkg/auth" + "github.com/containers/common/pkg/completion" "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/pkg/domain/entities" @@ -31,11 +33,12 @@ var ( It creates the pod and containers described in the YAML. The containers within the pod are then started and the ID of the new Pod is output.` kubeCmd = &cobra.Command{ - Use: "kube [options] KUBEFILE", - Short: "Play a pod based on Kubernetes YAML.", - Long: kubeDescription, - RunE: kube, - Args: cobra.ExactArgs(1), + Use: "kube [options] KUBEFILE", + Short: "Play a pod based on Kubernetes YAML.", + Long: kubeDescription, + RunE: kube, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.AutocompleteDefault, Example: `podman play kube nginx.yml podman play kube --creds user:password --seccomp-profile-root /custom/path apache.yml`, } @@ -50,17 +53,41 @@ func init() { flags := kubeCmd.Flags() flags.SetNormalizeFunc(utils.AliasFlags) - flags.StringVar(&kubeOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") - flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)") - flags.StringVar(&kubeOptions.LogDriver, "log-driver", "", "Logging driver for the container") + + credsFlagName := "creds" + flags.StringVar(&kubeOptions.CredentialsCLI, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + _ = kubeCmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone) + + networkFlagName := "network" + flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)") + _ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworks) + + logDriverFlagName := "log-driver" + flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container") + _ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver) + flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") - flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + + authfileFlagName := "authfile" + flags.StringVar(&kubeOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + _ = kubeCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + if !registry.IsRemote() { - flags.StringVar(&kubeOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") + + certDirFlagName := "cert-dir" + flags.StringVar(&kubeOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") + _ = kubeCmd.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault) + 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.StringSliceVar(&kubeOptions.ConfigMaps, "configmap", []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") + + seccompProfileRootFlagName := "seccomp-profile-root" + flags.StringVar(&kubeOptions.SeccompProfileRoot, seccompProfileRootFlagName, defaultSeccompRoot, "Directory path for seccomp profiles") + _ = kubeCmd.RegisterFlagCompletionFunc(seccompProfileRootFlagName, completion.AutocompleteDefault) + + configmapFlagName := "configmap" + flags.StringSliceVar(&kubeOptions.ConfigMaps, configmapFlagName, []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") + _ = kubeCmd.RegisterFlagCompletionFunc(configmapFlagName, completion.AutocompleteDefault) } _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index efa84dcb4..d33455e81 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" @@ -27,11 +28,12 @@ var ( You can then start it at any time with the podman pod start command. The pod will be created with the initial state 'created'.` createCommand = &cobra.Command{ - Use: "create [options]", - Args: validate.NoArgs, - Short: "Create a new empty pod", - Long: podCreateDescription, - RunE: create, + Use: "create [options]", + Args: validate.NoArgs, + Short: "Create a new empty pod", + Long: podCreateDescription, + RunE: create, + ValidArgsFunction: completion.AutocompleteNone, } ) @@ -51,19 +53,53 @@ func init() { }) flags := createCommand.Flags() flags.SetInterspersed(false) - flags.AddFlagSet(common.GetNetFlags()) - flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") + + common.DefineNetFlags(createCommand) + + cgroupParentflagName := "cgroup-parent" + flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod") + _ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault) + flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") - flags.StringVar(&createOptions.InfraConmonPidFile, "infra-conmon-pidfile", "", "Path to the file that will receive the POD of the infra container's conmon") - flags.String("infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") - flags.String("infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") - flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels") - flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])") - flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod") - flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod") - flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file") + + infraConmonPidfileFlagName := "infra-conmon-pidfile" + flags.StringVar(&createOptions.InfraConmonPidFile, infraConmonPidfileFlagName, "", "Path to the file that will receive the POD of the infra container's conmon") + _ = createCommand.RegisterFlagCompletionFunc(infraConmonPidfileFlagName, completion.AutocompleteDefault) + + infraImageFlagName := "infra-image" + flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) + + infraCommandFlagName := "infra-command" + flags.String(infraCommandFlagName, containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") + _ = createCommand.RegisterFlagCompletionFunc(infraCommandFlagName, completion.AutocompleteNone) + + labelFileFlagName := "label-file" + flags.StringSliceVar(&labelFile, labelFileFlagName, []string{}, "Read in a line delimited file of labels") + _ = createCommand.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) + + labelFlagName := "label" + flags.StringSliceVarP(&labels, labelFlagName, "l", []string{}, "Set metadata on pod (default [])") + _ = createCommand.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + + nameFlagName := "name" + flags.StringVarP(&createOptions.Name, nameFlagName, "n", "", "Assign a name to the pod") + _ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + + hostnameFlagName := "hostname" + flags.StringVarP(&createOptions.Hostname, hostnameFlagName, "", "", "Set a hostname to the pod") + _ = createCommand.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + + podIDFileFlagName := "pod-id-file" + flags.StringVar(&podIDFile, podIDFileFlagName, "", "Write the pod ID to the file") + _ = createCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + flags.BoolVar(&replace, "replace", false, "If a pod with the same exists, replace it") - flags.StringVar(&share, "share", specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") + + shareFlagName := "share" + flags.StringVar(&share, shareFlagName, specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") + _ = createCommand.RegisterFlagCompletionFunc(shareFlagName, common.AutocompletePodShareNamespace) + flags.SetNormalizeFunc(aliasNetworkFlag) } diff --git a/cmd/podman/pods/exists.go b/cmd/podman/pods/exists.go index cdaf2a707..a5c45803e 100644 --- a/cmd/podman/pods/exists.go +++ b/cmd/podman/pods/exists.go @@ -3,6 +3,7 @@ package pods import ( "context" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -12,11 +13,12 @@ var ( podExistsDescription = `If the named pod exists in local storage, podman pod exists exits with 0, otherwise the exit code will be 1.` existsCommand = &cobra.Command{ - Use: "exists POD", - Short: "Check if a pod exists in local storage", - Long: podExistsDescription, - RunE: exists, - Args: cobra.ExactArgs(1), + Use: "exists POD", + Short: "Check if a pod exists in local storage", + Long: podExistsDescription, + RunE: exists, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompletePods, Example: `podman pod exists podID podman pod exists mypod || podman pod create --name mypod`, DisableFlagsInUseLine: true, diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 7f81ba8fb..091094ff6 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -8,6 +8,7 @@ import ( "text/template" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -25,11 +26,12 @@ var ( By default, this will render all results in a JSON array.`) inspectCmd = &cobra.Command{ - Use: "inspect [options] POD [POD...]", - Short: "Displays a pod configuration", - Long: inspectDescription, - RunE: inspect, - Example: `podman pod inspect podID`, + Use: "inspect [options] POD [POD...]", + Short: "Displays a pod configuration", + Long: inspectDescription, + RunE: inspect, + ValidArgsFunction: common.AutocompletePods, + Example: `podman pod inspect podID`, } ) @@ -40,7 +42,11 @@ func init() { Parent: podCmd, }) flags := inspectCmd.Flags() - flags.StringVarP(&inspectOptions.Format, "format", "f", "json", "Format the output to a Go template or json") + + formatFlagName := "format" + flags.StringVarP(&inspectOptions.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + validate.AddLatestFlag(inspectCmd, &inspectOptions.Latest) } diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go index 1902a2c80..be8fd31df 100644 --- a/cmd/podman/pods/kill.go +++ b/cmd/podman/pods/kill.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -23,6 +24,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod kill podID podman pod kill --signal TERM mywebserver podman pod kill --latest`, @@ -41,7 +43,11 @@ func init() { }) flags := killCommand.Flags() flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods") - flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") + + signalFlagName := "signal" + flags.StringVarP(&killOpts.Signal, signalFlagName, "s", "KILL", "Signal to send to the containers in the pod") + _ = killCommand.RegisterFlagCompletionFunc(signalFlagName, common.AutocompleteStopSignal) + validate.AddLatestFlag(killCommand, &killOpts.Latest) } diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go index bba26f90d..108893173 100644 --- a/cmd/podman/pods/pause.go +++ b/cmd/podman/pods/pause.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -23,6 +24,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod pause podID1 podID2 podman pod pause --latest podman pod pause --all`, diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go index e3eae3f71..626ef2895 100644 --- a/cmd/podman/pods/prune.go +++ b/cmd/podman/pods/prune.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -23,12 +24,13 @@ var ( pruneDescription = fmt.Sprintf(`podman pod prune Removes all exited pods`) pruneCommand = &cobra.Command{ - Use: "prune [options]", - Args: validate.NoArgs, - Short: "Remove all stopped pods and their containers", - Long: pruneDescription, - RunE: prune, - Example: `podman pod prune`, + Use: "prune [options]", + Args: validate.NoArgs, + Short: "Remove all stopped pods and their containers", + Long: pruneDescription, + RunE: prune, + ValidArgsFunction: common.AutocompletePods, + Example: `podman pod prune`, } ) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 40fc71780..51c2e92f0 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -10,7 +10,9 @@ import ( "text/template" "time" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -25,12 +27,13 @@ var ( // Command: podman pod _ps_ psCmd = &cobra.Command{ - Use: "ps [options]", - Aliases: []string{"ls", "list"}, - Short: "List pods", - Long: psDescription, - RunE: pods, - Args: validate.NoArgs, + Use: "ps [options]", + Aliases: []string{"ls", "list"}, + Short: "List pods", + Long: psDescription, + RunE: pods, + Args: validate.NoArgs, + ValidArgsFunction: completion.AutocompleteNone, } ) @@ -51,13 +54,25 @@ func init() { flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated") flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status") // 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") + + filterFlagName := "filter" + flags.StringSliceVarP(&inputFilters, filterFlagName, "f", []string{}, "Filter output based on conditions given") + //TODO complete filters + _ = psCmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + + formatFlagName := "format" + flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template") + _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + 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") + + sortFlagName := "sort" + flags.StringVar(&psInput.Sort, sortFlagName, "created", "Sort output by created, id, name, or number") + _ = psCmd.RegisterFlagCompletionFunc(sortFlagName, common.AutocompletePodPsSort) + validate.AddLatestFlag(psCmd, &psInput.Latest) } diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go index 119b4ddee..7a4b28a45 100644 --- a/cmd/podman/pods/restart.go +++ b/cmd/podman/pods/restart.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -23,6 +24,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + ValidArgsFunction: common.AutocompletePods, Example: `podman pod restart podID1 podID2 podman pod restart --latest podman pod restart --all`, diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 714e075e2..ff238aa20 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -35,6 +36,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, + ValidArgsFunction: common.AutocompletePods, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 podman pod rm -f -a`, @@ -52,7 +54,11 @@ 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.StringArrayVarP(&rmOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + + podIDFileFlagName := "pod-id-file" + flags.StringArrayVarP(&rmOptions.PodIDFiles, podIDFileFlagName, "", nil, "Read the pod ID from the file") + _ = rmCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) if registry.IsRemote() { diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index 28ee4769a..7cd5c64d9 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -31,6 +32,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, + ValidArgsFunction: common.AutocompletePods, Example: `podman pod start podID podman pod start --latest podman pod start --all`, @@ -50,7 +52,11 @@ func init() { flags := startCommand.Flags() flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") - flags.StringArrayVarP(&startOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + + podIDFileFlagName := "pod-id-file" + flags.StringArrayVarP(&startOptions.PodIDFiles, podIDFileFlagName, "", nil, "Read the pod ID from the file") + _ = startCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + validate.AddLatestFlag(startCommand, &startOptions.Latest) } diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index c5d1e7f07..79e7cd8ed 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -10,6 +10,7 @@ import ( "github.com/buger/goterm" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -33,10 +34,11 @@ var ( statsDescription = `Display the containers' resource-usage statistics of one or more running pod` // Command: podman pod _pod_ statsCmd = &cobra.Command{ - Use: "stats [options] [POD...]", - Short: "Display a live stream of resource usage statistics for the containers in one or more pods", - Long: statsDescription, - RunE: stats, + Use: "stats [options] [POD...]", + Short: "Display a live stream of resource usage statistics for the containers in one or more pods", + Long: statsDescription, + RunE: stats, + ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod stats podman pod stats a69b23034235 named-pod podman pod stats --latest @@ -53,7 +55,11 @@ 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") + + formatFlagName := "format" + flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template") + _ = statsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + 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") validate.AddLatestFlag(statsCmd, &statsOptions.Latest) diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index a2a9b0b57..d03364028 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" @@ -36,6 +37,7 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, + ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod stop mywebserverpod podman pod stop --latest podman pod stop --time 0 490eb 3557fb`, @@ -51,8 +53,15 @@ 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.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") + + timeFlagName := "time" + flags.UintVarP(&stopOptions.TimeoutCLI, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") + _ = stopCommand.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + + podIDFileFlagName := "pod-id-file" + flags.StringArrayVarP(&stopOptions.PodIDFiles, podIDFileFlagName, "", nil, "Write the pod ID to the file") + _ = stopCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + validate.AddLatestFlag(stopCommand, &stopOptions.Latest) if registry.IsRemote() { diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 0ffa724da..45ef1e7c2 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -7,6 +7,7 @@ import ( "strings" "text/tabwriter" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -23,11 +24,12 @@ var ( topOptions = entities.PodTopOptions{} topCommand = &cobra.Command{ - Use: "top [options] POD [FORMAT-DESCRIPTORS|ARGS...]", - Short: "Display the running processes of containers in a pod", - Long: topDescription, - RunE: top, - Args: cobra.ArbitraryArgs, + Use: "top [options] POD [FORMAT-DESCRIPTORS|ARGS...]", + Short: "Display the running processes of containers in a pod", + Long: topDescription, + RunE: top, + Args: cobra.ArbitraryArgs, + ValidArgsFunction: common.AutocompletePodsRunning, Example: `podman pod top podID podman pod top --latest podman pod top podID pid seccomp args %C diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go index 15b30db14..499faec37 100644 --- a/cmd/podman/pods/unpause.go +++ b/cmd/podman/pods/unpause.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -23,6 +24,9 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, + // TODO have a function which shows only pods which could be unpaused + // for now show all + ValidArgsFunction: common.AutocompletePods, Example: `podman pod unpause podID1 podID2 podman pod unpause --all podman pod unpause --latest`, diff --git a/cmd/podman/registry/remote.go b/cmd/podman/registry/remote.go index 78b820269..6e9b1fdfc 100644 --- a/cmd/podman/registry/remote.go +++ b/cmd/podman/registry/remote.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -23,7 +24,15 @@ func IsRemote() bool { fs.Usage = func() {} fs.SetInterspersed(false) fs.BoolVarP(&remoteFromCLI.Value, "remote", "r", false, "") - _ = fs.Parse(os.Args[1:]) + + // The shell completion logic will call a command called "__complete" or "__completeNoDesc" + // This command will always be the second argument + // To still parse --remote correctly in this case we have to set args offset to two in this case + start := 1 + if len(os.Args) > 1 && (os.Args[1] == cobra.ShellCompRequestCmd || os.Args[1] == cobra.ShellCompNoDescRequestCmd) { + start = 2 + } + _ = fs.Parse(os.Args[start:]) }) return podmanOptions.EngineMode == entities.TunnelMode || remoteFromCLI.Value } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index b59b8341a..0a44f5eac 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -8,7 +8,9 @@ import ( "runtime/pprof" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -21,6 +23,7 @@ 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 @@ -67,9 +70,10 @@ var ( Version: version.Version.String(), DisableFlagsInUseLine: true, } - logLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} - logLevel = "error" - useSyslog bool + + logLevel = "error" + useSyslog bool + requireCleanup = true ) func init() { @@ -102,11 +106,36 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { // TODO: Remove trace statement in podman V2.1 logrus.Debugf("Called %s.PersistentPreRunE(%s)", cmd.Name(), strings.Join(os.Args, " ")) - // Help and commands with subcommands are special cases, no need for more setup - if cmd.Name() == "help" || cmd.HasSubCommands() { + // Help, completion and commands with subcommands are special cases, no need for more setup + // Completion cmd is used to generate the shell scripts + if cmd.Name() == "help" || cmd.Name() == "completion" || cmd.HasSubCommands() { + requireCleanup = false return nil } + // Special case if command is hidden completion command ("__complete","__completeNoDesc") + // Since __completeNoDesc is an alias the cm.Name is always __complete + if cmd.Name() == cobra.ShellCompRequestCmd { + // Parse the cli arguments after the the completion cmd (always called as second argument) + // This ensures that the --url, --identity and --connection flags are properly set + compCmd, _, err := cmd.Root().Traverse(os.Args[2:]) + if err != nil { + return err + } + // If we don't complete the root cmd hide all root flags + // so they won't show up in the completions on subcommands. + if compCmd != compCmd.Root() { + compCmd.Root().Flags().VisitAll(func(flag *pflag.Flag) { + flag.Hidden = true + }) + } + // No need for further setup when completing commands with subcommands. + if compCmd.HasSubCommands() { + requireCleanup = false + return nil + } + } + cfg := registry.PodmanConfig() // --connection is not as "special" as --remote so we can wait and process it here @@ -203,8 +232,7 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error { // TODO: Remove trace statement in podman V2.1 logrus.Debugf("Called %s.PersistentPostRunE(%s)", cmd.Name(), strings.Join(os.Args, " ")) - // Help and commands with subcommands are special cases, no need for more cleanup - if cmd.Name() == "help" || cmd.HasSubCommands() { + if !requireCleanup { return nil } @@ -226,14 +254,14 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error { func loggingHook() { var found bool - for _, l := range logLevels { + for _, l := range common.LogLevels { if l == strings.ToLower(logLevel) { found = true break } } if !found { - fmt.Fprintf(os.Stderr, "Log Level %q 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(common.LogLevels, ", ")) os.Exit(1) } @@ -254,9 +282,18 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { srv, uri, ident := resolveDestination() lFlags := cmd.Flags() - lFlags.StringVarP(&opts.Engine.ActiveService, "connection", "c", srv, "Connection to use for remote Podman service") - lFlags.StringVar(&opts.URI, "url", uri, "URL to access Podman service (CONTAINER_HOST)") - lFlags.StringVar(&opts.Identity, "identity", ident, "path to SSH identity file, (CONTAINER_SSHKEY)") + + connectionFlagName := "connection" + lFlags.StringVarP(&opts.Engine.ActiveService, connectionFlagName, "c", srv, "Connection to use for remote Podman service") + _ = cmd.RegisterFlagCompletionFunc(connectionFlagName, common.AutocompleteSystemConnections) + + urlFlagName := "url" + lFlags.StringVar(&opts.URI, urlFlagName, uri, "URL to access Podman service (CONTAINER_HOST)") + _ = cmd.RegisterFlagCompletionFunc(urlFlagName, completion.AutocompleteDefault) + + identityFlagName := "identity" + lFlags.StringVar(&opts.Identity, identityFlagName, ident, "path to SSH identity file, (CONTAINER_SSHKEY)") + _ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault) lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") pFlags := cmd.PersistentFlags() @@ -266,25 +303,67 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { } opts.Remote = true } else { - pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") + cgroupManagerFlagName := "cgroup-manager" + pFlags.StringVar(&cfg.Engine.CgroupManager, cgroupManagerFlagName, cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") + _ = cmd.RegisterFlagCompletionFunc(cgroupManagerFlagName, common.AutocompleteCgroupManager) + 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") + + conmonFlagName := "conmon" + pFlags.StringVar(&opts.ConmonPath, conmonFlagName, "", "Path of the conmon binary") + _ = cmd.RegisterFlagCompletionFunc(conmonFlagName, completion.AutocompleteDefault) + + networkCmdPathFlagName := "network-cmd-path" + pFlags.StringVar(&cfg.Engine.NetworkCmdPath, networkCmdPathFlagName, cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") + _ = cmd.RegisterFlagCompletionFunc(networkCmdPathFlagName, completion.AutocompleteDefault) + + cniConfigDirFlagName := "cni-config-dir" + pFlags.StringVar(&cfg.Network.NetworkConfigDir, cniConfigDirFlagName, cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") + _ = cmd.RegisterFlagCompletionFunc(cniConfigDirFlagName, completion.AutocompleteDefault) + 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)") + + eventsBackendFlagName := "events-backend" + pFlags.StringVar(&cfg.Engine.EventsLogger, eventsBackendFlagName, cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) + _ = cmd.RegisterFlagCompletionFunc(eventsBackendFlagName, common.AutocompleteEventBackend) + + hooksDirFlagName := "hooks-dir" + pFlags.StringSliceVar(&cfg.Engine.HooksDir, hooksDirFlagName, cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") + _ = cmd.RegisterFlagCompletionFunc(hooksDirFlagName, completion.AutocompleteDefault) + 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") + + namespaceFlagName := "namespace" + pFlags.StringVar(&cfg.Engine.Namespace, namespaceFlagName, cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") + _ = cmd.RegisterFlagCompletionFunc(namespaceFlagName, completion.AutocompleteNone) + + rootFlagName := "root" + pFlags.StringVar(&cfg.Engine.StaticDir, rootFlagName, "", "Path to the root directory in which data, including images, is stored") + _ = cmd.RegisterFlagCompletionFunc(rootFlagName, completion.AutocompleteDefault) + 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") + + runrootFlagName := "runroot" + pFlags.StringVar(&opts.Runroot, runrootFlagName, "", "Path to the 'run directory' where all state information is stored") + _ = cmd.RegisterFlagCompletionFunc(runrootFlagName, completion.AutocompleteDefault) + + runtimeFlagName := "runtime" + pFlags.StringVar(&opts.RuntimePath, runtimeFlagName, "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + _ = cmd.RegisterFlagCompletionFunc(runtimeFlagName, completion.AutocompleteDefault) + // -s is deprecated due to conflict with -s on subcommands - 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") + storageDriverFlagName := "storage-driver" + pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here? + + storageOptFlagName := "storage-opt" + pFlags.StringArrayVar(&opts.StorageOpts, storageOptFlagName, []string{}, "Used to pass an option to the storage driver") + _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, 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") + _ = cmd.RegisterFlagCompletionFunc(tmpdirFlagName, completion.AutocompleteDefault) - 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)") // Hide these flags for both ABI and Tunneling @@ -303,11 +382,17 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { // Override default --help information of `--help` global flag var dummyHelp bool 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, ", "))) + + logLevelFlagName := "log-level" + pFlags.StringVar(&logLevel, logLevelFlagName, logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(common.LogLevels, ", "))) + _ = rootCmd.RegisterFlagCompletionFunc(logLevelFlagName, common.AutocompleteLogLevel) // Only create these flags for ABI connections if !registry.IsRemote() { - pFlags.StringArrayVar(&opts.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime") + runtimeflagFlagName := "runtime-flag" + pFlags.StringArrayVar(&opts.RuntimeFlags, runtimeflagFlagName, []string{}, "add global flags for the container runtime") + _ = rootCmd.RegisterFlagCompletionFunc(runtimeflagFlagName, completion.AutocompleteNone) + pFlags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") } } diff --git a/cmd/podman/shell_completion_test.go b/cmd/podman/shell_completion_test.go new file mode 100644 index 000000000..d2b500b09 --- /dev/null +++ b/cmd/podman/shell_completion_test.go @@ -0,0 +1,71 @@ +/* + The purpose of this test is to keep a consistent + and great shell autocompletion experience. + + This test ensures that each command and flag has a shell completion + function set. (except boolean, hidden and deprecated flags) + + Shell completion functions are defined in: + - "github.com/containers/podman/v2/cmd/podman/common/completion.go" + - "github.com/containers/common/pkg/completion" + and are called Autocomplete... + + To apply such function to a command use the ValidArgsFunction field. + To apply such function to a flag use cmd.RegisterFlagCompletionFunc(name,func) + + If there are any questions/problems please tag Luap99. +*/ + +package main + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func TestShellCompletionFunctions(t *testing.T) { + + rootCmd := parseCommands() + checkCommand(t, rootCmd) + +} + +func checkCommand(t *testing.T, cmd *cobra.Command) { + + if cmd.HasSubCommands() { + for _, childCmd := range cmd.Commands() { + checkCommand(t, childCmd) + } + + // if not check if completion for that command is provided + } else if cmd.ValidArgsFunction == nil && cmd.ValidArgs == nil { + t.Errorf("%s command has no shell completion function set", cmd.CommandPath()) + } + + // loop over all local flags + cmd.LocalFlags().VisitAll(func(flag *pflag.Flag) { + + // an error means that there is a completion function for this flag + err := cmd.RegisterFlagCompletionFunc(flag.Name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveDefault + }) + + switch { + case flag.Value.Type() == "bool" && err != nil: + // make sure bool flags don't have a completion function + t.Errorf(`%s --%s is a bool flag but has a shell completion function set. +You have to remove this shell completion function.`, cmd.CommandPath(), flag.Name) + return + + case flag.Value.Type() == "bool" || flag.Hidden || len(flag.Deprecated) > 0: + // skip bool, hidden and deprecated flags + return + + case err == nil: + // there is no shell completion function + t.Errorf("%s --%s flag has no shell completion function set", cmd.CommandPath(), flag.Name) + } + }) +} diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go index 0d81a64ca..b3a23bffd 100644 --- a/cmd/podman/system/connection/add.go +++ b/cmd/podman/system/connection/add.go @@ -10,6 +10,7 @@ import ( "os/user" "regexp" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/system" @@ -34,7 +35,8 @@ var ( "destination" is of the form [user@]hostname or an URI of the form ssh://[user@]hostname[:port] `, - RunE: add, + RunE: add, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman system connection add laptop server.fubar.com podman system connection add --identity ~/.ssh/dev_rsa testing ssh://root@server.fubar.com:2222 podman system connection add --identity ~/.ssh/dev_rsa --port 22 production root@server.fubar.com @@ -57,9 +59,19 @@ func init() { }) flags := addCmd.Flags() - flags.IntVarP(&cOpts.Port, "port", "p", 22, "SSH port number for destination") - flags.StringVar(&cOpts.Identity, "identity", "", "path to SSH identity file") - 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)") + + portFlagName := "port" + flags.IntVarP(&cOpts.Port, portFlagName, "p", 22, "SSH port number for destination") + _ = addCmd.RegisterFlagCompletionFunc(portFlagName, completion.AutocompleteNone) + + identityFlagName := "identity" + flags.StringVar(&cOpts.Identity, identityFlagName, "", "path to SSH identity file") + _ = addCmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault) + + socketPathFlagName := "socket-path" + flags.StringVar(&cOpts.UDSPath, socketPathFlagName, "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)") + _ = addCmd.RegisterFlagCompletionFunc(socketPathFlagName, completion.AutocompleteDefault) + flags.BoolVarP(&cOpts.Default, "default", "d", false, "Set connection to be default") } diff --git a/cmd/podman/system/connection/default.go b/cmd/podman/system/connection/default.go index eafcf37b2..e2ae6ae7f 100644 --- a/cmd/podman/system/connection/default.go +++ b/cmd/podman/system/connection/default.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/system" "github.com/containers/podman/v2/pkg/domain/entities" @@ -18,6 +19,7 @@ var ( Short: "Set named destination as default", Long: `Set named destination as default for the Podman service`, DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteSystemConnections, RunE: defaultRunE, Example: `podman system connection default testing`, } diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go index 9010ec803..b434559b4 100644 --- a/cmd/podman/system/connection/list.go +++ b/cmd/podman/system/connection/list.go @@ -5,6 +5,7 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/system" @@ -23,8 +24,9 @@ var ( DisableFlagsInUseLine: true, Example: `podman system connection list podman system connection ls`, - RunE: list, - TraverseChildren: false, + ValidArgsFunction: completion.AutocompleteNone, + RunE: list, + TraverseChildren: false, } ) diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go index 58674efb6..429325f50 100644 --- a/cmd/podman/system/connection/remove.go +++ b/cmd/podman/system/connection/remove.go @@ -2,6 +2,7 @@ package connection import ( "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/system" "github.com/containers/podman/v2/pkg/domain/entities" @@ -17,6 +18,7 @@ var ( Long: `Delete named destination from podman configuration`, Short: "Delete named destination", DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteSystemConnections, RunE: rm, Example: `podman system connection remove devl podman system connection rm devl`, diff --git a/cmd/podman/system/connection/rename.go b/cmd/podman/system/connection/rename.go index bb2ca262a..7ab94d49a 100644 --- a/cmd/podman/system/connection/rename.go +++ b/cmd/podman/system/connection/rename.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/system" "github.com/containers/podman/v2/pkg/domain/entities" @@ -19,6 +20,7 @@ var ( Short: "Rename \"old\" to \"new\"", Long: `Rename destination for the Podman service from "old" to "new"`, DisableFlagsInUseLine: true, + ValidArgsFunction: common.AutocompleteSystemConnections, RunE: rename, Example: `podman system connection rename laptop devl, podman system connection mv laptop devl`, diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index fbdf274fb..a9eab24bb 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -8,6 +8,7 @@ import ( "text/template" "time" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" @@ -24,11 +25,12 @@ var ( Show podman disk usage ` dfSystemCommand = &cobra.Command{ - Use: "df [options]", - Args: validate.NoArgs, - Short: "Show podman disk usage", - Long: dfSystemDescription, - RunE: df, + Use: "df [options]", + Args: validate.NoArgs, + Short: "Show podman disk usage", + Long: dfSystemDescription, + RunE: df, + ValidArgsFunction: completion.AutocompleteNone, } ) @@ -44,7 +46,11 @@ func init() { }) flags := dfSystemCommand.Flags() flags.BoolVarP(&dfOptions.Verbose, "verbose", "v", false, "Show detailed information on disk usage") - flags.StringVar(&dfOptions.Format, "format", "", "Pretty-print images using a Go template") + + formatFlagName := "format" + flags.StringVar(&dfOptions.Format, formatFlagName, "", "Pretty-print images using a Go template") + _ = dfSystemCommand.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) + } func df(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index 368cd41a6..224ef89f3 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -6,7 +6,9 @@ import ( "os" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/events" @@ -20,11 +22,12 @@ var ( By default, streaming mode is used, printing new events as they occur. Previous events can be listed via --since and --until.` eventsCommand = &cobra.Command{ - Use: "events [options]", - Args: validate.NoArgs, - Short: "Show podman events", - Long: eventsDescription, - RunE: eventsCmd, + Use: "events [options]", + Args: validate.NoArgs, + Short: "Show podman events", + Long: eventsDescription, + RunE: eventsCmd, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman events podman events --filter event=create podman events --format {{.Image}} @@ -43,11 +46,25 @@ func init() { Command: eventsCommand, }) flags := eventsCommand.Flags() - flags.StringArrayVar(&eventOptions.Filter, "filter", []string{}, "filter output") - flags.StringVar(&eventFormat, "format", "", "format the output using a Go template") + + filterFlagName := "filter" + flags.StringArrayVar(&eventOptions.Filter, filterFlagName, []string{}, "filter output") + _ = eventsCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteEventFilter) + + formatFlagName := "format" + flags.StringVar(&eventFormat, formatFlagName, "", "format the output using a Go template") + _ = eventsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVar(&eventOptions.Stream, "stream", true, "stream new events; for testing only") - flags.StringVar(&eventOptions.Since, "since", "", "show all events created since timestamp") - flags.StringVar(&eventOptions.Until, "until", "", "show all events until timestamp") + + sinceFlagName := "since" + flags.StringVar(&eventOptions.Since, sinceFlagName, "", "show all events created since timestamp") + _ = eventsCommand.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) + + untilFlagName := "until" + flags.StringVar(&eventOptions.Until, untilFlagName, "", "show all events until timestamp") + _ = eventsCommand.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) + _ = flags.MarkHidden("stream") } diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index dece6b37e..17aeb2940 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -5,13 +5,14 @@ import ( "os" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/ghodss/yaml" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) var ( @@ -20,21 +21,23 @@ var ( Useful for the user and when reporting issues. ` infoCommand = &cobra.Command{ - Use: "info [options]", - Args: validate.NoArgs, - Long: infoDescription, - Short: "Display podman system information", - RunE: info, - Example: `podman info`, + Use: "info [options]", + Args: validate.NoArgs, + Long: infoDescription, + Short: "Display podman system information", + RunE: info, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman info`, } systemInfoCommand = &cobra.Command{ - Args: infoCommand.Args, - Use: infoCommand.Use, - Short: infoCommand.Short, - Long: infoCommand.Long, - RunE: infoCommand.RunE, - Example: `podman system info`, + Args: infoCommand.Args, + Use: infoCommand.Use, + Short: infoCommand.Short, + Long: infoCommand.Long, + RunE: infoCommand.RunE, + ValidArgsFunction: infoCommand.ValidArgsFunction, + Example: `podman system info`, } ) @@ -48,19 +51,24 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: infoCommand, }) - infoFlags(infoCommand.Flags()) + infoFlags(infoCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: systemInfoCommand, Parent: systemCmd, }) - infoFlags(systemInfoCommand.Flags()) + infoFlags(systemInfoCommand) } -func infoFlags(flags *pflag.FlagSet) { +func infoFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&debug, "debug", "D", false, "Display additional debug information") - flags.StringVarP(&inFormat, "format", "f", "", "Change the output format to JSON or a Go template") + + formatFlagName := "format" + flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func info(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/system/migrate.go b/cmd/podman/system/migrate.go index 7870df60b..234a49e4b 100644 --- a/cmd/podman/system/migrate.go +++ b/cmd/podman/system/migrate.go @@ -6,6 +6,7 @@ import ( "fmt" "os" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -21,11 +22,12 @@ var ( ` migrateCommand = &cobra.Command{ - Use: "migrate [options]", - Args: validate.NoArgs, - Short: "Migrate containers", - Long: migrateDescription, - Run: migrate, + Use: "migrate [options]", + Args: validate.NoArgs, + Short: "Migrate containers", + Long: migrateDescription, + Run: migrate, + ValidArgsFunction: completion.AutocompleteNone, } ) @@ -41,7 +43,10 @@ func init() { }) flags := migrateCommand.Flags() - flags.StringVar(&migrateOptions.NewRuntime, "new-runtime", "", "Specify a new runtime for all containers") + + newRuntimeFlagName := "new-runtime" + flags.StringVar(&migrateOptions.NewRuntime, newRuntimeFlagName, "", "Specify a new runtime for all containers") + _ = migrateCommand.RegisterFlagCompletionFunc(newRuntimeFlagName, completion.AutocompleteNone) } func migrate(cmd *cobra.Command, args []string) { diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index a229b06b0..be0d60604 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -25,12 +26,13 @@ var ( `) pruneCommand = &cobra.Command{ - Use: "prune [options]", - Short: "Remove unused data", - Args: validate.NoArgs, - Long: pruneDescription, - RunE: prune, - Example: `podman system prune`, + Use: "prune [options]", + Short: "Remove unused data", + Args: validate.NoArgs, + Long: pruneDescription, + RunE: prune, + ValidArgsFunction: completion.AutocompleteNone, + Example: `podman system prune`, } force bool ) diff --git a/cmd/podman/system/renumber.go b/cmd/podman/system/renumber.go index 1631ab093..b1683395f 100644 --- a/cmd/podman/system/renumber.go +++ b/cmd/podman/system/renumber.go @@ -6,6 +6,7 @@ import ( "fmt" "os" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -28,6 +29,7 @@ var ( Short: "Migrate lock numbers", Long: renumberDescription, Run: renumber, + ValidArgsFunction: completion.AutocompleteNone, } ) diff --git a/cmd/podman/system/reset.go b/cmd/podman/system/reset.go index 0b04c6ee0..d38a1a427 100644 --- a/cmd/podman/system/reset.go +++ b/cmd/podman/system/reset.go @@ -8,6 +8,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/pkg/domain/entities" @@ -22,11 +23,12 @@ var ( All containers will be stopped and removed, and all images, volumes and container content will be removed. ` systemResetCommand = &cobra.Command{ - Use: "reset [options]", - Args: validate.NoArgs, - Short: "Reset podman storage", - Long: systemResetDescription, - Run: reset, + Use: "reset [options]", + Args: validate.NoArgs, + Short: "Reset podman storage", + Long: systemResetDescription, + Run: reset, + ValidArgsFunction: completion.AutocompleteNone, } forceFlag bool diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 0476c632d..78062d135 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -9,6 +9,7 @@ import ( "syscall" "time" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" @@ -26,12 +27,13 @@ Enable a listening service for API access to Podman commands. ` srvCmd = &cobra.Command{ - Use: "service [options] [URI]", - Args: cobra.MaximumNArgs(1), - Short: "Run API service", - Long: srvDescription, - RunE: service, - Example: `podman system service --time=0 unix:///tmp/podman.sock`, + Use: "service [options] [URI]", + Args: cobra.MaximumNArgs(1), + Short: "Run API service", + Long: srvDescription, + RunE: service, + ValidArgsFunction: completion.AutocompleteDefault, + Example: `podman system service --time=0 unix:///tmp/podman.sock`, } srvArgs = struct { @@ -48,7 +50,11 @@ func init() { }) flags := srvCmd.Flags() - flags.Int64VarP(&srvArgs.Timeout, "time", "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") + + timeFlagName := "time" + flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") + _ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + flags.BoolVar(&srvArgs.Varlink, "varlink", false, "Use legacy varlink service instead of REST. Unit of --time changes from seconds to milliseconds.") _ = flags.MarkDeprecated("varlink", "valink API is deprecated.") diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go index 2d0113779..437cf7b2e 100644 --- a/cmd/podman/system/unshare.go +++ b/cmd/podman/system/unshare.go @@ -3,6 +3,7 @@ package system import ( "os" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" @@ -18,6 +19,7 @@ var ( Short: "Run a command in a modified user namespace", Long: unshareDescription, RunE: unshare, + ValidArgsFunction: completion.AutocompleteDefault, Example: `podman unshare id podman unshare cat /proc/self/uid_map, podman unshare podman-script.sh`, diff --git a/cmd/podman/system/varlink.go b/cmd/podman/system/varlink.go index 89669d51a..363ac9cca 100644 --- a/cmd/podman/system/varlink.go +++ b/cmd/podman/system/varlink.go @@ -5,6 +5,7 @@ package system import ( "time" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -16,13 +17,14 @@ var ( Tools speaking varlink protocol can remotely manage pods, containers and images. ` varlinkCmd = &cobra.Command{ - Use: "varlink [options] [URI]", - Args: cobra.MinimumNArgs(1), - Short: "Run varlink interface", - Long: varlinkDescription, - RunE: varlinkE, - Deprecated: "Please see 'podman system service' for RESTful APIs", - Hidden: true, + Use: "varlink [options] [URI]", + Args: cobra.MinimumNArgs(1), + Short: "Run varlink interface", + Long: varlinkDescription, + RunE: varlinkE, + ValidArgsFunction: completion.AutocompleteDefault, + Deprecated: "Please see 'podman system service' for RESTful APIs", + Hidden: true, Example: `podman varlink unix:/run/podman/io.podman podman varlink --time 5000 unix:/run/podman/io.podman`, } @@ -37,7 +39,11 @@ func init() { Command: varlinkCmd, }) flags := varlinkCmd.Flags() - flags.Int64VarP(&varlinkArgs.Timeout, "time", "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout") + + timeFlagName := "time" + flags.Int64VarP(&varlinkArgs.Timeout, timeFlagName, "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout") + _ = varlinkCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + flags.SetNormalizeFunc(aliasTimeoutFlag) } diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index b790a7511..cb7206b8a 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -8,7 +8,9 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" "github.com/containers/podman/v2/libpod/define" @@ -18,10 +20,11 @@ import ( var ( versionCommand = &cobra.Command{ - Use: "version [options]", - Args: validate.NoArgs, - Short: "Display the Podman Version Information", - RunE: version, + Use: "version [options]", + Args: validate.NoArgs, + Short: "Display the Podman Version Information", + RunE: version, + ValidArgsFunction: completion.AutocompleteNone, } versionFormat string ) @@ -32,7 +35,10 @@ func init() { Command: versionCommand, }) flags := versionCommand.Flags() - flags.StringVarP(&versionFormat, "format", "f", "", "Change the output format to JSON or a Go template") + + formatFlagName := "format" + flags.StringVarP(&versionFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") + _ = versionCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func version(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go index a54530183..8de343a24 100644 --- a/cmd/podman/volumes/create.go +++ b/cmd/podman/volumes/create.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" @@ -15,10 +16,11 @@ var ( createDescription = `If using the default driver, "local", the volume will be created on the host in the volumes directory under container storage.` createCommand = &cobra.Command{ - Use: "create [options] [NAME]", - Short: "Create a new volume", - Long: createDescription, - RunE: create, + Use: "create [options] [NAME]", + Short: "Create a new volume", + Long: createDescription, + RunE: create, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman volume create myvol podman volume create podman volume create --label foo=bar myvol`, @@ -40,9 +42,18 @@ func init() { Parent: volumeCmd, }) flags := createCommand.Flags() - flags.StringVar(&createOpts.Driver, "driver", "local", "Specify volume driver name") - flags.StringSliceVarP(&opts.Label, "label", "l", []string{}, "Set metadata for a volume (default [])") - flags.StringArrayVarP(&opts.Opts, "opt", "o", []string{}, "Set driver specific options (default [])") + + driverFlagName := "driver" + flags.StringVar(&createOpts.Driver, driverFlagName, "local", "Specify volume driver name") + _ = createCommand.RegisterFlagCompletionFunc(driverFlagName, completion.AutocompleteNone) + + labelFlagName := "label" + flags.StringSliceVarP(&opts.Label, labelFlagName, "l", []string{}, "Set metadata for a volume (default [])") + _ = createCommand.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + + optFlagName := "opt" + flags.StringArrayVarP(&opts.Opts, optFlagName, "o", []string{}, "Set driver specific options (default [])") + _ = createCommand.RegisterFlagCompletionFunc(optFlagName, completion.AutocompleteNone) } func create(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index c6edcf809..b5094224c 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -1,6 +1,7 @@ package volumes import ( + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" @@ -13,10 +14,11 @@ var ( Use a Go template to change the format from JSON.` inspectCommand = &cobra.Command{ - Use: "inspect [options] VOLUME [VOLUME...]", - Short: "Display detailed information on one or more volumes", - Long: volumeInspectDescription, - RunE: volumeInspect, + Use: "inspect [options] VOLUME [VOLUME...]", + Short: "Display detailed information on one or more volumes", + Long: volumeInspectDescription, + RunE: volumeInspect, + ValidArgsFunction: common.AutocompleteVolumes, Example: `podman volume inspect myvol podman volume inspect --all podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol`, @@ -36,7 +38,10 @@ func init() { inspectOpts = new(entities.InspectOptions) flags := inspectCommand.Flags() flags.BoolVarP(&inspectOpts.All, "all", "a", false, "Inspect all volumes") - flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format volume output using Go template") + + formatFlagName := "format" + flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format volume output using Go template") + _ = inspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) } func volumeInspect(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index ce0b7997d..5548c9c1b 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -8,7 +8,9 @@ import ( "text/tabwriter" "text/template" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/parse" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" @@ -24,12 +26,13 @@ podman volume ls List all available volumes. The output of the volumes can be filtered and the output format can be changed to JSON or a user specified Go template.` lsCommand = &cobra.Command{ - Use: "ls [options]", - Aliases: []string{"list"}, - Args: validate.NoArgs, - Short: "List volumes", - Long: volumeLsDescription, - RunE: list, + Use: "ls [options]", + Aliases: []string{"list"}, + Args: validate.NoArgs, + Short: "List volumes", + Long: volumeLsDescription, + RunE: list, + ValidArgsFunction: completion.AutocompleteNone, } ) @@ -50,8 +53,15 @@ func init() { Parent: volumeCmd, }) flags := lsCommand.Flags() - flags.StringSliceVarP(&cliOpts.Filter, "filter", "f", []string{}, "Filter volume output") - flags.StringVar(&cliOpts.Format, "format", "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template") + + filterFlagName := "filter" + flags.StringSliceVarP(&cliOpts.Filter, filterFlagName, "f", []string{}, "Filter volume output") + _ = lsCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + + formatFlagName := "format" + flags.StringVar(&cliOpts.Format, formatFlagName, "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template") + _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + flags.BoolVarP(&cliOpts.Quiet, "quiet", "q", false, "Print volume output in quiet mode") } diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go index 79e6f1a54..2f58b668f 100644 --- a/cmd/podman/volumes/prune.go +++ b/cmd/podman/volumes/prune.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/cmd/podman/validate" @@ -21,11 +22,12 @@ var ( The command prompts for confirmation which can be overridden with the --force flag. Note all data will be destroyed.` pruneCommand = &cobra.Command{ - Use: "prune [options]", - Args: validate.NoArgs, - Short: "Remove all unused volumes", - Long: volumePruneDescription, - RunE: prune, + Use: "prune [options]", + Args: validate.NoArgs, + Short: "Remove all unused volumes", + Long: volumePruneDescription, + RunE: prune, + ValidArgsFunction: completion.AutocompleteNone, } ) diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index 4026764ac..49f7b619e 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/containers/podman/v2/cmd/podman/common" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/libpod/define" @@ -18,11 +19,12 @@ var ( By default only volumes that are not being used by any containers will be removed. To remove the volumes anyways, use the --force flag.` rmCommand = &cobra.Command{ - Use: "rm [options] VOLUME [VOLUME...]", - Aliases: []string{"remove"}, - Short: "Remove one or more volumes", - Long: volumeRmDescription, - RunE: rm, + Use: "rm [options] VOLUME [VOLUME...]", + Aliases: []string{"remove"}, + Short: "Remove one or more volumes", + Long: volumeRmDescription, + RunE: rm, + ValidArgsFunction: common.AutocompleteVolumes, Example: `podman volume rm myvol1 myvol2 podman volume rm --all podman volume rm --force myvol`, diff --git a/docs/source/markdown/podman-completion.1.md b/docs/source/markdown/podman-completion.1.md new file mode 100644 index 000000000..43409e282 --- /dev/null +++ b/docs/source/markdown/podman-completion.1.md @@ -0,0 +1,56 @@ +% podman-completion(1) + +## NAME +podman\-completion - Generate shell completion scripts + +## SYNOPSIS +**podman completion** [*options*] *bash*|*zsh*|*fish* + +## DESCRIPTION +The completion command allows you to generate shell completion scripts. Supported shells are **bash**, **zsh** and **fish**. + +These script are used by the shell to provide suggestions and complete commands when you are typing the command and press [TAB]. + +Usually these scripts are automatically installed via rpm/deb packages. + +## OPTIONS +**--file**, **-f** + +Write the generated output to file. + +**--no-desc** + +Do not provide description in the completions. + +## Installation + +### BASH +Make sure you have `bash-completion` installed on your system. + +To load the completion script into your current session run: +`source <(podman completion bash)` + +To make it available in all your bash sessions run: +`podman completion bash -f /etc/bash_completion.d/podman` + + +### ZSH +If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once: +`echo "autoload -U compinit; compinit" >> ~/.zshrc` + +To make it available in all your zsh sessions run: +`podman completion zsh -f "${fpath[1]}/_podman"` + +Once you reload the shell the autocompletion should be working. + + +### FISH +To load the completion script into your current session run: +`podman completion fish | source` + +To make it available in all your fish sessions run: +`podman completion fish -f ~/.config/fish/completions/podman.fish` + + +## SEE ALSO +[podman(1)](podman.1.md) diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 993f285ed..87867bf35 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -205,6 +205,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-auto-update(1)](podman-auto-update.1.md) | Auto update containers according to their auto-update policy | | [podman-build(1)](podman-build.1.md) | Build a container image using a Containerfile. | | [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. | +| [podman-completion(1)](podman-completion.1.md) | Generate shell completion scripts | | [podman-container(1)](podman-container.1.md) | Manage containers. | | [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. | | [podman-create(1)](podman-create.1.md) | Create a new container. | diff --git a/hack/podman-commands.sh b/hack/podman-commands.sh index fd4ff2501..0455287d4 100755 --- a/hack/podman-commands.sh +++ b/hack/podman-commands.sh @@ -25,6 +25,12 @@ function podman_commands() { $PODMAN help "$@" |\ awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' |\ grep . + + # Special case: podman-completion is a hidden command + # it does not show in podman help so add it here + if [[ -z "$@" ]]; then + echo "completion" + fi } # Read a list of subcommands from a command's metadoc diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages index 082cc63f2..0c4d9d6e7 100755 --- a/hack/xref-helpmsgs-manpages +++ b/hack/xref-helpmsgs-manpages @@ -181,6 +181,9 @@ sub xref_by_man { next if $k eq 'varlink'; next if "@subcommand" eq 'system' && $k eq 'service'; + # Special case: podman completion is a hidden command + next if $k eq 'completion'; + warn "$ME: podman @subcommand: $k in $man, but not --help\n"; ++$Errs; } diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 1fc4479ff..52a214883 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -11,13 +11,13 @@ import ( // is set to the unit's (unique) name. const EnvVariable = "PODMAN_SYSTEMD_UNIT" -// restartPolicies includes all valid restart policies to be used in a unit +// RestartPolicies includes all valid restart policies to be used in a unit // file. -var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} +var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} // validateRestartPolicy checks that the user-provided policy is valid. func validateRestartPolicy(restart string) error { - for _, i := range restartPolicies { + for _, i := range RestartPolicies { if i == restart { return nil } -- cgit v1.2.3-54-g00ecf