package snapshot import ( gocontext "context" "fmt" "os" "strings" "text/tabwriter" "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/progress" "github.com/containerd/containerd/snapshot" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/urfave/cli" ) // Command is the cli command for managing snapshots var Command = cli.Command{ Name: "snapshot", Usage: "manage snapshots", Flags: commands.SnapshotterFlags, Subcommands: cli.Commands{ listCommand, usageCommand, removeCommand, prepareCommand, viewCommand, treeCommand, mountCommand, commitCommand, infoCommand, setLabelCommand, unpackCommand, }, } var listCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list snapshots", Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() var ( snapshotter = client.SnapshotService(context.GlobalString("snapshotter")) tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0) ) fmt.Fprintln(tw, "KEY\tPARENT\tKIND\t") if err := snapshotter.Walk(ctx, func(ctx gocontext.Context, info snapshot.Info) error { fmt.Fprintf(tw, "%v\t%v\t%v\t\n", info.Name, info.Parent, info.Kind) return nil }); err != nil { return err } return tw.Flush() }, } var usageCommand = cli.Command{ Name: "usage", Usage: "usage snapshots", ArgsUsage: "[flags] [, ...]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "b", Usage: "display size in bytes", }, }, Action: func(context *cli.Context) error { var displaySize func(int64) string if context.Bool("b") { displaySize = func(s int64) string { return fmt.Sprintf("%d", s) } } else { displaySize = func(s int64) string { return progress.Bytes(s).String() } } client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() var ( snapshotter = client.SnapshotService(context.GlobalString("snapshotter")) tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0) ) fmt.Fprintln(tw, "KEY\tSIZE\tINODES\t") if context.NArg() == 0 { if err := snapshotter.Walk(ctx, func(ctx gocontext.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 context.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() }, } var removeCommand = cli.Command{ Name: "remove", Aliases: []string{"rm"}, ArgsUsage: " [, ...]", Usage: "remove snapshots", Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) for _, key := range context.Args() { err = snapshotter.Remove(ctx, key) if err != nil { return errors.Wrapf(err, "failed to remove %q", key) } } return nil }, } var prepareCommand = cli.Command{ Name: "prepare", Usage: "prepare a snapshot from a committed snapshot", ArgsUsage: "[flags] []", Flags: []cli.Flag{ cli.StringFlag{ Name: "target, t", Usage: "mount target path, will print mount, if provided", }, }, Action: func(context *cli.Context) error { if context.NArg() != 2 { return cli.ShowSubcommandHelp(context) } var ( target = context.String("target") key = context.Args().Get(0) parent = context.Args().Get(1) ) client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) mounts, err := snapshotter.Prepare(ctx, key, parent) if err != nil { return err } if target != "" { printMounts(target, mounts) } return nil }, } var viewCommand = cli.Command{ Name: "view", Usage: "create a read-only snapshot from a committed snapshot", ArgsUsage: "[flags] []", Flags: []cli.Flag{ cli.StringFlag{ Name: "target, t", Usage: "mount target path, will print mount, if provided", }, }, Action: func(context *cli.Context) error { if context.NArg() != 2 { return cli.ShowSubcommandHelp(context) } var ( target = context.String("target") key = context.Args().Get(0) parent = context.Args().Get(1) ) client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) mounts, err := snapshotter.View(ctx, key, parent) if err != nil { return err } if target != "" { printMounts(target, mounts) } return nil }, } var mountCommand = cli.Command{ Name: "mounts", Aliases: []string{"m", "mount"}, Usage: "mount gets mount commands for the snapshots", ArgsUsage: " ", Action: func(context *cli.Context) error { if context.NArg() != 2 { return cli.ShowSubcommandHelp(context) } var ( target = context.Args().Get(0) key = context.Args().Get(1) ) client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) mounts, err := snapshotter.Mounts(ctx, key) if err != nil { return err } printMounts(target, mounts) return nil }, } var commitCommand = cli.Command{ Name: "commit", Usage: "commit an active snapshot into the provided name", ArgsUsage: " ", Action: func(context *cli.Context) error { if context.NArg() != 2 { return cli.ShowSubcommandHelp(context) } var ( key = context.Args().Get(0) active = context.Args().Get(1) ) client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) return snapshotter.Commit(ctx, key, active) }, } var treeCommand = cli.Command{ Name: "tree", Usage: "display tree view of snapshot branches", Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() var ( snapshotter = client.SnapshotService(context.GlobalString("snapshotter")) tree = make(map[string]*snapshotTreeNode) ) if err := snapshotter.Walk(ctx, func(ctx gocontext.Context, info snapshot.Info) error { // Get or create node and add node details node := getOrCreateTreeNode(info.Name, tree) if info.Parent != "" { node.Parent = info.Parent p := getOrCreateTreeNode(info.Parent, tree) p.Children = append(p.Children, info.Name) } return nil }); err != nil { return err } printTree(tree) return nil }, } var infoCommand = cli.Command{ Name: "info", Usage: "get info about a snapshot", ArgsUsage: "", Action: func(context *cli.Context) error { if context.NArg() != 1 { return cli.ShowSubcommandHelp(context) } key := context.Args().Get(0) client, ctx, cancel, err := commands.NewClient(context) if err != nil { return err } defer cancel() snapshotter := client.SnapshotService(context.GlobalString("snapshotter")) info, err := snapshotter.Stat(ctx, key) if err != nil { return err } commands.PrintAsJSON(info) return nil }, } var setLabelCommand = cli.Command{ Name: "label", Usage: "add labels to content", ArgsUsage: " [