mirror of
https://github.com/containers/podman.git
synced 2025-05-31 07:27:13 +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.
|
||||
|
||||
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