From 2c87d120df6f9c6e42ce30829f0c6c6f965907c7 Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Thu, 26 Jul 2018 16:35:14 -0700 Subject: [PATCH 1/2] ctr: add new metrics subcommand Signed-off-by: Samuel Karp --- cmd/ctr/commands/tasks/metrics.go | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 cmd/ctr/commands/tasks/metrics.go diff --git a/cmd/ctr/commands/tasks/metrics.go b/cmd/ctr/commands/tasks/metrics.go new file mode 100644 index 000000000..937706828 --- /dev/null +++ b/cmd/ctr/commands/tasks/metrics.go @@ -0,0 +1,83 @@ +// +build linux + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tasks + +import ( + "errors" + "fmt" + + "os" + "text/tabwriter" + + "github.com/containerd/cgroups" + "github.com/containerd/containerd/cmd/ctr/commands" + "github.com/containerd/typeurl" + "github.com/urfave/cli" +) + +func init() { + // metricsCommand is only added on Linux as github.com/containerd/cgroups + // does not compile on darwin or windows + Command.Subcommands = append(Command.Subcommands, metricsCommand) +} + +var metricsCommand = cli.Command{ + Name: "metrics", + Usage: "get a single data point of metrics for a task with the built-in Linux runtime", + ArgsUsage: "CONTAINER", + Aliases: []string{"metric"}, + Action: func(context *cli.Context) error { + client, ctx, cancel, err := commands.NewClient(context) + if err != nil { + return err + } + defer cancel() + container, err := client.LoadContainer(ctx, context.Args().First()) + if err != nil { + return err + } + task, err := container.Task(ctx, nil) + if err != nil { + return err + } + metric, err := task.Metrics(ctx) + if err != nil { + return nil + } + w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0) + fmt.Fprintf(w, "ID\tTIMESTAMP\t\n") + fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp) + + anydata, err := typeurl.UnmarshalAny(metric.Data) + if err != nil { + return err + } + data, ok := anydata.(*cgroups.Metrics) + if !ok { + return errors.New("cannot convert metric data to cgroups.Metrics") + } + fmt.Fprintf(w, "METRIC\tVALUE\t\n") + fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage) + fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache) + fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total) + fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU) + + return w.Flush() + }, +} From 9a34bb046ad91163561b6b2845ae2a9575f64687 Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Fri, 27 Jul 2018 09:41:49 -0700 Subject: [PATCH 2/2] metrics: add optional json output Signed-off-by: Samuel Karp --- cmd/ctr/commands/tasks/metrics.go | 47 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/cmd/ctr/commands/tasks/metrics.go b/cmd/ctr/commands/tasks/metrics.go index 937706828..a60cddad5 100644 --- a/cmd/ctr/commands/tasks/metrics.go +++ b/cmd/ctr/commands/tasks/metrics.go @@ -19,9 +19,9 @@ package tasks import ( + "encoding/json" "errors" "fmt" - "os" "text/tabwriter" @@ -37,11 +37,24 @@ func init() { Command.Subcommands = append(Command.Subcommands, metricsCommand) } +const ( + formatFlag = "format" + formatTable = "table" + formatJSON = "json" +) + var metricsCommand = cli.Command{ Name: "metrics", Usage: "get a single data point of metrics for a task with the built-in Linux runtime", ArgsUsage: "CONTAINER", Aliases: []string{"metric"}, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: formatFlag, + Usage: `"table" or "json"`, + Value: formatTable, + }, + }, Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) if err != nil { @@ -60,10 +73,6 @@ var metricsCommand = cli.Command{ if err != nil { return nil } - w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0) - fmt.Fprintf(w, "ID\tTIMESTAMP\t\n") - fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp) - anydata, err := typeurl.UnmarshalAny(metric.Data) if err != nil { return err @@ -72,12 +81,28 @@ var metricsCommand = cli.Command{ if !ok { return errors.New("cannot convert metric data to cgroups.Metrics") } - fmt.Fprintf(w, "METRIC\tVALUE\t\n") - fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage) - fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache) - fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total) - fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU) - return w.Flush() + switch context.String(formatFlag) { + case formatTable: + w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0) + fmt.Fprintf(w, "ID\tTIMESTAMP\t\n") + fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp) + + fmt.Fprintf(w, "METRIC\tVALUE\t\n") + fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage) + fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache) + fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total) + fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU) + return w.Flush() + case formatJSON: + marshaledJSON, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + fmt.Println(string(marshaledJSON)) + return nil + default: + return errors.New("format must be table or json") + } }, }