Add describer and printer for ClusterCIDR API

This commit is contained in:
Sarvesh Rangnekar 2022-07-30 00:25:57 +00:00
parent 299724d099
commit 0ee3719d0b
4 changed files with 457 additions and 1 deletions

View File

@ -37,6 +37,7 @@ import (
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
flowcontrolv1beta2 "k8s.io/api/flowcontrol/v1beta2"
networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
schedulingv1 "k8s.io/api/scheduling/v1"
@ -591,6 +592,18 @@ func AddHandlers(h printers.PrintHandler) {
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
}
h.TableHandler(scaleColumnDefinitions, printScale)
clusterCIDRColumnDefinitions := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "PerNodeHostBits", Type: "string", Description: networkingv1alpha1.ClusterCIDRSpec{}.SwaggerDoc()["perNodeHostBits"]},
{Name: "IPv4", Type: "string", Description: networkingv1alpha1.ClusterCIDRSpec{}.SwaggerDoc()["ipv4"]},
{Name: "IPv6", Type: "string", Description: networkingv1alpha1.ClusterCIDRSpec{}.SwaggerDoc()["ipv6"]},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
{Name: "NodeSelector", Type: "string", Priority: 1, Description: networkingv1alpha1.ClusterCIDRSpec{}.SwaggerDoc()["nodeSelector"]},
}
h.TableHandler(clusterCIDRColumnDefinitions, printClusterCIDR)
h.TableHandler(clusterCIDRColumnDefinitions, printClusterCIDRList)
}
// Pass ports=nil for all ports.
@ -2624,6 +2637,57 @@ func printPriorityLevelConfigurationList(list *flowcontrol.PriorityLevelConfigur
return rows, nil
}
func printClusterCIDR(obj *networking.ClusterCIDR, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
ipv4 := "<none>"
ipv6 := "<none>"
if obj.Spec.IPv4 != "" {
ipv4 = obj.Spec.IPv4
}
if obj.Spec.IPv6 != "" {
ipv6 = obj.Spec.IPv6
}
row.Cells = append(row.Cells, obj.Name, fmt.Sprint(obj.Spec.PerNodeHostBits), ipv4, ipv6, translateTimestampSince(obj.CreationTimestamp))
if options.Wide {
nodeSelector := "<none>"
if obj.Spec.NodeSelector != nil {
allTerms := make([]string, 0)
for _, term := range obj.Spec.NodeSelector.NodeSelectorTerms {
if len(term.MatchExpressions) > 0 {
matchExpressions := fmt.Sprintf("MatchExpressions: %v", term.MatchExpressions)
allTerms = append(allTerms, matchExpressions)
}
if len(term.MatchFields) > 0 {
matchFields := fmt.Sprintf("MatchFields: %v", term.MatchFields)
allTerms = append(allTerms, matchFields)
}
}
nodeSelector = strings.Join(allTerms, ",")
}
row.Cells = append(row.Cells, nodeSelector)
}
return []metav1.TableRow{row}, nil
}
func printClusterCIDRList(list *networking.ClusterCIDRList, options printers.GenerateOptions) ([]metav1.TableRow, error) {
rows := make([]metav1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printClusterCIDR(&list.Items[i], options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}
func printScale(obj *autoscaling.Scale, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},

View File

@ -6184,3 +6184,277 @@ func TestTableRowDeepCopyShouldNotPanic(t *testing.T) {
})
}
}
func TestPrintClusterCIDR(t *testing.T) {
ipv4CIDR := "10.1.0.0/16"
perNodeHostBits := int32(8)
ipv6CIDR := "fd00:1:1::/64"
tests := []struct {
ccc networking.ClusterCIDR
options printers.GenerateOptions
expected []metav1.TableRow
}{
{
// Test name, IPv4 only with no node selector.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test1"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test1", "8", ipv4CIDR, "<none>", "<unknown>"}}},
},
{
// Test name, IPv4 only with node selector, Not wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test2"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test2", "8", ipv4CIDR, "<none>", "<unknown>"}}},
},
{
// Test name, IPv4 only with no node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test3"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test3", "8", ipv4CIDR, "<none>", "<unknown>", "<none>"}}},
},
{
// Test name, IPv4 only with node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test4"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test4", "8", ipv4CIDR, "<none>", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
{
// Test name, IPv6 only with no node selector.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test5"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv6: ipv6CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age
expected: []metav1.TableRow{{Cells: []interface{}{"test5", "8", "<none>", ipv6CIDR, "<unknown>"}}},
},
{
// Test name, IPv6 only with node selector, Not wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test6"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv6: ipv6CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test6", "8", "<none>", ipv6CIDR, "<unknown>"}}},
},
{
// Test name, IPv6 only with no node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test7"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv6: ipv6CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test7", "8", "<none>", ipv6CIDR, "<unknown>", "<none>"}}},
},
{
// Test name, IPv6 only with node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test8"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv6: ipv6CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test8", "8", "<none>", ipv6CIDR, "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
{
// Test name, DualStack with no node selector.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test9"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
IPv6: ipv6CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test9", "8", ipv4CIDR, ipv6CIDR, "<unknown>"}}},
},
{
// Test name,DualStack with node selector, Not wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test10"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
IPv6: ipv6CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test10", "8", ipv4CIDR, ipv6CIDR, "<unknown>"}}},
},
{
// Test name, DualStack with no node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test11"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
IPv6: ipv6CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector.
expected: []metav1.TableRow{{Cells: []interface{}{"test11", "8", ipv4CIDR, ipv6CIDR, "<unknown>", "<none>"}}},
},
{
// Test name, DualStack with node selector, wide.
ccc: networking.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{Name: "test12"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: perNodeHostBits,
IPv4: ipv4CIDR,
IPv6: ipv6CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test12", "8", ipv4CIDR, ipv6CIDR, "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
}
for i, test := range tests {
rows, err := printClusterCIDR(&test.ccc, test.options)
if err != nil {
t.Fatal(err)
}
for i := range rows {
rows[i].Object.Object = nil
}
if !reflect.DeepEqual(test.expected, rows) {
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expected, rows))
}
}
}
func makeNodeSelector(key string, op api.NodeSelectorOperator, values []string) *api.NodeSelector {
return &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: key,
Operator: op,
Values: values,
},
},
},
},
}
}
func TestPrintClusterCIDRList(t *testing.T) {
cccList := networking.ClusterCIDRList{
Items: []networking.ClusterCIDR{
{
ObjectMeta: metav1.ObjectMeta{Name: "ccc1"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: int32(8),
IPv4: "10.1.0.0/16",
IPv6: "fd00:1:1::/64",
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "ccc2"},
Spec: networking.ClusterCIDRSpec{
PerNodeHostBits: int32(8),
IPv4: "10.2.0.0/16",
IPv6: "fd00:2:1::/64",
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
},
}
tests := []struct {
options printers.GenerateOptions
expected []metav1.TableRow
}{
{
// Test name, DualStack with node selector, wide.
options: printers.GenerateOptions{Wide: false},
expected: []metav1.TableRow{
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
{Cells: []interface{}{"ccc1", "8", "10.1.0.0/16", "fd00:1:1::/64", "<unknown>"}},
{Cells: []interface{}{"ccc2", "8", "10.2.0.0/16", "fd00:2:1::/64", "<unknown>"}},
},
},
{
// Test name, DualStack with node selector, wide.
options: printers.GenerateOptions{Wide: true},
expected: []metav1.TableRow{
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector.
{Cells: []interface{}{"ccc1", "8", "10.1.0.0/16", "fd00:1:1::/64", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}},
{Cells: []interface{}{"ccc2", "8", "10.2.0.0/16", "fd00:2:1::/64", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}},
},
},
}
for _, test := range tests {
rows, err := printClusterCIDRList(&cccList, test.options)
if err != nil {
t.Fatalf("Error printing service list: %#v", err)
}
for i := range rows {
rows[i].Object.Object = nil
}
if !reflect.DeepEqual(test.expected, rows) {
t.Errorf("mismatch: %s", diff.ObjectReflectDiff(test.expected, rows))
}
}
}

View File

@ -33,7 +33,6 @@ import (
"unicode"
"github.com/fatih/camelcase"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
@ -46,6 +45,7 @@ import (
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
@ -213,6 +213,7 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr
{Group: networkingv1beta1.GroupName, Kind: "IngressClass"}: &IngressClassDescriber{c},
{Group: networkingv1.GroupName, Kind: "Ingress"}: &IngressDescriber{c},
{Group: networkingv1.GroupName, Kind: "IngressClass"}: &IngressClassDescriber{c},
{Group: networkingv1alpha1.GroupName, Kind: "ClusterCIDR"}: &ClusterCIDRDescriber{c},
{Group: batchv1.GroupName, Kind: "Job"}: &JobDescriber{c},
{Group: batchv1.GroupName, Kind: "CronJob"}: &CronJobDescriber{c},
{Group: batchv1beta1.GroupName, Kind: "CronJob"}: &CronJobDescriber{c},
@ -2853,6 +2854,63 @@ func (i *IngressClassDescriber) describeIngressClassV1(ic *networkingv1.IngressC
})
}
// ClusterCIDRDescriber generates information about a ClusterCIDR.
type ClusterCIDRDescriber struct {
client clientset.Interface
}
func (c *ClusterCIDRDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
var events *corev1.EventList
ccV1alpha1, err := c.client.NetworkingV1alpha1().ClusterCIDRs().Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = searchEvents(c.client.CoreV1(), ccV1alpha1, describerSettings.ChunkSize)
}
return c.describeClusterCIDRV1alpha1(ccV1alpha1, events)
}
return "", err
}
func (c *ClusterCIDRDescriber) describeClusterCIDRV1alpha1(cc *networkingv1alpha1.ClusterCIDR, events *corev1.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Name:\t%v\n", cc.Name)
printLabelsMultiline(w, "Labels", cc.Labels)
printAnnotationsMultiline(w, "Annotations", cc.Annotations)
w.Write(LEVEL_0, "NodeSelector:\n")
if cc.Spec.NodeSelector != nil {
w.Write(LEVEL_1, "NodeSelector Terms:")
if len(cc.Spec.NodeSelector.NodeSelectorTerms) == 0 {
w.WriteLine("<none>")
} else {
w.WriteLine("")
for i, term := range cc.Spec.NodeSelector.NodeSelectorTerms {
printNodeSelectorTermsMultilineWithIndent(w, LEVEL_2, fmt.Sprintf("Term %v", i), "\t", term.MatchExpressions)
}
}
}
if cc.Spec.PerNodeHostBits != 0 {
w.Write(LEVEL_0, "PerNodeHostBits:\t%s\n", fmt.Sprint(cc.Spec.PerNodeHostBits))
}
if cc.Spec.IPv4 != "" {
w.Write(LEVEL_0, "IPv4:\t%s\n", cc.Spec.IPv4)
}
if cc.Spec.IPv6 != "" {
w.Write(LEVEL_0, "IPv6:\t%s\n", cc.Spec.IPv6)
}
if events != nil {
DescribeEvents(events, w)
}
return nil
})
}
// ServiceDescriber generates information about a service.
type ServiceDescriber struct {
clientset.Interface

View File

@ -25,6 +25,7 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
@ -34,6 +35,7 @@ import (
discoveryv1 "k8s.io/api/discovery/v1"
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
@ -5371,6 +5373,64 @@ Events: <none>` + "\n",
}
}
func TestDescribeClusterCIDR(t *testing.T) {
testcases := map[string]struct {
input *fake.Clientset
output string
}{
"ClusterCIDR v1alpha1": {
input: fake.NewSimpleClientset(&networkingv1alpha1.ClusterCIDR{
ObjectMeta: metav1.ObjectMeta{
Name: "foo.123",
},
Spec: networkingv1alpha1.ClusterCIDRSpec{
PerNodeHostBits: int32(8),
IPv4: "10.1.0.0/16",
IPv6: "fd00:1:1::/64",
NodeSelector: &corev1.NodeSelector{
NodeSelectorTerms: []corev1.NodeSelectorTerm{
{
MatchExpressions: []corev1.NodeSelectorRequirement{
{
Key: "foo",
Operator: "In",
Values: []string{"bar"}},
},
},
},
},
},
}),
output: `Name: foo.123
Labels: <none>
Annotations: <none>
NodeSelector:
NodeSelector Terms:
Term 0: foo in [bar]
PerNodeHostBits: 8
IPv4: 10.1.0.0/16
IPv6: fd00:1:1::/64
Events: <none>` + "\n",
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
c := &describeClient{T: t, Namespace: "foo", Interface: tc.input}
d := ClusterCIDRDescriber{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.Errorf("expected :\n%s\nbut got output:\n%s diff:\n%s", tc.output, out, cmp.Diff(tc.output, out))
}
})
}
}
func TestControllerRef(t *testing.T) {
var replicas int32 = 1
f := fake.NewSimpleClientset(