Cleanup handling of podman mount/unmount

We should default to the user name unmount rather then the internal
name of umount.

Also User namespace was not being handled correctly. We want to inform
the user that if they do a mount when in rootless mode that they have
to be first in the podman unshare state.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2020-07-09 14:53:46 -04:00
parent 2b7bc9b101
commit 8f7ed50cb2
15 changed files with 144 additions and 48 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/containers/libpod/v2/cmd/podman/utils" "github.com/containers/libpod/v2/cmd/podman/utils"
"github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/cmd/podman/validate"
"github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@ -31,6 +32,7 @@ var (
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
}, },
Annotations: map[string]string{ Annotations: map[string]string{
registry.UnshareNSRequired: "",
registry.ParentNSRequired: "", registry.ParentNSRequired: "",
}, },
} }
@ -51,7 +53,7 @@ var (
func mountFlags(flags *pflag.FlagSet) { func mountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers") 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") flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output")
} }
@ -90,14 +92,21 @@ func mount(_ *cobra.Command, args []string) error {
} }
return errs.PrintErrors() return errs.PrintErrors()
} }
if mountOpts.Format == "json" {
switch mountOpts.Format {
case "json":
return printJSON(reports) return printJSON(reports)
case "":
// do nothing
default:
return errors.Errorf("unknown --format argument: %s", mountOpts.Format)
} }
mrs := make([]mountReporter, 0, len(reports)) mrs := make([]mountReporter, 0, len(reports))
for _, r := range reports { for _, r := range reports {
mrs = append(mrs, mountReporter{r}) mrs = append(mrs, mountReporter{r})
} }
row := "{{.ID}} {{.Path}}" row := "{{.ID}} {{.Path}}\n"
format := "{{range . }}" + row + "{{end}}" format := "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("mounts").Parse(format) tmpl, err := template.New("mounts").Parse(format)
if err != nil { if err != nil {

View File

@ -18,31 +18,32 @@ var (
An unmount can be forced with the --force flag. An unmount can be forced with the --force flag.
` `
umountCommand = &cobra.Command{ unmountCommand = &cobra.Command{
Use: "umount [flags] CONTAINER [CONTAINER...]", Use: "unmount [flags] CONTAINER [CONTAINER...]",
Aliases: []string{"unmount"}, Aliases: []string{"umount"},
Short: "Unmounts working container's root filesystem", Short: "Unmounts working container's root filesystem",
Long: description, Long: description,
RunE: unmount, RunE: unmount,
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
}, },
Example: `podman umount ctrID Example: `podman unmount ctrID
podman umount ctrID1 ctrID2 ctrID3 podman unmount ctrID1 ctrID2 ctrID3
podman umount --all`, podman unmount --all`,
} }
containerUnmountCommand = &cobra.Command{ containerUnmountCommand = &cobra.Command{
Use: umountCommand.Use, Use: unmountCommand.Use,
Short: umountCommand.Short, Short: unmountCommand.Short,
Long: umountCommand.Long, Aliases: unmountCommand.Aliases,
RunE: umountCommand.RunE, Long: unmountCommand.Long,
RunE: unmountCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
}, },
Example: `podman container umount ctrID Example: `podman container unmount ctrID
podman container umount ctrID1 ctrID2 ctrID3 podman container unmount ctrID1 ctrID2 ctrID3
podman container umount --all`, podman container unmount --all`,
} }
) )
@ -50,25 +51,25 @@ var (
unmountOpts entities.ContainerUnmountOptions unmountOpts entities.ContainerUnmountOptions
) )
func umountFlags(flags *pflag.FlagSet) { func unmountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Umount all of the currently mounted containers") flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Unmount all of the currently mounted containers")
flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers") flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete unmount of the specified mounted containers")
} }
func init() { func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{ registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode}, Mode: []entities.EngineMode{entities.ABIMode},
Command: umountCommand, Command: unmountCommand,
}) })
umountFlags(umountCommand.Flags()) unmountFlags(unmountCommand.Flags())
validate.AddLatestFlag(umountCommand, &unmountOpts.Latest) validate.AddLatestFlag(unmountCommand, &unmountOpts.Latest)
registry.Commands = append(registry.Commands, registry.CliCommand{ registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode}, Mode: []entities.EngineMode{entities.ABIMode},
Command: containerUnmountCommand, Command: containerUnmountCommand,
Parent: containerCmd, Parent: containerCmd,
}) })
umountFlags(containerUnmountCommand.Flags()) unmountFlags(containerUnmountCommand.Flags())
validate.AddLatestFlag(containerUnmountCommand, &unmountOpts.Latest) validate.AddLatestFlag(containerUnmountCommand, &unmountOpts.Latest)
} }

View File

@ -40,13 +40,21 @@ func main() {
for _, m := range c.Mode { for _, m := range c.Mode {
if cfg.EngineMode == m { if cfg.EngineMode == m {
// Command cannot be run rootless // Command cannot be run rootless
_, found := c.Command.Annotations[registry.ParentNSRequired] _, 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 { if rootless.IsRootless() && found {
c.Command.RunE = func(cmd *cobra.Command, args []string) error { c.Command.RunE = func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath()) return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath())
} }
} }
}
parent := rootCmd parent := rootCmd
if c.Parent != nil { if c.Parent != nil {
parent = c.Parent parent = c.Parent

View File

@ -16,6 +16,7 @@ import (
const ( const (
ParentNSRequired = "ParentNSRequired" ParentNSRequired = "ParentNSRequired"
UnshareNSRequired = "UnshareNSRequired"
) )
var ( var (

View File

@ -1 +1 @@
.so man1/podman-umount.1 .so man1/podman-unmount.1

View File

@ -1 +1 @@
.so man1/podman-umount.1 .so man1/podman-unmount.1

View File

@ -0,0 +1 @@
.so man1/podman-unmount.1

View File

@ -1 +0,0 @@
.so man1/podman-umount.1

View File

@ -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. | | 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. | | 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. | | 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. | | 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. | | wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |

View File

@ -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 Mounts the specified containers' root file system in a location which can be
accessed from the host, and returns its location. 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. 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 ## RETURN VALUE
The location of the mounted file system. On error an empty string and errno is The location of the mounted file system. On error an empty string and errno is
returned. returned.
@ -27,7 +30,7 @@ Mount all containers.
**--format**=*format* **--format**=*format*
Print the mounted containers in specified format (json) Print the mounted containers in specified format (json).
**--latest**, **-l** **--latest**, **-l**
@ -70,4 +73,4 @@ a7060253093b /var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd
``` ```
## SEE ALSO ## SEE ALSO
podman(1), podman-umount(1), mount(8) podman(1), podman-umount(1), mount(8), podman-unshare(1)

View File

@ -1,23 +1,23 @@
% podman-umount(1) % podman-unmount(1)
## NAME ## NAME
podman\-umount - Unmount a working container's root filesystem podman\-unmount - Unmount a working container's root filesystem
## SYNOPSIS ## SYNOPSIS
**podman umount** [*options*] *container* [...] **podman unmount** [*options*] *container* [...]
**podman container umount** [*options*] *container* [...] **podman umount** [*options*] *container* [...]
**podman container unmount** [*options*] *container* [...] **podman container unmount** [*options*] *container* [...]
**podman unmount** [*options*] *container* [...] **podman container umount** [*options*] *container* [...]
## DESCRIPTION ## DESCRIPTION
Unmounts the specified containers' root file system, if no other processes Unmounts the specified containers' root file system, if no other processes
are using it. are using it.
Container storage increments a mount counter each time a container is mounted. 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 container's root filesystem is physically unmounted only when the mount
counter reaches zero indicating no other processes are using the mount. counter reaches zero indicating no other processes are using the mount.
An unmount can be forced with the --force flag. An unmount can be forced with the --force flag.
@ -45,11 +45,11 @@ The latest option is not supported on the remote client.
## EXAMPLE ## 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 ## SEE ALSO
podman(1), podman-mount(1) podman(1), podman-container-mount(1), podman-image-mount(1)

View File

@ -207,7 +207,7 @@ the exit codes follow the `chroot` standard, see below:
| [podman-system(1)](podman-system.1.md) | Manage podman. | | [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-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-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-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-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. | | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |

View File

@ -210,6 +210,13 @@ can_use_shortcut ()
ret = false; ret = false;
break; break;
} }
if (argv[argc+1] != NULL && strcmp (argv[argc], "container") == 0 &&
strcmp (argv[argc+1], "mount") == 0)
{
ret = false;
break;
}
} }
free (argv[0]); free (argv[0]);

View File

@ -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))
})
})

View File

@ -80,6 +80,11 @@ var _ = Describe("Podman mount", func() {
Expect(j.ExitCode()).To(Equal(0)) Expect(j.ExitCode()).To(Equal(0))
Expect(j.IsJSONOutputValid()).To(BeTrue()) 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 := podmanTest.Podman([]string{"umount", cid})
umount.WaitWithDefaultTimeout() umount.WaitWithDefaultTimeout()
Expect(umount.ExitCode()).To(Equal(0)) Expect(umount.ExitCode()).To(Equal(0))