diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index bbca655d342..fa84b8337c2 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -187,6 +187,8 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr {Group: extensionsv1beta1.GroupName, Kind: "Ingress"}: &IngressDescriber{c}, {Group: networkingv1beta1.GroupName, Kind: "Ingress"}: &IngressDescriber{c}, {Group: networkingv1beta1.GroupName, Kind: "IngressClass"}: &IngressClassDescriber{c}, + {Group: networkingv1.GroupName, Kind: "Ingress"}: &IngressDescriber{c}, + {Group: networkingv1.GroupName, Kind: "IngressClass"}: &IngressClassDescriber{c}, {Group: batchv1.GroupName, Kind: "Job"}: &JobDescriber{c}, {Group: batchv1.GroupName, Kind: "CronJob"}: &CronJobDescriber{c}, {Group: appsv1.GroupName, Kind: "StatefulSet"}: &StatefulSetDescriber{c}, @@ -2343,24 +2345,36 @@ func describeSecret(secret *corev1.Secret) (string, error) { } type IngressDescriber struct { - clientset.Interface + client clientset.Interface } func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { - c := i.NetworkingV1beta1().Ingresses(namespace) - ing, err := c.Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return "", err + var events *corev1.EventList + + // try ingress/v1 first (v1.19) and fallback to ingress/v1beta if an err occurs + netV1, err := i.client.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1) + } + return i.describeIngressV1(netV1, events) } - return i.describeIngress(ing, describerSettings) + netV1beta1, err := i.client.NetworkingV1beta1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1beta1) + } + return i.describeIngressV1beta1(netV1beta1, events) + } + return "", err } -func (i *IngressDescriber) describeBackend(ns string, backend *networkingv1beta1.IngressBackend) string { - endpoints, err := i.CoreV1().Endpoints(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{}) +func (i *IngressDescriber) describeBackendV1beta1(ns string, backend *networkingv1beta1.IngressBackend) string { + endpoints, err := i.client.CoreV1().Endpoints(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{}) if err != nil { return fmt.Sprintf("", err) } - service, err := i.CoreV1().Services(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{}) + service, err := i.client.CoreV1().Services(ns).Get(context.TODO(), backend.ServiceName, metav1.GetOptions{}) if err != nil { return fmt.Sprintf("", err) } @@ -2381,7 +2395,93 @@ func (i *IngressDescriber) describeBackend(ns string, backend *networkingv1beta1 return formatEndpoints(endpoints, sets.NewString(spName)) } -func (i *IngressDescriber) describeIngress(ing *networkingv1beta1.Ingress, describerSettings DescriberSettings) (string, error) { +func (i *IngressDescriber) describeBackendV1(ns string, backend *networkingv1.IngressBackend) string { + + if backend.Service != nil { + sb := serviceBackendStringer(backend.Service) + endpoints, err := i.client.CoreV1().Endpoints(ns).Get(context.TODO(), backend.Service.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Sprintf("%v ()", sb, err) + } + service, err := i.client.CoreV1().Services(ns).Get(context.TODO(), backend.Service.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Sprintf("%v()", sb, err) + } + spName := "" + for i := range service.Spec.Ports { + sp := &service.Spec.Ports[i] + if backend.Service.Port.Number != 0 && backend.Service.Port.Number == sp.Port { + spName = sp.Name + } else if len(backend.Service.Port.Name) > 0 && backend.Service.Port.Name == sp.Name { + spName = sp.Name + } + } + ep := formatEndpoints(endpoints, sets.NewString(spName)) + return fmt.Sprintf("%s\t %s)", sb, ep) + } + if backend.Resource != nil { + ic := backend.Resource + return fmt.Sprintf("APIGroup: %v, Kind: %v, Name: %v", *ic.APIGroup, ic.Kind, ic.Name) + } + return "" +} + +func (i *IngressDescriber) describeIngressV1(ing *networkingv1.Ingress, events *corev1.EventList) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%v\n", ing.Name) + w.Write(LEVEL_0, "Namespace:\t%v\n", ing.Namespace) + w.Write(LEVEL_0, "Address:\t%v\n", loadBalancerStatusStringer(ing.Status.LoadBalancer, true)) + def := ing.Spec.DefaultBackend + ns := ing.Namespace + if def == nil { + // Ingresses that don't specify a default backend inherit the + // default backend in the kube-system namespace. + def = &networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "default-http-backend", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + } + ns = metav1.NamespaceSystem + } + w.Write(LEVEL_0, "Default backend:\t%s\n", i.describeBackendV1(ns, def)) + if len(ing.Spec.TLS) != 0 { + describeIngressTLSV1(w, ing.Spec.TLS) + } + w.Write(LEVEL_0, "Rules:\n Host\tPath\tBackends\n") + w.Write(LEVEL_1, "----\t----\t--------\n") + count := 0 + for _, rules := range ing.Spec.Rules { + + if rules.HTTP == nil { + continue + } + count++ + host := rules.Host + if len(host) == 0 { + host = "*" + } + w.Write(LEVEL_1, "%s\t\n", host) + for _, path := range rules.HTTP.Paths { + w.Write(LEVEL_2, "\t%s \t%s\n", path.Path, i.describeBackendV1(ing.Namespace, &path.Backend)) + } + } + if count == 0 { + w.Write(LEVEL_1, "\t%s %s\n", "*", "*", i.describeBackendV1(ns, def)) + } + printAnnotationsMultiline(w, "Annotations", ing.Annotations) + + if events != nil { + DescribeEvents(events, w) + } + return nil + }) +} + +func (i *IngressDescriber) describeIngressV1beta1(ing *networkingv1beta1.Ingress, events *corev1.EventList) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) w.Write(LEVEL_0, "Name:\t%v\n", ing.Name) @@ -2398,14 +2498,15 @@ func (i *IngressDescriber) describeIngress(ing *networkingv1beta1.Ingress, descr } ns = metav1.NamespaceSystem } - w.Write(LEVEL_0, "Default backend:\t%s (%s)\n", backendStringer(def), i.describeBackend(ns, def)) + w.Write(LEVEL_0, "Default backend:\t%s (%s)\n", backendStringer(def), i.describeBackendV1beta1(ns, def)) if len(ing.Spec.TLS) != 0 { - describeIngressTLS(w, ing.Spec.TLS) + describeIngressTLSV1beta1(w, ing.Spec.TLS) } w.Write(LEVEL_0, "Rules:\n Host\tPath\tBackends\n") w.Write(LEVEL_1, "----\t----\t--------\n") count := 0 for _, rules := range ing.Spec.Rules { + if rules.HTTP == nil { continue } @@ -2416,25 +2517,33 @@ func (i *IngressDescriber) describeIngress(ing *networkingv1beta1.Ingress, descr } w.Write(LEVEL_1, "%s\t\n", host) for _, path := range rules.HTTP.Paths { - w.Write(LEVEL_2, "\t%s \t%s (%s)\n", path.Path, backendStringer(&path.Backend), i.describeBackend(ing.Namespace, &path.Backend)) + w.Write(LEVEL_2, "\t%s \t%s (%s)\n", path.Path, backendStringer(&path.Backend), i.describeBackendV1beta1(ing.Namespace, &path.Backend)) } } if count == 0 { - w.Write(LEVEL_1, "%s\t%s \t%s (%s)\n", "*", "*", backendStringer(def), i.describeBackend(ns, def)) + w.Write(LEVEL_1, "%s\t%s \t%s (%s)\n", "*", "*", backendStringer(def), i.describeBackendV1beta1(ns, def)) } printAnnotationsMultiline(w, "Annotations", ing.Annotations) - if describerSettings.ShowEvents { - events, _ := i.CoreV1().Events(ing.Namespace).Search(scheme.Scheme, ing) - if events != nil { - DescribeEvents(events, w) - } + if events != nil { + DescribeEvents(events, w) } return nil }) } -func describeIngressTLS(w PrefixWriter, ingTLS []networkingv1beta1.IngressTLS) { +func describeIngressTLSV1beta1(w PrefixWriter, ingTLS []networkingv1beta1.IngressTLS) { + w.Write(LEVEL_0, "TLS:\n") + for _, t := range ingTLS { + if t.SecretName == "" { + w.Write(LEVEL_1, "SNI routes %v\n", strings.Join(t.Hosts, ",")) + } else { + w.Write(LEVEL_1, "%v terminates %v\n", t.SecretName, strings.Join(t.Hosts, ",")) + } + } +} + +func describeIngressTLSV1(w PrefixWriter, ingTLS []networkingv1.IngressTLS) { w.Write(LEVEL_0, "TLS:\n") for _, t := range ingTLS { if t.SecretName == "" { @@ -2446,19 +2555,30 @@ func describeIngressTLS(w PrefixWriter, ingTLS []networkingv1beta1.IngressTLS) { } type IngressClassDescriber struct { - clientset.Interface + client clientset.Interface } func (i *IngressClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { - c := i.NetworkingV1beta1().IngressClasses() - ic, err := c.Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return "", err + var events *corev1.EventList + // try IngressClass/v1 first (v1.19) and fallback to IngressClass/v1beta if an err occurs + netV1, err := i.client.NetworkingV1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1) + } + return i.describeIngressClassV1(netV1, events) } - return i.describeIngressClass(ic, describerSettings) + netV1beta1, err := i.client.NetworkingV1beta1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + if describerSettings.ShowEvents { + events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1beta1) + } + return i.describeIngressClassV1beta1(netV1beta1, events) + } + return "", err } -func (i *IngressClassDescriber) describeIngressClass(ic *networkingv1beta1.IngressClass, describerSettings DescriberSettings) (string, error) { +func (i *IngressClassDescriber) describeIngressClassV1beta1(ic *networkingv1beta1.IngressClass, events *corev1.EventList) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) w.Write(LEVEL_0, "Name:\t%s\n", ic.Name) @@ -2474,7 +2594,32 @@ func (i *IngressClassDescriber) describeIngressClass(ic *networkingv1beta1.Ingre w.Write(LEVEL_1, "Kind:\t%v\n", ic.Spec.Parameters.Kind) w.Write(LEVEL_1, "Name:\t%v\n", ic.Spec.Parameters.Name) } + if events != nil { + DescribeEvents(events, w) + } + return nil + }) +} +func (i *IngressClassDescriber) describeIngressClassV1(ic *networkingv1.IngressClass, events *corev1.EventList) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", ic.Name) + printLabelsMultiline(w, "Labels", ic.Labels) + printAnnotationsMultiline(w, "Annotations", ic.Annotations) + w.Write(LEVEL_0, "Controller:\t%v\n", ic.Spec.Controller) + + if ic.Spec.Parameters != nil { + w.Write(LEVEL_0, "Parameters:\n") + if ic.Spec.Parameters.APIGroup != nil { + w.Write(LEVEL_1, "APIGroup:\t%v\n", *ic.Spec.Parameters.APIGroup) + } + w.Write(LEVEL_1, "Kind:\t%v\n", ic.Spec.Parameters.Kind) + w.Write(LEVEL_1, "Name:\t%v\n", ic.Spec.Parameters.Name) + } + if events != nil { + DescribeEvents(events, w) + } return nil }) } @@ -4896,6 +5041,21 @@ func extractCSRStatus(conditions []string, certificateBytes []byte) string { return status } +// backendStringer behaves just like a string interface and converts the given backend to a string. +func serviceBackendStringer(backend *networkingv1.IngressServiceBackend) string { + if backend == nil { + return "" + } + var bPort string + if backend.Port.Number != 0 { + sNum := int64(backend.Port.Number) + bPort = strconv.FormatInt(sNum, 10) + } else { + bPort = backend.Port.Name + } + return fmt.Sprintf("%v:%v", backend.Name, bPort) +} + // backendStringer behaves just like a string interface and converts the given backend to a string. func backendStringer(backend *networkingv1beta1.IngressBackend) string { if backend == nil { 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 f5986b61478..6536c76189a 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -1677,12 +1677,11 @@ func TestDescribeDeployment(t *testing.T) { } func TestDescribeIngress(t *testing.T) { - defaultBackend := networkingv1beta1.IngressBackend{ + backendV1beta1 := networkingv1beta1.IngressBackend{ ServiceName: "default-backend", ServicePort: intstr.FromInt(80), } - - fakeClient := fake.NewSimpleClientset(&networkingv1beta1.Ingress{ + v1beta1 := fake.NewSimpleClientset(&networkingv1beta1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", Namespace: "foo", @@ -1694,6 +1693,278 @@ func TestDescribeIngress(t *testing.T) { IngressRuleValue: networkingv1beta1.IngressRuleValue{ HTTP: &networkingv1beta1.HTTPIngressRuleValue{ Paths: []networkingv1beta1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendV1beta1, + }, + }, + }, + }, + }, + }, + }, + }) + backendV1 := networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "default-backend", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + } + + netv1 := fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendV1, + }, + }, + }, + }, + }, + }, + }, + }) + + backendResource := networkingv1.IngressBackend{ + Resource: &corev1.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("example.com"), + Kind: "foo", + Name: "bar", + }, + } + + tests := map[string]struct { + input *fake.Clientset + output string + }{ + "IngressRule.HTTP.Paths.Backend.Service v1beta1": { + input: v1beta1, + output: `Name: bar +Namespace: foo +Address: +Default backend: default-http-backend:80 () +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo default-backend:80 () +Annotations: +Events: ` + "\n", + }, + "IngressRule.HTTP.Paths.Backend.Service v1": { + input: netv1, + output: `Name: bar +Namespace: foo +Address: +Default backend: default-http-backend:80 () +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo default-backend:80 () +Annotations: +Events: ` + "\n", + }, + "IngressRule.HTTP.Paths.Backend.Resource v1": { + input: fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendResource, + }, + }, + }, + }, + }, + }, + }, + }), + output: `Name: bar +Namespace: foo +Address: +Default backend: default-http-backend:80 () +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo APIGroup: example.com, Kind: foo, Name: bar +Annotations: +Events: ` + "\n", + }, + "Spec.DefaultBackend.Service & IngressRule.HTTP.Paths.Backend.Service v1": { + input: fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + DefaultBackend: &backendV1, + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendV1, + }, + }, + }, + }, + }, + }, + }, + }), + output: `Name: bar +Namespace: foo +Address: +Default backend: default-backend:80 () +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo default-backend:80 () +Annotations: +Events: ` + "\n", + }, + "Spec.DefaultBackend.Resource & IngressRule.HTTP.Paths.Backend.Resource v1": { + input: fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + DefaultBackend: &backendResource, + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendResource, + }, + }, + }, + }, + }, + }, + }, + }), + output: `Name: bar +Namespace: foo +Address: +Default backend: APIGroup: example.com, Kind: foo, Name: bar +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo APIGroup: example.com, Kind: foo, Name: bar +Annotations: +Events: ` + "\n", + }, + "Spec.DefaultBackend.Resource & IngressRule.HTTP.Paths.Backend.Service v1": { + input: fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + DefaultBackend: &backendResource, + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: backendV1, + }, + }, + }, + }, + }, + }, + }, + }), + output: `Name: bar +Namespace: foo +Address: +Default backend: APIGroup: example.com, Kind: foo, Name: bar +Rules: + Host Path Backends + ---- ---- -------- + foo.bar.com + /foo default-backend:80 () +Annotations: +Events: ` + "\n", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + c := &describeClient{T: t, Namespace: "foo", Interface: test.input} + i := IngressDescriber{c} + out, err := i.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if out != test.output { + t.Logf(out) + t.Logf(test.output) + t.Errorf("expected: \n%q\n but got output: \n%q\n", test.output, out) + } + }) + } +} + +func TestDescribeIngressV1(t *testing.T) { + defaultBackend := networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "default-backend", + Port: networkingv1.ServiceBackendPort{ + Number: 80, + }, + }, + } + + fakeClient := fake.NewSimpleClientset(&networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ { Path: "/foo", Backend: defaultBackend, @@ -3097,27 +3368,6 @@ func TestDescribeResourceQuota(t *testing.T) { } func TestDescribeIngressClass(t *testing.T) { - fake := fake.NewSimpleClientset(&networkingv1beta1.IngressClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-class", - }, - Spec: networkingv1beta1.IngressClassSpec{ - Controller: "example.com/controller", - Parameters: &corev1.TypedLocalObjectReference{ - APIGroup: utilpointer.StringPtr("v1"), - Kind: "ConfigMap", - Name: "example-parameters", - }, - }, - }) - - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := IngressClassDescriber{c} - out, err := d.Describe("", "example-class", DescriberSettings{}) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - expectedOut := `Name: example-class Labels: Annotations: @@ -3127,9 +3377,57 @@ Parameters: Kind: ConfigMap Name: example-parameters` + "\n" - if out != expectedOut { - t.Logf(out) - t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out) + tests := map[string]struct { + input *fake.Clientset + output string + }{ + "basic IngressClass (v1beta1)": { + input: fake.NewSimpleClientset(&networkingv1beta1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-class", + }, + Spec: networkingv1beta1.IngressClassSpec{ + Controller: "example.com/controller", + Parameters: &corev1.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("v1"), + Kind: "ConfigMap", + Name: "example-parameters", + }, + }, + }), + output: expectedOut, + }, + "basic IngressClass (v1)": { + input: fake.NewSimpleClientset(&networkingv1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-class", + }, + Spec: networkingv1.IngressClassSpec{ + Controller: "example.com/controller", + Parameters: &corev1.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("v1"), + Kind: "ConfigMap", + Name: "example-parameters", + }, + }, + }), + output: expectedOut, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + c := &describeClient{T: t, Namespace: "foo", Interface: test.input} + d := IngressClassDescriber{c} + out, err := d.Describe("", "example-class", DescriberSettings{}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if out != expectedOut { + t.Logf(out) + t.Errorf("expected : %q\n but got output:\n %q", test.output, out) + } + }) } }