cmd/ctr, service/containers: implement container filter

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day
2017-06-21 15:29:58 -07:00
parent 3332042ab6
commit 396d89e423
16 changed files with 409 additions and 189 deletions

52
cmd/ctr/labels.go Normal file
View File

@@ -0,0 +1,52 @@
package main
import (
"errors"
"fmt"
"strings"
"github.com/urfave/cli"
)
var containersSetLabelsCommand = cli.Command{
Name: "set-labels",
Usage: "Set and clear labels for a container.",
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
Description: "Set and clear labels for a container.",
Flags: []cli.Flag{},
Action: func(clicontext *cli.Context) error {
var (
ctx, cancel = appContext(clicontext)
containerID, labels = objectWithLabelArgs(clicontext)
)
defer cancel()
client, err := newClient(clicontext)
if err != nil {
return err
}
if containerID == "" {
return errors.New("please specify a container")
}
container, err := client.LoadContainer(ctx, containerID)
if err != nil {
return err
}
setlabels, err := container.SetLabels(ctx, labels)
if err != nil {
return err
}
var labelStrings []string
for k, v := range setlabels {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", k, v))
}
fmt.Println(strings.Join(labelStrings, ","))
return nil
},
}

View File

@@ -3,11 +3,11 @@ package main
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/containerd/containerd"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
tasktypes "github.com/containerd/containerd/api/types/task"
"github.com/urfave/cli"
)
@@ -31,24 +31,18 @@ var taskListCommand = cli.Command{
},
}
var containerListCommand = cli.Command{
Name: "containers",
Usage: "manage containers (metadata)",
Aliases: []string{"c"},
Subcommands: []cli.Command{
{
Name: "list",
Usage: "list tasks",
Aliases: []string{"ls"},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
},
},
Action: containerListFn,
var containersListCommand = cli.Command{
Name: "list",
Usage: "list all tasks or those that match a filter",
ArgsUsage: "[filter, ...]",
Aliases: []string{"ls"},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
},
},
Action: containerListFn,
}
func taskListFn(context *cli.Context) error {
@@ -62,66 +56,25 @@ func taskListFn(context *cli.Context) error {
if err != nil {
return err
}
containers, err := client.Containers(ctx)
if err != nil {
return err
}
s := client.TaskService()
tasksResponse, err := s.List(ctx, &tasks.ListTasksRequest{})
response, err := s.List(ctx, &tasks.ListTasksRequest{})
if err != nil {
return err
}
// Join with tasks to get status.
tasksByContainerID := map[string]*tasktypes.Task{}
for _, task := range tasksResponse.Tasks {
tasksByContainerID[task.ContainerID] = task
}
if quiet {
for _, c := range containers {
task, ok := tasksByContainerID[c.ID()]
if ok {
fmt.Printf("%s\t%d\n", c.ID(), task.Pid)
} else {
//Since task is not running, PID is printed 0
fmt.Printf("%s\t%d\n", c.ID(), 0)
}
for _, task := range response.Tasks {
fmt.Println(task.ID)
}
} else {
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
fmt.Fprintln(w, "TASK-ID\tIMAGE\tPID\tSTATUS")
for _, c := range containers {
var imageName string
if image, err := c.Image(ctx); err != nil {
if err != containerd.ErrNoImage {
return err
}
imageName = "-"
} else {
imageName = image.Name()
}
var (
status string
pid uint32
)
task, ok := tasksByContainerID[c.ID()]
if ok {
status = task.Status.String()
pid = task.Pid
} else {
status = "STOPPED" // TODO(stevvooe): Is this assumption correct?
pid = 0
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\n",
c.ID(),
imageName,
pid,
status,
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "TASK\tPID\tSTATUS\t")
for _, task := range response.Tasks {
if _, err := fmt.Fprintf(w, "%s\t%d\t%s\n",
task.ID,
task.Pid,
task.Status.String(),
); err != nil {
return err
}
@@ -133,6 +86,7 @@ func taskListFn(context *cli.Context) error {
func containerListFn(context *cli.Context) error {
var (
filters = context.Args()
quiet = context.Bool("quiet")
ctx, cancel = appContext(context)
)
@@ -142,18 +96,28 @@ func containerListFn(context *cli.Context) error {
if err != nil {
return err
}
containers, err := client.Containers(ctx)
containers, err := client.Containers(ctx, filters...)
if err != nil {
return err
}
if quiet {
for _, c := range containers {
fmt.Printf("%s\n", c.ID())
}
} else {
cl := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
fmt.Fprintln(cl, "ID\tIMAGE\tRUNTIME\tSIZE")
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "CONTAINER\tIMAGE\tRUNTIME\tLABELS\t")
for _, c := range containers {
var labelStrings []string
for k, v := range c.Proto().Labels {
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
}
labels := strings.Join(labelStrings, ",")
if labels == "" {
labels = "-"
}
var imageName string
if image, err := c.Image(ctx); err != nil {
if err != containerd.ErrNoImage {
@@ -163,17 +127,18 @@ func containerListFn(context *cli.Context) error {
} else {
imageName = image.Name()
}
proto := c.Proto()
if _, err := fmt.Fprintf(cl, "%s\t%s\t%s\t%d\n",
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t\n",
c.ID(),
imageName,
proto.Runtime.Name,
proto.Size(),
labels,
); err != nil {
return err
}
}
return cl.Flush()
return w.Flush()
}
return nil
}

View File

@@ -60,7 +60,7 @@ containerd CLI
deleteCommand,
namespacesCommand,
eventsCommand,
containerListCommand,
containersCommand,
taskListCommand,
infoCommand,
killCommand,
@@ -83,3 +83,13 @@ containerd CLI
os.Exit(1)
}
}
var containersCommand = cli.Command{
Name: "containers",
Usage: "manage containers (metadata)",
Aliases: []string{"c"},
Subcommands: []cli.Command{
containersListCommand,
containersSetLabelsCommand,
},
}

View File

@@ -33,7 +33,7 @@ var namespacesCreateCommand = cli.Command{
Action: func(clicontext *cli.Context) error {
var (
ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext)
namespace, labels = objectWithLabelArgs(clicontext)
)
if namespace == "" {
@@ -53,27 +53,6 @@ var namespacesCreateCommand = cli.Command{
},
}
func namespaceWithLabelArgs(clicontext *cli.Context) (string, map[string]string) {
var (
namespace = clicontext.Args().First()
labelStrings = clicontext.Args().Tail()
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 namespace, labels
}
var namespacesSetLabelsCommand = cli.Command{
Name: "set-labels",
Usage: "Set and clear labels for a namespace.",
@@ -83,7 +62,7 @@ var namespacesSetLabelsCommand = cli.Command{
Action: func(clicontext *cli.Context) error {
var (
ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext)
namespace, labels = objectWithLabelArgs(clicontext)
)
namespaces, err := getNamespacesService(clicontext)

View File

@@ -74,6 +74,10 @@ var runCommand = cli.Command{
Name: "env",
Usage: "specify additional container environment variables (i.e. FOO=bar)",
},
cli.StringSliceFlag{
Name: "label",
Usage: "specify additional labels (foo=bar)",
},
cli.BoolFlag{
Name: "rm",
Usage: "remove the container after running",

View File

@@ -57,11 +57,15 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
err error
checkpointIndex digest.Digest
ref = context.Args().First()
id = context.Args().Get(1)
args = context.Args()[2:]
tty = context.Bool("tty")
ref = context.Args().First()
id = context.Args().Get(1)
args = context.Args()[2:]
tty = context.Bool("tty")
labelStrings = context.StringSlice("label")
)
labels := labelArgs(labelStrings)
if raw := context.String("checkpoint"); raw != "" {
if checkpointIndex, err = digest.Parse(raw); err != nil {
return nil, err
@@ -71,6 +75,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil {
return nil, err
}
if checkpointIndex == "" {
opts := []containerd.SpecOpts{
containerd.WithImageConfig(ctx, image),
@@ -96,12 +101,15 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
} else {
rootfs = containerd.WithNewRootFS(id, image)
}
return client.NewContainer(ctx, id,
containerd.WithSpec(spec),
containerd.WithImage(image),
containerd.WithContainerLabels(labels),
rootfs,
)
}
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
Digest: checkpointIndex,
}, id))

View File

@@ -247,3 +247,28 @@ 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
}