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

Add podman image mount #6851

Merged
merged 1 commit into from
Jul 29, 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
139 changes: 139 additions & 0 deletions cmd/podman/images/mount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package images

import (
"fmt"
"os"
"text/tabwriter"
"text/template"

"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var (
mountDescription = `podman image mount
Lists all mounted images mount points if no images is specified

podman image mount IMAGE-NAME-OR-ID
Mounts the specified image and prints the mountpoint
`

mountCommand = &cobra.Command{
Use: "mount [flags] [IMAGE...]",
Short: "Mount an images's root filesystem",
Long: mountDescription,
RunE: mount,
Example: `podman image mount imgID
podman image mount imgID1 imgID2 imgID3
podman image mount
podman image mount --all`,
Annotations: map[string]string{
registry.UnshareNSRequired: "",
registry.ParentNSRequired: "",
},
}
)

var (
mountOpts entities.ImageMountOptions
)

func mountFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all images")
flags.StringVar(&mountOpts.Format, "format", "", "Print the mounted images in specified format (json)")
}

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: mountCommand,
Parent: imageCmd,
})
mountFlags(mountCommand.Flags())
}

func mount(_ *cobra.Command, args []string) error {
var (
errs utils.OutputErrors
)
if len(args) > 0 && mountOpts.All {
return errors.New("when using the --all switch, you may not pass any image names or IDs")
}
reports, err := registry.ImageEngine().Mount(registry.GetContext(), args, mountOpts)
if err != nil {
return err
}
if len(args) > 0 || mountOpts.All {
for _, r := range reports {
if r.Err == nil {
fmt.Println(r.Path)
continue
}
errs = append(errs, r.Err)
}
return errs.PrintErrors()
}

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}}\n"
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.ImageMountReport) error {
type jreport struct {
ID string `json:"id"`
Names []string
Repositories []string
Mountpoint string `json:"mountpoint"`
}
jreports := make([]jreport, 0, len(reports))

for _, r := range reports {
jreports = append(jreports, jreport{
ID: r.Id,
Names: []string{r.Name},
Repositories: r.Repositories,
Mountpoint: r.Path,
})
}
b, err := json.MarshalIndent(jreports, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}

type mountReporter struct {
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
*entities.ImageMountReport
}

func (m mountReporter) ID() string {
if len(m.Repositories) > 0 {
return m.Repositories[0]
}
return m.Id
}
71 changes: 71 additions & 0 deletions cmd/podman/images/unmount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package images

import (
"fmt"

"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var (
description = `Image storage increments a mount counter each time an image is mounted.

When an image is unmounted, the mount counter is decremented. The image'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.
`
unmountCommand = &cobra.Command{
Use: "unmount [flags] IMAGE [IMAGE...]",
Aliases: []string{"umount"},
Short: "Unmount an image's root filesystem",
Long: description,
RunE: unmount,
Example: `podman unmount imgID
podman unmount imgID1 imgID2 imgID3
podman unmount --all`,
}
)

var (
unmountOpts entities.ImageUnmountOptions
)

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

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Parent: imageCmd,
Command: unmountCommand,
})
unmountFlags(unmountCommand.Flags())
}

func unmount(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
if len(args) < 1 && !unmountOpts.All {
return errors.New("image name or ID must be specified")
}
if len(args) > 0 && unmountOpts.All {
return errors.New("when using the --all switch, you may not pass any image names or IDs")
}
reports, err := registry.ImageEngine().Unmount(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()
}
53 changes: 53 additions & 0 deletions completions/bash/podman
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,56 @@ _podman_info() {
esac
}

_podman_image_umount() {
_podman_image_unmount
}

_podman_image_unmount() {
local boolean_options="
--all
-a
--help
-h
--force
-f
"
local options_with_args="
"

local all_options="$options_with_args $boolean_options"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
;;
*)
__podman_complete_images --force-tag --id
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
;;
esac
}

_podman_image_mount() {
local boolean_options="
--all
-a
--help
-h
"

local options_with_args="
--format
"

local all_options="$options_with_args $boolean_options"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
;;
*)
__podman_complete_images --force-tag --id
;;
esac
}

vrothberg marked this conversation as resolved.
Show resolved Hide resolved
_podman_image_build() {
_podman_build
}
Expand Down Expand Up @@ -1590,6 +1640,7 @@ _podman_image() {
inspect
load
ls
mount
prune
pull
push
Expand All @@ -1598,6 +1649,8 @@ _podman_image() {
sign
tag
trust
umount
unmount
untag
"
local aliases="
Expand Down
1 change: 1 addition & 0 deletions docs/source/markdown/links/podman-image-umount.1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.so man1/podman-image-unmount.1
76 changes: 76 additions & 0 deletions docs/source/markdown/podman-image-mount.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
% podman-image-mount(1)

## NAME
podman\-image\-mount - Mount an image's root filesystem

## SYNOPSIS
**podman image mount** [*options*] [*image* ...]

## DESCRIPTION
Mounts the specified images' 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, Podman will list all of the
currently mounted images.

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.

## OPTIONS

**--all**, **-a**

Mount all images.

**--format**=*format*

Print the mounted images in specified format (json).

## EXAMPLE

```
podman image mount fedora ubi8-init

/var/lib/containers/storage/overlay/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
/var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd7b5a125c19d887f17653cbcd5b6e30ba1/merged
```

```
podman mount

registry.fedoraproject.org/fedora:latest /var/lib/containers/storage/overlay/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
registry.access.redhat.com/ubi8-init:latest /var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd7b5a125c19d887f17653cbcd5b6e30ba1/merged
```

```
podman image mount --format json
[
{
"id": "00ff39a8bf19f810a7e641f7eb3ddc47635913a19c4996debd91fafb6b379069",
"Names": [
"sha256:58de585a231aca14a511347bc85b912a6f000159b49bc2b0582032911e5d3a6c"
],
"Repositories": [
"registry.fedoraproject.org/fedora:latest"
],
"mountpoint": "/var/lib/containers/storage/overlay/0ccfac04663bbe8813b5f24502ee0b7371ce5bf3c5adeb12e4258d191c2cf7bc/merged"
},
{
"id": "bcc2dc9a261774ad25a15e07bb515f9b77424266abf2a1252ec7bcfed1dd0ac2",
"Names": [
"sha256:d5f260b2e51b3ee9d05de1c31d261efc9af28e7d2d47cedf054c496d71424d63"
],
"Repositories": [
"registry.access.redhat.com/ubi8-init:latest"
],
"mountpoint": "/var/lib/containers/storage/overlay/d66b58e3391ea8ce4c81316c72e22b332618f2a28b461a32ed673e8998cdaeb8/merged"
}
]
```

## SEE ALSO
podman(1), podman-image-umount(1), mount(8), podman-unshare(1)
Loading