mirror of
https://github.com/containers/podman.git
synced 2025-06-17 15:08:08 +08:00
Merge pull request #8185 from rhatdan/mount
Add support for mounting external containers
This commit is contained in:
@ -13,7 +13,9 @@ Mounts the specified containers' root file system in a location which can be
|
|||||||
accessed from the host, and returns its location.
|
accessed from the host, and returns its location.
|
||||||
|
|
||||||
If you execute the command without any arguments, Podman will list all of the
|
If you execute the command without any arguments, Podman will list all of the
|
||||||
currently mounted containers.
|
currently mounted containers, including external containers. External containers are
|
||||||
|
containers in container/storage by tools other then Podman. For example Buildah and
|
||||||
|
CRI-O.
|
||||||
|
|
||||||
Rootless mode only supports mounting VFS driver, unless you enter the user namespace
|
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.
|
via the `podman unshare` command. All other storage drivers will fail to mount.
|
||||||
@ -26,7 +28,7 @@ returned.
|
|||||||
|
|
||||||
**--all**, **-a**
|
**--all**, **-a**
|
||||||
|
|
||||||
Mount all containers.
|
Mount all podman containers. (External containers will not be mounted)
|
||||||
|
|
||||||
**--format**=*format*
|
**--format**=*format*
|
||||||
|
|
||||||
|
@ -22,6 +22,10 @@ container's root filesystem is physically unmounted only when the mount
|
|||||||
counter reaches zero indicating no other processes are using the mount.
|
counter reaches zero indicating no other processes are using the mount.
|
||||||
An unmount can be forced with the --force flag.
|
An unmount can be forced with the --force flag.
|
||||||
|
|
||||||
|
Note: Podman can be used to unmount Podman containers as well as external containers.
|
||||||
|
External containers are containers created in container/storage by other tools like
|
||||||
|
Buildah and CRI-O.
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
**--all**, **-a**
|
**--all**, **-a**
|
||||||
|
|
||||||
|
@ -918,6 +918,56 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int
|
|||||||
return prunedContainers, pruneErrors, nil
|
return prunedContainers, pruneErrors, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MountStorageContainer mounts the storage container's root filesystem
|
||||||
|
func (r *Runtime) MountStorageContainer(id string) (string, error) {
|
||||||
|
if _, err := r.GetContainer(id); err == nil {
|
||||||
|
return "", errors.Wrapf(define.ErrCtrExists, "ctr %s is a libpod container", id)
|
||||||
|
}
|
||||||
|
container, err := r.store.Container(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
mountPoint, err := r.store.Mount(container.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error mounting storage for container %s", id)
|
||||||
|
}
|
||||||
|
return mountPoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmountStorageContainer unmounts the storage container's root filesystem
|
||||||
|
func (r *Runtime) UnmountStorageContainer(id string, force bool) (bool, error) {
|
||||||
|
if _, err := r.GetContainer(id); err == nil {
|
||||||
|
return false, errors.Wrapf(define.ErrCtrExists, "ctr %s is a libpod container", id)
|
||||||
|
}
|
||||||
|
container, err := r.store.Container(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return r.store.Unmount(container.ID, force)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountedStorageContainer returns whether a storage container is mounted
|
||||||
|
// along with the mount path
|
||||||
|
func (r *Runtime) IsStorageContainerMounted(id string) (bool, string, error) {
|
||||||
|
var path string
|
||||||
|
if _, err := r.GetContainer(id); err == nil {
|
||||||
|
return false, "", errors.Wrapf(define.ErrCtrExists, "ctr %s is a libpod container", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
mountCnt, err := r.storageService.MountedContainerImage(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
mounted := mountCnt > 0
|
||||||
|
if mounted {
|
||||||
|
path, err = r.storageService.GetMountpoint(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mounted, path, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StorageContainers returns a list of containers from containers/storage that
|
// StorageContainers returns a list of containers from containers/storage that
|
||||||
// are not currently known to Podman.
|
// are not currently known to Podman.
|
||||||
func (r *Runtime) StorageContainers() ([]storage.Container, error) {
|
func (r *Runtime) StorageContainers() ([]storage.Container, error) {
|
||||||
|
@ -1058,11 +1058,23 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
|
|||||||
os.Exit(ret)
|
os.Exit(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod)
|
reports := []*entities.ContainerMountReport{}
|
||||||
|
// Attempt to mount named containers directly from storage,
|
||||||
|
// this will fail and code will fall through to removing the container from libpod.`
|
||||||
|
names := []string{}
|
||||||
|
for _, ctr := range nameOrIDs {
|
||||||
|
report := entities.ContainerMountReport{Id: ctr}
|
||||||
|
if report.Path, report.Err = ic.Libpod.MountStorageContainer(ctr); report.Err != nil {
|
||||||
|
names = append(names, ctr)
|
||||||
|
} else {
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reports := make([]*entities.ContainerMountReport, 0, len(ctrs))
|
|
||||||
for _, ctr := range ctrs {
|
for _, ctr := range ctrs {
|
||||||
report := entities.ContainerMountReport{Id: ctr.ID()}
|
report := entities.ContainerMountReport{Id: ctr.ID()}
|
||||||
report.Path, report.Err = ctr.Mount()
|
report.Path, report.Err = ctr.Mount()
|
||||||
@ -1072,6 +1084,30 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
|
|||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storageCtrs, err := ic.Libpod.StorageContainers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sctr := range storageCtrs {
|
||||||
|
mounted, path, err := ic.Libpod.IsStorageContainerMounted(sctr.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
if len(sctr.Names) > 0 {
|
||||||
|
name = sctr.Names[0]
|
||||||
|
}
|
||||||
|
if mounted {
|
||||||
|
reports = append(reports, &entities.ContainerMountReport{
|
||||||
|
Id: sctr.ID,
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No containers were passed, so we send back what is mounted
|
// No containers were passed, so we send back what is mounted
|
||||||
ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod)
|
ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1091,15 +1127,44 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
|
func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) {
|
||||||
ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod)
|
reports := []*entities.ContainerUnmountReport{}
|
||||||
|
names := []string{}
|
||||||
|
if options.All {
|
||||||
|
storageCtrs, err := ic.Libpod.StorageContainers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, sctr := range storageCtrs {
|
||||||
|
mounted, _, _ := ic.Libpod.IsStorageContainerMounted(sctr.ID)
|
||||||
|
if mounted {
|
||||||
|
report := entities.ContainerUnmountReport{Id: sctr.ID}
|
||||||
|
if _, report.Err = ic.Libpod.UnmountStorageContainer(sctr.ID, options.Force); report.Err != nil {
|
||||||
|
if errors.Cause(report.Err) != define.ErrCtrExists {
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ctr := range nameOrIDs {
|
||||||
|
report := entities.ContainerUnmountReport{Id: ctr}
|
||||||
|
if _, report.Err = ic.Libpod.UnmountStorageContainer(ctr, options.Force); report.Err != nil {
|
||||||
|
names = append(names, ctr)
|
||||||
|
} else {
|
||||||
|
reports = append(reports, &report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reports := []*entities.ContainerUnmountReport{}
|
|
||||||
for _, ctr := range ctrs {
|
for _, ctr := range ctrs {
|
||||||
state, err := ctr.State()
|
state, err := ctr.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
load helpers
|
load helpers
|
||||||
|
|
||||||
|
|
||||||
@test "podman mount - basic test" {
|
@test "podman mount - basic test" {
|
||||||
# Only works with root (FIXME: does it work with rootless + vfs?)
|
# Only works with root (FIXME: does it work with rootless + vfs?)
|
||||||
skip_if_rootless "mount does not work rootless"
|
skip_if_rootless "mount does not work rootless"
|
||||||
@ -151,4 +150,34 @@ load helpers
|
|||||||
run_podman rm -f $cid
|
run_podman rm -f $cid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "podman mount external container - basic test" {
|
||||||
|
# Only works with root (FIXME: does it work with rootless + vfs?)
|
||||||
|
skip_if_rootless "mount does not work rootless"
|
||||||
|
skip_if_remote "mounting remote is meaningless"
|
||||||
|
|
||||||
|
# Create a container that podman does not know about
|
||||||
|
external_cid=$(buildah from $IMAGE)
|
||||||
|
|
||||||
|
run_podman mount $external_cid
|
||||||
|
mount_path=$output
|
||||||
|
|
||||||
|
# Test image will always have this file, and will always have the tag
|
||||||
|
test -d $mount_path
|
||||||
|
is $(< "$mount_path/home/podman/testimage-id") "$PODMAN_TEST_IMAGE_TAG" \
|
||||||
|
"Contents of well-known file in image"
|
||||||
|
|
||||||
|
# Make sure that 'podman mount' (no args) returns the expected path
|
||||||
|
run_podman mount --notruncate
|
||||||
|
|
||||||
|
reported_mountpoint=$(echo "$output" | awk '{print $2}')
|
||||||
|
is $reported_mountpoint $mount_path "mountpoint reported by 'podman mount'"
|
||||||
|
|
||||||
|
# umount, and make sure files are gone
|
||||||
|
run_podman umount $external_cid
|
||||||
|
if [ -d "$mount_path" ]; then
|
||||||
|
die "'podman umount' did not umount"
|
||||||
|
fi
|
||||||
|
buildah rm $external_cid
|
||||||
|
}
|
||||||
|
|
||||||
# vim: filetype=sh
|
# vim: filetype=sh
|
||||||
|
Reference in New Issue
Block a user