From 1db80ed9669a2e87cbcacb496194c3e5165a093d Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 14 Jul 2017 15:10:43 -0700 Subject: [PATCH] cmd/{ctr, dist}: move content command to ctr Signed-off-by: Stephen J Day --- cmd/ctr/content.go | 445 +++++++++++++++++++++++++++++++++++++++++++++ cmd/dist/active.go | 59 ------ cmd/dist/delete.go | 60 ------ cmd/dist/edit.go | 135 -------------- cmd/dist/get.go | 40 ---- cmd/dist/ingest.go | 55 ------ cmd/dist/labels.go | 92 ---------- cmd/dist/list.go | 73 -------- cmd/dist/main.go | 15 -- 9 files changed, 445 insertions(+), 529 deletions(-) create mode 100644 cmd/ctr/content.go delete mode 100644 cmd/dist/active.go delete mode 100644 cmd/dist/delete.go delete mode 100644 cmd/dist/edit.go delete mode 100644 cmd/dist/get.go delete mode 100644 cmd/dist/ingest.go delete mode 100644 cmd/dist/labels.go delete mode 100644 cmd/dist/list.go diff --git a/cmd/ctr/content.go b/cmd/ctr/content.go new file mode 100644 index 000000000..1a61abedb --- /dev/null +++ b/cmd/ctr/content.go @@ -0,0 +1,445 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strings" + "text/tabwriter" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + units "github.com/docker/go-units" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + contentCommand = cli.Command{ + Name: "content", + Usage: "content management", + Subcommands: cli.Commands{ + listContentCommand, + ingestContentCommand, + activeIngestCommand, + getContentCommand, + editContentCommand, + deleteContentCommand, + labelContentCommand, + }, + } + + getContentCommand = cli.Command{ + Name: "get", + Usage: "get the data for an object", + ArgsUsage: "[flags] [, ...]", + Description: "Display the image object.", + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, cancel := appContext(context) + defer cancel() + + dgst, err := digest.Parse(context.Args().First()) + if err != nil { + return err + } + + cs, err := getContentStore(context) + if err != nil { + return err + } + + rc, err := cs.Reader(ctx, dgst) + if err != nil { + return err + } + defer rc.Close() + + _, err = io.Copy(os.Stdout, rc) + return err + }, + } + + ingestContentCommand = cli.Command{ + Name: "ingest", + Usage: "accept content into the store", + ArgsUsage: "[flags] ", + Description: `Ingest objects into the local content store.`, + Flags: []cli.Flag{ + cli.Int64Flag{ + Name: "expected-size", + Usage: "validate against provided size", + }, + cli.StringFlag{ + Name: "expected-digest", + Usage: "verify content against expected digest", + }, + }, + Action: func(context *cli.Context) error { + var ( + ref = context.Args().First() + expectedSize = context.Int64("expected-size") + expectedDigest = digest.Digest(context.String("expected-digest")) + ) + + ctx, cancel := appContext(context) + defer cancel() + + if err := expectedDigest.Validate(); expectedDigest != "" && err != nil { + return err + } + + if ref == "" { + return errors.New("must specify a transaction reference") + } + + cs, err := getContentStore(context) + if err != nil { + return err + } + + // TODO(stevvooe): Allow ingest to be reentrant. Currently, we expect + // all data to be written in a single invocation. Allow multiple writes + // to the same transaction key followed by a commit. + return content.WriteBlob(ctx, cs, ref, os.Stdin, expectedSize, expectedDigest) + }, + } + + activeIngestCommand = cli.Command{ + Name: "active", + Usage: "display active transfers.", + ArgsUsage: "[flags] []", + Description: `Display the ongoing transfers.`, + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout, t", + Usage: "total timeout for fetch", + EnvVar: "CONTAINERD_FETCH_TIMEOUT", + }, + cli.StringFlag{ + Name: "root", + Usage: "path to content store root", + Value: "/tmp/content", // TODO(stevvooe): for now, just use the PWD/.content + }, + }, + Action: func(context *cli.Context) error { + var ( + match = context.Args().First() + ) + + ctx, cancel := appContext(context) + defer cancel() + + cs, err := getContentStore(context) + if err != nil { + return err + } + + active, err := cs.ListStatuses(ctx, match) + if err != nil { + return err + } + + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) + fmt.Fprintln(tw, "REF\tSIZE\tAGE\t") + for _, active := range active { + fmt.Fprintf(tw, "%s\t%s\t%s\t\n", + active.Ref, + units.HumanSize(float64(active.Offset)), + units.HumanDuration(time.Since(active.StartedAt))) + } + + return tw.Flush() + }, + } + + listContentCommand = cli.Command{ + Name: "list", + Aliases: []string{"ls"}, + Usage: "list all blobs in the store.", + ArgsUsage: "[flags] [, ...]", + Description: `List blobs in the content store.`, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "quiet, q", + Usage: "print only the blob digest", + }, + }, + Action: func(context *cli.Context) error { + var ( + quiet = context.Bool("quiet") + args = []string(context.Args()) + ) + ctx, cancel := appContext(context) + defer cancel() + + cs, err := getContentStore(context) + if err != nil { + return err + } + + var walkFn content.WalkFunc + if quiet { + walkFn = func(info content.Info) error { + fmt.Println(info.Digest) + return nil + } + } else { + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) + defer tw.Flush() + + fmt.Fprintln(tw, "DIGEST\tSIZE\tAGE\tLABELS") + walkFn = func(info content.Info) error { + var labelStrings []string + for k, v := range info.Labels { + labelStrings = append(labelStrings, strings.Join([]string{k, v}, "=")) + } + labels := strings.Join(labelStrings, ",") + if labels == "" { + labels = "-" + } + + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", + info.Digest, + units.HumanSize(float64(info.Size)), + units.HumanDuration(time.Since(info.CreatedAt)), + labels) + return nil + } + + } + + return cs.Walk(ctx, walkFn, args...) + }, + } + + labelContentCommand = cli.Command{ + Name: "label", + Usage: "adds labels to content", + ArgsUsage: "[flags] [