From 6e460cd65247fdb3e880622adb571680e30e22d3 Mon Sep 17 00:00:00 2001 From: Romain Date: Mon, 20 Mar 2023 16:46:05 +0100 Subject: [PATCH] Native Kubernetes service load-balancing --- .../kubernetes-crd-definition-v1.yml | 98 +++++++ .../traefik.containo.us_ingressroutes.yaml | 7 + .../traefik.containo.us_ingressroutetcps.yaml | 7 + .../traefik.containo.us_ingressrouteudps.yaml | 7 + .../traefik.containo.us_middlewares.yaml | 7 + .../traefik.containo.us_traefikservices.yaml | 21 ++ .../traefik.io_ingressroutes.yaml | 7 + .../traefik.io_ingressroutetcps.yaml | 7 + .../traefik.io_ingressrouteudps.yaml | 7 + .../traefik.io_middlewares.yaml | 7 + .../traefik.io_traefikservices.yaml | 21 ++ .../routing/providers/kubernetes-crd.md | 246 +++++++++++++----- .../routing/providers/kubernetes-ingress.md | 21 +- integration/fixtures/k8s/01-traefik-crd.yml | 98 +++++++ .../kubernetes/crd/fixtures/services.yml | 14 + .../kubernetes/crd/fixtures/tcp/services.yml | 14 + .../fixtures/tcp/with_native_service_lb.yml | 16 ++ .../kubernetes/crd/fixtures/udp/services.yml | 14 + .../fixtures/udp/with_native_service_lb.yml | 15 ++ .../crd/fixtures/with_native_service_lb.yml | 17 ++ pkg/provider/kubernetes/crd/kubernetes.go | 15 ++ .../kubernetes/crd/kubernetes_http.go | 14 + pkg/provider/kubernetes/crd/kubernetes_tcp.go | 9 + .../kubernetes/crd/kubernetes_test.go | 208 +++++++++++++++ pkg/provider/kubernetes/crd/kubernetes_udp.go | 9 + .../v1alpha1/ingressroute.go | 6 +- .../v1alpha1/ingressroutetcp.go | 5 + .../v1alpha1/ingressrouteudp.go | 5 + .../crd/traefikio/v1alpha1/ingressroute.go | 6 +- .../crd/traefikio/v1alpha1/ingressroutetcp.go | 5 + .../crd/traefikio/v1alpha1/ingressrouteudp.go | 5 + .../kubernetes/ingress/annotations.go | 1 + .../kubernetes/ingress/annotations_test.go | 2 + ...Ingress-with-native-service-lb_ingress.yml | 15 ++ ...Ingress-with-native-service-lb_service.yml | 15 ++ pkg/provider/kubernetes/ingress/kubernetes.go | 27 ++ .../kubernetes/ingress/kubernetes_test.go | 82 +++++- 37 files changed, 1013 insertions(+), 67 deletions(-) create mode 100644 pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml create mode 100644 pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb.yml create mode 100644 pkg/provider/kubernetes/crd/fixtures/with_native_service_lb.yml create mode 100644 pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_ingress.yml create mode 100644 pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_service.yml diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index c0ddc81dc..967624d80 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -104,6 +104,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes @@ -357,6 +364,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -542,6 +556,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -827,6 +848,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -1981,6 +2009,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -2072,6 +2107,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -2169,6 +2211,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -2391,6 +2440,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes @@ -2644,6 +2700,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -2829,6 +2892,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -3114,6 +3184,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -4268,6 +4345,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -4359,6 +4443,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -4456,6 +4547,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutes.yaml index 9550d645e..e4cad63b4 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutes.yaml @@ -104,6 +104,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutetcps.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutetcps.yaml index 37da83b34..390dbd3c5 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutetcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressroutetcps.yaml @@ -89,6 +89,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressrouteudps.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressrouteudps.yaml index 2ba4dade6..bfcd63ea3 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressrouteudps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_ingressrouteudps.yaml @@ -63,6 +63,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml index d7bd0b82c..b587d593d 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml @@ -250,6 +250,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_traefikservices.yaml index 358fdc1ea..3ef4c454a 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_traefikservices.yaml @@ -75,6 +75,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -166,6 +173,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -263,6 +277,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml index df1114ee6..93597744a 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml @@ -104,6 +104,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml index 3daa8c624..03142b1b5 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml @@ -89,6 +89,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml index fce3f9a00..c871718ba 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml @@ -63,6 +63,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index fbbff14eb..acc3e6670 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -250,6 +250,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index af1cca9c7..99ccfb4f6 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -75,6 +75,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -166,6 +173,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -263,6 +277,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 32a7cf501..3819ae760 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -349,15 +349,16 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne sameSite: none strategy: RoundRobin weight: 10 - tls: # [11] - secretName: supersecret # [12] - options: # [13] - name: opt # [14] - namespace: default # [15] - certResolver: foo # [16] - domains: # [17] - - main: example.net # [18] - sans: # [19] + nativeLB: true # [11] + tls: # [12] + secretName: supersecret # [13] + options: # [14] + name: opt # [15] + namespace: default # [16] + certResolver: foo # [17] + domains: # [18] + - main: example.net # [19] + sans: # [20] - a.example.net - b.example.net ``` @@ -374,15 +375,16 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne | [8] | `routes[n].services` | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup) | | [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | | [10] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). | -| [11] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | -| [12] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | -| [13] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | -| [14] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | -| [15] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | -| [16] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | -| [17] | `tls.domains` | List of [domains](../routers/index.md#domains) | -| [18] | `domains[n].main` | Defines the main domain name | -| [19] | `domains[n].sans` | List of SANs (alternative domains) | +| [11] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | +| [12] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | +| [13] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | +| [14] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | +| [15] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | +| [16] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | +| [17] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver) | +| [18] | `tls.domains` | List of [domains](../routers/index.md#domains) | +| [19] | `domains[n].main` | Defines the main domain name | +| [20] | `domains[n].sans` | List of SANs (alternative domains) | ??? example "Declaring an IngressRoute" @@ -639,6 +641,47 @@ More information in the dedicated server [load balancing](../services/index.md#l task: app2 ``` +!!! important "Kubernetes Service Native Load-Balancing" + + To avoid creating the server load-balancer with the pods IPs and use Kubernetes Service clusterIP directly, + one should set the service `NativeLB` option to true. + Please note that, by default, Traefik reuses the established connections to the backends for performance purposes. This can prevent the requests load balancing between the replicas from behaving as one would expect when the option is set. + By default, `NativeLB` is false. + + ??? example "Example" + + ```yaml + --- + apiVersion: traefik.containo.us/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: svc + port: 80 + # Here, nativeLB instructs to build the servers load balancer with the Kubernetes Service clusterIP only. + nativeLB: true + + --- + apiVersion: v1 + kind: Service + metadata: + name: svc + namespace: default + spec: + type: ClusterIP + ... + ``` + ### Kind: `Middleware` `Middleware` is the CRD implementation of a [Traefik middleware](../../middlewares/http/overview.md). @@ -1103,45 +1146,47 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube terminationDelay: 400 # [11] proxyProtocol: # [12] version: 1 # [13] - tls: # [14] - secretName: supersecret # [15] - options: # [16] - name: opt # [17] - namespace: default # [18] - certResolver: foo # [19] - domains: # [20] - - main: example.net # [21] - sans: # [22] + nativeLB: true # [14] + tls: # [15] + secretName: supersecret # [16] + options: # [17] + name: opt # [18] + namespace: default # [19] + certResolver: foo # [20] + domains: # [21] + - main: example.net # [22] + sans: # [23] - a.example.net - b.example.net - passthrough: false # [23] + passthrough: false # [24] ``` -| Ref | Attribute | Purpose | -|------|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names | -| [2] | `routes` | List of routes | -| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule_1) of the underlying router | -| [4] | `routes[n].priority` | Defines the [priority](../routers/index.md#priority_1) to disambiguate rules of the same length, for route matching | -| [5] | `middlewares[n].name` | Defines the [MiddlewareTCP](#kind-middlewaretcp) name | -| [6] | `middlewares[n].namespace` | Defines the [MiddlewareTCP](#kind-middlewaretcp) namespace | -| [7] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions (See below for `ExternalName Service` setup) | -| [8] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) | -| [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | -| [10] | `services[n].weight` | Defines the weight to apply to the server load balancing | -| [11] | `services[n].terminationDelay` | corresponds to the deadline that the proxy sets, after one of its connected peers indicates it has closed the writing capability of its connection, to close the reading capability as well, hence fully terminating the connection. It is a duration in milliseconds, defaulting to 100. A negative value means an infinite deadline (i.e. the reading capability is never closed). | -| [12] | `proxyProtocol` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) configuration | -| [13] | `version` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) version | -| [14] | `tls` | Defines [TLS](../routers/index.md#tls_1) certificate configuration | -| [15] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | -| [16] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | -| [17] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | -| [18] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | -| [19] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver_1) | -| [20] | `tls.domains` | List of [domains](../routers/index.md#domains_1) | -| [21] | `domains[n].main` | Defines the main domain name | -| [22] | `domains[n].sans` | List of SANs (alternative domains) | -| [23] | `tls.passthrough` | If `true`, delegates the TLS termination to the backend | +| Ref | Attribute | Purpose | +|------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names | +| [2] | `routes` | List of routes | +| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule_1) of the underlying router | +| [4] | `routes[n].priority` | Defines the [priority](../routers/index.md#priority_1) to disambiguate rules of the same length, for route matching | +| [5] | `middlewares[n].name` | Defines the [MiddlewareTCP](#kind-middlewaretcp) name | +| [6] | `middlewares[n].namespace` | Defines the [MiddlewareTCP](#kind-middlewaretcp) namespace | +| [7] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions (See below for `ExternalName Service` setup) | +| [8] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) | +| [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | +| [10] | `services[n].weight` | Defines the weight to apply to the server load balancing | +| [11] | `services[n].terminationDelay` | corresponds to the deadline that the proxy sets, after one of its connected peers indicates it has closed the writing capability of its connection, to close the reading capability as well, hence fully terminating the connection. It is a duration in milliseconds, defaulting to 100. A negative value means an infinite deadline (i.e. the reading capability is never closed). | +| [12] | `services[n].proxyProtocol` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) configuration | +| [13] | `services[n].proxyProtocol.version` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) version | +| [14] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | +| [15] | `tls` | Defines [TLS](../routers/index.md#tls_1) certificate configuration | +| [16] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) | +| [17] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) | +| [18] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name | +| [19] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace | +| [20] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver_1) | +| [21] | `tls.domains` | List of [domains](../routers/index.md#domains_1) | +| [22] | `domains[n].main` | Defines the main domain name | +| [23] | `domains[n].sans` | List of SANs (alternative domains) | +| [24] | `tls.passthrough` | If `true`, delegates the TLS termination to the backend | ??? example "Declaring an IngressRouteTCP" @@ -1275,6 +1320,45 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube - port: 80 ``` +!!! important "Kubernetes Service Native Load-Balancing" + + To avoid creating the server load-balancer with the pods IPs and use Kubernetes Service clusterIP directly, + one should set the TCP service `NativeLB` option to true. + By default, `NativeLB` is false. + + ??? example "Examples" + + ```yaml + --- + apiVersion: traefik.containo.us/v1alpha1 + kind: IngressRouteTCP + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: HostSNI(`*`) + services: + - name: svc + port: 80 + # Here, nativeLB instructs to build the servers load balancer with the Kubernetes Service clusterIP only. + nativeLB: true + + --- + apiVersion: v1 + kind: Service + metadata: + name: svc + namespace: default + spec: + type: ClusterIP + ... + ``` + ### Kind: `MiddlewareTCP` `MiddlewareTCP` is the CRD implementation of a [Traefik TCP middleware](../../middlewares/tcp/overview.md). @@ -1348,16 +1432,18 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube - name: foo # [4] port: 8080 # [5] weight: 10 # [6] + nativeLB: true # [7] ``` -| Ref | Attribute | Purpose | -|------|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names | -| [2] | `routes` | List of routes | -| [3] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions (See below for `ExternalName Service` setup) | -| [4] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) | -| [6] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | -| [7] | `services[n].weight` | Defines the weight to apply to the server load balancing | +| Ref | Attribute | Purpose | +|-----|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names | +| [2] | `routes` | List of routes | +| [3] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions (See below for `ExternalName Service` setup) | +| [4] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) | +| [5] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | +| [6] | `services[n].weight` | Defines the weight to apply to the server load balancing | +| [7] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | ??? example "Declaring an IngressRouteUDP" @@ -1479,6 +1565,44 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube - port: 80 ``` +!!! important "Kubernetes Service Native Load-Balancing" + + To avoid creating the server load-balancer with the pods IPs and use Kubernetes Service clusterIP directly, + one should set the UDP service `NativeLB` option to true. + By default, `NativeLB` is false. + + ??? example "Example" + + ```yaml + --- + apiVersion: traefik.containo.us/v1alpha1 + kind: IngressRouteUDP + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - services: + - name: svc + port: 80 + # Here, nativeLB instructs to build the servers load balancer with the Kubernetes Service clusterIP only. + nativeLB: true + + --- + apiVersion: v1 + kind: Service + metadata: + name: svc + namespace: default + spec: + type: ClusterIP + ... + ``` + ### Kind: `TLSOption` `TLSOption` is the CRD implementation of a [Traefik "TLS Option"](../../https/tls.md#tls-options). diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index a82f602eb..d2f4ff14b 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -299,6 +299,17 @@ which in turn will create the resulting routers, services, handlers, etc. #### On Service +??? info "`traefik.ingress.kubernetes.io/service.nativelb`" + + Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + Please note that, by default, Traefik reuses the established connections to the backends for performance purposes. This can prevent the requests load balancing between the replicas from behaving as one would expect when the option is set. + By default, NativeLB is false. + + ```yaml + traefik.ingress.kubernetes.io/service.nativelb: "true" + ``` + ??? info "`traefik.ingress.kubernetes.io/service.serversscheme`" Overrides the default scheme. @@ -888,11 +899,15 @@ TLS certificates can be managed in Secrets objects. ### Communication Between Traefik and Pods -!!! info "It is not possible to route requests directly to [Kubernetes services](https://kubernetes.io/docs/concepts/services-networking/service/ "Link to Kubernetes service docs")" +!!! info "Routing directly to [Kubernetes services](https://kubernetes.io/docs/concepts/services-networking/service/ "Link to Kubernetes service docs")" - You can use an `ExternalName` service to forward requests to the Kubernetes service through DNS. + To route directly to the Kubernetes service, + one can use the `traefik.ingress.kubernetes.io/service.nativelb` annotation on the Kubernetes service. + It controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. - For doing so, you have to [allow external name services](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services"). + One alternative is to use an `ExternalName` service to forward requests to the Kubernetes service through DNS. + To do so, one must [allow external name services](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services"). Traefik automatically requests endpoint information based on the service provided in the ingress spec. Although Traefik will connect directly to the endpoints (pods), diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index c0ddc81dc..967624d80 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -104,6 +104,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes @@ -357,6 +364,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -542,6 +556,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -827,6 +848,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -1981,6 +2009,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -2072,6 +2107,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -2169,6 +2211,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -2391,6 +2440,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes @@ -2644,6 +2700,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -2829,6 +2892,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs + or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean port: anyOf: - type: integer @@ -3114,6 +3184,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if + the only child is the Kubernetes Service clusterIP. The + Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -4268,6 +4345,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. @@ -4359,6 +4443,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the + only child is the Kubernetes Service clusterIP. The Kubernetes + Service itself does load-balance to the pods. By default, NativeLB + is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, @@ -4456,6 +4547,13 @@ spec: description: Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. type: string + nativeLB: + description: NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or + if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the + pods. By default, NativeLB is false. + type: boolean passHostHeader: description: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. diff --git a/pkg/provider/kubernetes/crd/fixtures/services.yml b/pkg/provider/kubernetes/crd/fixtures/services.yml index 8de8ff369..818a5bd3d 100644 --- a/pkg/provider/kubernetes/crd/fixtures/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/services.yml @@ -252,3 +252,17 @@ apiVersion: v1 metadata: name: whoami-without-endpoints-subsets namespace: default + +--- +apiVersion: v1 +kind: Service +metadata: + name: native-svc + namespace: default + +spec: + ports: + - name: web + port: 80 + type: ClusterIP + clusterIP: 10.10.0.1 diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml index 9b5cfd973..8c18ffdc8 100644 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml @@ -261,3 +261,17 @@ apiVersion: v1 metadata: name: whoamitcp-without-endpoints-subsets namespace: default + +--- +apiVersion: v1 +kind: Service +metadata: + name: native-svc + namespace: default + +spec: + ports: + - name: myapp + port: 8000 + type: ClusterIP + clusterIP: 10.10.0.1 diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml new file mode 100644 index 000000000..f95202ec0 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml @@ -0,0 +1,16 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: HostSNI(`foo.com`) + services: + - name: native-svc + port: 8000 + nativeLB: true diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml index d58973c82..adaa7272e 100644 --- a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml @@ -220,3 +220,17 @@ apiVersion: v1 metadata: name: whoamiudp-without-endpoints-subsets namespace: default + +--- +apiVersion: v1 +kind: Service +metadata: + name: native-svc + namespace: default + +spec: + ports: + - name: myapp + port: 8000 + type: ClusterIP + clusterIP: 10.10.0.1 diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb.yml new file mode 100644 index 000000000..6942f106b --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb.yml @@ -0,0 +1,15 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteUDP +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - services: + - name: native-svc + port: 8000 + nativeLB: true diff --git a/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb.yml new file mode 100644 index 000000000..259ea216c --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb.yml @@ -0,0 +1,17 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - match: Host(`foo.com`) + kind: Rule + services: + - name: native-svc + port: 80 + nativeLB: true diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 63cf9aadb..2e3ab3781 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -10,8 +10,10 @@ import ( "encoding/json" "errors" "fmt" + "net" "os" "sort" + "strconv" "strings" "time" @@ -395,6 +397,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) return conf } +// getServicePort always returns a valid port, an error otherwise. func getServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.ServicePort, error) { if svc == nil { return nil, errors.New("service is not defined") @@ -427,6 +430,18 @@ func getServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.Servi return &corev1.ServicePort{Port: port.IntVal}, nil } +func getNativeServiceAddress(service corev1.Service, svcPort corev1.ServicePort) (string, error) { + if service.Spec.ClusterIP == "None" { + return "", fmt.Errorf("no clusterIP on headless service: %s/%s", service.Namespace, service.Name) + } + + if service.Spec.ClusterIP == "" { + return "", fmt.Errorf("no clusterIP found for service: %s/%s", service.Namespace, service.Name) + } + + return net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(svcPort.Port))), nil +} + func createPluginMiddleware(k8sClient Client, ns string, plugins map[string]apiextensionv1.JSON) (map[string]dynamic.PluginConf, error) { if plugins == nil { return nil, nil diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 7e1a4ab14..ba8314584 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -368,6 +368,20 @@ func (c configBuilder) loadServers(parentNamespace string, svc v1alpha1.LoadBala return nil, err } + if svc.NativeLB { + address, err := getNativeServiceAddress(*service, *svcPort) + if err != nil { + return nil, fmt.Errorf("getting native Kubernetes Service address: %w", err) + } + + protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port) + if err != nil { + return nil, err + } + + return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, address)}}, nil + } + var servers []dynamic.Server if service.Spec.Type == corev1.ServiceTypeExternalName { if !c.allowExternalNameServices { diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 19b32435b..63028e4ab 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -226,6 +226,15 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc v1alpha1. return nil, err } + if svc.NativeLB { + address, err := getNativeServiceAddress(*service, *svcPort) + if err != nil { + return nil, fmt.Errorf("getting native Kubernetes Service address: %w", err) + } + + return []dynamic.TCPServer{{Address: address}}, nil + } + var servers []dynamic.TCPServer if service.Spec.Type == corev1.ServiceTypeExternalName { servers = append(servers, dynamic.TCPServer{ diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index ad85fe518..c572e9c56 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -6242,6 +6242,214 @@ func TestExternalNameService(t *testing.T) { } } +func TestNativeLB(t *testing.T) { + testCases := []struct { + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + }{ + { + desc: "Empty", + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "HTTP with native Service LB", + paths: []string{"services.yml", "with_native_service_lb.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{ + "default-test-route-6f97418635c7e18853da": { + EntryPoints: []string{"foo"}, + Service: "default-test-route-6f97418635c7e18853da", + Rule: "Host(`foo.com`)", + Priority: 0, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-route-6f97418635c7e18853da": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "TCP with native Service LB", + paths: []string{"tcp/services.yml", "tcp/with_native_service_lb.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "default-test.route-fdd3e9338e47a45efefc": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-fdd3e9338e47a45efefc", + Rule: "HostSNI(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-test.route-fdd3e9338e47a45efefc": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.1:8000", + Port: "", + }, + }, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "UDP with native Service LB", + paths: []string{"udp/services.yml", "udp/with_native_service_lb.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{ + "default-test.route-0": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-0", + }, + }, + Services: map[string]*dynamic.UDPService{ + "default-test.route-0": { + LoadBalancer: &dynamic.UDPServersLoadBalancer{ + Servers: []dynamic.UDPServer{ + { + Address: "10.10.0.1:8000", + Port: "", + }, + }, + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var k8sObjects []runtime.Object + var crdObjects []runtime.Object + for _, path := range test.paths { + yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path)) + if err != nil { + panic(err) + } + + objects := k8s.MustParseYaml(yamlContent) + for _, obj := range objects { + switch o := obj.(type) { + case *corev1.Service, *corev1.Endpoints, *corev1.Secret: + k8sObjects = append(k8sObjects, o) + case *v1alpha1.IngressRoute: + crdObjects = append(crdObjects, o) + case *v1alpha1.IngressRouteTCP: + crdObjects = append(crdObjects, o) + case *v1alpha1.IngressRouteUDP: + crdObjects = append(crdObjects, o) + case *v1alpha1.Middleware: + crdObjects = append(crdObjects, o) + case *v1alpha1.TraefikService: + crdObjects = append(crdObjects, o) + case *v1alpha1.TLSOption: + crdObjects = append(crdObjects, o) + case *v1alpha1.TLSStore: + crdObjects = append(crdObjects, o) + default: + } + } + } + + kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + crdClient := crdfake.NewSimpleClientset(crdObjects...) + + client := newClientImpl(kubeClient, crdClient) + + stopCh := make(chan struct{}) + + eventCh, err := client.WatchAll([]string{"default", "cross-ns"}, stopCh) + require.NoError(t, err) + + if k8sObjects != nil || crdObjects != nil { + // just wait for the first event + <-eventCh + } + + p := Provider{} + + conf := p.loadConfigurationFromCRD(context.Background(), client) + assert.Equal(t, test.expected, conf) + }) + } +} + func TestCreateBasicAuthCredentials(t *testing.T) { var k8sObjects []runtime.Object var crdObjects []runtime.Object diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go index 24b754c2f..c79b39af4 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_udp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go @@ -120,6 +120,15 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc v1alpha1. return nil, err } + if svc.NativeLB { + address, err := getNativeServiceAddress(*service, *svcPort) + if err != nil { + return nil, fmt.Errorf("getting native Kubernetes Service address: %w", err) + } + + return []dynamic.UDPServer{{Address: address}}, nil + } + var servers []dynamic.UDPServer if service.Spec.Type == corev1.ServiceTypeExternalName { servers = append(servers, dynamic.UDPServer{ diff --git a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroute.go index 4783c24f1..e81dc6723 100644 --- a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroute.go @@ -115,10 +115,14 @@ type LoadBalancerSpec struct { // It allows to configure the transport between Traefik and your servers. // Can only be used on a Kubernetes Service. ServersTransport string `json:"serversTransport,omitempty"` - // Weight defines the weight and should only be specified when Name references a TraefikService object // (and to be precise, one that embeds a Weighted Round Robin). Weight *int `json:"weight,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // Service defines an upstream HTTP service to proxy traffic to. diff --git a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroutetcp.go index 64b6c1872..51ebf95f1 100644 --- a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressroutetcp.go @@ -78,6 +78,11 @@ type ServiceTCP struct { // ProxyProtocol defines the PROXY protocol configuration. // More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // +genclient diff --git a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressrouteudp.go index 4d313ad9c..fed269d22 100644 --- a/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1/ingressrouteudp.go @@ -33,6 +33,11 @@ type ServiceUDP struct { Port intstr.IntOrString `json:"port"` // Weight defines the weight used when balancing requests between multiple Kubernetes Service. Weight *int `json:"weight,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // +genclient diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go index 4783c24f1..e81dc6723 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go @@ -115,10 +115,14 @@ type LoadBalancerSpec struct { // It allows to configure the transport between Traefik and your servers. // Can only be used on a Kubernetes Service. ServersTransport string `json:"serversTransport,omitempty"` - // Weight defines the weight and should only be specified when Name references a TraefikService object // (and to be precise, one that embeds a Weighted Round Robin). Weight *int `json:"weight,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // Service defines an upstream HTTP service to proxy traffic to. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go index 64b6c1872..51ebf95f1 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go @@ -78,6 +78,11 @@ type ServiceTCP struct { // ProxyProtocol defines the PROXY protocol configuration. // More info: https://doc.traefik.io/traefik/v2.9/routing/services/#proxy-protocol ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // +genclient diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go index 4d313ad9c..fed269d22 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go @@ -33,6 +33,11 @@ type ServiceUDP struct { Port intstr.IntOrString `json:"port"` // Weight defines the weight used when balancing requests between multiple Kubernetes Service. Weight *int `json:"weight,omitempty"` + // NativeLB controls, when creating the load-balancer, + // whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + // The Kubernetes Service itself does load-balance to the pods. + // By default, NativeLB is false. + NativeLB bool `json:"nativeLB,omitempty"` } // +genclient diff --git a/pkg/provider/kubernetes/ingress/annotations.go b/pkg/provider/kubernetes/ingress/annotations.go index 25807cdd2..6e624f722 100644 --- a/pkg/provider/kubernetes/ingress/annotations.go +++ b/pkg/provider/kubernetes/ingress/annotations.go @@ -45,6 +45,7 @@ type ServiceIng struct { ServersTransport string `json:"serversTransport,omitempty"` PassHostHeader *bool `json:"passHostHeader"` Sticky *dynamic.Sticky `json:"sticky,omitempty" label:"allowEmpty"` + NativeLB bool `json:"nativeLB,omitempty"` } // SetDefaults sets the default values. diff --git a/pkg/provider/kubernetes/ingress/annotations_test.go b/pkg/provider/kubernetes/ingress/annotations_test.go index 075e76f90..45ef1cf78 100644 --- a/pkg/provider/kubernetes/ingress/annotations_test.go +++ b/pkg/provider/kubernetes/ingress/annotations_test.go @@ -106,6 +106,7 @@ func Test_parseServiceConfig(t *testing.T) { "traefik.ingress.kubernetes.io/service.serversscheme": "protocol", "traefik.ingress.kubernetes.io/service.serverstransport": "foobar@file", "traefik.ingress.kubernetes.io/service.passhostheader": "true", + "traefik.ingress.kubernetes.io/service.nativelb": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.httponly": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.name": "foobar", @@ -125,6 +126,7 @@ func Test_parseServiceConfig(t *testing.T) { ServersScheme: "protocol", ServersTransport: "foobar@file", PassHostHeader: Bool(true), + NativeLB: true, }, }, }, diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_ingress.yml new file mode 100644 index 000000000..f9645ad09 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_ingress.yml @@ -0,0 +1,15 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1beta1 +metadata: + name: "" + namespace: testing + +spec: + rules: + - host: traefik.tchouk + http: + paths: + - path: /bar + backend: + serviceName: service1 + servicePort: 8080 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_service.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_service.yml new file mode 100644 index 000000000..e394301fc --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-service-lb_service.yml @@ -0,0 +1,15 @@ +kind: Service +apiVersion: v1 +metadata: + name: service1 + namespace: testing + annotations: + traefik.ingress.kubernetes.io/service.nativelb: "true" + +spec: + ports: + - port: 8080 + clusterIP: 10.0.0.1 + type: ClusterIP + externalName: traefik.wtf + diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 5605f97b9..0698b860f 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -538,6 +538,21 @@ func (p *Provider) loadService(client Client, namespace string, backend networki if svcConfig.Service.ServersTransport != "" { svc.LoadBalancer.ServersTransport = svcConfig.Service.ServersTransport } + + if svcConfig.Service.NativeLB { + address, err := getNativeServiceAddress(*service, portSpec) + if err != nil { + return nil, fmt.Errorf("getting native Kubernetes Service address: %w", err) + } + + protocol := getProtocol(portSpec, portSpec.Name, svcConfig) + + svc.LoadBalancer.Servers = []dynamic.Server{ + {URL: fmt.Sprintf("%s://%s", protocol, address)}, + } + + return svc, nil + } } if service.Spec.Type == corev1.ServiceTypeExternalName { @@ -587,6 +602,18 @@ func (p *Provider) loadService(client Client, namespace string, backend networki return svc, nil } +func getNativeServiceAddress(service corev1.Service, svcPort corev1.ServicePort) (string, error) { + if service.Spec.ClusterIP == "None" { + return "", fmt.Errorf("no clusterIP on headless service: %s/%s", service.Namespace, service.Name) + } + + if service.Spec.ClusterIP == "" { + return "", fmt.Errorf("no clusterIP found for service: %s/%s", service.Namespace, service.Name) + } + + return net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(svcPort.Port))), nil +} + func getProtocol(portSpec corev1.ServicePort, portName string, svcConfig *ServiceConfig) string { if svcConfig != nil && svcConfig.Service != nil && svcConfig.Service.ServersScheme != "" { return svcConfig.Service.ServersScheme diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index 06a2a62fa..6f4977e61 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -5,6 +5,7 @@ import ( "errors" "math" "os" + "path/filepath" "strings" "testing" @@ -1790,8 +1791,87 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { } } +func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { + testCases := []struct { + desc string + ingressClass string + serverVersion string + expected *dynamic.Configuration + }{ + { + desc: "Ingress with native service lb", + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{ + "testing-traefik-tchouk-bar": { + Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Service: "testing-service1-8080", + }, + }, + Services: map[string]*dynamic.Service{ + "testing-service1-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: Bool(true), + Servers: []dynamic.Server{ + { + URL: "http://10.0.0.1:8080", + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var paths []string + _, err := os.Stat(generateTestFilename("_ingress", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_ingress", test.desc)) + } + _, err = os.Stat(generateTestFilename("_endpoint", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_endpoint", test.desc)) + } + _, err = os.Stat(generateTestFilename("_service", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_service", test.desc)) + } + _, err = os.Stat(generateTestFilename("_secret", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_secret", test.desc)) + } + _, err = os.Stat(generateTestFilename("_ingressclass", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_ingressclass", test.desc)) + } + + serverVersion := test.serverVersion + if serverVersion == "" { + serverVersion = "v1.17" + } + + clientMock := newClientMock(serverVersion, paths...) + + p := Provider{IngressClass: test.ingressClass} + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) + + assert.Equal(t, test.expected, conf) + }) + } +} + func generateTestFilename(suffix, desc string) string { - return "./fixtures/" + strings.ReplaceAll(desc, " ", "-") + suffix + ".yml" + return filepath.Join("fixtures", strings.ReplaceAll(desc, " ", "-")+suffix+".yml") } func TestGetCertificates(t *testing.T) {