kubectl: Use metrics-server for kubectl top commands

This commit is contained in:
Frederic Branczyk
2017-11-15 20:41:04 +01:00
parent ddea2dd56f
commit b1448adb59
16 changed files with 819 additions and 60 deletions

View File

@@ -19,6 +19,7 @@ package cmd
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
@@ -26,9 +27,13 @@ import (
"net/url"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest/fake"
core "k8s.io/client-go/testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/metrics/pkg/apis/metrics/v1alpha1"
metricsv1alpha1api "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
metricsfake "k8s.io/metrics/pkg/client/clientset_generated/clientset/fake"
)
const (
@@ -38,7 +43,7 @@ const (
func TestTopNodeAllMetrics(t *testing.T) {
initTestErrorHandler(t)
metrics, nodes := testNodeMetricsData()
metrics, nodes := testNodeV1alpha1MetricsData()
expectedMetricsPath := fmt.Sprintf("%s/%s/nodes", baseMetricsAddress, metricsApiVersion)
expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
@@ -48,6 +53,10 @@ func TestTopNodeAllMetrics(t *testing.T) {
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbody)))}, nil
case p == expectedMetricsPath && m == "GET":
body, err := marshallBody(metrics)
if err != nil {
@@ -83,7 +92,7 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) {
customBaseMetricsAddress := customBaseHeapsterServiceAddress + "/apis/metrics"
initTestErrorHandler(t)
metrics, nodes := testNodeMetricsData()
metrics, nodes := testNodeV1alpha1MetricsData()
expectedMetricsPath := fmt.Sprintf("%s/%s/nodes", customBaseMetricsAddress, metricsApiVersion)
expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
@@ -93,6 +102,10 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) {
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbody)))}, nil
case p == expectedMetricsPath && m == "GET":
body, err := marshallBody(metrics)
if err != nil {
@@ -132,10 +145,10 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) {
func TestTopNodeWithNameMetrics(t *testing.T) {
initTestErrorHandler(t)
metrics, nodes := testNodeMetricsData()
metrics, nodes := testNodeV1alpha1MetricsData()
expectedMetrics := metrics.Items[0]
expectedNode := nodes.Items[0]
nonExpectedMetrics := v1alpha1.NodeMetricsList{
nonExpectedMetrics := metricsv1alpha1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[1:],
}
@@ -148,6 +161,10 @@ func TestTopNodeWithNameMetrics(t *testing.T) {
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbody)))}, nil
case p == expectedPath && m == "GET":
body, err := marshallBody(expectedMetrics)
if err != nil {
@@ -183,8 +200,8 @@ func TestTopNodeWithNameMetrics(t *testing.T) {
func TestTopNodeWithLabelSelectorMetrics(t *testing.T) {
initTestErrorHandler(t)
metrics, nodes := testNodeMetricsData()
expectedMetrics := v1alpha1.NodeMetricsList{
metrics, nodes := testNodeV1alpha1MetricsData()
expectedMetrics := metricsv1alpha1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[0:1],
}
@@ -192,7 +209,7 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) {
ListMeta: nodes.ListMeta,
Items: nodes.Items[0:1],
}
nonExpectedMetrics := v1alpha1.NodeMetricsList{
nonExpectedMetrics := metricsv1alpha1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[1:],
}
@@ -207,6 +224,10 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) {
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbody)))}, nil
case p == expectedPath && m == "GET" && q == expectedQuery:
body, err := marshallBody(expectedMetrics)
if err != nil {
@@ -242,3 +263,164 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) {
}
}
}
func TestTopNodeAllMetricsFromMetricsServer(t *testing.T) {
initTestErrorHandler(t)
expectedMetrics, nodes := testNodeV1beta1MetricsData()
expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
case p == expectedNodePath && m == "GET":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, nodes)}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
return nil, nil
}
}),
}
fakemetricsClientset := &metricsfake.Clientset{}
fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
return true, expectedMetrics, nil
})
tf.MetricsClientSet = fakemetricsClientset
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopNode(f, nil, buf)
cmd.Run(cmd, []string{})
// Check the presence of node names in the output.
result := buf.String()
for _, m := range expectedMetrics.Items {
if !strings.Contains(result, m.Name) {
t.Errorf("missing metrics for %s: \n%s", m.Name, result)
}
}
}
func TestTopNodeWithNameMetricsFromMetricsServer(t *testing.T) {
initTestErrorHandler(t)
metrics, nodes := testNodeV1beta1MetricsData()
expectedMetrics := metrics.Items[0]
expectedNode := nodes.Items[0]
nonExpectedMetrics := metricsv1beta1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[1:],
}
expectedNodePath := fmt.Sprintf("/%s/%s/nodes/%s", apiPrefix, apiVersion, expectedMetrics.Name)
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
case p == expectedNodePath && m == "GET":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &expectedNode)}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
return nil, nil
}
}),
}
fakemetricsClientset := &metricsfake.Clientset{}
fakemetricsClientset.AddReactor("get", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
return true, &expectedMetrics, nil
})
tf.MetricsClientSet = fakemetricsClientset
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopNode(f, nil, buf)
cmd.Run(cmd, []string{expectedMetrics.Name})
// Check the presence of node names in the output.
result := buf.String()
if !strings.Contains(result, expectedMetrics.Name) {
t.Errorf("missing metrics for %s: \n%s", expectedMetrics.Name, result)
}
for _, m := range nonExpectedMetrics.Items {
if strings.Contains(result, m.Name) {
t.Errorf("unexpected metrics for %s: \n%s", m.Name, result)
}
}
}
func TestTopNodeWithLabelSelectorMetricsFromMetricsServer(t *testing.T) {
initTestErrorHandler(t)
metrics, nodes := testNodeV1beta1MetricsData()
expectedMetrics := &metricsv1beta1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[0:1],
}
expectedNodes := v1.NodeList{
ListMeta: nodes.ListMeta,
Items: nodes.Items[0:1],
}
nonExpectedMetrics := &metricsv1beta1api.NodeMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[1:],
}
label := "key=value"
expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m, _ := req.URL.Path, req.Method, req.URL.RawQuery; {
case p == "/api":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
case p == "/apis":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
case p == expectedNodePath && m == "GET":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &expectedNodes)}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
return nil, nil
}
}),
}
fakemetricsClientset := &metricsfake.Clientset{}
fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
return true, expectedMetrics, nil
})
tf.MetricsClientSet = fakemetricsClientset
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopNode(f, nil, buf)
cmd.Flags().Set("selector", label)
cmd.Run(cmd, []string{})
// Check the presence of node names in the output.
result := buf.String()
for _, m := range expectedMetrics.Items {
if !strings.Contains(result, m.Name) {
t.Errorf("missing metrics for %s: \n%s", m.Name, result)
}
}
for _, m := range nonExpectedMetrics.Items {
if strings.Contains(result, m.Name) {
t.Errorf("unexpected metrics for %s: \n%s", m.Name, result)
}
}
}