mirror of
https://github.com/containers/podman.git
synced 2025-05-21 09:05:56 +08:00
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:
@ -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*
|
||||
|
||||
|
@ -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**
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user