diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/common/completion.go | 442 | ||||
-rw-r--r-- | cmd/podman/common/create.go | 18 | ||||
-rw-r--r-- | cmd/podman/common/create_opts.go | 73 | ||||
-rw-r--r-- | cmd/podman/common/createparse.go | 2 | ||||
-rw-r--r-- | cmd/podman/common/netflags.go | 34 | ||||
-rw-r--r-- | cmd/podman/common/volumes.go | 102 | ||||
-rw-r--r-- | cmd/podman/containers/ps.go | 23 | ||||
-rw-r--r-- | cmd/podman/containers/top.go | 8 | ||||
-rw-r--r-- | cmd/podman/images/list.go | 3 | ||||
-rw-r--r-- | cmd/podman/networks/connect.go | 2 | ||||
-rw-r--r-- | cmd/podman/networks/disconnect.go | 2 | ||||
-rw-r--r-- | cmd/podman/networks/list.go | 2 | ||||
-rw-r--r-- | cmd/podman/networks/rm.go | 1 | ||||
-rw-r--r-- | cmd/podman/pods/create.go | 30 | ||||
-rw-r--r-- | cmd/podman/pods/ps.go | 3 | ||||
-rw-r--r-- | cmd/podman/root.go | 52 | ||||
-rw-r--r-- | cmd/podman/volumes/list.go | 2 |
17 files changed, 453 insertions, 346 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 00123f9e6..6723cbf62 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/registries" + "github.com/containers/podman/v2/pkg/rootless" systemdGen "github.com/containers/podman/v2/pkg/systemd/generate" "github.com/spf13/cobra" ) @@ -23,104 +24,132 @@ var ( LogLevels = []string{"debug", "info", "warn", "error", "fatal", "panic"} ) -func getContainers(toComplete string, statuses ...string) ([]string, cobra.ShellCompDirective) { +type completeType int + +const ( + // complete names and IDs after two chars + completeDefault completeType = iota + // only complete IDs + completeIDs + // only complete Names + completeNames +) + +type keyValueCompletion map[string]func(s string) ([]string, cobra.ShellCompDirective) + +func setupContainerEngine(cmd *cobra.Command) (entities.ContainerEngine, error) { + containerEngine, err := registry.NewContainerEngine(cmd, []string{}) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, err + } + if !registry.IsRemote() && rootless.IsRootless() { + err := containerEngine.SetupRootless(registry.Context(), cmd) + if err != nil { + return nil, err + } + } + return containerEngine, nil +} + +func setupImageEngine(cmd *cobra.Command) (entities.ImageEngine, error) { + imageEngine, err := registry.NewImageEngine(cmd, []string{}) + if err != nil { + return nil, err + } + // we also need to set up the container engine since this + // is required to setup the rootless namespace + if _, err = setupContainerEngine(cmd); err != nil { + return nil, err + } + return imageEngine, nil +} + +func getContainers(cmd *cobra.Command, toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOpts := entities.ContainerListOptions{ Filters: make(map[string][]string), } listOpts.All = true listOpts.Pod = true + if len(statuses) > 0 { + listOpts.Filters["status"] = statuses + } - // TODO: The api doesn't handle several different statuses correct see: - // https://github.com/containers/podman/issues/8344 - // Instead of looping over the statuses we should be able to set - // listOpts.Filters["status"] = statuses - - var containers []entities.ListContainer - var err error - if len(statuses) == 0 { - containers, err = registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts) - if err != nil { - cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError - } - } else { - for _, s := range statuses { - listOpts.Filters["status"] = []string{s} - res, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts) - if err != nil { - cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError - } - containers = append(containers, res...) - } + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + containers, err := engine.ContainerList(registry.GetContext(), listOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } for _, c := range containers { - // include ids in suggestions if more then 2 chars are typed - if len(toComplete) > 1 && strings.HasPrefix(c.ID, toComplete) { + // include ids in suggestions if cType == completeIDs or + // more then 2 chars are typed and cType == completeDefault + if ((len(toComplete) > 1 && cType == completeDefault) || + cType == completeIDs) && strings.HasPrefix(c.ID, toComplete) { suggestions = append(suggestions, c.ID[0:12]+"\t"+c.PodName) } // include name in suggestions - if strings.HasPrefix(c.Names[0], toComplete) { + if cType != completeIDs && strings.HasPrefix(c.Names[0], toComplete) { suggestions = append(suggestions, c.Names[0]+"\t"+c.PodName) } } return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getPods(toComplete string, statuses ...string) ([]string, cobra.ShellCompDirective) { +func getPods(cmd *cobra.Command, toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOpts := entities.PodPSOptions{ Filters: make(map[string][]string), } + if len(statuses) > 0 { + listOpts.Filters["status"] = statuses + } - // TODO: The api doesn't handle several different statuses correct see: - // https://github.com/containers/podman/issues/8344 - // Instead of looping over the statuses we should be able to set - // listOpts.Filters["status"] = statuses - - var pods []*entities.ListPodsReport - var err error - if len(statuses) == 0 { - pods, err = registry.ContainerEngine().PodPs(registry.GetContext(), listOpts) - if err != nil { - cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError - } - } else { - for _, s := range statuses { - listOpts.Filters["status"] = []string{s} - res, err := registry.ContainerEngine().PodPs(registry.GetContext(), listOpts) - if err != nil { - cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError - } - pods = append(pods, res...) - } + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + pods, err := engine.PodPs(registry.GetContext(), listOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } for _, pod := range pods { - // include ids in suggestions if more then 2 chars are typed - if len(toComplete) > 1 && strings.HasPrefix(pod.Id, toComplete) { + // include ids in suggestions if cType == completeIDs or + // more then 2 chars are typed and cType == completeDefault + if ((len(toComplete) > 1 && cType == completeDefault) || + cType == completeIDs) && strings.HasPrefix(pod.Id, toComplete) { suggestions = append(suggestions, pod.Id[0:12]) } // include name in suggestions - if strings.HasPrefix(pod.Name, toComplete) { + if cType != completeIDs && strings.HasPrefix(pod.Name, toComplete) { suggestions = append(suggestions, pod.Name) } } return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getVolumes(toComplete string) ([]string, cobra.ShellCompDirective) { +func getVolumes(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} lsOpts := entities.VolumeListOptions{} - volumes, err := registry.ContainerEngine().VolumeList(registry.GetContext(), lsOpts) + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + volumes, err := engine.VolumeList(registry.GetContext(), lsOpts) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for _, v := range volumes { @@ -131,14 +160,19 @@ func getVolumes(toComplete string) ([]string, cobra.ShellCompDirective) { return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getImages(toComplete string) ([]string, cobra.ShellCompDirective) { +func getImages(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOptions := entities.ImageListOptions{} - images, err := registry.ImageEngine().List(registry.GetContext(), listOptions) + engine, err := setupImageEngine(cmd) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp + } + images, err := engine.List(registry.GetContext(), listOptions) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } for _, image := range images { @@ -181,25 +215,30 @@ func getRegistries() ([]string, cobra.ShellCompDirective) { regs, err := registries.GetRegistries() if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } return regs, cobra.ShellCompDirectiveNoFileComp } -func getNetworks(toComplete string) ([]string, cobra.ShellCompDirective) { +func getNetworks(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} - networkListOptions := entities.NetworkListOptions{ - Filter: "name=" + toComplete, - } + networkListOptions := entities.NetworkListOptions{} - networks, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions) + engine, err := setupContainerEngine(cmd) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp + } + networks, err := engine.NetworkList(registry.Context(), networkListOptions) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } - for _, network := range networks { - suggestions = append(suggestions, network.Name) + for _, n := range networks { + if strings.HasPrefix(n.Name, toComplete) { + suggestions = append(suggestions, n.Name) + } } return suggestions, cobra.ShellCompDirectiveNoFileComp } @@ -244,6 +283,36 @@ func validCurrentCmdLine(cmd *cobra.Command, args []string, toComplete string) b return true } +func prefixSlice(pre string, slice []string) []string { + for i := range slice { + slice[i] = pre + slice[i] + } + return slice +} + +func completeKeyValues(toComplete string, k keyValueCompletion) ([]string, cobra.ShellCompDirective) { + suggestions := make([]string, 0, len(k)) + directive := cobra.ShellCompDirectiveNoFileComp + for key, getComps := range k { + if strings.HasPrefix(toComplete, key) { + if getComps != nil { + suggestions, dir := getComps(toComplete[len(key):]) + return prefixSlice(key, suggestions), dir + } + return nil, cobra.ShellCompDirectiveNoFileComp + } + if strings.HasPrefix(key, toComplete) { + suggestions = append(suggestions, key) + latKey := key[len(key)-1:] + if latKey == "=" || latKey == ":" { + // make sure we don't add a space after ':' or '=' + directive = cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp + } + } + } + return suggestions, directive +} + /* Autocomplete Functions for cobra ValidArgsFunction */ // AutocompleteContainers - Autocomplete all container names. @@ -251,7 +320,7 @@ func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete) + return getContainers(cmd, toComplete, completeDefault) } // AutocompleteContainersCreated - Autocomplete only created container names. @@ -259,7 +328,7 @@ func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, "created") + return getContainers(cmd, toComplete, completeDefault, "created") } // AutocompleteContainersExited - Autocomplete only exited container names. @@ -267,7 +336,7 @@ func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, "exited") + return getContainers(cmd, toComplete, completeDefault, "exited") } // AutocompleteContainersPaused - Autocomplete only paused container names. @@ -275,7 +344,7 @@ func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, "paused") + return getContainers(cmd, toComplete, completeDefault, "paused") } // AutocompleteContainersRunning - Autocomplete only running container names. @@ -283,7 +352,7 @@ func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, "running") + return getContainers(cmd, toComplete, completeDefault, "running") } // AutocompleteContainersStartable - Autocomplete only created and exited container names. @@ -291,7 +360,7 @@ func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, "created", "exited") + return getContainers(cmd, toComplete, completeDefault, "created", "exited") } // AutocompletePods - Autocomplete all pod names. @@ -299,7 +368,7 @@ func AutocompletePods(cmd *cobra.Command, args []string, toComplete string) ([]s if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getPods(toComplete) + return getPods(cmd, toComplete, completeDefault) } // AutocompletePodsRunning - Autocomplete only running pod names. @@ -308,7 +377,7 @@ func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete strin if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getPods(toComplete, "running", "degraded") + return getPods(cmd, toComplete, completeDefault, "running", "degraded") } // AutocompleteContainersAndPods - Autocomplete container names and pod names. @@ -316,8 +385,8 @@ func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete) - pods, _ := getPods(toComplete) + containers, _ := getContainers(cmd, toComplete, completeDefault) + pods, _ := getPods(cmd, toComplete, completeDefault) return append(containers, pods...), cobra.ShellCompDirectiveNoFileComp } @@ -326,8 +395,8 @@ func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete) - images, _ := getImages(toComplete) + containers, _ := getContainers(cmd, toComplete, completeDefault) + images, _ := getImages(cmd, toComplete) return append(containers, images...), cobra.ShellCompDirectiveNoFileComp } @@ -336,7 +405,7 @@ func AutocompleteVolumes(cmd *cobra.Command, args []string, toComplete string) ( if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getVolumes(toComplete) + return getVolumes(cmd, toComplete) } // AutocompleteImages - Autocomplete images. @@ -344,7 +413,7 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([ if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getImages(toComplete) + return getImages(cmd, toComplete) } // AutocompleteCreateRun - Autocomplete only the fist argument as image and then do file completion. @@ -353,7 +422,7 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 1 { - return getImages(toComplete) + return getImages(cmd, toComplete) } // TODO: add path completion for files in the image return nil, cobra.ShellCompDirectiveDefault @@ -372,7 +441,7 @@ func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string) if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getNetworks(toComplete) + return getNetworks(cmd, toComplete) } // AutocompleteCpCommand - Autocomplete podman cp command args. @@ -381,7 +450,7 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 2 { - containers, _ := getContainers(toComplete) + containers, _ := getContainers(cmd, toComplete, completeDefault) for _, container := range containers { // TODO: Add path completion for inside the container if possible if strings.HasPrefix(container, toComplete) { @@ -395,6 +464,18 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } +// AutocompleteNetworkConnectCmd - Autocomplete podman network connect/disconnect command args. +func AutocompleteNetworkConnectCmd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return getNetworks(cmd, toComplete) + } + if len(args) == 1 { + return getContainers(cmd, toComplete, completeDefault) + } + // 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) { if !validCurrentCmdLine(cmd, args, toComplete) { @@ -404,7 +485,7 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete cfg, err := config.ReadCustomConfig() if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for k, v := range cfg.Engine.ServiceDestinations { @@ -448,40 +529,20 @@ func AutocompleteCreateAttach(cmd *cobra.Command, args []string, toComplete stri // 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(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 + kv := keyValueCompletion{ + "container:": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "ns:": func(s string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveDefault }, + "host": nil, + "private": nil, } - return namespacesOptions, cobra.ShellCompDirectiveNoFileComp + return completeKeyValues(toComplete, kv) } // 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") - } + results = append(results, "auto", "keep-id") return results, directive } @@ -535,29 +596,18 @@ func AutocompleteRestartOption(cmd *cobra.Command, args []string, toComplete str // 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 + kv := keyValueCompletion{ + "apparmor=": nil, + "no-new-privileges": nil, + "seccomp=": func(s string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveDefault }, + "label=": func(s string) ([]string, cobra.ShellCompDirective) { + if strings.HasPrefix(s, "d") { + return []string{"disable"}, cobra.ShellCompDirectiveNoFileComp + } + return []string{"user:", "role:", "type:", "level:", "filetype:", "disable"}, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp + }, } - return SecurityOptions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace + return completeKeyValues(toComplete, kv) } // AutocompleteStopSignal - Autocomplete stop signal options. @@ -583,7 +633,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) // but at this point we don't know the image. file, err := os.Open("/etc/group") if err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } defer file.Close() @@ -599,7 +650,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) } } if err = scanner.Err(); err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } return groups, cobra.ShellCompDirectiveNoFileComp } @@ -608,7 +660,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) // but at this point we don't know the image. file, err := os.Open("/etc/passwd") if err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } defer file.Close() @@ -623,7 +676,7 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) } } if err = scanner.Err(); err != nil { - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } return users, cobra.ShellCompDirectiveNoSpace } @@ -639,7 +692,7 @@ func AutocompleteMountFlag(cmd *cobra.Command, args []string, toComplete string) // AutocompleteVolumeFlag - Autocomplete volume flag options. // -> volumes and paths func AutocompleteVolumeFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - volumes, _ := getVolumes(toComplete) + volumes, _ := getVolumes(cmd, toComplete) directive := cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveDefault if strings.Contains(toComplete, ":") { // add space after second path @@ -657,8 +710,22 @@ func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string // 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 + eventTypes := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"attach", "checkpoint", "cleanup", "commit", "create", "exec", + "export", "import", "init", "kill", "mount", "pause", "prune", "remove", + "restart", "restore", "start", "stop", "sync", "unmount", "unpause", + "pull", "push", "save", "tag", "untag", "refresh", "renumber", + }, cobra.ShellCompDirectiveNoFileComp + } + kv := keyValueCompletion{ + "container=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "image=": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, + "pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) }, + "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "event=": eventTypes, + "type=": eventTypes, + } + return completeKeyValues(toComplete, kv) } // AutocompleteSystemdRestartOptions - Autocomplete systemd restart options. @@ -763,3 +830,92 @@ func AutocompleteSDNotify(cmd *cobra.Command, args []string, toComplete string) types := []string{"container", "conmon", "ignore"} return types, cobra.ShellCompDirectiveNoFileComp } + +var containerStatuses = []string{"created", "running", "paused", "stopped", "exited", "unknown"} + +// AutocompletePsFilters - Autocomplete ps filter options. +func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + kv := keyValueCompletion{ + "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) }, + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) }, + "status=": func(_ string) ([]string, cobra.ShellCompDirective) { + return containerStatuses, cobra.ShellCompDirectiveNoFileComp + }, + "ancestor": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, + "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "health=": func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{define.HealthCheckHealthy, + define.HealthCheckUnhealthy}, cobra.ShellCompDirectiveNoFileComp + }, + "label=": nil, + "exited=": nil, + "until=": nil, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompletePodPsFilters - Autocomplete pod ps filter options. +func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + kv := keyValueCompletion{ + "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeIDs) }, + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeNames) }, + "status=": func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"stopped", "running", + "paused", "exited", "dead", "created", "degraded"}, cobra.ShellCompDirectiveNoFileComp + }, + "ctr-ids=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) }, + "ctr-names=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) }, + "ctr-number=": nil, + "ctr-status=": func(_ string) ([]string, cobra.ShellCompDirective) { + return containerStatuses, cobra.ShellCompDirectiveNoFileComp + }, + "label=": nil, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompleteImageFilters - Autocomplete image ls --filter options. +func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + getBool := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp + } + getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) } + kv := keyValueCompletion{ + "before=": getImg, + "since=": getImg, + "label=": nil, + "reference=": nil, + "dangling=": getBool, + "readonly=": getBool, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompleteNetworkFilters - Autocomplete network ls --filter options. +func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + kv := keyValueCompletion{ + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) }, + "plugin=": nil, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompleteVolumeFilters - Autocomplete volume ls --filter options. +func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + local := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"local"}, cobra.ShellCompDirectiveNoFileComp + } + kv := keyValueCompletion{ + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "driver=": local, + "scope=": local, + "label=": nil, + "opt=": nil, + "dangling=": func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp + }, + } + return completeKeyValues(toComplete, kv) +} diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index ab3a984f0..599b430ea 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -84,7 +84,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { cgroupsFlagName := "cgroups" createFlags.StringVar( &cf.CGroupsMode, - cgroupsFlagName, containerConfig.Cgroups(), + cgroupsFlagName, cgroupConfig(), `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, ) _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) @@ -180,7 +180,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { deviceFlagName := "device" createFlags.StringSliceVar( &cf.Devices, - deviceFlagName, containerConfig.Devices(), + deviceFlagName, devices(), fmt.Sprintf("Add a host device to the container"), ) _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) @@ -238,7 +238,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { envFlagName := "env" createFlags.StringArrayP( - envFlagName, "e", containerConfig.Env(), + envFlagName, "e", env(), "Set environment variables in container", ) _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) @@ -357,7 +357,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { initPathFlagName := "init-path" createFlags.StringVar( &cf.InitPath, - initPathFlagName, containerConfig.InitPath(), + initPathFlagName, 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"), ) @@ -508,7 +508,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { pidsLimitFlagName := "pids-limit" createFlags.Int64( - pidsLimitFlagName, containerConfig.PidsLimit(), + pidsLimitFlagName, pidsLimit(), "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", ) _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) @@ -543,7 +543,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { pullFlagName := "pull" createFlags.StringVar( &cf.Pull, - pullFlagName, containerConfig.Engine.PullPolicy, + pullFlagName, policy(), `Pull image before creating ("always"|"missing"|"never")`, ) _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) @@ -606,7 +606,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { shmSizeFlagName := "shm-size" createFlags.String( - shmSizeFlagName, containerConfig.ShmSize(), + shmSizeFlagName, shmSize(), "Size of /dev/shm "+sizeWithUnitFormat, ) _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) @@ -715,7 +715,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ulimitFlagName := "ulimit" createFlags.StringSliceVar( &cf.Ulimit, - ulimitFlagName, containerConfig.Ulimits(), + ulimitFlagName, ulimits(), "Ulimit options", ) _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) @@ -753,7 +753,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { volumeFlagName := "volume" createFlags.StringArrayVarP( &cf.Volume, - volumeFlagName, "v", containerConfig.Volumes(), + volumeFlagName, "v", volumes(), "Bind mount a volume into the container", ) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 4b52663c3..dc3202c7f 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" @@ -136,7 +137,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup aliases []string capAdd []string cappDrop []string - entrypoint string + entrypoint *string init bool specPorts []specgen.PortMapping ) @@ -180,13 +181,14 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup // marshall it to json; otherwise it should just be the string // value if len(cc.Config.Entrypoint) > 0 { - entrypoint = cc.Config.Entrypoint[0] + entrypoint = &cc.Config.Entrypoint[0] if len(cc.Config.Entrypoint) > 1 { b, err := json.Marshal(cc.Config.Entrypoint) if err != nil { return nil, nil, err } - entrypoint = string(b) + var jsonString = string(b) + entrypoint = &jsonString } } @@ -321,7 +323,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup DeviceReadIOPs: readIops, DeviceWriteBPs: writeBps, DeviceWriteIOPs: writeIops, - Entrypoint: &entrypoint, + Entrypoint: entrypoint, Env: cc.Config.Env, Expose: expose, GroupAdd: cc.HostConfig.GroupAdd, @@ -440,3 +442,66 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup cmd = append(cmd, cc.Config.Cmd...) return &cliOpts, cmd, nil } + +func ulimits() []string { + if !registry.IsRemote() { + return containerConfig.Ulimits() + } + return nil +} + +func cgroupConfig() string { + if !registry.IsRemote() { + return containerConfig.Cgroups() + } + return "" +} + +func devices() []string { + if !registry.IsRemote() { + return containerConfig.Devices() + } + return nil +} + +func env() []string { + if !registry.IsRemote() { + return containerConfig.Env() + } + return nil +} + +func initPath() string { + if !registry.IsRemote() { + return containerConfig.InitPath() + } + return "" +} + +func pidsLimit() int64 { + if !registry.IsRemote() { + return containerConfig.PidsLimit() + } + return -1 +} + +func policy() string { + if !registry.IsRemote() { + return containerConfig.Engine.PullPolicy + } + return "" +} + +func shmSize() string { + if !registry.IsRemote() { + return containerConfig.ShmSize() + } + return "" +} + +func volumes() []string { + if !registry.IsRemote() { + return containerConfig.Volumes() + } + return nil +} diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go index 09ee5aa0c..3a69f11b6 100644 --- a/cmd/podman/common/createparse.go +++ b/cmd/podman/common/createparse.go @@ -9,7 +9,7 @@ import ( // by validate must not need any state information on the flag (i.e. changed) func (c *ContainerCLIOpts) validate() error { var () - if c.Rm && c.Restart != "" && c.Restart != "no" { + if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) } diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index cae52ccaa..898d65bd0 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -59,8 +59,8 @@ func DefineNetFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(macAddressFlagName, completion.AutocompleteNone) networkFlagName := "network" - netFlags.String( - networkFlagName, containerConfig.NetNS(), + netFlags.StringArray( + networkFlagName, []string{containerConfig.NetNS()}, "Connect a container to a network", ) _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworks) @@ -194,25 +194,29 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { } if cmd.Flags().Changed("network") { - network, err := cmd.Flags().GetString("network") + networks, err := cmd.Flags().GetStringArray("network") if err != nil { return nil, err } + for i, network := range networks { + parts := strings.SplitN(network, ":", 2) - parts := strings.SplitN(network, ":", 2) - - ns, cniNets, err := specgen.ParseNetworkNamespace(network) - if err != nil { - return nil, err - } + ns, cniNets, err := specgen.ParseNetworkNamespace(network) + if err != nil { + return nil, err + } + if i > 0 && (len(cniNets) == 0 || len(opts.CNINetworks) == 0) { + return nil, errors.Errorf("network conflict between type %s and %s", opts.Network.NSMode, ns.NSMode) + } - if len(parts) > 1 { - opts.NetworkOptions = make(map[string][]string) - opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - cniNets = nil + if len(parts) > 1 { + opts.NetworkOptions = make(map[string][]string) + opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + cniNets = nil + } + opts.Network = ns + opts.CNINetworks = append(opts.CNINetworks, cniNets...) } - opts.Network = ns - opts.CNINetworks = cniNets } aliases, err := cmd.Flags().GetStringSlice("network-alias") diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index b3c160ddf..0468f15e0 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -10,7 +10,6 @@ import ( "github.com/containers/podman/v2/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const ( @@ -45,7 +44,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo } // Next --volumes flag. - volumeMounts, volumeVolumes, overlayVolumes, err := getVolumeMounts(volumeFlag) + volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) if err != nil { return nil, nil, nil, nil, err } @@ -594,105 +593,6 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) { return newVolume, nil } -func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.OverlayVolume, error) { - mounts := make(map[string]spec.Mount) - volumes := make(map[string]*specgen.NamedVolume) - overlayVolumes := make(map[string]*specgen.OverlayVolume) - - volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") - - for _, vol := range volumeFlag { - var ( - options []string - src string - dest string - err error - ) - - splitVol := strings.Split(vol, ":") - if len(splitVol) > 3 { - return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol) - } - - src = splitVol[0] - if len(splitVol) == 1 { - // This is an anonymous named volume. Only thing given - // is destination. - // Name/source will be blank, and populated by libpod. - src = "" - dest = splitVol[0] - } else if len(splitVol) > 1 { - dest = splitVol[1] - } - if len(splitVol) > 2 { - if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { - return nil, nil, nil, err - } - } - - // Do not check source dir for anonymous volumes - if len(splitVol) > 1 { - if err := parse.ValidateVolumeHostDir(src); err != nil { - return nil, nil, nil, err - } - } - if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return nil, nil, nil, err - } - - cleanDest := filepath.Clean(dest) - - if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { - // This is not a named volume - overlayFlag := false - for _, o := range options { - if o == "O" { - overlayFlag = true - if len(options) > 1 { - return nil, nil, nil, errors.New("can't use 'O' with other options") - } - } - } - if overlayFlag { - // This is a overlay volume - newOverlayVol := new(specgen.OverlayVolume) - newOverlayVol.Destination = cleanDest - newOverlayVol.Source = src - if _, ok := overlayVolumes[newOverlayVol.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination) - } - overlayVolumes[newOverlayVol.Destination] = newOverlayVol - } else { - newMount := spec.Mount{ - Destination: cleanDest, - Type: string(TypeBind), - Source: src, - Options: options, - } - if _, ok := mounts[newMount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) - } - mounts[newMount.Destination] = newMount - } - } else { - // This is a named volume - newNamedVol := new(specgen.NamedVolume) - newNamedVol.Name = src - newNamedVol.Dest = cleanDest - newNamedVol.Options = options - - if _, ok := volumes[newNamedVol.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) - } - volumes[newNamedVol.Dest] = newNamedVol - } - - logrus.Debugf("User mount %s:%s options %v", src, dest, options) - } - - return mounts, volumes, overlayVolumes, nil -} - // GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { m := make(map[string]spec.Mount) diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 642feb5e0..6f84cf9b8 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -29,15 +29,25 @@ var ( psDescription = "Prints out information about the containers" psCommand = &cobra.Command{ Use: "ps [options]", - Args: validate.NoArgs, Short: "List containers", Long: psDescription, RunE: ps, + Args: validate.NoArgs, ValidArgsFunction: completion.AutocompleteNone, Example: `podman ps -a podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" podman ps --size --sort names`, } + + psContainerCommand = &cobra.Command{ + Use: psCommand.Use, + Short: psCommand.Short, + Long: psCommand.Long, + RunE: psCommand.RunE, + Args: psCommand.Args, + ValidArgsFunction: psCommand.ValidArgsFunction, + Example: strings.ReplaceAll(psCommand.Example, "podman ps", "podman container ps"), + } ) var ( listOpts = entities.ContainerListOptions{ @@ -54,6 +64,14 @@ func init() { }) listFlagSet(psCommand) validate.AddLatestFlag(psCommand, &listOpts.Latest) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: psContainerCommand, + Parent: containerCmd, + }) + listFlagSet(psContainerCommand) + validate.AddLatestFlag(psContainerCommand, &listOpts.Latest) } func listFlagSet(cmd *cobra.Command) { @@ -64,8 +82,7 @@ func listFlagSet(cmd *cobra.Command) { filterFlagName := "filter" flags.StringSliceVarP(&filters, filterFlagName, "f", []string{}, "Filter output based on conditions given") - //TODO add custom filter function - _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePsFilters) formatFlagName := "format" flags.StringVar(&listOpts.Format, formatFlagName, "", "Pretty-print containers to JSON or using a Go template") diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index 3eb6d2af2..f00b1dce1 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -18,12 +18,10 @@ import ( ) var ( - topDescription = `Similar to system "top" command. - - Specify format descriptors to alter the output. - - Running "podman top -l pid pcpu seccomp" will print the process ID, the CPU percentage and the seccomp mode of each process of the latest container.` + topDescription = `Display the running processes of a container. + The top command extends the ps(1) compatible AIX descriptors with container-specific ones as shown below. In the presence of ps(1) specific flags (e.g, -eo), Podman will execute ps(1) inside the container. +` topOptions = entities.TopOptions{} topCommand = &cobra.Command{ diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 4692699f2..bcb31e6ee 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -79,8 +79,7 @@ func imageListFlagSet(cmd *cobra.Command) { 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) + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteImageFilters) formatFlagName := "format" flags.StringVar(&listFlag.format, formatFlagName, "", "Change the output format to JSON or a Go template") diff --git a/cmd/podman/networks/connect.go b/cmd/podman/networks/connect.go index a7636688c..8afc0c7c0 100644 --- a/cmd/podman/networks/connect.go +++ b/cmd/podman/networks/connect.go @@ -17,7 +17,7 @@ var ( RunE: networkConnect, Example: `podman network connect web secondary`, Args: cobra.ExactArgs(2), - ValidArgsFunction: common.AutocompleteNetworks, + ValidArgsFunction: common.AutocompleteNetworkConnectCmd, } ) diff --git a/cmd/podman/networks/disconnect.go b/cmd/podman/networks/disconnect.go index 598c23a1c..a30315774 100644 --- a/cmd/podman/networks/disconnect.go +++ b/cmd/podman/networks/disconnect.go @@ -17,7 +17,7 @@ var ( RunE: networkDisconnect, Example: `podman network disconnect web secondary`, Args: cobra.ExactArgs(2), - ValidArgsFunction: common.AutocompleteNetworks, + ValidArgsFunction: common.AutocompleteNetworkConnectCmd, } ) diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index f2a5a431a..dcba3f186 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -46,7 +46,7 @@ func networkListFlags(flags *pflag.FlagSet) { filterFlagName := "filter" flags.StringVarP(&networkListOptions.Filter, filterFlagName, "", "", "Provide filter values (e.g. 'name=podman')") - _ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteNetworkFilters) } diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 34e756a3a..1504d9385 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -18,6 +18,7 @@ var ( networkrmDescription = `Remove networks` networkrmCommand = &cobra.Command{ Use: "rm [options] NETWORK [NETWORK...]", + Aliases: []string{"remove"}, Short: "network rm", Long: networkrmDescription, RunE: networkRm, diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 449d60bb9..5b0aa2fe4 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -94,7 +94,7 @@ func init() { 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.BoolVar(&replace, "replace", false, "If a pod with the same name exists, replace it") shareFlagName := "share" flags.StringVar(&share, shareFlagName, specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") @@ -171,33 +171,7 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - createOptions.Net.Network = specgen.Namespace{} - if cmd.Flag("network").Changed { - netInput, err := cmd.Flags().GetString("network") - if err != nil { - return err - } - parts := strings.SplitN(netInput, ":", 2) - - n := specgen.Namespace{} - switch { - case netInput == "bridge": - n.NSMode = specgen.Bridge - case netInput == "host": - n.NSMode = specgen.Host - case netInput == "slirp4netns", strings.HasPrefix(netInput, "slirp4netns:"): - n.NSMode = specgen.Slirp - if len(parts) > 1 { - createOptions.Net.NetworkOptions = make(map[string][]string) - createOptions.Net.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - } - default: - // Container and NS mode are presently unsupported - n.NSMode = specgen.Bridge - createOptions.Net.CNINetworks = strings.Split(netInput, ",") - } - createOptions.Net.Network = n - } + if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 51c2e92f0..99d324411 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -57,8 +57,7 @@ func init() { filterFlagName := "filter" flags.StringSliceVarP(&inputFilters, filterFlagName, "f", []string{}, "Filter output based on conditions given") - //TODO complete filters - _ = psCmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = psCmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePodPsFilters) formatFlagName := "format" flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template") diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 34d92cd0f..7840e6100 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -113,33 +113,9 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { 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 - var connErr error conn := cmd.Root().LocalFlags().Lookup("connection") if conn != nil && conn.Changed { cfg.Engine.ActiveService = conn.Value.String() @@ -147,19 +123,37 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { var err error cfg.URI, cfg.Identity, err = cfg.ActiveDestination() if err != nil { - connErr = errors.Wrap(err, "failed to resolve active destination") + return errors.Wrap(err, "failed to resolve active destination") } if err := cmd.Root().LocalFlags().Set("url", cfg.URI); err != nil { - connErr = errors.Wrap(err, "failed to override --url flag") + return errors.Wrap(err, "failed to override --url flag") } if err := cmd.Root().LocalFlags().Set("identity", cfg.Identity); err != nil { - connErr = errors.Wrap(err, "failed to override --identity flag") + return errors.Wrap(err, "failed to override --identity flag") } } - if connErr != nil { - return connErr + + // 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 the completion logic setups the engines as needed. + requireCleanup = false + return nil } // Prep the engines diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index b0d999765..7e54de38a 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -56,7 +56,7 @@ func init() { filterFlagName := "filter" flags.StringSliceVarP(&cliOpts.Filter, filterFlagName, "f", []string{}, "Filter volume output") - _ = lsCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = lsCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteVolumeFilters) formatFlagName := "format" flags.StringVar(&cliOpts.Format, formatFlagName, "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template") |