diff --git a/cmd/podman/volumes/exists.go b/cmd/podman/volumes/exists.go index 3cac2722072f..b1bdf9da5a3d 100644 --- a/cmd/podman/volumes/exists.go +++ b/cmd/podman/volumes/exists.go @@ -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`, diff --git a/cmd/podman/volumes/mount.go b/cmd/podman/volumes/mount.go new file mode 100644 index 000000000000..0270ef90b39c --- /dev/null +++ b/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 volume", + 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() +} diff --git a/cmd/podman/volumes/unmount.go b/cmd/podman/volumes/unmount.go new file mode 100644 index 000000000000..a72fd55cee49 --- /dev/null +++ b/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 volume", + 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() +} diff --git a/docs/source/markdown/podman-volume-mount.1.md b/docs/source/markdown/podman-volume-mount.1.md new file mode 100644 index 000000000000..13e6d1abb941 --- /dev/null +++ b/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 type 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)** diff --git a/docs/source/markdown/podman-volume-unmount.1.md b/docs/source/markdown/podman-volume-unmount.1.md new file mode 100644 index 000000000000..5002f03c1491 --- /dev/null +++ b/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 an 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)** diff --git a/docs/source/markdown/podman-volume.1.md b/docs/source/markdown/podman-volume.1.md index d8bc11aad7e1..d05f007c83c2 100644 --- a/docs/source/markdown/podman-volume.1.md +++ b/docs/source/markdown/podman-volume.1.md @@ -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)** diff --git a/libpod/volume.go b/libpod/volume.go index d60d978ed22d..e88bc80083fa 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -255,3 +255,11 @@ 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) { + return v.config.MountPoint, v.mount() +} + +func (v *Volume) Unmount() error { + return v.unmount(false) +} diff --git a/libpod/volume_internal_linux.go b/libpod/volume_internal_linux.go index 60d3667a99ce..7d7dea9d0849 100644 --- a/libpod/volume_internal_linux.go +++ b/libpod/volume_internal_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package libpod diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5dedceaccfdd..d2c08a5a9b30 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -98,6 +98,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) ([]*ContainerMountReport, 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) ([]*ContainerUnmountReport, error) } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index ee7269807c56..6b95acdace84 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -178,3 +178,35 @@ func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) ( } return &entities.BoolReport{Value: false}, nil } + +func (ir *ContainerEngine) VolumeMount(ctx context.Context, nameOrIDs []string) ([]*entities.ContainerMountReport, error) { + reports := []*entities.ContainerMountReport{} + for _, name := range nameOrIDs { + report := entities.ContainerMountReport{Id: name} + vol, err := ir.Libpod.LookupVolume(name) + if err != nil { + report.Err = err + } else { + report.Path, report.Err = vol.Mount() + } + reports = append(reports, &report) + } + + return reports, nil +} + +func (ir *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string) ([]*entities.ContainerUnmountReport, error) { + reports := []*entities.ContainerUnmountReport{} + for _, name := range nameOrIDs { + report := entities.ContainerUnmountReport{Id: name} + vol, err := ir.Libpod.LookupVolume(name) + if err != nil { + report.Err = err + } else { + report.Err = vol.Unmount() + } + reports = append(reports, &report) + } + + return reports, nil +} diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index f4abeab0fca8..758893a8e348 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -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.ContainerMountReport, error) { + return nil, errors.New("mounting volumes is not supported for remote clients") +} + +func (ic *ContainerEngine) VolumeUnmount(ctx context.Context, nameOrIDs []string) ([]*entities.ContainerUnmountReport, error) { + return nil, errors.New("unmounting volumes is not supported for remote clients") +} diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats index a3c972b3e0a0..054b42fed352 100644 --- a/test/system/160-volumes.bats +++ b/test/system/160-volumes.bats @@ -387,4 +387,13 @@ NeedsChown | true run_podman volume rm $myvolume } +@test "podman volume mount" { + skip_if_remote "podman --remote volume mount not supported" + run_podman volume mount ${volume} + root_single_job=$output + + run_podman volume unmount ${volume} + root_multi_job=$output +} + # vim: filetype=sh