diff options
-rw-r--r-- | cmd/podman/create.go | 65 | ||||
-rw-r--r-- | cmd/podman/exists.go | 37 | ||||
-rw-r--r-- | cmd/podman/logs.go | 17 | ||||
-rw-r--r-- | cmd/podman/pod.go | 1 | ||||
-rw-r--r-- | cmd/podman/rmi.go | 19 | ||||
-rw-r--r-- | completions/bash/podman | 8 | ||||
-rw-r--r-- | contrib/python/pypodman/pypodman/lib/actions/inspect_action.py | 14 | ||||
-rw-r--r-- | docs/podman-create.1.md | 3 | ||||
-rw-r--r-- | docs/podman-pod-exists.1.md | 40 | ||||
-rw-r--r-- | docs/podman-run.1.md | 3 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 26 | ||||
-rw-r--r-- | pkg/spec/createconfig.go | 7 | ||||
-rw-r--r-- | test/e2e/create_test.go | 11 | ||||
-rw-r--r-- | test/e2e/exists_test.go | 32 | ||||
-rw-r--r-- | test/e2e/run_test.go | 11 |
15 files changed, 256 insertions, 38 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go index bcf830c7c..c40650f83 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -11,6 +11,7 @@ import ( "syscall" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" ann "github.com/containers/libpod/pkg/annotations" @@ -375,8 +376,8 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string { return entrypoint } -func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string) (map[string]string, error) { - pod, err := runtime.LookupPod(c.String("pod")) +func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { + pod, err := runtime.LookupPod(podName) if err != nil { return namespaces, err } @@ -409,6 +410,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim inputCommand, command []string memoryLimit, memoryReservation, memorySwap, memoryKernel int64 blkioWeight uint16 + namespaces map[string]string ) idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname")) if err != nil { @@ -492,12 +494,21 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together") } + // EXPOSED PORTS + var portBindings map[nat.Port][]nat.PortBinding + if data != nil { + portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts) + if err != nil { + return nil, err + } + } + // Kernel Namespaces // TODO Fix handling of namespace from pod // Instead of integrating here, should be done in libpod // However, that also involves setting up security opts // when the pod's namespace is integrated - namespaces := map[string]string{ + namespaces = map[string]string{ "pid": c.String("pid"), "net": c.String("net"), "ipc": c.String("ipc"), @@ -505,8 +516,41 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim "uts": c.String("uts"), } + originalPodName := c.String("pod") + podName := strings.Replace(originalPodName, "new:", "", 1) + // after we strip out :new, make sure there is something left for a pod name + if len(podName) < 1 && c.IsSet("pod") { + return nil, errors.Errorf("new pod name must be at least one character") + } if c.IsSet("pod") { - namespaces, err = configurePod(c, runtime, namespaces) + if strings.HasPrefix(originalPodName, "new:") { + // pod does not exist; lets make it + var podOptions []libpod.PodCreateOption + podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups()) + if len(portBindings) > 0 { + ociPortBindings, err := cc.NatToOCIPortBindings(portBindings) + if err != nil { + return nil, err + } + podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings)) + } + + podNsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ",")) + if err != nil { + return nil, err + } + podOptions = append(podOptions, podNsOptions...) + // make pod + pod, err := runtime.NewPod(ctx, podOptions...) + if err != nil { + return nil, err + } + logrus.Debugf("pod %s created by new container request", pod.ID()) + + // The container now cannot have port bindings; so we reset the map + portBindings = make(map[nat.Port][]nat.PortBinding) + } + namespaces, err = configurePod(c, runtime, namespaces, podName) if err != nil { return nil, err } @@ -535,7 +579,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim // Make sure if network is set to container namespace, port binding is not also being asked for netMode := ns.NetworkMode(namespaces["net"]) if netMode.IsContainer() { - if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") { + if len(portBindings) > 0 { return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") } } @@ -644,15 +688,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image") } - // EXPOSED PORTS - var portBindings map[nat.Port][]nat.PortBinding - if data != nil { - portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts) - if err != nil { - return nil, err - } - } - // SHM Size shmSize, err := units.FromHumanSize(c.String("shm-size")) if err != nil { @@ -746,7 +781,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim NetMode: netMode, UtsMode: utsMode, PidMode: pidMode, - Pod: c.String("pod"), + Pod: podName, Privileged: c.Bool("privileged"), Publish: c.StringSlice("publish"), PublishAll: c.Bool("publish-all"), diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index 2f7b7c185..2e2559ec7 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -44,6 +44,23 @@ var ( } ) +var ( + podExistsDescription = ` + podman pod exists + + Check if a pod exists in local storage +` + + podExistsCommand = cli.Command{ + Name: "exists", + Usage: "Check if a pod exists in local storage", + Description: podExistsDescription, + Action: podExistsCmd, + ArgsUsage: "POD-NAME", + OnUsageError: usageErrorHandler, + } +) + func imageExistsCmd(c *cli.Context) error { args := c.Args() if len(args) > 1 || len(args) < 1 { @@ -81,3 +98,23 @@ func containerExistsCmd(c *cli.Context) error { } return nil } + +func podExistsCmd(c *cli.Context) error { + args := c.Args() + if len(args) > 1 || len(args) < 1 { + return errors.New("you may only check for the existence of one pod at a time") + } + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + if _, err := runtime.LookupPod(args[0]); err != nil { + if errors.Cause(err) == libpod.ErrNoSuchPod { + os.Exit(1) + } + return err + } + return nil +} diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index 84aca5e61..75947c34e 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -40,14 +40,15 @@ var ( logsDescription = "The podman logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution" + "order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs" logsCommand = cli.Command{ - Name: "logs", - Usage: "Fetch the logs of a container", - Description: logsDescription, - Flags: sortFlags(logsFlags), - Action: logsCmd, - ArgsUsage: "CONTAINER", - SkipArgReorder: true, - OnUsageError: usageErrorHandler, + Name: "logs", + Usage: "Fetch the logs of a container", + Description: logsDescription, + Flags: sortFlags(logsFlags), + Action: logsCmd, + ArgsUsage: "CONTAINER", + SkipArgReorder: true, + OnUsageError: usageErrorHandler, + UseShortOptionHandling: true, } ) diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index 0c6ec5e8c..a30361134 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -11,6 +11,7 @@ Pods are a group of one or more containers sharing the same network, pid and ipc ` podSubCommands = []cli.Command{ podCreateCommand, + podExistsCommand, podInspectCommand, podKillCommand, podPauseCommand, diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index c0a0d69df..0f4f8765b 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -91,8 +91,23 @@ func rmiCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "unable to query local images") } - for _, i := range imagesToDelete { - removeImage(i) + lastNumberofImages := 0 + for len(imagesToDelete) > 0 { + if lastNumberofImages == len(imagesToDelete) { + return errors.New("unable to delete all images; re-run the rmi command again.") + } + for _, i := range imagesToDelete { + isParent, err := i.IsParent() + if err != nil { + return err + } + if isParent { + continue + } + removeImage(i) + } + lastNumberofImages = len(imagesToDelete) + imagesToDelete, err = runtime.ImageRuntime().GetImages() } } else { // Create image.image objects for deletion from user input. diff --git a/completions/bash/podman b/completions/bash/podman index 3cccf2192..9518cfa22 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2235,6 +2235,14 @@ _podman_container_exists() { " } +_podman_pod_exists() { + local options_with_args=" + " + + local boolean_options=" + " +} + _podman_image_exists() { local options_with_args=" " diff --git a/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py b/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py index 514b4702a..a581e7e4e 100644 --- a/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py +++ b/contrib/python/pypodman/pypodman/lib/actions/inspect_action.py @@ -23,9 +23,9 @@ class Inspect(AbstractActionBase): help='Type of object to inspect', ) parser.add_argument( - 'size', + '--size', action='store_true', - default=True, + default=False, help='Display the total file size if the type is a container.' ' Always True.') parser.add_argument( @@ -59,7 +59,7 @@ class Inspect(AbstractActionBase): def inspect(self): """Inspect provided podman objects.""" - output = {} + output = [] try: for ident in self._args.objects: obj = None @@ -78,7 +78,13 @@ class Inspect(AbstractActionBase): msg = 'Object "{}" not found'.format(ident) print(msg, file=sys.stderr, flush=True) else: - output.update(obj._asdict()) + fields = obj._asdict() + if not self._args.size: + try: + del fields['sizerootfs'] + except KeyError: + pass + output.append(fields) except podman.ErrorOccurred as e: sys.stdout.flush() print( diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 6fbdd0d03..f1409a554 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -455,7 +455,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain **--pod**="" -Run container in an existing pod +Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`. +To make a pod with more granular options, use the `podman pod create` command before creating a container. **--privileged**=*true*|*false* diff --git a/docs/podman-pod-exists.1.md b/docs/podman-pod-exists.1.md new file mode 100644 index 000000000..8fb2fc90e --- /dev/null +++ b/docs/podman-pod-exists.1.md @@ -0,0 +1,40 @@ +% podman-pod-exits(1) Podman Man Pages +% Brent Baude +% December 2018 +# NAME +podman-pod-exists- Check if a pod exists in local storage + +# SYNOPSIS +**podman pod exists** +[**-h**|**--help**] +POD + +# DESCRIPTION +**podman pod exists** checks if a pod exists in local storage. The **ID** or **Name** +of the pod may be used as input. Podman will return an exit code +of `0` when the pod is found. A `1` will be returned otherwise. An exit code of `125` indicates there +was an issue accessing the local storage. + +## Examples ## + +Check if a pod called `web` exists in local storage (the pod does actually exist). +``` +$ sudo podman pod exists web +$ echo $? +0 +$ +``` + +Check if a pod called `backend` exists in local storage (the pod does not actually exist). +``` +$ sudo podman pod exists backend +$ echo $? +1 +$ +``` + +## SEE ALSO +podman-pod(1), podman(1) + +# HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index a6761a393..5917f6f7a 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -439,7 +439,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain **--pod**="" -Run container in an existing pod +Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`. +To make a pod with more granular options, use the `podman pod create` command before creating a container. **--privileged**=*true*|*false* diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 85b0ef392..07002da3f 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -74,7 +74,7 @@ func GetRootlessUID() int { func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error { path, err := exec.LookPath(tool) if err != nil { - return err + return errors.Wrapf(err, "cannot find %s", tool) } appendTriplet := func(l []string, a, b, c int) []string { @@ -92,7 +92,11 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) Path: path, Args: args, } - return cmd.Run() + + if err := cmd.Run(); err != nil { + return errors.Wrapf(err, "cannot setup namespace using %s", tool) + } + return nil } // JoinNS re-exec podman in a new userNS and join the user namespace of the specified @@ -191,11 +195,13 @@ func BecomeRootInUserNS() (bool, int, error) { return false, -1, errors.Errorf("cannot re-exec process") } + allowSingleIDMapping := os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") != "" + var uids, gids []idtools.IDMap username := os.Getenv("USER") if username == "" { user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid())) - if err != nil && os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" { + if err != nil && !allowSingleIDMapping { if os.IsNotExist(err) { return false, 0, errors.Wrapf(err, "/etc/subuid or /etc/subgid does not exist, see subuid/subgid man pages for information on these files") } @@ -206,7 +212,7 @@ func BecomeRootInUserNS() (bool, int, error) { } } mappings, err := idtools.NewIDMappings(username, username) - if os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" { + if !allowSingleIDMapping { if err != nil { return false, -1, err } @@ -236,7 +242,11 @@ func BecomeRootInUserNS() (bool, int, error) { uidsMapped := false if mappings != nil && uids != nil { - uidsMapped = tryMappingTool("newuidmap", pid, os.Getuid(), uids) == nil + err := tryMappingTool("newuidmap", pid, os.Getuid(), uids) + if !allowSingleIDMapping && err != nil { + return false, 0, err + } + uidsMapped = err == nil } if !uidsMapped { setgroups := fmt.Sprintf("/proc/%d/setgroups", pid) @@ -254,7 +264,11 @@ func BecomeRootInUserNS() (bool, int, error) { gidsMapped := false if mappings != nil && gids != nil { - gidsMapped = tryMappingTool("newgidmap", pid, os.Getgid(), gids) == nil + err := tryMappingTool("newgidmap", pid, os.Getgid(), gids) + if !allowSingleIDMapping && err != nil { + return false, 0, err + } + gidsMapped = err == nil } if !gidsMapped { gidMap := fmt.Sprintf("/proc/%d/gid_map", pid) diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index a0fd40318..25f8cd7a1 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -496,8 +496,13 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib // CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) { + return NatToOCIPortBindings(c.PortBindings) +} + +// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice +func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) { var portBindings []ocicni.PortMapping - for containerPb, hostPb := range c.PortBindings { + for containerPb, hostPb := range ports { var pm ocicni.PortMapping pm.ContainerPort = int32(containerPb.Int()) for _, i := range hostPb { diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 366a58f02..684a7cd88 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -189,4 +189,15 @@ var _ = Describe("Podman create", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test rw,nosuid,nodev,noexec,relatime - tmpfs")) }) + + It("podman create --pod automatically", func() { + session := podmanTest.Podman([]string{"create", "--pod", "new:foobar", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString("foobar") + Expect(match).To(BeTrue()) + }) }) diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go index 9165e8902..d9652de4b 100644 --- a/test/e2e/exists_test.go +++ b/test/e2e/exists_test.go @@ -82,4 +82,36 @@ var _ = Describe("Podman image|container exists", func() { Expect(session.ExitCode()).To(Equal(1)) }) + It("podman pod exists in local storage by name", func() { + setup, rc, _ := podmanTest.CreatePod("foobar") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod exists in local storage by container ID", func() { + setup, rc, podID := podmanTest.CreatePod("") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod exists in local storage by short container ID", func() { + setup, rc, podID := podmanTest.CreatePod("") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", podID[0:12]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod does not exist in local storage", func() { + session := podmanTest.Podman([]string{"pod", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 38504828b..aaee7fa53 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -628,4 +628,15 @@ USER mail` isSharedOnly := !strings.Contains(shared[0], "shared,") Expect(isSharedOnly).Should(BeTrue()) }) + + It("podman run --pod automatically", func() { + session := podmanTest.Podman([]string{"run", "--pod", "new:foobar", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString("foobar") + Expect(match).To(BeTrue()) + }) }) |