summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/volumes/exists.go2
-rw-r--r--cmd/podman/volumes/mount.go51
-rw-r--r--cmd/podman/volumes/unmount.go48
-rw-r--r--docs/source/markdown/podman-volume-mount.1.md28
-rw-r--r--docs/source/markdown/podman-volume-unmount.1.md27
-rw-r--r--docs/source/markdown/podman-volume.1.md2
-rw-r--r--libpod/volume.go13
-rw-r--r--libpod/volume_internal_linux.go1
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/volumes.go14
-rw-r--r--pkg/domain/infra/abi/volumes.go32
-rw-r--r--pkg/domain/infra/tunnel/volumes.go8
-rw-r--r--test/system/160-volumes.bats25
13 files changed, 252 insertions, 1 deletions
diff --git a/cmd/podman/volumes/exists.go b/cmd/podman/volumes/exists.go
index 3cac27220..b1bdf9da5 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 000000000..1f78187b8
--- /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 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()
+}
diff --git a/cmd/podman/volumes/unmount.go b/cmd/podman/volumes/unmount.go
new file mode 100644
index 000000000..dd0cebc06
--- /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 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()
+}
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 000000000..a5f35a34d
--- /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 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)**
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 000000000..e2fcd425f
--- /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 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)**
diff --git a/docs/source/markdown/podman-volume.1.md b/docs/source/markdown/podman-volume.1.md
index d8bc11aad..d05f007c8 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 d60d978ed..f79ceaa87 100644
--- a/libpod/volume.go
+++ b/libpod/volume.go
@@ -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)
+}
diff --git a/libpod/volume_internal_linux.go b/libpod/volume_internal_linux.go
index 60d3667a9..7d7dea9d0 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 21272d33f..7cf7ca17f 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -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)
}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 9bdce8392..f2e60a0db 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -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
+}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index ee7269807..19fc6d2d3 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 (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
+}
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index f4abeab0f..33e090148 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.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")
+}
diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats
index a3c972b3e..d0088b994 100644
--- a/test/system/160-volumes.bats
+++ b/test/system/160-volumes.bats
@@ -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