mirror of
https://github.com/containers/podman.git
synced 2025-06-20 17:13:43 +08:00
podmanv2 mount and umount
add the ability to mount and unmount containers for the local client only Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
121
cmd/podmanV2/containers/mount.go
Normal file
121
cmd/podmanV2/containers/mount.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/tabwriter"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/parse"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/utils"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mountDescription = `podman mount
|
||||||
|
Lists all mounted containers mount points if no container is specified
|
||||||
|
|
||||||
|
podman mount CONTAINER-NAME-OR-ID
|
||||||
|
Mounts the specified container and outputs the mountpoint
|
||||||
|
`
|
||||||
|
|
||||||
|
mountCommand = &cobra.Command{
|
||||||
|
Use: "mount [flags] [CONTAINER]",
|
||||||
|
Short: "Mount a working container's root filesystem",
|
||||||
|
Long: mountDescription,
|
||||||
|
PreRunE: preRunE,
|
||||||
|
RunE: mount,
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mountOpts entities.ContainerMountOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: mountCommand,
|
||||||
|
})
|
||||||
|
flags := mountCommand.Flags()
|
||||||
|
flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers")
|
||||||
|
flags.StringVar(&mountOpts.Format, "format", "", "Change the output format to Go template")
|
||||||
|
flags.BoolVarP(&mountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
|
||||||
|
flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output")
|
||||||
|
}
|
||||||
|
|
||||||
|
func mount(cmd *cobra.Command, args []string) error {
|
||||||
|
var (
|
||||||
|
errs utils.OutputErrors
|
||||||
|
mrs []mountReporter
|
||||||
|
)
|
||||||
|
reports, err := registry.ContainerEngine().ContainerMount(registry.GetContext(), args, mountOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(args) > 0 || mountOpts.Latest || mountOpts.All {
|
||||||
|
for _, r := range reports {
|
||||||
|
if r.Err == nil {
|
||||||
|
fmt.Println(r.Path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errs = append(errs, r.Err)
|
||||||
|
}
|
||||||
|
return errs.PrintErrors()
|
||||||
|
}
|
||||||
|
if mountOpts.Format == "json" {
|
||||||
|
return printJSON(reports)
|
||||||
|
}
|
||||||
|
for _, r := range reports {
|
||||||
|
mrs = append(mrs, mountReporter{r})
|
||||||
|
}
|
||||||
|
row := "{{.ID}} {{.Path}}"
|
||||||
|
format := "{{range . }}" + row + "{{end}}"
|
||||||
|
tmpl, err := template.New("mounts").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
|
||||||
|
defer w.Flush()
|
||||||
|
return tmpl.Execute(w, mrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJSON(reports []*entities.ContainerMountReport) error {
|
||||||
|
type jreport struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Names []string
|
||||||
|
Mountpoint string `json:"mountpoint"`
|
||||||
|
}
|
||||||
|
var jreports []jreport
|
||||||
|
|
||||||
|
for _, r := range reports {
|
||||||
|
jreports = append(jreports, jreport{
|
||||||
|
ID: r.Id,
|
||||||
|
Names: []string{r.Name},
|
||||||
|
Mountpoint: r.Path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
b, err := json.MarshalIndent(jreports, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mountReporter struct {
|
||||||
|
*entities.ContainerMountReport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mountReporter) ID() string {
|
||||||
|
if mountOpts.NoTruncate {
|
||||||
|
return m.Id
|
||||||
|
}
|
||||||
|
return m.Id[0:12]
|
||||||
|
}
|
65
cmd/podmanV2/containers/unmount.go
Normal file
65
cmd/podmanV2/containers/unmount.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/parse"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/utils"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
description = `Container storage increments a mount counter each time a container is mounted.
|
||||||
|
|
||||||
|
When a container is unmounted, the mount counter is decremented. 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.
|
||||||
|
`
|
||||||
|
umountCommand = &cobra.Command{
|
||||||
|
Use: "umount [flags] CONTAINER [CONTAINER...]",
|
||||||
|
Aliases: []string{"unmount"},
|
||||||
|
Short: "Unmounts working container's root filesystem",
|
||||||
|
Long: description,
|
||||||
|
RunE: unmount,
|
||||||
|
PreRunE: preRunE,
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
|
||||||
|
},
|
||||||
|
Example: `podman umount ctrID
|
||||||
|
podman umount ctrID1 ctrID2 ctrID3
|
||||||
|
podman umount --all`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
unmountOpts entities.ContainerUnmountOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: umountCommand,
|
||||||
|
})
|
||||||
|
flags := umountCommand.Flags()
|
||||||
|
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")
|
||||||
|
flags.BoolVarP(&unmountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmount(cmd *cobra.Command, args []string) error {
|
||||||
|
var errs utils.OutputErrors
|
||||||
|
reports, err := registry.ContainerEngine().ContainerUnmount(registry.GetContext(), args, unmountOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, r := range reports {
|
||||||
|
if r.Err == nil {
|
||||||
|
fmt.Println(r.Id)
|
||||||
|
} else {
|
||||||
|
errs = append(errs, r.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errs.PrintErrors()
|
||||||
|
}
|
@ -297,3 +297,33 @@ type ContainerInitReport struct {
|
|||||||
Err error
|
Err error
|
||||||
Id string
|
Id string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ContainerMountOptions describes the input values for mounting containers
|
||||||
|
// in the CLI
|
||||||
|
type ContainerMountOptions struct {
|
||||||
|
All bool
|
||||||
|
Format string
|
||||||
|
Latest bool
|
||||||
|
NoTruncate bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerUnmountOptions are the options from the cli for unmounting
|
||||||
|
type ContainerUnmountOptions struct {
|
||||||
|
All bool
|
||||||
|
Force bool
|
||||||
|
Latest bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerMountReport describes the response from container mount
|
||||||
|
type ContainerMountReport struct {
|
||||||
|
Err error
|
||||||
|
Id string
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerUnmountReport describes the response from umounting a container
|
||||||
|
type ContainerUnmountReport struct {
|
||||||
|
Err error
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ type ContainerEngine interface {
|
|||||||
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
|
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
|
||||||
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
|
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
|
||||||
ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error)
|
ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error)
|
||||||
|
ContainerMount(ctx context.Context, nameOrIds []string, options ContainerMountOptions) ([]*ContainerMountReport, error)
|
||||||
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||||
ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error
|
ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error
|
||||||
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
|
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
|
||||||
@ -30,6 +31,7 @@ type ContainerEngine interface {
|
|||||||
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
|
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
|
||||||
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
|
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
|
||||||
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
|
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
|
||||||
|
ContainerUnmount(ctx context.Context, nameOrIds []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error)
|
||||||
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||||
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -21,9 +22,11 @@ import (
|
|||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
||||||
"github.com/containers/libpod/pkg/ps"
|
"github.com/containers/libpod/pkg/ps"
|
||||||
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/containers/libpod/pkg/signal"
|
"github.com/containers/libpod/pkg/signal"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
"github.com/containers/libpod/pkg/specgen/generate"
|
"github.com/containers/libpod/pkg/specgen/generate"
|
||||||
|
"github.com/containers/storage"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -808,3 +811,85 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
|
|||||||
}
|
}
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) {
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
if driver := ic.Libpod.StorageConfig().GraphDriverName; driver != "vfs" {
|
||||||
|
// Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part
|
||||||
|
// of the mount command.
|
||||||
|
return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
became, ret, err := rootless.BecomeRootInUserNS("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if became {
|
||||||
|
os.Exit(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var reports []*entities.ContainerMountReport
|
||||||
|
ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ctr := range ctrs {
|
||||||
|
report := entities.ContainerMountReport{Id: ctr.ID()}
|
||||||
|
report.Path, report.Err = ctr.Mount()
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
if len(reports) > 0 {
|
||||||
|
return reports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No containers were passed, so we send back what is mounted
|
||||||
|
ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ctr := range ctrs {
|
||||||
|
mounted, path, err := ctr.Mounted()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mounted {
|
||||||
|
reports = append(reports, &entities.ContainerMountReport{
|
||||||
|
Id: ctr.ID(),
|
||||||
|
Name: ctr.Name(),
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
|
||||||
|
var reports []*entities.ContainerUnmountReport
|
||||||
|
ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ctr := range ctrs {
|
||||||
|
state, err := ctr.State()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if state == define.ContainerStateRunning {
|
||||||
|
logrus.Debugf("Error umounting container %s, is running", ctr.ID())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ContainerUnmountReport{Id: ctr.ID()}
|
||||||
|
if err := ctr.Unmount(options.Force); err != nil {
|
||||||
|
if options.All && errors.Cause(err) == storage.ErrLayerNotMounted {
|
||||||
|
logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
report.Err = errors.Wrapf(err, "error unmounting container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
return reports, nil
|
||||||
|
}
|
||||||
|
@ -354,3 +354,11 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
|
|||||||
}
|
}
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) {
|
||||||
|
return nil, errors.New("mounting containers is not supported for remote clients")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
|
||||||
|
return nil, errors.New("unmounting containers is not supported for remote clients")
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user