ctr: add commands package with shared utility functions

Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
Jess Valarezo 2017-10-23 17:40:18 -07:00
parent 04659d9405
commit a19a20303a
19 changed files with 123 additions and 128 deletions

View File

@ -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))
}

View File

@ -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

View File

@ -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),

View File

@ -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] <remote> <object> [<hint>, ...]",
Description: `Fetch objects by identifier from a remote.`,
Flags: registryFlags,
Flags: commands.RegistryFlags,
Action: func(context *cli.Context) error {
var (
ref = context.Args().First()

View File

@ -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 {

View File

@ -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 {

View File

@ -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
},

View File

@ -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")
}

View File

@ -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] <name> [<key>=<value]",
Description: "Create a new namespace. It must be unique.",
Action: func(context *cli.Context) error {
namespace, labels := objectWithLabelArgs(context)
namespace, labels := commands.ObjectWithLabelArgs(context)
if namespace == "" {
return errors.New("please specify a namespace")
}
@ -51,7 +52,7 @@ var namespacesSetLabelsCommand = cli.Command{
Description: "Set and clear labels for a namespace.",
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
namespace, labels := objectWithLabelArgs(context)
namespace, labels := commands.ObjectWithLabelArgs(context)
if namespace == "" {
return errors.New("please specify a namespace")
}

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/log"
"github.com/urfave/cli"
)
@ -20,7 +21,7 @@ command. As part of this process, we do the following:
2. Prepare the snapshot filesystem with the pulled resources.
3. Register metadata for the image.
`,
Flags: append(registryFlags, append(snapshotterFlags, labelFlag)...),
Flags: append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
Action: func(context *cli.Context) error {
var (
ref = context.Args().First()

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/progress"
@ -37,7 +38,7 @@ var pushCommand = cli.Command{
creating the associated configuration, and creating the manifest
which references those resources.
`,
Flags: append(registryFlags, cli.StringFlag{
Flags: append(commands.RegistryFlags, cli.StringFlag{
Name: "manifest",
Usage: "digest of manifest",
}, cli.StringFlag{

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/log"
digest "github.com/opencontainers/go-digest"
@ -15,7 +16,7 @@ var pushObjectCommand = cli.Command{
Usage: "push an object to a remote",
ArgsUsage: "[flags] <remote> <object> <type>",
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)

View File

@ -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] <digest>",
Flags: snapshotterFlags,
Flags: commands.SnapshotterFlags,
Action: func(context *cli.Context) error {
dgst, err := digest.Parse(context.Args().First())
if err != nil {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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
},
}

View File

@ -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

View File

@ -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))
}