From a3d5a818f6e4b5a57d4d120c64f4e8c462b641de Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 21 Jun 2017 16:20:28 -0700 Subject: [PATCH] Update snapshot command in ctr Move existing snapshot command to archive subcommand of snapshot. Add list command for listing snapshots. Add usage command for showing snapshot disk usage. Signed-off-by: Derek McGowan --- cmd/ctr/snapshot.go | 123 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 4 deletions(-) diff --git a/cmd/ctr/snapshot.go b/cmd/ctr/snapshot.go index 825741fc0..5ae6ebb09 100644 --- a/cmd/ctr/snapshot.go +++ b/cmd/ctr/snapshot.go @@ -1,21 +1,36 @@ package main import ( + "context" "errors" "fmt" + "os" + "text/tabwriter" + "github.com/containerd/containerd/progress" "github.com/containerd/containerd/rootfs" + "github.com/containerd/containerd/snapshot" "github.com/urfave/cli" ) var snapshotCommand = cli.Command{ - Name: "snapshot", - Usage: "snapshot a container into an archive", - ArgsUsage: "", + Name: "snapshot", + Usage: "snapshot management", + Subcommands: cli.Commands{ + archiveSnapshotCommand, + listSnapshotCommand, + usageSnapshotCommand, + }, +} + +var archiveSnapshotCommand = cli.Command{ + Name: "archive", + Usage: "Create an archive of a snapshot", + ArgsUsage: "[flags] id", Flags: []cli.Flag{ cli.StringFlag{ Name: "id", - Usage: "id of the container", + Usage: "id of the container or snapshot", }, }, Action: func(clicontext *cli.Context) error { @@ -50,3 +65,103 @@ var snapshotCommand = cli.Command{ return nil }, } + +var listSnapshotCommand = cli.Command{ + Name: "list", + Aliases: []string{"ls"}, + Usage: "List snapshots", + Action: func(clicontext *cli.Context) error { + ctx, cancel := appContext(clicontext) + defer cancel() + + client, err := newClient(clicontext) + if err != nil { + return err + } + + snapshotter := client.SnapshotService() + + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0) + fmt.Fprintln(tw, "ID\tParent\tState\tReadonly\t") + + if err := snapshotter.Walk(ctx, func(ctx context.Context, info snapshot.Info) error { + fmt.Fprintf(tw, "%v\t%v\t%v\t%t\t\n", info.Name, info.Parent, state(info.Kind), info.Readonly) + return nil + }); err != nil { + return err + } + + return tw.Flush() + }, +} + +func state(k snapshot.Kind) string { + switch k { + case snapshot.KindActive: + return "active" + case snapshot.KindCommitted: + return "committed" + default: + return "" + } +} + +var usageSnapshotCommand = cli.Command{ + Name: "usage", + Usage: "Usage snapshots", + ArgsUsage: "[flags] [id] ...", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "b", + Usage: "display size in bytes", + }, + }, + Action: func(clicontext *cli.Context) error { + ctx, cancel := appContext(clicontext) + defer cancel() + + client, err := newClient(clicontext) + if err != nil { + return err + } + + var displaySize func(int64) string + if clicontext.Bool("b") { + displaySize = func(s int64) string { + return fmt.Sprintf("%d", s) + } + } else { + displaySize = func(s int64) string { + return progress.Bytes(s).String() + } + } + + snapshotter := client.SnapshotService() + + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0) + fmt.Fprintln(tw, "ID\tSize\tInodes\t") + + if clicontext.NArg() == 0 { + if err := snapshotter.Walk(ctx, func(ctx context.Context, info snapshot.Info) error { + usage, err := snapshotter.Usage(ctx, info.Name) + if err != nil { + return err + } + fmt.Fprintf(tw, "%v\t%s\t%d\t\n", info.Name, displaySize(usage.Size), usage.Inodes) + return nil + }); err != nil { + return err + } + } else { + for _, id := range clicontext.Args() { + usage, err := snapshotter.Usage(ctx, id) + if err != nil { + return err + } + fmt.Fprintf(tw, "%v\t%s\t%d\t\n", id, displaySize(usage.Size), usage.Inodes) + } + } + + return tw.Flush() + }, +}