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 volume mount support #13318

Merged
merged 1 commit into from
Mar 1, 2022
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
2 changes: 1 addition & 1 deletion cmd/podman/volumes/exists.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test doesn't really do anything when rootless. Why not just skip_if_rootless?

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