From e1e1fd640c3104c953f240128a6cfca1c4ce7dd2 Mon Sep 17 00:00:00 2001 From: Manuel Zapf Date: Mon, 17 May 2021 16:50:09 +0200 Subject: [PATCH] Upgrade IngressClass to use v1 over v1Beta on Kube 1.19+ --- pkg/provider/kubernetes/ingress/client.go | 61 ++++++++++++++++--- .../kubernetes/ingress/client_mock_test.go | 10 ++- ...9-Ingress-with-ingressClassv1_endpoint.yml | 11 ++++ ...19-Ingress-with-ingressClassv1_ingress.yml | 16 +++++ ...gress-with-ingressClassv1_ingressclass.yml | 6 ++ ...19-Ingress-with-ingressClassv1_service.yml | 10 +++ pkg/provider/kubernetes/ingress/kubernetes.go | 5 +- .../kubernetes/ingress/kubernetes_test.go | 28 +++++++++ 8 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_endpoint.yml create mode 100644 pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingress.yml create mode 100644 pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingressclass.yml create mode 100644 pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_service.yml diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go index 7a0a0333c..b827e52a5 100644 --- a/pkg/provider/kubernetes/ingress/client.go +++ b/pkg/provider/kubernetes/ingress/client.go @@ -41,7 +41,7 @@ type marshaler interface { type Client interface { WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) GetIngresses() []*networkingv1.Ingress - GetIngressClasses() ([]*networkingv1beta1.IngressClass, error) + GetIngressClasses() ([]*networkingv1.IngressClass, error) GetService(namespace, name string) (*corev1.Service, bool, error) GetSecret(namespace, name string) (*corev1.Secret, bool, error) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) @@ -207,7 +207,13 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< if supportsIngressClass(serverVersion) { c.clusterFactory = informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod) - c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler) + + if supportsNetworkingV1Ingress(serverVersion) { + c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) + } else { + c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler) + } + c.clusterFactory.Start(stopCh) for typ, ok := range c.clusterFactory.WaitForCacheSync(stopCh) { @@ -282,6 +288,21 @@ func toNetworkingV1(ing marshaler) (*networkingv1.Ingress, error) { return ni, nil } +func toNetworkingV1IngressClass(ing marshaler) (*networkingv1.IngressClass, error) { + data, err := ing.Marshal() + if err != nil { + return nil, err + } + + ni := &networkingv1.IngressClass{} + err = ni.Unmarshal(data) + if err != nil { + return nil, err + } + + return ni, nil +} + func addServiceFromV1Beta1(ing *networkingv1.Ingress, old networkingv1beta1.Ingress) { if old.Spec.Backend != nil { port := networkingv1.ServiceBackendPort{} @@ -450,17 +471,43 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, return secret, exist, err } -func (c *clientWrapper) GetIngressClasses() ([]*networkingv1beta1.IngressClass, error) { +func (c *clientWrapper) GetIngressClasses() ([]*networkingv1.IngressClass, error) { + serverVersion, err := c.GetServerVersion() + if err != nil { + log.WithoutContext().Errorf("Failed to get server version: %v", err) + return nil, err + } + if c.clusterFactory == nil { return nil, errors.New("cluster factory not loaded") } - ingressClasses, err := c.clusterFactory.Networking().V1beta1().IngressClasses().Lister().List(labels.Everything()) + var ics []*networkingv1.IngressClass + if !supportsNetworkingV1Ingress(serverVersion) { + ingressClasses, err := c.clusterFactory.Networking().V1beta1().IngressClasses().Lister().List(labels.Everything()) + if err != nil { + return nil, err + } + + for _, ic := range ingressClasses { + if ic.Spec.Controller == traefikDefaultIngressClassController { + icN, err := toNetworkingV1IngressClass(ic) + if err != nil { + log.WithoutContext().Errorf("Failed to convert ingress class %s from networking/v1beta1 to networking/v1: %v", ic.Name, err) + continue + } + ics = append(ics, icN) + } + } + + return ics, nil + } + + ingressClasses, err := c.clusterFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything()) if err != nil { return nil, err } - var ics []*networkingv1beta1.IngressClass for _, ic := range ingressClasses { if ic.Spec.Controller == traefikDefaultIngressClassController { ics = append(ics, ic) @@ -525,8 +572,8 @@ func supportsIngressClass(serverVersion *version.Version) bool { } // filterIngressClassByName return a slice containing ingressclasses with the correct name. -func filterIngressClassByName(ingressClassName string, ics []*networkingv1beta1.IngressClass) []*networkingv1beta1.IngressClass { - var ingressClasses []*networkingv1beta1.IngressClass +func filterIngressClassByName(ingressClassName string, ics []*networkingv1.IngressClass) []*networkingv1.IngressClass { + var ingressClasses []*networkingv1.IngressClass for _, ic := range ics { if ic.Name == ingressClassName { diff --git a/pkg/provider/kubernetes/ingress/client_mock_test.go b/pkg/provider/kubernetes/ingress/client_mock_test.go index 493bdf5ec..195580692 100644 --- a/pkg/provider/kubernetes/ingress/client_mock_test.go +++ b/pkg/provider/kubernetes/ingress/client_mock_test.go @@ -18,7 +18,7 @@ type clientMock struct { services []*corev1.Service secrets []*corev1.Secret endpoints []*corev1.Endpoints - ingressClasses []*networkingv1beta1.IngressClass + ingressClasses []*networkingv1.IngressClass serverVersion *version.Version @@ -60,6 +60,12 @@ func newClientMock(serverVersion string, paths ...string) clientMock { case *networkingv1.Ingress: c.ingresses = append(c.ingresses, o) case *networkingv1beta1.IngressClass: + ic, err := toNetworkingV1IngressClass(o) + if err != nil { + panic(err) + } + c.ingressClasses = append(c.ingressClasses, ic) + case *networkingv1.IngressClass: c.ingressClasses = append(c.ingressClasses, o) default: panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o)) @@ -118,7 +124,7 @@ func (c clientMock) GetSecret(namespace, name string) (*corev1.Secret, bool, err return nil, false, nil } -func (c clientMock) GetIngressClasses() ([]*networkingv1beta1.IngressClass, error) { +func (c clientMock) GetIngressClasses() ([]*networkingv1.IngressClass, error) { return c.ingressClasses, nil } diff --git a/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_endpoint.yml b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_endpoint.yml new file mode 100644 index 000000000..6ed60d79c --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_endpoint.yml @@ -0,0 +1,11 @@ +kind: Endpoints +apiVersion: v1 +metadata: + name: service1 + namespace: testing + +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingress.yml new file mode 100644 index 000000000..c9be2e68c --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingress.yml @@ -0,0 +1,16 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: "" + namespace: testing +spec: + ingressClassName: traefik-lb-v1 + rules: + - http: + paths: + - path: /bar + backend: + service: + name: service1 + port: + number: 80 diff --git a/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingressclass.yml b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingressclass.yml new file mode 100644 index 000000000..744ecc9cd --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_ingressclass.yml @@ -0,0 +1,6 @@ +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: traefik-lb-v1 +spec: + controller: traefik.io/ingress-controller diff --git a/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_service.yml b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_service.yml new file mode 100644 index 000000000..0ec7e2269 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/v19-Ingress-with-ingressClassv1_service.yml @@ -0,0 +1,10 @@ +kind: Service +apiVersion: v1 +metadata: + name: service1 + namespace: testing + +spec: + ports: + - port: 80 + clusterIP: 10.0.0.1 diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 778067c0e..770f565fd 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -24,7 +24,6 @@ import ( "github.com/traefik/traefik/v2/pkg/tls" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" - networkingv1beta1 "k8s.io/api/networking/v1beta1" "k8s.io/apimachinery/pkg/labels" ) @@ -191,7 +190,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl return conf } - var ingressClasses []*networkingv1beta1.IngressClass + var ingressClasses []*networkingv1.IngressClass if supportsIngressClass(serverVersion) { ics, err := client.GetIngressClasses() @@ -378,7 +377,7 @@ func (p *Provider) updateIngressStatus(ing *networkingv1.Ingress, k8sClient Clie return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress) } -func (p *Provider) shouldProcessIngress(ingress *networkingv1.Ingress, ingressClasses []*networkingv1beta1.IngressClass) bool { +func (p *Provider) shouldProcessIngress(ingress *networkingv1.Ingress, ingressClasses []*networkingv1.IngressClass) bool { // configuration through the new kubernetes ingressClass if ingress.Spec.IngressClassName != nil { for _, ic := range ingressClasses { diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index 663cec314..c0853f1bb 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -1538,6 +1538,34 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, + { + desc: "v19 Ingress with ingressClassv1", + serverVersion: "v1.19", + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{ + "testing-bar": { + Rule: "PathPrefix(`/bar`)", + Service: "testing-service1-80", + }, + }, + Services: map[string]*dynamic.Service{ + "testing-service1-80": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: Bool(true), + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:8080", + }, + }, + }, + }, + }, + }, + }, + }, { desc: "v19 Ingress with named port", serverVersion: "v1.19",