diff options
-rw-r--r-- | cmd/podman/common/completion.go | 132 | ||||
-rw-r--r-- | cmd/podman/common/netflags.go | 34 | ||||
-rw-r--r-- | cmd/podman/containers/top.go | 8 | ||||
-rw-r--r-- | cmd/podman/pods/create.go | 30 | ||||
-rw-r--r-- | cmd/podman/root.go | 52 | ||||
-rw-r--r-- | docs/source/markdown/podman-create.1.md | 2 | ||||
-rw-r--r-- | docs/source/markdown/podman-run.1.md | 2 | ||||
-rw-r--r-- | docs/source/markdown/podman-top.1.md | 2 | ||||
-rw-r--r-- | libpod/container.go | 20 | ||||
-rw-r--r-- | libpod/container_internal.go | 9 | ||||
-rw-r--r-- | libpod/networking_linux.go | 21 | ||||
-rw-r--r-- | libpod/rootless_cni_linux.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers.go | 9 | ||||
-rw-r--r-- | pkg/domain/entities/images.go | 8 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 6 | ||||
-rw-r--r-- | test/e2e/pod_create_test.go | 20 | ||||
-rw-r--r-- | test/e2e/run_networking_test.go | 29 |
17 files changed, 236 insertions, 152 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index db4d3d0d3..c96f7436d 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" ) @@ -36,7 +37,35 @@ const ( type keyValueCompletion map[string]func(s string) ([]string, cobra.ShellCompDirective) -func getContainers(toComplete string, cType completeType, statuses ...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), @@ -47,7 +76,12 @@ func getContainers(toComplete string, cType completeType, statuses ...string) ([ listOpts.Filters["status"] = statuses } - containers, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts) + 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.ShellCompDirectiveError @@ -68,7 +102,7 @@ func getContainers(toComplete string, cType completeType, statuses ...string) ([ return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getPods(toComplete string, cType completeType, 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), @@ -77,7 +111,12 @@ func getPods(toComplete string, cType completeType, statuses ...string) ([]strin listOpts.Filters["status"] = statuses } - pods, err := registry.ContainerEngine().PodPs(registry.GetContext(), listOpts) + 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.ShellCompDirectiveError @@ -98,11 +137,16 @@ func getPods(toComplete string, cType completeType, statuses ...string) ([]strin 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 @@ -116,11 +160,16 @@ 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.ShellCompDirectiveNoFileComp + } + images, err := engine.List(registry.GetContext(), listOptions) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveError @@ -171,11 +220,16 @@ func getRegistries() ([]string, cobra.ShellCompDirective) { 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{} - networks, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions) + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + networks, err := engine.NetworkList(registry.Context(), networkListOptions) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveError @@ -266,7 +320,7 @@ func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault) + return getContainers(cmd, toComplete, completeDefault) } // AutocompleteContainersCreated - Autocomplete only created container names. @@ -274,7 +328,7 @@ func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "created") + return getContainers(cmd, toComplete, completeDefault, "created") } // AutocompleteContainersExited - Autocomplete only exited container names. @@ -282,7 +336,7 @@ func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "exited") + return getContainers(cmd, toComplete, completeDefault, "exited") } // AutocompleteContainersPaused - Autocomplete only paused container names. @@ -290,7 +344,7 @@ func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "paused") + return getContainers(cmd, toComplete, completeDefault, "paused") } // AutocompleteContainersRunning - Autocomplete only running container names. @@ -298,7 +352,7 @@ func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "running") + return getContainers(cmd, toComplete, completeDefault, "running") } // AutocompleteContainersStartable - Autocomplete only created and exited container names. @@ -306,7 +360,7 @@ func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "created", "exited") + return getContainers(cmd, toComplete, completeDefault, "created", "exited") } // AutocompletePods - Autocomplete all pod names. @@ -314,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, completeDefault) + return getPods(cmd, toComplete, completeDefault) } // AutocompletePodsRunning - Autocomplete only running pod names. @@ -323,7 +377,7 @@ func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete strin if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getPods(toComplete, completeDefault, "running", "degraded") + return getPods(cmd, toComplete, completeDefault, "running", "degraded") } // AutocompleteContainersAndPods - Autocomplete container names and pod names. @@ -331,8 +385,8 @@ func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete, completeDefault) - pods, _ := getPods(toComplete, completeDefault) + containers, _ := getContainers(cmd, toComplete, completeDefault) + pods, _ := getPods(cmd, toComplete, completeDefault) return append(containers, pods...), cobra.ShellCompDirectiveNoFileComp } @@ -341,8 +395,8 @@ func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete, completeDefault) - images, _ := getImages(toComplete) + containers, _ := getContainers(cmd, toComplete, completeDefault) + images, _ := getImages(cmd, toComplete) return append(containers, images...), cobra.ShellCompDirectiveNoFileComp } @@ -351,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. @@ -359,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. @@ -368,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 @@ -387,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. @@ -396,7 +450,7 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 2 { - containers, _ := getContainers(toComplete, completeDefault) + containers, _ := getContainers(cmd, toComplete, completeDefault) for _, container := range containers { // TODO: Add path completion for inside the container if possible if strings.HasPrefix(container, toComplete) { @@ -464,7 +518,7 @@ func AutocompleteCreateAttach(cmd *cobra.Command, args []string, toComplete stri // -> host,container:[name],ns:[path],private func AutocompleteNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ - "container:": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, + "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, @@ -623,7 +677,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 @@ -753,15 +807,15 @@ var containerStatuses = []string{"created", "running", "paused", "stopped", "exi // 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(s, completeIDs) }, - "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeNames) }, + "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(s) }, - "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, - "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, - "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(s) }, + "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 @@ -776,14 +830,14 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) // 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(s, completeIDs) }, - "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(s, completeNames) }, + "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(s, completeIDs) }, - "ctr-names=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeNames) }, + "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 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/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/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/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/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 8251ba3b6..eb73609d4 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -588,7 +588,7 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma separated; +- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index bc3d5a8bb..184c12acd 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -614,7 +614,7 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma separated; +- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: diff --git a/docs/source/markdown/podman-top.1.md b/docs/source/markdown/podman-top.1.md index f307f96da..cfb89567c 100644 --- a/docs/source/markdown/podman-top.1.md +++ b/docs/source/markdown/podman-top.1.md @@ -9,7 +9,7 @@ podman\-top - Display the running processes of a container **podman container top** [*options*] *container* [*format-descriptors*] ## DESCRIPTION -Display the running processes of the container. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can also specify options and or flags of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and flags in the container. +Display the running processes of the container. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can also specify options and or flags of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and flags in the container. Please use the "h*" descriptors if you want to extract host-related information. For instance, `podman top $name hpid huser` to display the PID and user of the processes in the host context. ## OPTIONS diff --git a/libpod/container.go b/libpod/container.go index 31c958959..e954d84eb 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -13,6 +13,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/lock" + "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -1095,13 +1096,17 @@ func (c *Container) Umask() string { // values at runtime via network connect and disconnect. // If the container is configured to use CNI and this function returns an empty // array, the container will still be connected to the default network. -func (c *Container) Networks() ([]string, error) { +// The second return parameter, a bool, indicates that the container container +// is joining the default CNI network - the network name will be included in the +// returned array of network names, but the container did not explicitly join +// this network. +func (c *Container) Networks() ([]string, bool, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() if err := c.syncContainer(); err != nil { - return nil, err + return nil, false, err } } @@ -1109,19 +1114,22 @@ func (c *Container) Networks() ([]string, error) { } // Unlocked accessor for networks -func (c *Container) networks() ([]string, error) { +func (c *Container) networks() ([]string, bool, error) { networks, err := c.runtime.state.GetNetworks(c) if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { - return c.config.Networks, nil + if len(c.config.Networks) == 0 && !rootless.IsRootless() { + return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil + } + return c.config.Networks, false, nil } - return networks, err + return networks, false, err } // networksByNameIndex provides us with a map of container networks where key // is network name and value is the index position func (c *Container) networksByNameIndex() (map[string]int, error) { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return nil, err } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index ffd0dc92f..b6a3244ea 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -643,18 +643,13 @@ func (c *Container) removeIPv4Allocations() error { cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName() } - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return err } - switch { - case len(networks) > 0 && len(networks) != len(c.state.NetworkStatus): + if len(networks) != len(c.state.NetworkStatus) { return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus)) - case len(networks) == 0 && len(c.state.NetworkStatus) != 1: - return errors.Wrapf(define.ErrInternal, "network mismatch: did not specify CNI networks but joined more than one (%d)", len(c.state.NetworkStatus)) - case len(networks) == 0 && cniDefaultNetwork == "": - return errors.Wrapf(define.ErrInternal, "could not retrieve name of CNI default network") } for index, result := range c.state.NetworkStatus { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 8dce7c9fe..7a0bebd95 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -110,10 +110,15 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re podName := getCNIPodName(ctr) - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return nil, err } + // All networks have been removed from the container. + // This is effectively forcing net=none. + if len(networks) == 0 { + return nil, nil + } // Update container map of interface descriptions if err := ctr.setupNetworkDescriptions(networks); err != nil { @@ -224,7 +229,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { if ctr.config.NetMode.IsSlirp4netns() { return r.setupSlirp4netns(ctr) } - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return err } @@ -744,13 +749,13 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return err } // rootless containers do not use the CNI plugin directly - if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() { + if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 { var requestedIP net.IP if ctr.requestedIP != nil { requestedIP = ctr.requestedIP @@ -863,7 +868,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e settings := new(define.InspectNetworkSettings) settings.Ports = makeInspectPortBindings(c.config.PortMappings) - networks, err := c.networks() + networks, isDefault, err := c.networks() if err != nil { return nil, err } @@ -872,7 +877,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e if c.state.NetNS == nil { // We still want to make dummy configurations for each CNI net // the container joined. - if len(networks) > 0 { + if len(networks) > 0 && !isDefault { settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks)) for _, net := range networks { cniNet := new(define.InspectAdditionalNetwork) @@ -893,7 +898,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // If we have CNI networks - handle that here - if len(networks) > 0 { + if len(networks) > 0 && !isDefault { if len(networks) != len(c.state.NetworkStatus) { return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(networks), len(c.state.NetworkStatus)) } @@ -1101,7 +1106,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return err } - ctrNetworks, err := c.networks() + ctrNetworks, _, err := c.networks() if err != nil { return err } diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index 1d6158cc2..2c2977f9f 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -40,7 +40,7 @@ const ( // // AllocRootlessCNI does not lock c. c should be already locked. func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return nil, nil, err } @@ -81,7 +81,7 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes. // // DeallocRootlessCNI does not lock c. c should be already locked. func DeallocRootlessCNI(ctx context.Context, c *Container) error { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return err } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 00be8e845..0ee96b855 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -298,6 +298,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, state.Running = true } + formatCapabilities(inspect.HostConfig.CapDrop) + formatCapabilities(inspect.HostConfig.CapAdd) + h, err := json.Marshal(inspect.HostConfig) if err != nil { return nil, err @@ -428,3 +431,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, } return &c, nil } + +func formatCapabilities(slice []string) { + for i := range slice { + slice[i] = strings.TrimPrefix(slice[i], "CAP_") + } +} diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 101542a98..cbd6e9192 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -51,10 +51,10 @@ func (i *Image) Id() string { // nolint } type ImageSummary struct { - ID string `json:"Id"` - ParentId string `json:",omitempty"` // nolint - RepoTags []string `json:",omitempty"` - Created int64 `json:",omitempty"` + ID string `json:"Id"` + ParentId string `json:",omitempty"` // nolint + RepoTags []string `json:",omitempty"` + Created int64 Size int64 `json:",omitempty"` SharedSize int `json:",omitempty"` VirtualSize int64 `json:",omitempty"` diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 90c56d366..11108a5c1 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -272,16 +272,10 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { toReturn.NSMode = Private case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying ns:") - } toReturn.NSMode = Path toReturn.Value = split[1] case strings.HasPrefix(ns, "container:"): split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:") - } toReturn.NSMode = FromContainer toReturn.Value = split[1] default: diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index be0a2f6f0..ccfbcefae 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -476,4 +477,23 @@ entrypoint ["/fromimage"] Expect(status3.ExitCode()).To(Equal(0)) Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) }) + + It("podman create pod invalid network config", func() { + net1 := "n1" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net1) + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"pod", "create", "--network", "host", "--network", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("host")) + Expect(session.ErrorToString()).To(ContainSubstring("bridge")) + + session = podmanTest.Podman([]string{"pod", "create", "--network", "container:abc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) + }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 3e80e953e..1d416498c 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -665,4 +665,33 @@ var _ = Describe("Podman run networking", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(BeZero()) }) + + It("podman run with multiple networks", func() { + net1 := "n1" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net1) + Expect(session.ExitCode()).To(BeZero()) + + net2 := "n2" + stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net2) + Expect(session.ExitCode()).To(BeZero()) + + run := podmanTest.Podman([]string{"run", "--network", net1, "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(BeZero()) + Expect(len(run.OutputToStringArray())).To(Equal(3)) + Expect(run.OutputToString()).To(ContainSubstring("lo")) + Expect(run.OutputToString()).To(ContainSubstring("eth0")) + Expect(run.OutputToString()).To(ContainSubstring("eth1")) + + //invalid config network host and cni should fail + run = podmanTest.Podman([]string{"run", "--network", "host", "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(125)) + Expect(run.ErrorToString()).To(ContainSubstring("host")) + Expect(run.ErrorToString()).To(ContainSubstring("bridge")) + }) }) |