mirror of
https://github.com/containers/podman.git
synced 2025-07-04 01:48:28 +08:00
Merge pull request #7085 from rhatdan/cmount
Cleanup handling of podman mount/unmount
This commit is contained in:
@ -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,7 +32,8 @@ var (
|
|||||||
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
|
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
|
||||||
},
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
registry.ParentNSRequired: "",
|
registry.UnshareNSRequired: "",
|
||||||
|
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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 rootless.IsRootless() && found {
|
if found {
|
||||||
c.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
if rootless.IsRootless() && found && os.Getuid() != 0 {
|
||||||
return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath())
|
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
|
parent := rootCmd
|
||||||
if c.Parent != nil {
|
if c.Parent != nil {
|
||||||
parent = c.Parent
|
parent = c.Parent
|
||||||
|
@ -15,7 +15,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ParentNSRequired = "ParentNSRequired"
|
ParentNSRequired = "ParentNSRequired"
|
||||||
|
UnshareNSRequired = "UnshareNSRequired"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1 +1 @@
|
|||||||
.so man1/podman-umount.1
|
.so man1/podman-unmount.1
|
||||||
|
@ -1 +1 @@
|
|||||||
.so man1/podman-umount.1
|
.so man1/podman-unmount.1
|
||||||
|
1
docs/source/markdown/links/podman-umount.1
Normal file
1
docs/source/markdown/links/podman-umount.1
Normal file
@ -0,0 +1 @@
|
|||||||
|
.so man1/podman-unmount.1
|
@ -1 +0,0 @@
|
|||||||
.so man1/podman-umount.1
|
|
@ -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. |
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
@ -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. |
|
||||||
|
@ -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]);
|
||||||
|
62
test/e2e/mount_rootless_test.go
Normal file
62
test/e2e/mount_rootless_test.go
Normal 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))
|
||||||
|
})
|
||||||
|
})
|
@ -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))
|
||||||
|
Reference in New Issue
Block a user