From 0cc45cf26a428d5c5bb55180dd11d5c53ecdbcf9 Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Mon, 31 Jul 2017 15:35:14 -0400 Subject: [PATCH] Add kpod-mount and kpod-umount to mount and umount container images This command will allow users to manipulate and examine the container images from outside of the container. Signed-off-by: Daniel J Walsh --- cmd/kpod/main.go | 2 + cmd/kpod/mount.go | 117 ++++++++++++++++++++++++++++++++++++++++++ cmd/kpod/umount.go | 41 +++++++++++++++ completions/bash/kpod | 51 ++++++++++++++++-- docs/kpod-mount.1.md | 50 ++++++++++++++++++ docs/kpod-push.1.md | 2 +- docs/kpod-umount.1.md | 19 +++++++ test/kpod.bats | 2 +- test/kpod_mount.bats | 49 ++++++++++++++++++ 9 files changed, 328 insertions(+), 5 deletions(-) create mode 100644 cmd/kpod/mount.go create mode 100644 cmd/kpod/umount.go create mode 100644 docs/kpod-mount.1.md create mode 100644 docs/kpod-umount.1.md create mode 100644 test/kpod_mount.bats diff --git a/cmd/kpod/main.go b/cmd/kpod/main.go index fec3f6fa..9b5d48b8 100644 --- a/cmd/kpod/main.go +++ b/cmd/kpod/main.go @@ -26,10 +26,12 @@ func main() { imagesCommand, infoCommand, inspectCommand, + mountCommand, pullCommand, pushCommand, rmiCommand, tagCommand, + umountCommand, versionCommand, saveCommand, loadCommand, diff --git a/cmd/kpod/mount.go b/cmd/kpod/mount.go new file mode 100644 index 00000000..424a46f9 --- /dev/null +++ b/cmd/kpod/mount.go @@ -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 +} diff --git a/cmd/kpod/umount.go b/cmd/kpod/umount.go new file mode 100644 index 00000000..bad6752a --- /dev/null +++ b/cmd/kpod/umount.go @@ -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 +} diff --git a/completions/bash/kpod b/completions/bash/kpod index fc4f3720..fee71121 100644 --- a/completions/bash/kpod +++ b/completions/bash/kpod @@ -71,6 +71,48 @@ _kpod_pull() { _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() { local boolean_options=" --disable-compression @@ -175,15 +217,18 @@ _kpod_kpod() { --help -h " commands=" + history images launch + load + mount + pull push rmi tag + umount + unmount version - pull - history - load " case "$prev" in diff --git a/docs/kpod-mount.1.md b/docs/kpod-mount.1.md new file mode 100644 index 00000000..09b062cf --- /dev/null +++ b/docs/kpod-mount.1.md @@ -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) diff --git a/docs/kpod-push.1.md b/docs/kpod-push.1.md index 17f18d31..6970a172 100644 --- a/docs/kpod-push.1.md +++ b/docs/kpod-push.1.md @@ -10,7 +10,7 @@ kpod push - Push an image from local storage to elsewhere ## DESCRIPTION Pushes an image from local storage to a specified destination, decompressing -and recompessing layers as needed. +and recompressing layers as needed. ## imageID Image stored in local container/storage diff --git a/docs/kpod-umount.1.md b/docs/kpod-umount.1.md new file mode 100644 index 00000000..ad95b717 --- /dev/null +++ b/docs/kpod-umount.1.md @@ -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) diff --git a/test/kpod.bats b/test/kpod.bats index 8ca52c27..4f6e6ae9 100644 --- a/test/kpod.bats +++ b/test/kpod.bats @@ -18,7 +18,7 @@ function teardown() { } @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" [ "$status" -eq 0 ] run ${KPOD_BINARY} $KPOD_OPTIONS rmi debian:6.0.10 diff --git a/test/kpod_mount.bats b/test/kpod_mount.bats new file mode 100644 index 00000000..517b627c --- /dev/null +++ b/test/kpod_mount.bats @@ -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 +}