Make top pod test table-driven

This commit is contained in:
Ivan Shvedunov 2016-10-17 21:08:40 +03:00 committed by Ivan Shvedunov
parent 6b9a944285
commit 9b9c1e6952
2 changed files with 200 additions and 337 deletions

View File

@ -18,192 +18,101 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"time"
"net/url" "net/url"
metricsapi "k8s.io/heapster/metrics/apis/metrics/v1alpha1" metricsapi "k8s.io/heapster/metrics/apis/metrics/v1alpha1"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/restclient/fake" "k8s.io/kubernetes/pkg/client/restclient/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
) )
func TestTopPodAllNamespacesMetrics(t *testing.T) { const (
topPathPrefix = baseMetricsAddress + "/" + metricsApiVersion
)
func TestTopPod(t *testing.T) {
testNS := "testns"
testCases := []struct {
name string
namespace string
flags map[string]string
args []string
expectedPath string
expectedQuery string
namespaces []string
containers bool
listsNamespaces bool
}{
{
name: "all namespaces",
flags: map[string]string{"all-namespaces": "true"},
expectedPath: topPathPrefix + "/pods",
namespaces: []string{testNS, "secondtestns", "thirdtestns"},
listsNamespaces: true,
},
{
name: "all in namespace",
expectedPath: topPathPrefix + "/namespaces/" + testNS + "/pods",
namespaces: []string{testNS, testNS},
},
{
name: "pod with name",
args: []string{"pod1"},
expectedPath: topPathPrefix + "/namespaces/" + testNS + "/pods/pod1",
namespaces: []string{testNS},
},
{
name: "pod with label selector",
flags: map[string]string{"selector": "key=value"},
expectedPath: topPathPrefix + "/namespaces/" + testNS + "/pods",
expectedQuery: "labelSelector=" + url.QueryEscape("key=value"),
namespaces: []string{testNS, testNS},
},
{
name: "pod with container metrics",
flags: map[string]string{"containers": "true"},
args: []string{"pod1"},
expectedPath: topPathPrefix + "/namespaces/" + testNS + "/pods/pod1",
namespaces: []string{testNS},
containers: true,
},
}
initTestErrorHandler(t) initTestErrorHandler(t)
metrics := testPodMetricsData() for _, testCase := range testCases {
firstTestNamespace := "testnamespace" t.Logf("Running test case: %s", testCase.name)
secondTestNamespace := "secondtestns" metricsList := testPodMetricsData()
thirdTestNamespace := "thirdtestns" var expectedMetrics []metricsapi.PodMetrics
metrics.Items[0].Namespace = firstTestNamespace var expectedContainerNames, nonExpectedMetricsNames []string
metrics.Items[1].Namespace = secondTestNamespace for n, m := range metricsList {
metrics.Items[2].Namespace = thirdTestNamespace if n < len(testCase.namespaces) {
m.Namespace = testCase.namespaces[n]
expectedPath := fmt.Sprintf("%s/%s/pods", baseMetricsAddress, metricsApiVersion) expectedMetrics = append(expectedMetrics, m)
for _, c := range m.Containers {
f, tf, _, ns := cmdtesting.NewAPIFactory() expectedContainerNames = append(expectedContainerNames, c.Name)
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 == expectedPath && m == "GET":
body, err := marshallBody(metrics)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\nExpected path: %#v", req, req.URL, expectedPath)
return nil, nil
}
}),
}
tf.Namespace = firstTestNamespace
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopPod(f, buf)
cmd.Flags().Set("all-namespaces", "true")
cmd.Run(cmd, []string{})
// Check the presence of pod names and namespaces in the output.
result := buf.String()
for _, m := range metrics.Items {
if !strings.Contains(result, m.Name) {
t.Errorf("missing metrics for %s: \n%s", m.Name, result)
}
if !strings.Contains(result, m.Namespace) {
t.Errorf("missing metrics for %s/%s: \n%s", m.Namespace, m.Name, result)
} }
} else {
nonExpectedMetricsNames = append(nonExpectedMetricsNames, m.Name)
} }
} }
func TestTopPodAllInNamespaceMetrics(t *testing.T) { var response interface{}
initTestErrorHandler(t) if len(expectedMetrics) == 1 {
metrics := testPodMetricsData() response = expectedMetrics[0]
testNamespace := "testnamespace" } else {
nonTestNamespace := "anothernamespace" response = metricsapi.PodMetricsList{
expectedMetrics := metricsapi.PodMetricsList{ ListMeta: unversioned.ListMeta{
ListMeta: metrics.ListMeta, ResourceVersion: "2",
Items: metrics.Items[0:2], },
} Items: expectedMetrics,
for _, m := range expectedMetrics.Items {
m.Namespace = testNamespace
}
nonExpectedMetrics := metricsapi.PodMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[2:],
}
for _, m := range nonExpectedMetrics.Items {
m.Namespace = nonTestNamespace
}
expectedPath := fmt.Sprintf("%s/%s/namespaces/%s/pods", baseMetricsAddress, metricsApiVersion, testNamespace)
f, tf, _, 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 == expectedPath && m == "GET":
body, err := marshallBody(expectedMetrics)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\nExpected path: %#v", req, req.URL, expectedPath)
return nil, nil
}
}),
}
tf.Namespace = testNamespace
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopPod(f, buf)
cmd.Run(cmd, []string{})
// Check the presence of pod 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)
}
}
}
func TestTopPodWithNameMetrics(t *testing.T) {
initTestErrorHandler(t)
metrics := testPodMetricsData()
expectedMetrics := metrics.Items[0]
nonExpectedMetrics := metricsapi.PodMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[1:],
}
testNamespace := "testnamespace"
expectedMetrics.Namespace = testNamespace
expectedPath := fmt.Sprintf("%s/%s/namespaces/%s/pods/%s", baseMetricsAddress, metricsApiVersion, testNamespace, expectedMetrics.Name)
f, tf, _, 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 == expectedPath && m == "GET":
body, err := marshallBody(expectedMetrics)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\nExpected path: %#v", req, req.URL, expectedPath)
return nil, nil
}
}),
}
tf.Namespace = testNamespace
tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopPod(f, buf)
cmd.Run(cmd, []string{expectedMetrics.Name})
// Check the presence of pod 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 TestTopPodWithLabelSelectorMetrics(t *testing.T) {
initTestErrorHandler(t)
metrics := testPodMetricsData()
expectedMetrics := metricsapi.PodMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[0:2],
}
nonExpectedMetrics := metricsapi.PodMetricsList{
ListMeta: metrics.ListMeta,
Items: metrics.Items[2:],
}
label := "key=value"
testNamespace := "testnamespace"
expectedPath := fmt.Sprintf("%s/%s/namespaces/%s/pods", baseMetricsAddress, metricsApiVersion, testNamespace)
expectedQuery := fmt.Sprintf("labelSelector=%s", url.QueryEscape(label))
f, tf, _, ns := cmdtesting.NewAPIFactory() f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -211,91 +120,121 @@ func TestTopPodWithLabelSelectorMetrics(t *testing.T) {
NegotiatedSerializer: ns, NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; { switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; {
case p == expectedPath && m == "GET" && q == expectedQuery: case p == testCase.expectedPath && m == "GET" && (testCase.expectedQuery == "" || q == testCase.expectedQuery):
body, err := marshallBody(expectedMetrics) body, err := marshallBody(response)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("%s: unexpected error: %v", testCase.name, err)
} }
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
default: default:
t.Fatalf("unexpected request: %#v\nGot URL: %#v\nExpected path: %#v", req, req.URL, expectedPath) t.Fatalf("%s: unexpected request: %#v\nGot URL: %#v\nExpected path: %#v\nExpected query: %#v",
testCase.name, req, req.URL, testCase.expectedPath, testCase.expectedQuery)
return nil, nil return nil, nil
} }
}), }),
} }
tf.Namespace = testNamespace tf.Namespace = testNS
tf.ClientConfig = defaultClientConfig() tf.ClientConfig = defaultClientConfig()
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdTopPod(f, buf) cmd := NewCmdTopPod(f, buf)
cmd.Flags().Set("selector", label) for name, value := range testCase.flags {
cmd.Run(cmd, []string{}) cmd.Flags().Set(name, value)
}
cmd.Run(cmd, testCase.args)
// Check the presence of pod names in the output. // Check the presence of pod names&namespaces/container names in the output.
result := buf.String() result := buf.String()
for _, m := range expectedMetrics.Items { if testCase.containers {
for _, containerName := range expectedContainerNames {
if !strings.Contains(result, containerName) {
t.Errorf("%s: missing metrics for container %s: \n%s", testCase.name, containerName, result)
}
}
}
for _, m := range expectedMetrics {
if !strings.Contains(result, m.Name) { if !strings.Contains(result, m.Name) {
t.Errorf("missing metrics for %s: \n%s", m.Name, result) t.Errorf("%s: missing metrics for %s: \n%s", testCase.name, m.Name, result)
}
if testCase.listsNamespaces && !strings.Contains(result, m.Namespace) {
t.Errorf("%s: missing metrics for %s/%s: \n%s", testCase.name, m.Namespace, m.Name, result)
} }
} }
for _, m := range nonExpectedMetrics.Items { for _, name := range nonExpectedMetricsNames {
if strings.Contains(result, m.Name) { if strings.Contains(result, name) {
t.Errorf("unexpected metrics for %s: \n%s", m.Name, result) t.Errorf("%s: unexpected metrics for %s: \n%s", testCase.name, name, result)
}
} }
} }
} }
func TestTopPodWithContainersMetrics(t *testing.T) { func testPodMetricsData() []metricsapi.PodMetrics {
initTestErrorHandler(t) return []metricsapi.PodMetrics{
metrics := testPodMetricsData() {
expectedMetrics := metrics.Items[0] ObjectMeta: v1.ObjectMeta{Name: "pod1", Namespace: "test", ResourceVersion: "10"},
nonExpectedMetrics := metricsapi.PodMetricsList{ Window: unversioned.Duration{Duration: time.Minute},
ListMeta: metrics.ListMeta, Containers: []metricsapi.ContainerMetrics{
Items: metrics.Items[1:], {
} Name: "container1-1",
testNamespace := "testnamespace" Usage: v1.ResourceList{
expectedMetrics.Namespace = testNamespace v1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI),
expectedPath := fmt.Sprintf("%s/%s/namespaces/%s/pods/%s", baseMetricsAddress, metricsApiVersion, testNamespace, expectedMetrics.Name) v1.ResourceMemory: *resource.NewQuantity(2*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
f, tf, _, ns := cmdtesting.NewAPIFactory() },
tf.Printer = &testPrinter{} },
tf.Client = &fake.RESTClient{ {
NegotiatedSerializer: ns, Name: "container1-2",
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { Usage: v1.ResourceList{
switch p, m := req.URL.Path, req.Method; { v1.ResourceCPU: *resource.NewMilliQuantity(4, resource.DecimalSI),
case p == expectedPath && m == "GET": v1.ResourceMemory: *resource.NewQuantity(5*(1024*1024), resource.DecimalSI),
body, err := marshallBody(expectedMetrics) v1.ResourceStorage: *resource.NewQuantity(6*(1024*1024), resource.DecimalSI),
if err != nil { },
t.Errorf("unexpected error: %v", err) },
} },
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil },
default: {
t.Fatalf("unexpected request: %#v\nGot URL: %#v\nExpected path: %#v", req, req.URL, expectedPath) ObjectMeta: v1.ObjectMeta{Name: "pod2", Namespace: "test", ResourceVersion: "11"},
return nil, nil Window: unversioned.Duration{Duration: time.Minute},
} Containers: []metricsapi.ContainerMetrics{
}), {
} Name: "container2-1",
tf.Namespace = testNamespace Usage: v1.ResourceList{
tf.ClientConfig = defaultClientConfig() v1.ResourceCPU: *resource.NewMilliQuantity(7, resource.DecimalSI),
buf := bytes.NewBuffer([]byte{}) v1.ResourceMemory: *resource.NewQuantity(8*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(9*(1024*1024), resource.DecimalSI),
cmd := NewCmdTopPod(f, buf) },
cmd.Flags().Set("containers", "true") },
cmd.Run(cmd, []string{expectedMetrics.Name}) {
Name: "container2-2",
// Check the presence of pod names in the output. Usage: v1.ResourceList{
result := buf.String() v1.ResourceCPU: *resource.NewMilliQuantity(10, resource.DecimalSI),
if !strings.Contains(result, expectedMetrics.Name) { v1.ResourceMemory: *resource.NewQuantity(11*(1024*1024), resource.DecimalSI),
t.Errorf("missing metrics for %s: \n%s", expectedMetrics.Name, result) v1.ResourceStorage: *resource.NewQuantity(12*(1024*1024), resource.DecimalSI),
} },
for _, m := range expectedMetrics.Containers { },
if !strings.Contains(result, m.Name) { {
t.Errorf("missing metrics for container %s: \n%s", m.Name, result) Name: "container2-3",
} Usage: v1.ResourceList{
} v1.ResourceCPU: *resource.NewMilliQuantity(13, resource.DecimalSI),
for _, m := range nonExpectedMetrics.Items { v1.ResourceMemory: *resource.NewQuantity(14*(1024*1024), resource.DecimalSI),
if strings.Contains(result, m.Name) { v1.ResourceStorage: *resource.NewQuantity(15*(1024*1024), resource.DecimalSI),
t.Errorf("unexpected metrics for %s: \n%s", m.Name, result) },
} },
},
},
{
ObjectMeta: v1.ObjectMeta{Name: "pod3", Namespace: "test", ResourceVersion: "12"},
Window: unversioned.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{
{
Name: "container3-1",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(7, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(8*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(9*(1024*1024), resource.DecimalSI),
},
},
},
},
} }
} }

View File

@ -115,79 +115,3 @@ func testNodeMetricsData() (*metricsapi.NodeMetricsList, *api.NodeList) {
} }
return metrics, nodes return metrics, nodes
} }
func testPodMetricsData() *metricsapi.PodMetricsList {
return &metricsapi.PodMetricsList{
ListMeta: unversioned.ListMeta{
ResourceVersion: "2",
},
Items: []metricsapi.PodMetrics{
{
ObjectMeta: v1.ObjectMeta{Name: "pod1", Namespace: "test", ResourceVersion: "10"},
Window: unversioned.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{
{
Name: "container1-1",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(2*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
},
},
{
Name: "container1-2",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(4, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(5*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(6*(1024*1024), resource.DecimalSI),
},
},
},
},
{
ObjectMeta: v1.ObjectMeta{Name: "pod2", Namespace: "test", ResourceVersion: "11"},
Window: unversioned.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{
{
Name: "container2-1",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(7, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(8*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(9*(1024*1024), resource.DecimalSI),
},
},
{
Name: "container2-2",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(10, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(11*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(12*(1024*1024), resource.DecimalSI),
},
},
{
Name: "container2-3",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(13, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(14*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(15*(1024*1024), resource.DecimalSI),
},
},
},
},
{
ObjectMeta: v1.ObjectMeta{Name: "pod3", Namespace: "test", ResourceVersion: "12"},
Window: unversioned.Duration{Duration: time.Minute},
Containers: []metricsapi.ContainerMetrics{
{
Name: "container3-1",
Usage: v1.ResourceList{
v1.ResourceCPU: *resource.NewMilliQuantity(7, resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(8*(1024*1024), resource.DecimalSI),
v1.ResourceStorage: *resource.NewQuantity(9*(1024*1024), resource.DecimalSI),
},
},
},
},
},
}
}