Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup handling of podman mount/unmount #7353

Merged
merged 1 commit into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions cmd/podman/containers/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/containers/libpod/v2/cmd/podman/utils"
"github.com/containers/libpod/v2/cmd/podman/validate"
"github.com/containers/libpod/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
Expand All @@ -31,7 +32,8 @@ var (
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
},
Annotations: map[string]string{
registry.ParentNSRequired: "",
registry.UnshareNSRequired: "",
registry.ParentNSRequired: "",
},
}

Expand All @@ -51,7 +53,7 @@ var (

func mountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers")
flags.StringVar(&mountOpts.Format, "format", "", "Change the output format to Go template")
flags.StringVar(&mountOpts.Format, "format", "", "Print the mounted containers in specified format (json)")
flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output")
}

Expand Down Expand Up @@ -90,14 +92,21 @@ func mount(_ *cobra.Command, args []string) error {
}
return errs.PrintErrors()
}
if mountOpts.Format == "json" {

switch mountOpts.Format {
case "json":
return printJSON(reports)
case "":
// do nothing
default:
return errors.Errorf("unknown --format argument: %s", mountOpts.Format)
}

mrs := make([]mountReporter, 0, len(reports))
for _, r := range reports {
mrs = append(mrs, mountReporter{r})
}
row := "{{.ID}} {{.Path}}"
row := "{{.ID}} {{.Path}}\n"
format := "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("mounts").Parse(format)
if err != nil {
Expand Down
41 changes: 21 additions & 20 deletions cmd/podman/containers/unmount.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,58 @@ var (

An unmount can be forced with the --force flag.
`
umountCommand = &cobra.Command{
Use: "umount [flags] CONTAINER [CONTAINER...]",
Aliases: []string{"unmount"},
unmountCommand = &cobra.Command{
Use: "unmount [flags] CONTAINER [CONTAINER...]",
Aliases: []string{"umount"},
Short: "Unmounts working container's root filesystem",
Long: description,
RunE: unmount,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
},
Example: `podman umount ctrID
podman umount ctrID1 ctrID2 ctrID3
podman umount --all`,
Example: `podman unmount ctrID
podman unmount ctrID1 ctrID2 ctrID3
podman unmount --all`,
}

containerUnmountCommand = &cobra.Command{
Use: umountCommand.Use,
Short: umountCommand.Short,
Long: umountCommand.Long,
RunE: umountCommand.RunE,
Use: unmountCommand.Use,
Short: unmountCommand.Short,
Aliases: unmountCommand.Aliases,
Long: unmountCommand.Long,
RunE: unmountCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
},
Example: `podman container umount ctrID
podman container umount ctrID1 ctrID2 ctrID3
podman container umount --all`,
Example: `podman container unmount ctrID
podman container unmount ctrID1 ctrID2 ctrID3
podman container unmount --all`,
}
)

var (
unmountOpts entities.ContainerUnmountOptions
)

func umountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Umount all of the currently mounted containers")
flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers")
func unmountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Unmount all of the currently mounted containers")
flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete unmount of the specified mounted containers")
}

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: umountCommand,
Command: unmountCommand,
})
umountFlags(umountCommand.Flags())
validate.AddLatestFlag(umountCommand, &unmountOpts.Latest)
unmountFlags(unmountCommand.Flags())
validate.AddLatestFlag(unmountCommand, &unmountOpts.Latest)

registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: containerUnmountCommand,
Parent: containerCmd,
})
umountFlags(containerUnmountCommand.Flags())
unmountFlags(containerUnmountCommand.Flags())
validate.AddLatestFlag(containerUnmountCommand, &unmountOpts.Latest)
}

Expand Down
18 changes: 13 additions & 5 deletions cmd/podman/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,21 @@ func main() {
for _, m := range c.Mode {
if cfg.EngineMode == m {
// Command cannot be run rootless
_, found := c.Command.Annotations[registry.ParentNSRequired]
if rootless.IsRootless() && found {
c.Command.RunE = func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath())
_, found := c.Command.Annotations[registry.UnshareNSRequired]
if found {
if rootless.IsRootless() && found && os.Getuid() != 0 {
c.Command.RunE = func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot run command %q in rootless mode, must execute `podman unshare` first", cmd.CommandPath())
}
}
} else {
_, found = c.Command.Annotations[registry.ParentNSRequired]
if rootless.IsRootless() && found {
c.Command.RunE = func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath())
}
}
}

parent := rootCmd
if c.Parent != nil {
parent = c.Parent
Expand Down
3 changes: 2 additions & 1 deletion cmd/podman/registry/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
)

const (
ParentNSRequired = "ParentNSRequired"
ParentNSRequired = "ParentNSRequired"
UnshareNSRequired = "UnshareNSRequired"
)

var (
Expand Down
2 changes: 1 addition & 1 deletion docs/source/markdown/links/podman-container-umount.1
Original file line number Diff line number Diff line change
@@ -1 +1 @@
.so man1/podman-umount.1
.so man1/podman-unmount.1
2 changes: 1 addition & 1 deletion docs/source/markdown/links/podman-container-unmount.1
Original file line number Diff line number Diff line change
@@ -1 +1 @@
.so man1/podman-umount.1
.so man1/podman-unmount.1
1 change: 1 addition & 0 deletions docs/source/markdown/links/podman-umount.1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.so man1/podman-unmount.1
1 change: 0 additions & 1 deletion docs/source/markdown/links/podman-unmount.1

This file was deleted.

2 changes: 1 addition & 1 deletion docs/source/markdown/podman-container.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The container command allows you to manage containers
| stats | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. |
| stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
| umount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem.(Alias unmount) |
| unmount | [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem.(Alias unmount) |
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |

Expand Down
9 changes: 6 additions & 3 deletions docs/source/markdown/podman-mount.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ podman\-mount - Mount a working container's root filesystem
Mounts the specified containers' root file system in a location which can be
accessed from the host, and returns its location.

If you execute the command without any arguments, the tool will list all of the
If you execute the command without any arguments, Podman will list all of the
currently mounted containers.

Rootless mode only supports mounting VFS driver, unless you enter the user namespace
via the `podman unshare` command. All other storage drivers will fail to mount.

## RETURN VALUE
The location of the mounted file system. On error an empty string and errno is
returned.
Expand All @@ -27,7 +30,7 @@ Mount all containers.

**--format**=*format*

Print the mounted containers in specified format (json)
Print the mounted containers in specified format (json).

**--latest**, **-l**

Expand Down Expand Up @@ -70,4 +73,4 @@ a7060253093b /var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd
```

## SEE ALSO
podman(1), podman-umount(1), mount(8)
podman(1), podman-umount(1), mount(8), podman-unshare(1)
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
% podman-umount(1)
% podman-unmount(1)

## NAME
podman\-umount - Unmount a working container's root filesystem
podman\-unmount - Unmount a working container's root filesystem

## SYNOPSIS
**podman umount** *container* [...]
**podman unmount** [*options*] *container* [...]

**podman container umount** *container* [...]
**podman umount** [*options*] *container* [...]

**podman container unmount** *container* [...]
**podman container unmount** [*options*] *container* [...]

**podman unmount** *container* [...]
**podman container umount** [*options*] *container* [...]

## DESCRIPTION
Unmounts the specified containers' root file system, if no other processes
are using it.

Container storage increments a mount counter each time a container is mounted.
When a container is unmounted, the mount counter is decremented and the
When a container is unmounted, the mount counter is decremented, and the
container's root filesystem is physically unmounted only when the mount
counter reaches zero indicating no other processes are using the mount.
An unmount can be forced with the --force flag.
Expand Down Expand Up @@ -45,11 +45,11 @@ The latest option is not supported on the remote client.

## EXAMPLE

podman umount containerID
podman container unmount containerID

podman umount containerID1 containerID2 containerID3
podman unmount containerID1 containerID2 containerID3

podman umount --all
podman unmount --all

## SEE ALSO
podman(1), podman-mount(1)
podman(1), podman-container-mount(1), podman-image-mount(1)
2 changes: 1 addition & 1 deletion docs/source/markdown/podman.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ the exit codes follow the `chroot` standard, see below:
| [podman-system(1)](podman-system.1.md) | Manage podman. |
| [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
| [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
| [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| [podman-unmount(1)](podman-unmount.1.md) | Unmount a working container's root filesystem. |
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. |
| [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
Expand Down
7 changes: 7 additions & 0 deletions pkg/rootless/rootless_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ can_use_shortcut ()
ret = false;
break;
}

if (argv[argc+1] != NULL && strcmp (argv[argc], "container") == 0 &&
strcmp (argv[argc+1], "mount") == 0)
{
ret = false;
break;
}
}

free (argv[0]);
Expand Down
62 changes: 62 additions & 0 deletions test/e2e/mount_rootless_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// +build !remote

package integration

import (
"os"

. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Podman mount", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
)

BeforeEach(func() {
if os.Geteuid() == 0 {
Skip("This function is not enabled for rootfull podman")
}
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
podmanTest.SeedImages()
})

AfterEach(func() {
podmanTest.Cleanup()
f := CurrentGinkgoTestDescription()
processTestResult(f)

})

It("podman mount", func() {
setup := podmanTest.Podman([]string{"create", ALPINE, "ls"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
cid := setup.OutputToString()

mount := podmanTest.Podman([]string{"mount", cid})
mount.WaitWithDefaultTimeout()
Expect(mount.ExitCode()).ToNot(Equal(0))
Expect(mount.ErrorToString()).To(ContainSubstring("podman unshare"))
})

It("podman unshare podman mount", func() {
setup := podmanTest.Podman([]string{"create", ALPINE, "ls"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
cid := setup.OutputToString()

session := podmanTest.Podman([]string{"unshare", PODMAN_BINARY, "mount", cid})
session.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
})
})
5 changes: 5 additions & 0 deletions test/e2e/mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ var _ = Describe("Podman mount", func() {
Expect(j.ExitCode()).To(Equal(0))
Expect(j.IsJSONOutputValid()).To(BeTrue())

j = podmanTest.Podman([]string{"mount", "--format='{{.foobar}}'"})
j.WaitWithDefaultTimeout()
Expect(j.ExitCode()).ToNot(Equal(0))
Expect(j.ErrorToString()).To(ContainSubstring("unknown --format"))

umount := podmanTest.Podman([]string{"umount", cid})
umount.WaitWithDefaultTimeout()
Expect(umount.ExitCode()).To(Equal(0))
Expand Down