Merge pull request #694 from rhatdan/mount
Add kpod-mount and kpod-umount to mount and umount container images
This commit is contained in:
commit
8e394e0589
9 changed files with 328 additions and 5 deletions
|
@ -26,10 +26,12 @@ func main() {
|
||||||
imagesCommand,
|
imagesCommand,
|
||||||
infoCommand,
|
infoCommand,
|
||||||
inspectCommand,
|
inspectCommand,
|
||||||
|
mountCommand,
|
||||||
pullCommand,
|
pullCommand,
|
||||||
pushCommand,
|
pushCommand,
|
||||||
rmiCommand,
|
rmiCommand,
|
||||||
tagCommand,
|
tagCommand,
|
||||||
|
umountCommand,
|
||||||
versionCommand,
|
versionCommand,
|
||||||
saveCommand,
|
saveCommand,
|
||||||
loadCommand,
|
loadCommand,
|
||||||
|
|
117
cmd/kpod/mount.go
Normal file
117
cmd/kpod/mount.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
js "encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mountDescription = `
|
||||||
|
kpod mount
|
||||||
|
Lists all mounted containers mount points
|
||||||
|
|
||||||
|
kpod mount CONTAINER-NAME-OR-ID
|
||||||
|
Mounts the specified container and outputs the mountpoint
|
||||||
|
`
|
||||||
|
|
||||||
|
mountFlags = []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "notruncate",
|
||||||
|
Usage: "do not truncate output",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "label",
|
||||||
|
Usage: "SELinux label for the mount point",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "format",
|
||||||
|
Usage: "Print mounted containers in specified format",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mountCommand = cli.Command{
|
||||||
|
Name: "mount",
|
||||||
|
Usage: "Mount a working container's root filesystem",
|
||||||
|
Description: mountDescription,
|
||||||
|
Action: mountCmd,
|
||||||
|
ArgsUsage: "[CONTAINER-NAME-OR-ID]",
|
||||||
|
Flags: mountFlags,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MountOutputParams stores info about each layer
|
||||||
|
type jsonMountPoint struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Names []string `json:"names"`
|
||||||
|
MountPoint string `json:"mountpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountCmd(c *cli.Context) error {
|
||||||
|
formats := map[string]bool{
|
||||||
|
"": true,
|
||||||
|
"json": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := c.Args()
|
||||||
|
json := c.String("format") == "json"
|
||||||
|
if !formats[c.String("format")] {
|
||||||
|
return errors.Errorf("%q is not a supported format", c.String("format"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
return errors.Errorf("too many arguments specified")
|
||||||
|
}
|
||||||
|
config, err := getConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Could not get config")
|
||||||
|
}
|
||||||
|
store, err := getStore(config)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error getting store")
|
||||||
|
}
|
||||||
|
if len(args) == 1 {
|
||||||
|
if json {
|
||||||
|
return errors.Wrapf(err, "json option can not be used with a container id")
|
||||||
|
}
|
||||||
|
mountPoint, err := store.Mount(args[0], c.String("label"))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error finding container %q", args[0])
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", mountPoint)
|
||||||
|
} else {
|
||||||
|
jsonMountPoints := []jsonMountPoint{}
|
||||||
|
containers, err2 := store.Containers()
|
||||||
|
if err2 != nil {
|
||||||
|
return errors.Wrapf(err2, "error reading list of all containers")
|
||||||
|
}
|
||||||
|
for _, container := range containers {
|
||||||
|
layer, err := store.Layer(container.LayerID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error finding layer %q for container %q", container.LayerID, container.ID)
|
||||||
|
}
|
||||||
|
if layer.MountPoint == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if json {
|
||||||
|
jsonMountPoints = append(jsonMountPoints, jsonMountPoint{ID: container.ID, Names: container.Names, MountPoint: layer.MountPoint})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool("notruncate") {
|
||||||
|
fmt.Printf("%-64s %s\n", container.ID, layer.MountPoint)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%-12.12s %s\n", container.ID, layer.MountPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if json {
|
||||||
|
data, err := js.MarshalIndent(jsonMountPoints, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
41
cmd/kpod/umount.go
Normal file
41
cmd/kpod/umount.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
umountCommand = cli.Command{
|
||||||
|
Name: "umount",
|
||||||
|
Aliases: []string{"unmount"},
|
||||||
|
Usage: "Unmount a working container's root filesystem",
|
||||||
|
Description: "Unmounts a working container's root filesystem",
|
||||||
|
Action: umountCmd,
|
||||||
|
ArgsUsage: "CONTAINER-NAME-OR-ID",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func umountCmd(c *cli.Context) error {
|
||||||
|
args := c.Args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.Errorf("container ID must be specified")
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
return errors.Errorf("too many arguments specified")
|
||||||
|
}
|
||||||
|
config, err := getConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Could not get config")
|
||||||
|
}
|
||||||
|
store, err := getStore(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store.Unmount(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error unmounting container %q", args[0])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -71,6 +71,48 @@ _kpod_pull() {
|
||||||
_complete_ "$options_with_args" "$boolean_options"
|
_complete_ "$options_with_args" "$boolean_options"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_kpod_unmount() {
|
||||||
|
_kpod_umount $@
|
||||||
|
}
|
||||||
|
|
||||||
|
_kpod_umount() {
|
||||||
|
local boolean_options="
|
||||||
|
--help
|
||||||
|
-h
|
||||||
|
"
|
||||||
|
local options_with_args="
|
||||||
|
"
|
||||||
|
|
||||||
|
local all_options="$options_with_args $boolean_options"
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_kpod_mount() {
|
||||||
|
local boolean_options="
|
||||||
|
--help
|
||||||
|
-h
|
||||||
|
--notruncate
|
||||||
|
"
|
||||||
|
|
||||||
|
local options_with_args="
|
||||||
|
--label
|
||||||
|
--format
|
||||||
|
"
|
||||||
|
|
||||||
|
local all_options="$options_with_args $boolean_options"
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_kpod_push() {
|
_kpod_push() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--disable-compression
|
--disable-compression
|
||||||
|
@ -175,15 +217,18 @@ _kpod_kpod() {
|
||||||
--help -h
|
--help -h
|
||||||
"
|
"
|
||||||
commands="
|
commands="
|
||||||
|
history
|
||||||
images
|
images
|
||||||
launch
|
launch
|
||||||
|
load
|
||||||
|
mount
|
||||||
|
pull
|
||||||
push
|
push
|
||||||
rmi
|
rmi
|
||||||
tag
|
tag
|
||||||
|
umount
|
||||||
|
unmount
|
||||||
version
|
version
|
||||||
pull
|
|
||||||
history
|
|
||||||
load
|
|
||||||
"
|
"
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
|
|
50
docs/kpod-mount.1.md
Normal file
50
docs/kpod-mount.1.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
% kpod(1) kpod-mount - Mount a working container's root filesystem.
|
||||||
|
% Dan Walsh
|
||||||
|
# kpod-mount "1" "July 2017" "kpod"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
kpod mount - Mount a working container's root filesystem.
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**kpod** **mount**
|
||||||
|
|
||||||
|
**kpod** **mount** **containerID**
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
Mounts the specified container's 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, the tool will list all of the
|
||||||
|
currently mounted containers.
|
||||||
|
|
||||||
|
## RETURN VALUE
|
||||||
|
The location of the mounted file system. On error an empty string and errno is
|
||||||
|
returned.
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
**--format**
|
||||||
|
Print the mounted containers in specified format (json)
|
||||||
|
|
||||||
|
**--notruncate**
|
||||||
|
|
||||||
|
Do not truncate IDs in output.
|
||||||
|
|
||||||
|
**--label**
|
||||||
|
|
||||||
|
SELinux label for the mount point
|
||||||
|
|
||||||
|
## EXAMPLE
|
||||||
|
|
||||||
|
kpod mount c831414b10a3
|
||||||
|
|
||||||
|
/var/lib/containers/storage/overlay2/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
|
||||||
|
|
||||||
|
kpod mount
|
||||||
|
|
||||||
|
c831414b10a3 /var/lib/containers/storage/overlay2/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
|
||||||
|
|
||||||
|
a7060253093b /var/lib/containers/storage/overlay2/0ff7d7ca68bed1ace424f9df154d2dd7b5a125c19d887f17653cbcd5b6e30ba1/merged
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
kpod(1), kpod-umount(1), mount(8)
|
|
@ -10,7 +10,7 @@ kpod push - Push an image from local storage to elsewhere
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
Pushes an image from local storage to a specified destination, decompressing
|
Pushes an image from local storage to a specified destination, decompressing
|
||||||
and recompessing layers as needed.
|
and recompressing layers as needed.
|
||||||
|
|
||||||
## imageID
|
## imageID
|
||||||
Image stored in local container/storage
|
Image stored in local container/storage
|
||||||
|
|
19
docs/kpod-umount.1.md
Normal file
19
docs/kpod-umount.1.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
% kpod(1) kpod-umount - Unmount a working container's root filesystem.
|
||||||
|
% Dan Walsh
|
||||||
|
# kpod-umount "1" "July 2017" "kpod"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
kpod umount - Unmount a working container's root file system.
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**kpod** **umount** **containerID**
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
Unmounts the specified container's root file system.
|
||||||
|
|
||||||
|
## EXAMPLE
|
||||||
|
|
||||||
|
kpod umount containerID
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
kpod(1), kpod-mount(1)
|
|
@ -18,7 +18,7 @@ function teardown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod pull from docker with tag" {
|
@test "kpod pull from docker with tag" {
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS pull debian:6.0.10
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull debian:6.0.10
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS rmi debian:6.0.10
|
run ${KPOD_BINARY} $KPOD_OPTIONS rmi debian:6.0.10
|
||||||
|
|
49
test/kpod_mount.bats
Normal file
49
test/kpod_mount.bats
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
cleanup_test
|
||||||
|
}
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
IMAGE="redis:alpine"
|
||||||
|
ROOT="$TESTDIR/crio"
|
||||||
|
RUNROOT="$TESTDIR/crio-run"
|
||||||
|
KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT ${STORAGE_OPTS}"
|
||||||
|
|
||||||
|
@test "mount" {
|
||||||
|
start_crio
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crioctl image pull "$IMAGE"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} mount $ctr_id
|
||||||
|
echo "$output"
|
||||||
|
echo ${KPOD_BINARY} ${KPOD_OPTIONS} mount $ctr_id
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} mount --notruncate | grep $ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} unmount $ctr_id
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} mount $ctr_id
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
root="$output"
|
||||||
|
run bash -c "${KPOD_BINARY} ${KPOD_OPTIONS} mount --format=json | python -m json.tool | grep $ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
touch $root/foobar
|
||||||
|
${KPOD_BINARY} ${KPOD_OPTIONS} unmount $ctr_id
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
Loading…
Reference in a new issue