...

Source file src/k8s.io/kubectl/pkg/cmd/top/top_node_test.go

Documentation: k8s.io/kubectl/pkg/cmd/top

     1  /*
     2  Copyright 2016 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  
    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  	// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
    81  	// TODO then check the particular Run functionality and harvest results from fake clients
    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  	// Check the presence of node names in the output.
    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  	// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
   148  	// TODO then check the particular Run functionality and harvest results from fake clients
   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  	// Check the presence of node names in the output.
   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  	// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
   227  	// TODO then check the particular Run functionality and harvest results from fake clients
   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  	// Check the presence of node names in the output.
   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  	// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
   303  	// TODO then check the particular Run functionality and harvest results from fake clients
   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  	// Check the presence of node names in the output.
   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) // don't process first (header) and last (empty) line
   330  
   331  	for i, line := range resultLines[1 : len(resultLines)-1] { // don't process first (header) and last (empty) line
   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  	// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
   389  	// TODO then check the particular Run functionality and harvest results from fake clients
   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  	// Check the presence of node names in the output.
   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) // don't process first (header) and last (empty) line
   416  
   417  	for i, line := range resultLines[1 : len(resultLines)-1] { // don't process first (header) and last (empty) line
   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