mirror of
https://github.com/containers/podman.git
synced 2025-06-20 00:51:16 +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
|
||||
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)
|
||||
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, 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)
|
||||
ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) 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)
|
||||
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, 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)
|
||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -21,9 +22,11 @@ import (
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
||||
"github.com/containers/libpod/pkg/ps"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/pkg/signal"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/containers/libpod/pkg/specgen/generate"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -808,3 +811,85 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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