Skip to content

Commit f24ad23

Browse files
committed
add option watch
Signed-off-by: yeya24 <[email protected]>
1 parent 80049a9 commit f24ad23

File tree

5 files changed

+101
-55
lines changed

5 files changed

+101
-55
lines changed

cmd/crictl/container.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ func ListContainers(client pb.RuntimeServiceClient, opts listOptions) error {
639639

640640
display := newTableDisplay(20, 1, 3, ' ', 0)
641641
if !opts.verbose && !opts.quiet {
642-
display.AddRow([]string{container, image, created, state, name, attempt, podID})
642+
display.AddRow([]string{columnContainer, columnImage, columnCreated, columnState, columnName, columnAttempt, columnPodID})
643643
}
644644
for _, c := range r.Containers {
645645
// Filter by pod name/namespace regular expressions.

cmd/crictl/display.go

+32-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package main
218

319
import (
@@ -8,18 +24,22 @@ import (
824
)
925

1026
const (
11-
container = "CONTAINER"
12-
image = "IMAGE"
13-
imageID = "IMAGE ID"
14-
created = "CREATED"
15-
state = "STATE"
16-
name = "NAME"
17-
attempt = "ATTEMPT"
18-
podID = "POD ID"
19-
namespace = "NAMESPACE"
20-
size = "SIZE"
21-
tag = "TAG"
22-
digest = "DIGEST"
27+
columnContainer = "CONTAINER"
28+
columnImage = "IMAGE"
29+
columnImageID = "IMAGE ID"
30+
columnCreated = "CREATED"
31+
columnState = "STATE"
32+
columnName = "NAME"
33+
columnAttempt = "ATTEMPT"
34+
columnPodID = "POD ID"
35+
columnNamespace = "NAMESPACE"
36+
columnSize = "SIZE"
37+
columnTag = "TAG"
38+
columnDigest = "DIGEST"
39+
columnMemory = "MEM"
40+
columnInodes = "INODES"
41+
columnDisk = "DISK"
42+
columnCPU = "CPU %"
2343
)
2444

2545
// display use to output something on screen with table format.

cmd/crictl/image.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ package main
1919
import (
2020
"errors"
2121
"fmt"
22+
"sort"
23+
"strings"
24+
2225
"github.com/docker/go-units"
2326
"github.com/sirupsen/logrus"
2427
"github.com/urfave/cli"
2528
"golang.org/x/net/context"
2629
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
27-
"sort"
28-
"strings"
2930
)
3031

3132
type imageByRef []*pb.Image
@@ -150,9 +151,9 @@ var listImageCommand = cli.Command{
150151
noTrunc := context.Bool("no-trunc")
151152
if !verbose && !quiet {
152153
if showDigest {
153-
display.AddRow([]string{image, tag, digest, imageID, size})
154+
display.AddRow([]string{columnImage, columnTag, columnDigest, columnImageID, columnSize})
154155
} else {
155-
display.AddRow([]string{image, tag, imageID, size})
156+
display.AddRow([]string{columnImage, columnTag, columnImageID, columnSize})
156157
}
157158
}
158159
for _, image := range r.Images {

cmd/crictl/sandbox.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ func ListPodSandboxes(client pb.RuntimeServiceClient, opts listOptions) error {
450450

451451
display := newTableDisplay(20, 1, 3, ' ', 0)
452452
if !opts.verbose && !opts.quiet {
453-
display.AddRow([]string{podID, created, state, name, namespace, attempt})
453+
display.AddRow([]string{columnPodID, columnCreated, columnState, columnName, columnNamespace, columnAttempt})
454454
}
455455
for _, pod := range r.Items {
456456
// Filter by pod name/namespace regular expressions.

cmd/crictl/stats.go

+62-37
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ type statsOptions struct {
4141
labels map[string]string
4242
// output format
4343
output string
44+
// live watch
45+
watch bool
4446
}
4547

4648
var statsCommand = cli.Command{
4749
Name: "stats",
48-
// TODO(random-liu): Support live monitoring of resource usage.
4950
Usage: "List container(s) resource usage statistics",
5051
SkipArgReorder: true,
5152
UseShortOptionHandling: true,
@@ -77,6 +78,10 @@ var statsCommand = cli.Command{
7778
Value: 1,
7879
Usage: "Sample duration for CPU usage in seconds",
7980
},
81+
cli.BoolFlag{
82+
Name: "watch, w",
83+
Usage: "Watch pod resources",
84+
},
8085
},
8186
Action: func(context *cli.Context) error {
8287
var err error
@@ -90,6 +95,7 @@ var statsCommand = cli.Command{
9095
podID: context.String("pod"),
9196
sample: time.Duration(context.Int("seconds")) * time.Second,
9297
output: context.String("output"),
98+
watch: context.Bool("watch"),
9399
}
94100
opts.labels, err = parseLabelStringSlice(context.StringSlice("label"))
95101
if err != nil {
@@ -127,14 +133,39 @@ func ContainerStats(client pb.RuntimeServiceClient, opts statsOptions) error {
127133
request := &pb.ListContainerStatsRequest{
128134
Filter: filter,
129135
}
136+
137+
display := newTableDisplay(20, 1, 3, ' ', 0)
138+
if !opts.watch {
139+
if err := displayStats(client, request, display, opts); err != nil {
140+
return err
141+
}
142+
} else {
143+
for range time.Tick(500 * time.Millisecond) {
144+
if err := displayStats(client, request, display, opts); err != nil {
145+
return err
146+
}
147+
}
148+
}
149+
150+
return nil
151+
}
152+
153+
func getContainerStats(client pb.RuntimeServiceClient, request *pb.ListContainerStatsRequest) (*pb.ListContainerStatsResponse, error) {
130154
logrus.Debugf("ListContainerStatsRequest: %v", request)
131155
r, err := client.ListContainerStats(context.Background(), request)
132156
logrus.Debugf("ListContainerResponse: %v", r)
133157
if err != nil {
134-
return err
158+
return nil, err
135159
}
136160
sort.Sort(containerStatsByID(r.Stats))
161+
return r, nil
162+
}
137163

164+
func displayStats(client pb.RuntimeServiceClient, request *pb.ListContainerStatsRequest, display *display, opts statsOptions) error {
165+
r, err := getContainerStats(client, request)
166+
if err != nil {
167+
return err
168+
}
138169
switch opts.output {
139170
case "json":
140171
return outputProtobufObjAsJSON(r)
@@ -148,48 +179,42 @@ func ContainerStats(client pb.RuntimeServiceClient, opts statsOptions) error {
148179

149180
time.Sleep(opts.sample)
150181

151-
logrus.Debugf("ListContainerStatsRequest: %v", request)
152-
r, err = client.ListContainerStats(context.Background(), request)
153-
logrus.Debugf("ListContainerResponse: %v", r)
182+
r, err = getContainerStats(client, request)
154183
if err != nil {
155184
return err
156185
}
157-
sort.Sort(containerStatsByID(r.Stats))
158186

159-
display := newTableDisplay(20, 1, 3, ' ', 0)
160-
for range time.Tick(500 * time.Millisecond) {
161-
display.AddRow([]string{"CONTAINER", "CPU %", "MEM", "DISK", "INODES"})
162-
for _, s := range r.GetStats() {
163-
id := getTruncatedID(s.Attributes.Id, "")
164-
cpu := s.GetCpu().GetUsageCoreNanoSeconds().GetValue()
165-
mem := s.GetMemory().GetWorkingSetBytes().GetValue()
166-
disk := s.GetWritableLayer().GetUsedBytes().GetValue()
167-
inodes := s.GetWritableLayer().GetInodesUsed().GetValue()
168-
if !opts.all && cpu == 0 && mem == 0 {
169-
// Skip non-running container
170-
continue
171-
}
172-
old, ok := oldStats[s.Attributes.Id]
173-
if !ok {
174-
// Skip new container
175-
continue
176-
}
177-
var cpuPerc float64
178-
if cpu != 0 {
179-
// Only generate cpuPerc for running container
180-
duration := s.GetCpu().GetTimestamp() - old.GetCpu().GetTimestamp()
181-
if duration == 0 {
182-
return fmt.Errorf("cpu stat is not updated during sample")
183-
}
184-
cpuPerc = float64(cpu-old.GetCpu().GetUsageCoreNanoSeconds().GetValue()) / float64(duration) * 100
187+
display.AddRow([]string{columnContainer, columnCPU, columnMemory, columnDisk, columnInodes})
188+
for _, s := range r.GetStats() {
189+
id := getTruncatedID(s.Attributes.Id, "")
190+
cpu := s.GetCpu().GetUsageCoreNanoSeconds().GetValue()
191+
mem := s.GetMemory().GetWorkingSetBytes().GetValue()
192+
disk := s.GetWritableLayer().GetUsedBytes().GetValue()
193+
inodes := s.GetWritableLayer().GetInodesUsed().GetValue()
194+
if !opts.all && cpu == 0 && mem == 0 {
195+
// Skip non-running container
196+
continue
197+
}
198+
old, ok := oldStats[s.Attributes.Id]
199+
if !ok {
200+
// Skip new container
201+
continue
202+
}
203+
var cpuPerc float64
204+
if cpu != 0 {
205+
// Only generate cpuPerc for running container
206+
duration := s.GetCpu().GetTimestamp() - old.GetCpu().GetTimestamp()
207+
if duration == 0 {
208+
return fmt.Errorf("cpu stat is not updated during sample")
185209
}
186-
display.AddRow([]string{id, fmt.Sprintf("%.2f", cpuPerc), units.HumanSize(float64(mem)),
187-
units.HumanSize(float64(disk)), fmt.Sprintf("%d", inodes)})
188-
210+
cpuPerc = float64(cpu-old.GetCpu().GetUsageCoreNanoSeconds().GetValue()) / float64(duration) * 100
189211
}
190-
display.ClearScreen()
191-
display.Flush()
212+
display.AddRow([]string{id, fmt.Sprintf("%.2f", cpuPerc), units.HumanSize(float64(mem)),
213+
units.HumanSize(float64(disk)), fmt.Sprintf("%d", inodes)})
214+
192215
}
216+
display.ClearScreen()
217+
display.Flush()
193218

194219
return nil
195220
}

0 commit comments

Comments
 (0)