Add support for mounting external containers

Continue progress on use of external containers.

This PR adds the ability to mount, umount and list the
storage containers whether they are in libpod or not.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2020-10-29 13:28:01 -04:00
parent ab273a9cbd
commit 6ca705bf1a
5 changed files with 157 additions and 7 deletions

View File

@ -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.
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
via the `podman unshare` command. All other storage drivers will fail to mount.
@ -26,7 +28,7 @@ returned.
**--all**, **-a**
Mount all containers.
Mount all podman containers. (External containers will not be mounted)
**--format**=*format*

View File

@ -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.
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
**--all**, **-a**

View File

@ -918,6 +918,56 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int
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
// are not currently known to Podman.
func (r *Runtime) StorageContainers() ([]storage.Container, error) {

View File

@ -1058,11 +1058,23 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
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 {
return nil, err
}
reports := make([]*entities.ContainerMountReport, 0, len(ctrs))
for _, ctr := range ctrs {
report := entities.ContainerMountReport{Id: ctr.ID()}
report.Path, report.Err = ctr.Mount()
@ -1072,6 +1084,30 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
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
ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod)
if err != nil {
@ -1091,15 +1127,44 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin
})
}
}
return reports, nil
}
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 {
return nil, err
}
reports := []*entities.ContainerUnmountReport{}
for _, ctr := range ctrs {
state, err := ctr.State()
if err != nil {

View File

@ -2,7 +2,6 @@
load helpers
@test "podman mount - basic test" {
# Only works with root (FIXME: does it work with rootless + vfs?)
skip_if_rootless "mount does not work rootless"
@ -151,4 +150,34 @@ load helpers
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