Merge pull request #737 from umohnani8/kpod_export
Add 'kpod export' command
This commit is contained in:
commit
fb2ee59225
5 changed files with 196 additions and 0 deletions
103
cmd/kpod/export.go
Normal file
103
cmd/kpod/export.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
type exportOptions struct {
|
||||||
|
output string
|
||||||
|
container string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
exportFlags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "output, o",
|
||||||
|
Usage: "Write to a file, default is STDOUT",
|
||||||
|
Value: "/dev/stdout",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exportDescription = "Exports container's filesystem contents as a tar archive" +
|
||||||
|
" and saves it on the local machine."
|
||||||
|
exportCommand = cli.Command{
|
||||||
|
Name: "export",
|
||||||
|
Usage: "Export container's filesystem contents as a tar archive",
|
||||||
|
Description: exportDescription,
|
||||||
|
Flags: exportFlags,
|
||||||
|
Action: exportCmd,
|
||||||
|
ArgsUsage: "CONTAINER",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// exportCmd saves a container to a tarball on disk
|
||||||
|
func exportCmd(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 given, need 1 at most.")
|
||||||
|
}
|
||||||
|
container := args[0]
|
||||||
|
|
||||||
|
config, err := getConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get config")
|
||||||
|
}
|
||||||
|
store, err := getStore(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
output := c.String("output")
|
||||||
|
if output == "/dev/stdout" {
|
||||||
|
file := os.Stdout
|
||||||
|
if logrus.IsTerminal(file) {
|
||||||
|
return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := exportOptions{
|
||||||
|
output: output,
|
||||||
|
container: container,
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportContainer(store, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportContainer exports the contents of a container and saves it as
|
||||||
|
// a tarball on disk
|
||||||
|
func exportContainer(store storage.Store, opts exportOptions) error {
|
||||||
|
mountPoint, err := store.Mount(opts.container, "")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error finding container %q", opts.container)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := store.Unmount(opts.container); err != nil {
|
||||||
|
fmt.Printf("error unmounting container %q: %v\n", opts.container, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
input, err := archive.Tar(mountPoint, archive.Uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading container directory %q", opts.container)
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile, err := os.Create(opts.output)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error creating file %q", opts.output)
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(outFile, input)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ func main() {
|
||||||
app.Version = Version
|
app.Version = Version
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
|
exportCommand,
|
||||||
historyCommand,
|
historyCommand,
|
||||||
imagesCommand,
|
imagesCommand,
|
||||||
infoCommand,
|
infoCommand,
|
||||||
|
|
|
@ -182,6 +182,15 @@ _kpod_save() {
|
||||||
_complete_ "$options_with_args" "$boolean_options"
|
_complete_ "$options_with_args" "$boolean_options"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_kpod_export() {
|
||||||
|
local options_with_args="
|
||||||
|
--output -o
|
||||||
|
"
|
||||||
|
local boolean_options="
|
||||||
|
"
|
||||||
|
_complete_ "$options_with_args" "$boolean_options"
|
||||||
|
}
|
||||||
|
|
||||||
_complete_() {
|
_complete_() {
|
||||||
local options_with_args=$1
|
local options_with_args=$1
|
||||||
local boolean_options="$2 -h --help"
|
local boolean_options="$2 -h --help"
|
||||||
|
|
48
docs/kpod-export.1.md
Normal file
48
docs/kpod-export.1.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
% kpod(1) kpod-export - Simple tool to export a container's filesystem as a tarball
|
||||||
|
% Urvashi Mohnani
|
||||||
|
# kpod-export "1" "July 2017" "kpod"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
kpod-export - Export container's filesystem contents as a tar archive
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**kpod export**
|
||||||
|
**CONTAINER**
|
||||||
|
[**--help**|**-h**]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
**kpod export** exports the filesystem of a container and saves it as a tarball
|
||||||
|
on the local machine. **kpod export** writes to STDOUT by default and can be
|
||||||
|
redirected to a file using the **output flag**.
|
||||||
|
|
||||||
|
**kpod [GLOBAL OPTIONS]**
|
||||||
|
|
||||||
|
**kpod export [GLOBAL OPTIONS]**
|
||||||
|
|
||||||
|
**kpod export [OPTIONS] CONTAINER [GLOBAL OPTIONS]**
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
**--output, -o**
|
||||||
|
Write to a file, default is STDOUT
|
||||||
|
|
||||||
|
## GLOBAL OPTIONS
|
||||||
|
|
||||||
|
**--help, -h**
|
||||||
|
Print usage statement
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
# kpod export -o redis-container.tar 883504668ec465463bc0fe7e63d53154ac3b696ea8d7b233748918664ea90e57
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# kpod export > redis-container.tar 883504668ec465463bc0fe7e63d53154ac3b696ea8d7b233748918664ea90e57
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
kpod(1), kpod-import(1), crio(8), crio.conf(5)
|
||||||
|
|
||||||
|
## HISTORY
|
||||||
|
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
|
35
test/kpod_export.bats
Normal file
35
test/kpod_export.bats
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
IMAGE="redis:alpine"
|
||||||
|
ROOT="$TESTDIR/crio"
|
||||||
|
RUNROOT="$TESTDIR/crio-run"
|
||||||
|
KPOD_OPTIONS="--root $ROOT --runroot $RUNROOT ${STORAGE_OPTS}"
|
||||||
|
|
||||||
|
|
||||||
|
@test "kpod export output flag" {
|
||||||
|
start_crio
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crioctl image pull "$IMAGE"
|
||||||
|
[ "$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} export -o container.tar "$ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
cleanup_ctrs
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
cleanup_pods
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
stop_crio
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
rm -f container.tar
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
Loading…
Reference in a new issue