From ba34956dd6bb348e5ba83580c6864001316cd67c Mon Sep 17 00:00:00 2001 From: Swetha Repakula Date: Mon, 8 Mar 2021 13:32:26 -0800 Subject: [PATCH] Add v1 describers for EndpointSlice --- .../k8s.io/kubectl/pkg/describe/describe.go | 104 +++++++++++- .../kubectl/pkg/describe/describe_test.go | 149 +++++++++++++----- 2 files changed, 205 insertions(+), 48 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index 5e8cc8f824d..bb995ed791e 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -42,6 +42,7 @@ import ( certificatesv1beta1 "k8s.io/api/certificates/v1beta1" coordinationv1 "k8s.io/api/coordination/v1" corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" discoveryv1beta1 "k8s.io/api/discovery/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" networkingv1 "k8s.io/api/networking/v1" @@ -200,6 +201,7 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr {Group: corev1.GroupName, Kind: "ConfigMap"}: &ConfigMapDescriber{c}, {Group: corev1.GroupName, Kind: "PriorityClass"}: &PriorityClassDescriber{c}, {Group: discoveryv1beta1.GroupName, Kind: "EndpointSlice"}: &EndpointSliceDescriber{c}, + {Group: discoveryv1.GroupName, Kind: "EndpointSlice"}: &EndpointSliceDescriber{c}, {Group: extensionsv1beta1.GroupName, Kind: "ReplicaSet"}: &ReplicaSetDescriber{c}, {Group: extensionsv1beta1.GroupName, Kind: "NetworkPolicy"}: &NetworkPolicyDescriber{c}, {Group: extensionsv1beta1.GroupName, Kind: "PodSecurityPolicy"}: &PodSecurityPolicyDescriber{c}, @@ -2881,22 +2883,112 @@ type EndpointSliceDescriber struct { } func (d *EndpointSliceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { - c := d.DiscoveryV1beta1().EndpointSlices(namespace) + var events *corev1.EventList + // try endpointslice/v1 first (v1.21) and fallback to v1beta1 if error occurs - eps, err := c.Get(context.TODO(), name, metav1.GetOptions{}) + epsV1, err := d.DiscoveryV1().EndpointSlices(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, epsV1) + } + return describeEndpointSliceV1(epsV1, events) + } + + epsV1beta1, err := d.DiscoveryV1beta1().EndpointSlices(namespace).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return "", err } - var events *corev1.EventList if describerSettings.ShowEvents { - events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, eps) + events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, epsV1beta1) } - return describeEndpointSlice(eps, events) + return describeEndpointSliceV1beta1(epsV1beta1, events) } -func describeEndpointSlice(eps *discoveryv1beta1.EndpointSlice, events *corev1.EventList) (string, error) { +func describeEndpointSliceV1(eps *discoveryv1.EndpointSlice, events *corev1.EventList) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", eps.Name) + w.Write(LEVEL_0, "Namespace:\t%s\n", eps.Namespace) + printLabelsMultiline(w, "Labels", eps.Labels) + printAnnotationsMultiline(w, "Annotations", eps.Annotations) + + w.Write(LEVEL_0, "AddressType:\t%s\n", string(eps.AddressType)) + + if len(eps.Ports) == 0 { + w.Write(LEVEL_0, "Ports: \n") + } else { + w.Write(LEVEL_0, "Ports:\n") + w.Write(LEVEL_1, "Name\tPort\tProtocol\n") + w.Write(LEVEL_1, "----\t----\t--------\n") + for _, port := range eps.Ports { + portName := "" + if port.Name != nil && len(*port.Name) > 0 { + portName = *port.Name + } + + portNum := "" + if port.Port != nil { + portNum = strconv.Itoa(int(*port.Port)) + } + + w.Write(LEVEL_1, "%s\t%s\t%s\n", portName, portNum, *port.Protocol) + } + } + + if len(eps.Endpoints) == 0 { + w.Write(LEVEL_0, "Endpoints: \n") + } else { + w.Write(LEVEL_0, "Endpoints:\n") + for i := range eps.Endpoints { + endpoint := &eps.Endpoints[i] + + addressesString := strings.Join(endpoint.Addresses, ", ") + if len(addressesString) == 0 { + addressesString = "" + } + w.Write(LEVEL_1, "- Addresses:\t%s\n", addressesString) + + w.Write(LEVEL_2, "Conditions:\n") + readyText := "" + if endpoint.Conditions.Ready != nil { + readyText = strconv.FormatBool(*endpoint.Conditions.Ready) + } + w.Write(LEVEL_3, "Ready:\t%s\n", readyText) + + hostnameText := "" + if endpoint.Hostname != nil { + hostnameText = *endpoint.Hostname + } + w.Write(LEVEL_2, "Hostname:\t%s\n", hostnameText) + + if endpoint.TargetRef != nil { + w.Write(LEVEL_2, "TargetRef:\t%s/%s\n", endpoint.TargetRef.Kind, endpoint.TargetRef.Name) + } + + nodeNameText := "" + if endpoint.NodeName != nil { + nodeNameText = *endpoint.NodeName + } + w.Write(LEVEL_2, "NodeName:\t%s\n", nodeNameText) + + zoneText := "" + if endpoint.NodeName != nil { + zoneText = *endpoint.Zone + } + w.Write(LEVEL_2, "Zone:\t%s\n", zoneText) + } + } + + if events != nil { + DescribeEvents(events, w) + } + return nil + }) +} + +func describeEndpointSliceV1beta1(eps *discoveryv1beta1.EndpointSlice, events *corev1.EventList) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) w.Write(LEVEL_0, "Name:\t%s\n", eps.Name) diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go index dbb5de886ed..c5391e1339c 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -31,6 +31,7 @@ import ( batchv1 "k8s.io/api/batch/v1" coordinationv1 "k8s.io/api/coordination/v1" corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" discoveryv1beta1 "k8s.io/api/discovery/v1beta1" networkingv1 "k8s.io/api/networking/v1" networkingv1beta1 "k8s.io/api/networking/v1beta1" @@ -4721,47 +4722,45 @@ func TestDescribeEndpointSlice(t *testing.T) { protocolTCP := corev1.ProtocolTCP port80 := int32(80) - fake := fake.NewSimpleClientset(&discoveryv1beta1.EndpointSlice{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo.123", - Namespace: "bar", - }, - AddressType: discoveryv1beta1.AddressTypeIPv4, - Endpoints: []discoveryv1beta1.Endpoint{ - { - Addresses: []string{"1.2.3.4", "1.2.3.5"}, - Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, - TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-123"}, - Topology: map[string]string{ - "topology.kubernetes.io/zone": "us-central1-a", - "topology.kubernetes.io/region": "us-central1", + testcases := map[string]struct { + input *fake.Clientset + output string + }{ + "EndpointSlices v1beta1": { + input: fake.NewSimpleClientset(&discoveryv1beta1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo.123", + Namespace: "bar", }, - }, { - Addresses: []string{"1.2.3.6", "1.2.3.7"}, - Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, - TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-124"}, - Topology: map[string]string{ - "topology.kubernetes.io/zone": "us-central1-b", - "topology.kubernetes.io/region": "us-central1", + AddressType: discoveryv1beta1.AddressTypeIPv4, + Endpoints: []discoveryv1beta1.Endpoint{ + { + Addresses: []string{"1.2.3.4", "1.2.3.5"}, + Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-123"}, + Topology: map[string]string{ + "topology.kubernetes.io/zone": "us-central1-a", + "topology.kubernetes.io/region": "us-central1", + }, + }, { + Addresses: []string{"1.2.3.6", "1.2.3.7"}, + Conditions: discoveryv1beta1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-124"}, + Topology: map[string]string{ + "topology.kubernetes.io/zone": "us-central1-b", + "topology.kubernetes.io/region": "us-central1", + }, + }, }, - }, - }, - Ports: []discoveryv1beta1.EndpointPort{ - { - Protocol: &protocolTCP, - Port: &port80, - }, - }, - }) + Ports: []discoveryv1beta1.EndpointPort{ + { + Protocol: &protocolTCP, + Port: &port80, + }, + }, + }), - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := EndpointSliceDescriber{c} - out, err := d.Describe("bar", "foo.123", DescriberSettings{ShowEvents: true}) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - expectedOut := `Name: foo.123 + output: `Name: foo.123 Namespace: bar Labels: Annotations: @@ -4785,11 +4784,77 @@ Endpoints: TargetRef: Pod/test-124 Topology: topology.kubernetes.io/region=us-central1 topology.kubernetes.io/zone=us-central1-b -Events: ` + "\n" +Events: ` + "\n", + }, + "EndpointSlices v1": { + input: fake.NewSimpleClientset(&discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo.123", + Namespace: "bar", + }, + AddressType: discoveryv1.AddressTypeIPv4, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{"1.2.3.4", "1.2.3.5"}, + Conditions: discoveryv1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-123"}, + Zone: utilpointer.StringPtr("us-central1-a"), + NodeName: utilpointer.StringPtr("node-1"), + }, { + Addresses: []string{"1.2.3.6", "1.2.3.7"}, + Conditions: discoveryv1.EndpointConditions{Ready: utilpointer.BoolPtr(true)}, + TargetRef: &corev1.ObjectReference{Kind: "Pod", Name: "test-124"}, + }, + }, + Ports: []discoveryv1.EndpointPort{ + { + Protocol: &protocolTCP, + Port: &port80, + }, + }, + }), - if out != expectedOut { - t.Logf(out) - t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out) + output: `Name: foo.123 +Namespace: bar +Labels: +Annotations: +AddressType: IPv4 +Ports: + Name Port Protocol + ---- ---- -------- + 80 TCP +Endpoints: + - Addresses: 1.2.3.4, 1.2.3.5 + Conditions: + Ready: true + Hostname: + TargetRef: Pod/test-123 + NodeName: node-1 + Zone: us-central1-a + - Addresses: 1.2.3.6, 1.2.3.7 + Conditions: + Ready: true + Hostname: + TargetRef: Pod/test-124 + NodeName: + Zone: +Events: ` + "\n", + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + c := &describeClient{T: t, Namespace: "foo", Interface: tc.input} + d := EndpointSliceDescriber{c} + out, err := d.Describe("bar", "foo.123", DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if out != tc.output { + t.Logf(out) + t.Errorf("expected :\n%s\nbut got output:\n%s", tc.output, out) + } + }) } }