Skip to content

Commit

Permalink
Add podman volume mount support
Browse files Browse the repository at this point in the history
Fixes: containers#12768

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
  • Loading branch information
rhatdan authored and Keon Chen committed Mar 29, 2022
1 parent dc0c506 commit 94e5f2a
Show file tree
Hide file tree
Showing 13 changed files with 252 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cmd/podman/volumes/exists.go
Expand Up @@ -10,7 +10,7 @@ var (
volumeExistsDescription = `If the given volume exists, podman volume exists exits with 0, otherwise the exit code will be 1.`
volumeExistsCommand = &cobra.Command{
Use: "exists VOLUME",
Short: "volume exists",
Short: "Volume exists",
Long: volumeExistsDescription,
RunE: volumeExists,
Example: `podman volume exists myvol`,
Expand Down
51 changes: 51 additions & 0 deletions cmd/podman/volumes/mount.go
@@ -0,0 +1,51 @@
package volumes

import (
"fmt"

"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/spf13/cobra"
)

var (
volumeMountDescription = `Mount a volume and return the mountpoint`
volumeMountCommand = &cobra.Command{
Annotations: map[string]string{
registry.UnshareNSRequired: "",
registry.ParentNSRequired: "",
registry.EngineMode: registry.ABIMode,
},
Use: "mount NAME",
Short: "Mount volume",
Long: volumeMountDescription,
RunE: volumeMount,
Example: `podman volume mount myvol`,
Args: cobra.ExactArgs(1),
ValidArgsFunction: common.AutocompleteVolumes,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: volumeMountCommand,
Parent: volumeCmd,
})
}

func volumeMount(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
reports, err := registry.ContainerEngine().VolumeMount(registry.GetContext(), args)
if err != nil {
return err
}
for _, r := range reports {
if r.Err == nil {
fmt.Println(r.Path)
continue
}
errs = append(errs, r.Err)
}
return errs.PrintErrors()
}
48 changes: 48 additions & 0 deletions cmd/podman/volumes/unmount.go
@@ -0,0 +1,48 @@
package volumes

import (
"fmt"

"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/spf13/cobra"
)

var (
volumeUnmountDescription = `Unmount a volume`
volumeUnmountCommand = &cobra.Command{
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
Use: "unmount NAME",
Short: "Unmount volume",
Long: volumeUnmountDescription,
RunE: volumeUnmount,
Example: `podman volume unmount myvol`,
Args: cobra.ExactArgs(1),
ValidArgsFunction: common.AutocompleteVolumes,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: volumeUnmountCommand,
Parent: volumeCmd,
})
}

func volumeUnmount(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
reports, err := registry.ContainerEngine().VolumeUnmount(registry.GetContext(), args)
if err != nil {
return err
}
for _, r := range reports {
var errs utils.OutputErrors
if r.Err == nil {
fmt.Println(r.Id)
} else {
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}
28 changes: 28 additions & 0 deletions docs/source/markdown/podman-volume-mount.1.md
@@ -0,0 +1,28 @@
% podman-volume-mount(1)

## NAME
podman\-volume\-mount - Mount a volume filesystem

## SYNOPSIS
**podman volume mount** [*volume* ...]

## DESCRIPTION
Mounts the specified volumes' file system in a location which can be
accessed from the host, and returns its location.

Rootless mode only supports mounting file volumes, unless you enter the user namespace
via the `podman unshare` command. All other volume types will fail to mount.

## RETURN VALUE
The location of the mounted file system. On error an empty string and errno is
returned.

## EXAMPLE

```
podman volume mount foo
/home/dwalsh/.local/share/containers/storage/volumes/foo/_data
```

## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**, **[podman-volume-unmount(1)](podman-volume-unmount.1.md)**, **[podman-unshare(1)](podman-unshare.1.md)**, **mount(8)**
27 changes: 27 additions & 0 deletions docs/source/markdown/podman-volume-unmount.1.md
@@ -0,0 +1,27 @@
% podman-volume-unmount(1)

## NAME
podman\-volume\-unmount - Unmount a volume

## SYNOPSIS
**podman volume unmount** *volume* [...]

**podman volume umount** *volume* [...]

## DESCRIPTION
Unmounts the specified volume, if there are no other containers
using it.

Volume storage increments a mount counter each time a volume is mounted.
When a volume is unmounted, the mount counter is decremented, and the
volume's filesystem is physically unmounted only when the mount
counter reaches zero indicating no other processes are using the mount.

## EXAMPLE

podman volume unmount volumeID

podman volume unmount volumeID1 volumeID2 volumeID3

## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**, **[podman-volume-mount(1)](podman-volume-mount.1.md)**
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-volume.1.md
Expand Up @@ -19,8 +19,10 @@ podman volume is a set of subcommands that manage volumes.
| import | [podman-volume-import(1)](podman-volume-import.1.md) | Import tarball contents into a podman volume. |
| inspect | [podman-volume-inspect(1)](podman-volume-inspect.1.md) | Get detailed information on one or more volumes. |
| ls | [podman-volume-ls(1)](podman-volume-ls.1.md) | List all the available volumes. |
| mount | [podman-volume-mount(1)](podman-volume-mount.1.md) | Mount a volume filesystem. |
| prune | [podman-volume-prune(1)](podman-volume-prune.1.md) | Remove all unused volumes. |
| rm | [podman-volume-rm(1)](podman-volume-rm.1.md) | Remove one or more volumes. |
| unmount | [podman-volume-unmount(1)](podman-volume-unmount.1.md) | Unmount a volume. |

## SEE ALSO
**[podman(1)](podman.1.md)**
Expand Down
13 changes: 13 additions & 0 deletions libpod/volume.go
Expand Up @@ -255,3 +255,16 @@ func (v *Volume) IsDangling() (bool, error) {
func (v *Volume) UsesVolumeDriver() bool {
return !(v.config.Driver == define.VolumeDriverLocal || v.config.Driver == "")
}

func (v *Volume) Mount() (string, error) {
v.lock.Lock()
defer v.lock.Unlock()
err := v.mount()
return v.config.MountPoint, err
}

func (v *Volume) Unmount() error {
v.lock.Lock()
defer v.lock.Unlock()
return v.unmount(false)
}
1 change: 1 addition & 0 deletions libpod/volume_internal_linux.go
@@ -1,3 +1,4 @@
//go:build linux
// +build linux

package libpod
Expand Down
2 changes: 2 additions & 0 deletions pkg/domain/entities/engine_container.go
Expand Up @@ -99,6 +99,8 @@ type ContainerEngine interface {
VolumeMounted(ctx context.Context, namesOrID string) (*BoolReport, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
VolumeMount(ctx context.Context, namesOrIds []string) ([]*VolumeMountReport, error)
VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
VolumeUnmount(ctx context.Context, namesOrIds []string) ([]*VolumeUnmountReport, error)
}
14 changes: 14 additions & 0 deletions pkg/domain/entities/volumes.go
Expand Up @@ -187,3 +187,17 @@ type VolumeCreateBody struct {
// Required: true
Name string `json:"Name"`
}

// VolumeMountReport describes the response from volume mount
type VolumeMountReport struct {
Err error
Id string //nolint
Name string
Path string
}

// VolumeUnmountReport describes the response from umounting a volume
type VolumeUnmountReport struct {
Err error
Id string //nolint
}
32 changes: 32 additions & 0 deletions pkg/domain/infra/abi/volumes.go
Expand Up @@ -178,3 +178,35 @@ func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) (
}
return &entities.BoolReport{Value: false}, nil
}

func (ic *ContainerEngine) VolumeMount(ctx context.Context, nameOrIDs []string) ([]*entities.VolumeMountReport, error) {
reports := []*entities.VolumeMountReport{}
for _, name := range nameOrIDs {
report := entities.VolumeMountReport{Id: name}
vol, err := ic.Libpod.LookupVolume(name)
if err != nil {
report.Err = err
} else {
report.Path, report.Err = vol.Mount()
}
reports = append(reports, &report)
}

return reports, nil
}

func (ic *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string) ([]*entities.VolumeUnmountReport, error) {
reports := []*entities.VolumeUnmountReport{}
for _, name := range nameOrIDs {
report := entities.VolumeUnmountReport{Id: name}
vol, err := ic.Libpod.LookupVolume(name)
if err != nil {
report.Err = err
} else {
report.Err = vol.Unmount()
}
reports = append(reports, &report)
}

return reports, nil
}
8 changes: 8 additions & 0 deletions pkg/domain/infra/tunnel/volumes.go
Expand Up @@ -100,3 +100,11 @@ func (ic *ContainerEngine) VolumeExists(ctx context.Context, nameOrID string) (*
func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
return nil, errors.New("not implemented")
}

func (ic *ContainerEngine) VolumeMount(ctx context.Context, nameOrIDs []string) ([]*entities.VolumeMountReport, error) {
return nil, errors.New("mounting volumes is not supported for remote clients")
}

func (ic *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string) ([]*entities.VolumeUnmountReport, error) {
return nil, errors.New("unmounting volumes is not supported for remote clients")
}
25 changes: 25 additions & 0 deletions test/system/160-volumes.bats
Expand Up @@ -387,4 +387,29 @@ NeedsChown | true
run_podman volume rm $myvolume
}

@test "podman volume mount" {
skip_if_remote "podman --remote volume mount not supported"
myvolume=myvol$(random_string)
myfile=myfile$(random_string)
mytext=$(random_string)

# Create a named volume
run_podman volume create $myvolume
is "$output" "$myvolume" "output from volume create"

if ! is_rootless ; then
# image mount is hard to test as a rootless user
# and does not work remotely
run_podman volume mount ${myvolume}
mnt=${output}
echo $mytext >$mnt/$myfile
run_podman run -v ${myvolume}:/vol:z $IMAGE cat /vol/$myfile
is "$output" "$mytext" "$myfile should exist within the containers volume and contain $mytext"
run_podman volume unmount ${myvolume}
else
run_podman 125 volume mount ${myvolume}
is "$output" "Error: cannot run command \"podman volume mount\" in rootless mode, must execute.*podman unshare.*first" "Should fail and complain about unshare"
fi
}

# vim: filetype=sh

0 comments on commit 94e5f2a

Please sign in to comment.