From 2b65c74b9450d5f9027d1fab188469c1d410b1aa Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Mon, 24 Jun 2019 08:49:28 -0700 Subject: [PATCH] Add kubectl get/list/watch tests for table output --- pkg/kubectl/cmd/get/get_test.go | 679 ++++++++++++++++++++++++++++++++ 1 file changed, 679 insertions(+) diff --git a/pkg/kubectl/cmd/get/get_test.go b/pkg/kubectl/cmd/get/get_test.go index ffaf5442849..456e8fd307e 100644 --- a/pkg/kubectl/cmd/get/get_test.go +++ b/pkg/kubectl/cmd/get/get_test.go @@ -243,6 +243,31 @@ foo 0/0 0 } } +func TestGetTableObjects(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetObjectsShowKind(t *testing.T) { pods, _, _ := cmdtesting.TestData() @@ -269,6 +294,32 @@ pod/foo 0/0 0 } } +func TestGetTableObjectsShowKind(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Flags().Set("show-kind", "true") + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE +pod/foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetMultipleResourceTypesShowKinds(t *testing.T) { pods, svcs, _ := cmdtesting.TestData() @@ -324,6 +375,61 @@ service/baz ClusterIP } } +func TestGetMultipleTableResourceTypesShowKinds(t *testing.T) { + pods, svcs, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch p, m := req.URL.Path, req.Method; { + case p == "/namespaces/test/pods" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil + case p == "/namespaces/test/replicationcontrollers" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &corev1.ReplicationControllerList{})}, nil + case p == "/namespaces/test/services" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svcs.Items...)}, nil + case p == "/namespaces/test/statefulsets" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &appsv1.StatefulSetList{})}, nil + case p == "/namespaces/test/horizontalpodautoscalers" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &autoscalingv1.HorizontalPodAutoscalerList{})}, nil + case p == "/namespaces/test/jobs" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &batchv1.JobList{})}, nil + case p == "/namespaces/test/cronjobs" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &batchv1beta1.CronJobList{})}, nil + case p == "/namespaces/test/daemonsets" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &appsv1.DaemonSetList{})}, nil + case p == "/namespaces/test/deployments" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &extensionsv1beta1.DeploymentList{})}, nil + case p == "/namespaces/test/replicasets" && m == "GET": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &extensionsv1beta1.ReplicaSetList{})}, nil + + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"all"}) + + expected := `NAME READY STATUS RESTARTS AGE +pod/foo 0/0 0 +pod/bar 0/0 0 +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/baz ClusterIP +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetObjectsShowLabels(t *testing.T) { pods, _, _ := cmdtesting.TestData() @@ -350,6 +456,32 @@ foo 0/0 0 } } +func TestGetTableObjectsShowLabels(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Flags().Set("show-labels", "true") + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE LABELS +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetEmptyTable(t *testing.T) { tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() @@ -768,6 +900,32 @@ foo 0/0 0 } } +func TestGetTableObjectsIdentifiedByFile(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items[0])}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml") + cmd.Run(cmd, []string{}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetListObjects(t *testing.T) { pods, _, _ := cmdtesting.TestData() @@ -794,6 +952,32 @@ bar 0/0 0 } } +func TestGetListTableObjects(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"pods"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +bar 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetListComponentStatus(t *testing.T) { statuses := testComponentStatusData() @@ -921,6 +1105,44 @@ service/baz ClusterIP } } +func TestGetMultipleTypeTableObjects(t *testing.T) { + pods, svc, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch req.URL.Path { + case "/namespaces/test/pods": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil + case "/namespaces/test/services": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"pods,services"}) + + expected := `NAME READY STATUS RESTARTS AGE +pod/foo 0/0 0 +pod/bar 0/0 0 +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/baz ClusterIP +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := cmdtesting.TestData() @@ -1065,6 +1287,49 @@ service/baz ClusterIP } } +func TestGetMultipleTypeTableObjectsWithLabelSelector(t *testing.T) { + pods, svc, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil + case "/namespaces/test/services": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("selector", "a=b") + cmd.Run(cmd, []string{"pods,services"}) + + expected := `NAME READY STATUS RESTARTS AGE +pod/foo 0/0 0 +pod/bar 0/0 0 +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/baz ClusterIP +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) { pods, svc, _ := cmdtesting.TestData() @@ -1108,6 +1373,49 @@ service/baz ClusterIP } } +func TestGetMultipleTypeTableObjectsWithFieldSelector(t *testing.T) { + pods, svc, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods.Items...)}, nil + case "/namespaces/test/services": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items...)}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("field-selector", "a=b") + cmd.Run(cmd, []string{"pods,services"}) + + expected := `NAME READY STATUS RESTARTS AGE +pod/foo 0/0 0 +pod/bar 0/0 0 +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/baz ClusterIP +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { _, svc, _ := cmdtesting.TestData() node := &corev1.Node{ @@ -1151,6 +1459,49 @@ node/foo Unknown } } +func TestGetMultipleTypeTableObjectsWithDirectReference(t *testing.T) { + _, svc, _ := cmdtesting.TestData() + node := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + } + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch req.URL.Path { + case "/nodes/foo": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: nodeTableObjBody(codec, *node)}, nil + case "/namespaces/test/services/bar": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: serviceTableObjBody(codec, svc.Items[0])}, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Run(cmd, []string{"services/bar", "node/foo"}) + + expected := `NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/baz ClusterIP +NAME STATUS ROLES AGE VERSION +node/foo Unknown +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func watchTestData() ([]corev1.Pod, []watch.Event) { pods := []corev1.Pod{ { @@ -1308,6 +1659,57 @@ foo 0/0 0 } } +func TestWatchTableLabelSelector(t *testing.T) { + pods, events := watchTestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + podList := &corev1.PodList{ + Items: pods, + ListMeta: metav1.ListMeta{ + ResourceVersion: "10", + }, + } + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" { + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil + } + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch", "true") + cmd.Flags().Set("selector", "a=b") + cmd.Run(cmd, []string{"pods"}) + + expected := `NAME READY STATUS RESTARTS AGE +bar 0/0 0 +foo 0/0 0 +foo 0/0 0 +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestWatchFieldSelector(t *testing.T) { pods, events := watchTestData() @@ -1359,6 +1761,57 @@ foo 0/0 0 } } +func TestWatchTableFieldSelector(t *testing.T) { + pods, events := watchTestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + podList := &corev1.PodList{ + Items: pods, + ListMeta: metav1.ListMeta{ + ResourceVersion: "10", + }, + } + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" { + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil + } + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch", "true") + cmd.Flags().Set("field-selector", "a=b") + cmd.Run(cmd, []string{"pods"}) + + expected := `NAME READY STATUS RESTARTS AGE +bar 0/0 0 +foo 0/0 0 +foo 0/0 0 +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestWatchResource(t *testing.T) { pods, events := watchTestData() @@ -1402,6 +1855,49 @@ foo 0/0 0 } } +func TestWatchTableResource(t *testing.T) { + pods, events := watchTestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch req.URL.Path { + case "/namespaces/test/pods/foo": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods[1])}, nil + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" && req.URL.Query().Get("fieldSelector") == "metadata.name=foo" { + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[1:])}, nil + } + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch", "true") + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +foo 0/0 0 +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestWatchResourceTable(t *testing.T) { columns := []metav1beta1.TableColumnDefinition{ {Name: "Name", Type: "string", Format: "name", Description: "the name", Priority: 0}, @@ -1595,6 +2091,48 @@ foo 0/0 0 } } +func TestWatchOnlyTableResource(t *testing.T) { + pods, events := watchTestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch req.URL.Path { + case "/namespaces/test/pods/foo": + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, pods[1])}, nil + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" && req.URL.Query().Get("fieldSelector") == "metadata.name=foo" { + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[1:])}, nil + } + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch-only", "true") + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestWatchOnlyList(t *testing.T) { pods, events := watchTestData() @@ -1640,6 +2178,51 @@ foo 0/0 0 } } +func TestWatchOnlyTableList(t *testing.T) { + pods, events := watchTestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + podList := &corev1.PodList{ + Items: pods, + ListMeta: metav1.ListMeta{ + ResourceVersion: "10", + }, + } + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + switch req.URL.Path { + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" { + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableWatchBody(codec, events[2:])}, nil + } + return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: podTableObjBody(codec, podList.Items...)}, nil + default: + t.Fatalf("request url: %#v,and request: %#v", req.URL, req) + return nil, nil + } + }), + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch-only", "true") + cmd.Run(cmd, []string{"pods"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { buf := bytes.NewBuffer([]byte{}) enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec) @@ -1650,3 +2233,99 @@ func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { } return ioutil.NopCloser(buf) } + +var podColumns = []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Ready", Type: "string", Format: ""}, + {Name: "Status", Type: "string", Format: ""}, + {Name: "Restarts", Type: "integer", Format: ""}, + {Name: "Age", Type: "string", Format: ""}, + {Name: "IP", Type: "string", Format: "", Priority: 1}, + {Name: "Node", Type: "string", Format: "", Priority: 1}, + {Name: "Nominated Node", Type: "string", Format: "", Priority: 1}, + {Name: "Readiness Gates", Type: "string", Format: "", Priority: 1}, +} + +// build a meta table response from a pod list +func podTableObjBody(codec runtime.Codec, pods ...corev1.Pod) io.ReadCloser { + table := &metav1.Table{ + ColumnDefinitions: podColumns, + } + for i := range pods { + b := bytes.NewBuffer(nil) + codec.Encode(&pods[i], b) + table.Rows = append(table.Rows, metav1.TableRow{ + Object: runtime.RawExtension{Raw: b.Bytes()}, + Cells: []interface{}{pods[i].Name, "0/0", "", int64(0), "", "", "", "", ""}, + }) + } + return cmdtesting.ObjBody(codec, table) +} + +// build meta table watch events from pod watch events +func podTableWatchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { + tableEvents := []watch.Event{} + for i, e := range events { + b := bytes.NewBuffer(nil) + codec.Encode(e.Object, b) + var columns []metav1.TableColumnDefinition + if i == 0 { + columns = podColumns + } + tableEvents = append(tableEvents, watch.Event{ + Type: e.Type, + Object: &metav1.Table{ + ColumnDefinitions: columns, + Rows: []metav1.TableRow{{ + Object: runtime.RawExtension{Raw: b.Bytes()}, + Cells: []interface{}{e.Object.(*corev1.Pod).Name, "0/0", "", int64(0), "", "", "", "", ""}, + }}}, + }) + } + return watchBody(codec, tableEvents) +} + +// build a meta table response from a service list +func serviceTableObjBody(codec runtime.Codec, services ...corev1.Service) io.ReadCloser { + table := &metav1.Table{ + ColumnDefinitions: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Type", Type: "string", Format: ""}, + {Name: "Cluster-IP", Type: "string", Format: ""}, + {Name: "External-IP", Type: "string", Format: ""}, + {Name: "Port(s)", Type: "string", Format: ""}, + {Name: "Age", Type: "string", Format: ""}, + }, + } + for i := range services { + b := bytes.NewBuffer(nil) + codec.Encode(&services[i], b) + table.Rows = append(table.Rows, metav1.TableRow{ + Object: runtime.RawExtension{Raw: b.Bytes()}, + Cells: []interface{}{services[i].Name, "ClusterIP", "", "", "", ""}, + }) + } + return cmdtesting.ObjBody(codec, table) +} + +// build a meta table response from a node list +func nodeTableObjBody(codec runtime.Codec, nodes ...corev1.Node) io.ReadCloser { + table := &metav1.Table{ + ColumnDefinitions: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Status", Type: "string", Format: ""}, + {Name: "Roles", Type: "string", Format: ""}, + {Name: "Age", Type: "string", Format: ""}, + {Name: "Version", Type: "string", Format: ""}, + }, + } + for i := range nodes { + b := bytes.NewBuffer(nil) + codec.Encode(&nodes[i], b) + table.Rows = append(table.Rows, metav1.TableRow{ + Object: runtime.RawExtension{Raw: b.Bytes()}, + Cells: []interface{}{nodes[i].Name, "Unknown", "", "", ""}, + }) + } + return cmdtesting.ObjBody(codec, table) +}