1
16
17 package top
18
19 import (
20 "bytes"
21 "fmt"
22 "io"
23 "net/http"
24 "reflect"
25 "strings"
26 "testing"
27
28 "k8s.io/api/core/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 "k8s.io/cli-runtime/pkg/genericiooptions"
31 "k8s.io/client-go/rest/fake"
32 core "k8s.io/client-go/testing"
33 cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
34 "k8s.io/kubectl/pkg/scheme"
35 metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
36 metricsfake "k8s.io/metrics/pkg/client/clientset/versioned/fake"
37 )
38
39 const (
40 apiPrefix = "api"
41 apiVersion = "v1"
42 )
43
44 func TestTopNodeAllMetricsFrom(t *testing.T) {
45 cmdtesting.InitTestErrorHandler(t)
46 expectedMetrics, nodes := testNodeV1beta1MetricsData()
47 expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
48
49 tf := cmdtesting.NewTestFactory().WithNamespace("test")
50 defer tf.Cleanup()
51
52 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
53 ns := scheme.Codecs.WithoutConversion()
54
55 tf.Client = &fake.RESTClient{
56 NegotiatedSerializer: ns,
57 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
58 switch p, m := req.URL.Path, req.Method; {
59 case p == "/api":
60 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
61 case p == "/apis":
62 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
63 case p == expectedNodePath && m == "GET":
64 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, nodes)}, nil
65 default:
66 t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
67 return nil, nil
68 }
69 }),
70 }
71 fakemetricsClientset := &metricsfake.Clientset{}
72 fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
73 return true, expectedMetrics, nil
74 })
75 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
76 streams, _, buf, _ := genericiooptions.NewTestIOStreams()
77
78 cmd := NewCmdTopNode(tf, nil, streams)
79
80
81
82 cmdOptions := &TopNodeOptions{
83 IOStreams: streams,
84 }
85 if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
86 t.Fatal(err)
87 }
88 cmdOptions.MetricsClient = fakemetricsClientset
89 if err := cmdOptions.Validate(); err != nil {
90 t.Fatal(err)
91 }
92 if err := cmdOptions.RunTopNode(); err != nil {
93 t.Fatal(err)
94 }
95
96
97 result := buf.String()
98 for _, m := range expectedMetrics.Items {
99 if !strings.Contains(result, m.Name) {
100 t.Errorf("missing metrics for %s: \n%s", m.Name, result)
101 }
102 }
103 }
104
105 func TestTopNodeWithNameMetricsFrom(t *testing.T) {
106 cmdtesting.InitTestErrorHandler(t)
107 metrics, nodes := testNodeV1beta1MetricsData()
108 expectedMetrics := metrics.Items[0]
109 expectedNode := nodes.Items[0]
110 nonExpectedMetrics := metricsv1beta1api.NodeMetricsList{
111 ListMeta: metrics.ListMeta,
112 Items: metrics.Items[1:],
113 }
114 expectedNodePath := fmt.Sprintf("/%s/%s/nodes/%s", apiPrefix, apiVersion, expectedMetrics.Name)
115
116 tf := cmdtesting.NewTestFactory().WithNamespace("test")
117 defer tf.Cleanup()
118
119 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
120 ns := scheme.Codecs.WithoutConversion()
121
122 tf.Client = &fake.RESTClient{
123 NegotiatedSerializer: ns,
124 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
125 switch p, m := req.URL.Path, req.Method; {
126 case p == "/api":
127 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
128 case p == "/apis":
129 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
130 case p == expectedNodePath && m == "GET":
131 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &expectedNode)}, nil
132 default:
133 t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
134 return nil, nil
135 }
136 }),
137 }
138 fakemetricsClientset := &metricsfake.Clientset{}
139 fakemetricsClientset.AddReactor("get", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
140 return true, &expectedMetrics, nil
141 })
142 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
143 streams, _, buf, _ := genericiooptions.NewTestIOStreams()
144
145 cmd := NewCmdTopNode(tf, nil, streams)
146
147
148
149 cmdOptions := &TopNodeOptions{
150 IOStreams: streams,
151 }
152 if err := cmdOptions.Complete(tf, cmd, []string{expectedMetrics.Name}); err != nil {
153 t.Fatal(err)
154 }
155 cmdOptions.MetricsClient = fakemetricsClientset
156 if err := cmdOptions.Validate(); err != nil {
157 t.Fatal(err)
158 }
159 if err := cmdOptions.RunTopNode(); err != nil {
160 t.Fatal(err)
161 }
162
163
164 result := buf.String()
165 if !strings.Contains(result, expectedMetrics.Name) {
166 t.Errorf("missing metrics for %s: \n%s", expectedMetrics.Name, result)
167 }
168 for _, m := range nonExpectedMetrics.Items {
169 if strings.Contains(result, m.Name) {
170 t.Errorf("unexpected metrics for %s: \n%s", m.Name, result)
171 }
172 }
173 }
174
175 func TestTopNodeWithLabelSelectorMetricsFrom(t *testing.T) {
176 cmdtesting.InitTestErrorHandler(t)
177 metrics, nodes := testNodeV1beta1MetricsData()
178 expectedMetrics := &metricsv1beta1api.NodeMetricsList{
179 ListMeta: metrics.ListMeta,
180 Items: metrics.Items[0:1],
181 }
182 expectedNodes := v1.NodeList{
183 ListMeta: nodes.ListMeta,
184 Items: nodes.Items[0:1],
185 }
186 nonExpectedMetrics := &metricsv1beta1api.NodeMetricsList{
187 ListMeta: metrics.ListMeta,
188 Items: metrics.Items[1:],
189 }
190 label := "key=value"
191 expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
192
193 tf := cmdtesting.NewTestFactory().WithNamespace("test")
194 defer tf.Cleanup()
195
196 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
197 ns := scheme.Codecs.WithoutConversion()
198
199 tf.Client = &fake.RESTClient{
200 NegotiatedSerializer: ns,
201 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
202 switch p, m, _ := req.URL.Path, req.Method, req.URL.RawQuery; {
203 case p == "/api":
204 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
205 case p == "/apis":
206 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
207 case p == expectedNodePath && m == "GET":
208 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &expectedNodes)}, nil
209 default:
210 t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
211 return nil, nil
212 }
213 }),
214 }
215
216 fakemetricsClientset := &metricsfake.Clientset{}
217 fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
218 return true, expectedMetrics, nil
219 })
220 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
221 streams, _, buf, _ := genericiooptions.NewTestIOStreams()
222
223 cmd := NewCmdTopNode(tf, nil, streams)
224 cmd.Flags().Set("selector", label)
225
226
227
228 cmdOptions := &TopNodeOptions{
229 IOStreams: streams,
230 }
231 if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
232 t.Fatal(err)
233 }
234 cmdOptions.MetricsClient = fakemetricsClientset
235 if err := cmdOptions.Validate(); err != nil {
236 t.Fatal(err)
237 }
238 if err := cmdOptions.RunTopNode(); err != nil {
239 t.Fatal(err)
240 }
241
242
243 result := buf.String()
244 for _, m := range expectedMetrics.Items {
245 if !strings.Contains(result, m.Name) {
246 t.Errorf("missing metrics for %s: \n%s", m.Name, result)
247 }
248 }
249 for _, m := range nonExpectedMetrics.Items {
250 if strings.Contains(result, m.Name) {
251 t.Errorf("unexpected metrics for %s: \n%s", m.Name, result)
252 }
253 }
254 }
255
256 func TestTopNodeWithSortByCpuMetricsFrom(t *testing.T) {
257 cmdtesting.InitTestErrorHandler(t)
258 metrics, nodes := testNodeV1beta1MetricsData()
259 expectedMetrics := &metricsv1beta1api.NodeMetricsList{
260 ListMeta: metrics.ListMeta,
261 Items: metrics.Items[:],
262 }
263 expectedNodes := v1.NodeList{
264 ListMeta: nodes.ListMeta,
265 Items: nodes.Items[:],
266 }
267 expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
268 expectedNodesNames := []string{"node2", "node3", "node1"}
269
270 tf := cmdtesting.NewTestFactory().WithNamespace("test")
271 defer tf.Cleanup()
272
273 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
274 ns := scheme.Codecs
275
276 tf.Client = &fake.RESTClient{
277 NegotiatedSerializer: ns,
278 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
279 switch p, m := req.URL.Path, req.Method; {
280 case p == "/api":
281 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
282 case p == "/apis":
283 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
284 case p == expectedNodePath && m == "GET":
285 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &expectedNodes)}, nil
286 default:
287 t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
288 return nil, nil
289 }
290 }),
291 }
292 fakemetricsClientset := &metricsfake.Clientset{}
293 fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
294 return true, expectedMetrics, nil
295 })
296 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
297 streams, _, buf, _ := genericiooptions.NewTestIOStreams()
298
299 cmd := NewCmdTopNode(tf, nil, streams)
300 cmd.Flags().Set("sort-by", "cpu")
301
302
303
304 cmdOptions := &TopNodeOptions{
305 IOStreams: streams,
306 SortBy: "cpu",
307 }
308 if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
309 t.Fatal(err)
310 }
311 cmdOptions.MetricsClient = fakemetricsClientset
312 if err := cmdOptions.Validate(); err != nil {
313 t.Fatal(err)
314 }
315 if err := cmdOptions.RunTopNode(); err != nil {
316 t.Fatal(err)
317 }
318
319
320 result := buf.String()
321
322 for _, m := range expectedMetrics.Items {
323 if !strings.Contains(result, m.Name) {
324 t.Errorf("missing metrics for %s: \n%s", m.Name, result)
325 }
326 }
327
328 resultLines := strings.Split(result, "\n")
329 resultNodes := make([]string, len(resultLines)-2)
330
331 for i, line := range resultLines[1 : len(resultLines)-1] {
332 lineFirstColumn := strings.Split(line, " ")[0]
333 resultNodes[i] = lineFirstColumn
334 }
335
336 if !reflect.DeepEqual(resultNodes, expectedNodesNames) {
337 t.Errorf("kinds not matching:\n\texpectedKinds: %v\n\tgotKinds: %v\n", expectedNodesNames, resultNodes)
338 }
339
340 }
341
342 func TestTopNodeWithSortByMemoryMetricsFrom(t *testing.T) {
343 cmdtesting.InitTestErrorHandler(t)
344 metrics, nodes := testNodeV1beta1MetricsData()
345 expectedMetrics := &metricsv1beta1api.NodeMetricsList{
346 ListMeta: metrics.ListMeta,
347 Items: metrics.Items[:],
348 }
349 expectedNodes := v1.NodeList{
350 ListMeta: nodes.ListMeta,
351 Items: nodes.Items[:],
352 }
353 expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
354 expectedNodesNames := []string{"node2", "node3", "node1"}
355
356 tf := cmdtesting.NewTestFactory().WithNamespace("test")
357 defer tf.Cleanup()
358
359 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
360 ns := scheme.Codecs
361
362 tf.Client = &fake.RESTClient{
363 NegotiatedSerializer: ns,
364 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
365 switch p, m := req.URL.Path, req.Method; {
366 case p == "/api":
367 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
368 case p == "/apis":
369 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
370 case p == expectedNodePath && m == "GET":
371 return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &expectedNodes)}, nil
372 default:
373 t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
374 return nil, nil
375 }
376 }),
377 }
378 fakemetricsClientset := &metricsfake.Clientset{}
379 fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
380 return true, expectedMetrics, nil
381 })
382 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
383 streams, _, buf, _ := genericiooptions.NewTestIOStreams()
384
385 cmd := NewCmdTopNode(tf, nil, streams)
386 cmd.Flags().Set("sort-by", "memory")
387
388
389
390 cmdOptions := &TopNodeOptions{
391 IOStreams: streams,
392 SortBy: "memory",
393 }
394 if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
395 t.Fatal(err)
396 }
397 cmdOptions.MetricsClient = fakemetricsClientset
398 if err := cmdOptions.Validate(); err != nil {
399 t.Fatal(err)
400 }
401 if err := cmdOptions.RunTopNode(); err != nil {
402 t.Fatal(err)
403 }
404
405
406 result := buf.String()
407
408 for _, m := range expectedMetrics.Items {
409 if !strings.Contains(result, m.Name) {
410 t.Errorf("missing metrics for %s: \n%s", m.Name, result)
411 }
412 }
413
414 resultLines := strings.Split(result, "\n")
415 resultNodes := make([]string, len(resultLines)-2)
416
417 for i, line := range resultLines[1 : len(resultLines)-1] {
418 lineFirstColumn := strings.Split(line, " ")[0]
419 resultNodes[i] = lineFirstColumn
420 }
421
422 if !reflect.DeepEqual(resultNodes, expectedNodesNames) {
423 t.Errorf("kinds not matching:\n\texpectedKinds: %v\n\tgotKinds: %v\n", expectedNodesNames, resultNodes)
424 }
425
426 }
427
View as plain text