From a19a20303aedbb82c728eccc20210400da680237 Mon Sep 17 00:00:00 2001 From: Jess Valarezo Date: Mon, 23 Oct 2017 17:40:18 -0700 Subject: [PATCH] ctr: add commands package with shared utility functions Signed-off-by: Jess Valarezo --- cmd/ctr/commands/commands.go | 84 +++++++++++++++++++++++++++ cmd/ctr/content.go | 3 +- cmd/ctr/fetch.go | 5 +- cmd/ctr/fetchobject.go | 3 +- cmd/ctr/images.go | 3 +- cmd/ctr/import.go | 5 +- cmd/ctr/info.go | 3 +- cmd/ctr/labels.go | 3 +- cmd/ctr/namespaces.go | 5 +- cmd/ctr/pull.go | 3 +- cmd/ctr/push.go | 3 +- cmd/ctr/pushobject.go | 3 +- cmd/ctr/rootfs.go | 3 +- cmd/ctr/run.go | 3 +- cmd/ctr/run_unix.go | 3 +- cmd/ctr/run_windows.go | 3 +- cmd/ctr/shim.go | 3 +- cmd/ctr/snapshot.go | 7 ++- cmd/ctr/utils.go | 106 ----------------------------------- 19 files changed, 123 insertions(+), 128 deletions(-) create mode 100644 cmd/ctr/commands/commands.go diff --git a/cmd/ctr/commands/commands.go b/cmd/ctr/commands/commands.go new file mode 100644 index 000000000..a1de37465 --- /dev/null +++ b/cmd/ctr/commands/commands.go @@ -0,0 +1,84 @@ +package commands + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/containerd/containerd" + "github.com/urfave/cli" +) + +var ( + // SnapshotterFlags are cli flags specifying snapshotter names + SnapshotterFlags = []cli.Flag{ + cli.StringFlag{ + Name: "snapshotter", + Usage: "snapshotter name. Empty value stands for the daemon default value.", + Value: containerd.DefaultSnapshotter, + }, + } + + // LabelFlag is a cli flag specifying labels + LabelFlag = cli.StringSliceFlag{ + Name: "label", + Usage: "labels to attach to the image", + } + + // RegistryFlags are cli flags specifying registry options + RegistryFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "skip-verify,k", + Usage: "skip SSL certificate validation", + }, + cli.BoolFlag{ + Name: "plain-http", + Usage: "allow connections using plain HTTP", + }, + cli.StringFlag{ + Name: "user,u", + Usage: "user[:password] Registry user and password", + }, + cli.StringFlag{ + Name: "refresh", + Usage: "refresh token for authorization server", + }, + } +) + +// ObjectWithLabelArgs returns the first arg and a LabelArgs object +func ObjectWithLabelArgs(clicontext *cli.Context) (string, map[string]string) { + var ( + first = clicontext.Args().First() + labelStrings = clicontext.Args().Tail() + ) + + return first, LabelArgs(labelStrings) +} + +// LabelArgs returns a map of label key,value pairs +func LabelArgs(labelStrings []string) map[string]string { + labels := make(map[string]string, len(labelStrings)) + for _, label := range labelStrings { + parts := strings.SplitN(label, "=", 2) + key := parts[0] + value := "true" + if len(parts) > 1 { + value = parts[1] + } + + labels[key] = value + } + + return labels +} + +// PrintAsJSON prints input in JSON format +func PrintAsJSON(x interface{}) { + b, err := json.MarshalIndent(x, "", " ") + if err != nil { + fmt.Fprintf(os.Stderr, "can't marshal %+v as a JSON string: %v\n", x, err) + } + fmt.Println(string(b)) +} diff --git a/cmd/ctr/content.go b/cmd/ctr/content.go index 11333c029..689ee4104 100644 --- a/cmd/ctr/content.go +++ b/cmd/ctr/content.go @@ -10,6 +10,7 @@ import ( "text/tabwriter" "time" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" @@ -212,7 +213,7 @@ var ( Description: `Labels blobs in the content store`, Flags: []cli.Flag{}, Action: func(context *cli.Context) error { - object, labels := objectWithLabelArgs(context) + object, labels := commands.ObjectWithLabelArgs(context) client, ctx, cancel, err := newClient(context) if err != nil { return err diff --git a/cmd/ctr/fetch.go b/cmd/ctr/fetch.go index f6d47aa1e..e67ac846d 100644 --- a/cmd/ctr/fetch.go +++ b/cmd/ctr/fetch.go @@ -10,6 +10,7 @@ import ( "time" "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" @@ -39,7 +40,7 @@ not use this implementation as a guide. The end goal should be having metadata, content and snapshots ready for a direct use via the 'ctr run'. Most of this is experimental and there are few leaps to make this work.`, - Flags: append(registryFlags, labelFlag), + Flags: append(commands.RegistryFlags, commands.LabelFlag), Action: func(clicontext *cli.Context) error { var ( ref = clicontext.Args().First() @@ -82,7 +83,7 @@ func fetch(ref string, cliContext *cli.Context) (containerd.Image, error) { }) log.G(pctx).WithField("image", ref).Debug("fetching") - labels := labelArgs(cliContext.StringSlice("label")) + labels := commands.LabelArgs(cliContext.StringSlice("label")) img, err := client.Pull(pctx, ref, containerd.WithPullLabels(labels), containerd.WithResolver(resolver), diff --git a/cmd/ctr/fetchobject.go b/cmd/ctr/fetchobject.go index cf4cf8303..d801e723c 100644 --- a/cmd/ctr/fetchobject.go +++ b/cmd/ctr/fetchobject.go @@ -4,6 +4,7 @@ import ( "io" "os" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/log" "github.com/urfave/cli" ) @@ -17,7 +18,7 @@ var fetchObjectCommand = cli.Command{ Usage: "retrieve objects from a remote", ArgsUsage: "[flags] [, ...]", Description: `Fetch objects by identifier from a remote.`, - Flags: registryFlags, + Flags: commands.RegistryFlags, Action: func(context *cli.Context) error { var ( ref = context.Args().First() diff --git a/cmd/ctr/images.go b/cmd/ctr/images.go index a3764930e..cf9b97f75 100644 --- a/cmd/ctr/images.go +++ b/cmd/ctr/images.go @@ -7,6 +7,7 @@ import ( "strings" "text/tabwriter" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" @@ -127,7 +128,7 @@ var imagesSetLabelsCommand = cli.Command{ Action: func(context *cli.Context) error { var ( replaceAll = context.Bool("replace-all") - name, labels = objectWithLabelArgs(context) + name, labels = commands.ObjectWithLabelArgs(context) ) client, ctx, cancel, err := newClient(context) if err != nil { diff --git a/cmd/ctr/import.go b/cmd/ctr/import.go index 51cff0709..7c55ab21b 100644 --- a/cmd/ctr/import.go +++ b/cmd/ctr/import.go @@ -6,6 +6,7 @@ import ( "os" "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/log" "github.com/urfave/cli" ) @@ -21,14 +22,14 @@ var imagesImportCommand = cli.Command{ Value: "", Usage: "reference object e.g. tag@digest (default: use the object specified in ref)", }, - labelFlag, + commands.LabelFlag, }, Action: func(context *cli.Context) error { var ( ref = context.Args().First() in = context.Args().Get(1) refObject = context.String("ref-object") - labels = labelArgs(context.StringSlice("label")) + labels = commands.LabelArgs(context.StringSlice("label")) ) client, ctx, cancel, err := newClient(context) if err != nil { diff --git a/cmd/ctr/info.go b/cmd/ctr/info.go index d7e89604a..fc5b0efce 100644 --- a/cmd/ctr/info.go +++ b/cmd/ctr/info.go @@ -1,6 +1,7 @@ package main import ( + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -27,7 +28,7 @@ var containerInfoCommand = cli.Command{ if err != nil { return err } - printAsJSON(info) + commands.PrintAsJSON(info) return nil }, diff --git a/cmd/ctr/labels.go b/cmd/ctr/labels.go index 74612a292..019a446aa 100644 --- a/cmd/ctr/labels.go +++ b/cmd/ctr/labels.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/urfave/cli" ) @@ -15,7 +16,7 @@ var containersSetLabelsCommand = cli.Command{ Description: "Set and clear labels for a container.", Flags: []cli.Flag{}, Action: func(context *cli.Context) error { - containerID, labels := objectWithLabelArgs(context) + containerID, labels := commands.ObjectWithLabelArgs(context) if containerID == "" { return errors.New("please specify a container") } diff --git a/cmd/ctr/namespaces.go b/cmd/ctr/namespaces.go index 658dda665..8f8db9cde 100644 --- a/cmd/ctr/namespaces.go +++ b/cmd/ctr/namespaces.go @@ -7,6 +7,7 @@ import ( "strings" "text/tabwriter" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" "github.com/pkg/errors" @@ -30,7 +31,7 @@ var namespacesCreateCommand = cli.Command{ ArgsUsage: "[flags] [= ", Description: `Push objects by identifier to a remote.`, - Flags: registryFlags, + Flags: commands.RegistryFlags, Action: func(context *cli.Context) error { var ( ref = context.Args().Get(0) diff --git a/cmd/ctr/rootfs.go b/cmd/ctr/rootfs.go index d985dcfcb..56332af00 100644 --- a/cmd/ctr/rootfs.go +++ b/cmd/ctr/rootfs.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/log" digest "github.com/opencontainers/go-digest" "github.com/urfave/cli" @@ -21,7 +22,7 @@ var rootfsUnpackCommand = cli.Command{ Name: "unpack", Usage: "unpack applies layers from a manifest to a snapshot", ArgsUsage: "[flags] ", - Flags: snapshotterFlags, + Flags: commands.SnapshotterFlags, Action: func(context *cli.Context) error { dgst, err := digest.Parse(context.Args().First()) if err != nil { diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 054371d24..80b71af78 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -8,6 +8,7 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -100,7 +101,7 @@ var runCommand = cli.Command{ Name: "detach,d", Usage: "detach from the task after it has started execution", }, - }, snapshotterFlags...), + }, commands.SnapshotterFlags...), Action: func(context *cli.Context) error { var ( err error diff --git a/cmd/ctr/run_unix.go b/cmd/ctr/run_unix.go index c890a61a5..ca195a82c 100644 --- a/cmd/ctr/run_unix.go +++ b/cmd/ctr/run_unix.go @@ -11,6 +11,7 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -76,7 +77,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli opts []containerd.SpecOpts cOpts []containerd.NewContainerOpts ) - cOpts = append(cOpts, containerd.WithContainerLabels(labelArgs(context.StringSlice("label")))) + cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) if context.Bool("rootfs") { opts = append(opts, containerd.WithRootFSPath(ref)) } else { diff --git a/cmd/ctr/run_windows.go b/cmd/ctr/run_windows.go index 2879faec6..2ad33e5bc 100644 --- a/cmd/ctr/run_windows.go +++ b/cmd/ctr/run_windows.go @@ -6,6 +6,7 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" @@ -92,7 +93,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli labelStrings = context.StringSlice("label") ) - labels := labelArgs(labelStrings) + labels := commands.LabelArgs(labelStrings) // TODO(mlaventure): get base image once we have a snapshotter diff --git a/cmd/ctr/shim.go b/cmd/ctr/shim.go index 2973c00e6..c3145cbb4 100644 --- a/cmd/ctr/shim.go +++ b/cmd/ctr/shim.go @@ -13,6 +13,7 @@ import ( "google.golang.org/grpc" "github.com/containerd/console" + "github.com/containerd/containerd/cmd/ctr/commands" shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/typeurl" protobuf "github.com/gogo/protobuf/types" @@ -182,7 +183,7 @@ var shimStateCommand = cli.Command{ if err != nil { return err } - printAsJSON(r) + commands.PrintAsJSON(r) return nil }, } diff --git a/cmd/ctr/snapshot.go b/cmd/ctr/snapshot.go index ba55733ee..6d714bf0b 100644 --- a/cmd/ctr/snapshot.go +++ b/cmd/ctr/snapshot.go @@ -7,6 +7,7 @@ import ( "strings" "text/tabwriter" + "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/progress" "github.com/containerd/containerd/snapshot" @@ -17,7 +18,7 @@ import ( var snapshotCommand = cli.Command{ Name: "snapshot", Usage: "snapshot management", - Flags: snapshotterFlags, + Flags: commands.SnapshotterFlags, Subcommands: cli.Commands{ listSnapshotCommand, usageSnapshotCommand, @@ -325,7 +326,7 @@ var infoSnapshotCommand = cli.Command{ return err } - printAsJSON(info) + commands.PrintAsJSON(info) return nil }, @@ -338,7 +339,7 @@ var labelSnapshotCommand = cli.Command{ Description: `Labels snapshots in the snapshotter`, Flags: []cli.Flag{}, Action: func(context *cli.Context) error { - key, labels := objectWithLabelArgs(context) + key, labels := commands.ObjectWithLabelArgs(context) client, ctx, cancel, err := newClient(context) if err != nil { return err diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go index 23e9d6d82..bd6fa8da3 100644 --- a/cmd/ctr/utils.go +++ b/cmd/ctr/utils.go @@ -5,7 +5,6 @@ import ( gocontext "context" "crypto/tls" "encoding/csv" - "encoding/json" "fmt" "net" "net/http" @@ -18,54 +17,15 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images" "github.com/containerd/containerd/namespaces" - "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" - "github.com/containerd/containerd/rootfs" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) -var ( - snapshotterFlags = []cli.Flag{ - cli.StringFlag{ - Name: "snapshotter", - Usage: "snapshotter name. Empty value stands for the daemon default value.", - Value: containerd.DefaultSnapshotter, - }, - } - - labelFlag = cli.StringSliceFlag{ - Name: "label", - Usage: "labels to attach to the pulled image", - } - - registryFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "skip-verify,k", - Usage: "skip SSL certificate validation", - }, - cli.BoolFlag{ - Name: "plain-http", - Usage: "allow connections using plain HTTP", - }, - cli.StringFlag{ - Name: "user,u", - Usage: "user[:password] Registry user and password", - }, - cli.StringFlag{ - Name: "refresh", - Usage: "refresh token for authorization server", - }, - } -) - // appContext returns the context for a command. Should only be called once per // command, near the start. // @@ -114,39 +74,6 @@ func passwordPrompt() (string, error) { return string(line), nil } -func getImageLayers(ctx gocontext.Context, image images.Image, cs content.Store) ([]rootfs.Layer, error) { - p, err := content.ReadBlob(ctx, cs, image.Target.Digest) - if err != nil { - return nil, errors.Wrapf(err, "failed to read manifest blob") - } - - var manifest ocispec.Manifest - if err := json.Unmarshal(p, &manifest); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal manifest") - } - - diffIDs, err := image.RootFS(ctx, cs, platforms.Default()) - if err != nil { - return nil, errors.Wrap(err, "failed to resolve rootfs") - } - - if len(diffIDs) != len(manifest.Layers) { - return nil, errors.Errorf("mismatched image rootfs and manifest layers") - } - - layers := make([]rootfs.Layer, len(diffIDs)) - for i := range diffIDs { - layers[i].Diff = ocispec.Descriptor{ - // TODO: derive media type from compressed type - MediaType: ocispec.MediaTypeImageLayer, - Digest: diffIDs[i], - } - layers[i].Blob = manifest.Layers[i] - } - - return layers, nil -} - // getResolver prepares the resolver from the environment and options. func getResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolver, error) { username := clicontext.String("user") @@ -312,36 +239,3 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string { return defaults } - -func objectWithLabelArgs(clicontext *cli.Context) (string, map[string]string) { - var ( - namespace = clicontext.Args().First() - labelStrings = clicontext.Args().Tail() - ) - - return namespace, labelArgs(labelStrings) -} - -func labelArgs(labelStrings []string) map[string]string { - labels := make(map[string]string, len(labelStrings)) - for _, label := range labelStrings { - parts := strings.SplitN(label, "=", 2) - key := parts[0] - value := "true" - if len(parts) > 1 { - value = parts[1] - } - - labels[key] = value - } - - return labels -} - -func printAsJSON(x interface{}) { - b, err := json.MarshalIndent(x, "", " ") - if err != nil { - fmt.Fprintf(os.Stderr, "can't marshal %+v as a JSON string: %v\n", x, err) - } - fmt.Println(string(b)) -}