From a50345bf8de568218db8cad5b9b38b36b23b002c Mon Sep 17 00:00:00 2001 From: Romain Date: Thu, 1 Aug 2024 15:50:04 +0200 Subject: [PATCH] Allow to disable Kubernetes cluster scope resources discovery Co-authored-by: Kevin Pollet --- docs/content/migration/v2.md | 2 +- docs/content/migration/v3.md | 28 +++++- docs/content/providers/kubernetes-ingress.md | 32 +++++++ .../reference/static-configuration/cli-ref.md | 8 +- .../reference/static-configuration/env-ref.md | 8 +- .../reference/static-configuration/file.toml | 2 + .../reference/static-configuration/file.yaml | 2 + pkg/cli/deprecation.go | 38 ++++++-- pkg/provider/kubernetes/crd/client.go | 35 ++++--- pkg/provider/kubernetes/crd/kubernetes.go | 24 ++--- .../kubernetes/crd/kubernetes_http.go | 28 +++--- pkg/provider/kubernetes/crd/kubernetes_tcp.go | 4 + .../kubernetes/crd/kubernetes_test.go | 96 ++++++++++++++++++- pkg/provider/kubernetes/crd/kubernetes_udp.go | 4 + pkg/provider/kubernetes/ingress/client.go | 56 +++++------ ...th-node-port-lb-cluster-scope-disabled.yml | 45 +++++++++ pkg/provider/kubernetes/ingress/kubernetes.go | 14 ++- .../kubernetes/ingress/kubernetes_test.go | 76 +++------------ 18 files changed, 349 insertions(+), 153 deletions(-) create mode 100644 pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index aa803fc81..0e56a8fee 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -455,7 +455,7 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](. In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and [route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces. -Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated. +Therefore, the RBAC and CRD definitions must be updated. ## v2.6.0 to v2.6.1 diff --git a/docs/content/migration/v3.md b/docs/content/migration/v3.md index 4529a89a4..01a237173 100644 --- a/docs/content/migration/v3.md +++ b/docs/content/migration/v3.md @@ -10,9 +10,11 @@ description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions ### Kubernetes Provider RBACs Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses. +It also brings NodePort load-balancing which requires Nodes resources lookup. -Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) provider RBACs), -the `endpoints` right has to be removed and the following `endpointslices` right has to be added. +Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs): + +- the `endpoints` right has to be removed and the following `endpointslices` right has to be added: ```yaml ... @@ -26,6 +28,21 @@ the `endpoints` right has to be removed and the following `endpointslices` right ... ``` +- the `nodes` right has to be added: + +```yaml + ... + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + ... +``` + #### Gateway API: KubernetesGateway Provider In v3.1, the KubernetesGateway Provider is no longer an experimental feature. @@ -51,3 +68,10 @@ It can be enabled without the associated `experimental.kubernetesgateway` option The `kubernetesgateway` option should be removed from the experimental section of the static configuration. To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md). + +## v3.1.0 to v3.1.1 + +### IngressClass Lookup + +The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1.1, and will be removed in the next major version. +Please use the `disableClusterScopeResources` option instead to avoid cluster scope resources discovery (IngressClass, Nodes). diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index 2da41808d..20e7457c9 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -287,6 +287,11 @@ providers: _Optional, Default: false_ +??? warning "Deprecated" + + The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1, and will be removed in the next major version. + Please use the `disableClusterScopeResources` option instead. + If the parameter is set to `true`, Traefik will not discover IngressClasses in the cluster. By doing so, it alleviates the requirement of giving Traefik the rights to look IngressClasses up. @@ -312,6 +317,33 @@ providers: --providers.kubernetesingress.disableingressclasslookup=true ``` +### `disableClusterScopeResources` + +_Optional, Default: false_ + +When this parameter is set to `true`, +Traefik will not discover cluster scope resources (`IngressClass` and `Nodes`). +By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources. +Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option). +This will also prevent from using the `NodePortLB` options on services. + +```yaml tab="File (YAML)" +providers: + kubernetesIngress: + disableClusterScopeResources: true + # ... +``` + +```toml tab="File (TOML)" +[providers.kubernetesIngress] + disableClusterScopeResources = true + # ... +``` + +```bash tab="CLI" +--providers.kubernetesingress.disableClusterScopeResources=true +``` + ### `ingressEndpoint` #### `hostname` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index a261ffa27..cd245706d 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -726,6 +726,9 @@ Allow ExternalName services. (Default: ```false```) `--providers.kubernetescrd.certauthfilepath`: Kubernetes certificate authority file path (not needed for in-cluster client). +`--providers.kubernetescrd.disableclusterscoperesources`: +Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```) + `--providers.kubernetescrd.endpoint`: Kubernetes server endpoint (required for external cluster client). @@ -798,8 +801,11 @@ Allow ExternalName services. (Default: ```false```) `--providers.kubernetesingress.certauthfilepath`: Kubernetes certificate authority file path (not needed for in-cluster client). +`--providers.kubernetesingress.disableclusterscoperesources`: +Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```) + `--providers.kubernetesingress.disableingressclasslookup`: -Disables the lookup of IngressClasses. (Default: ```false```) +Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```) `--providers.kubernetesingress.endpoint`: Kubernetes server endpoint (required for external cluster client). diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 43882e52f..02873474b 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -726,6 +726,9 @@ Allow ExternalName services. (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`: Kubernetes certificate authority file path (not needed for in-cluster client). +`TRAEFIK_PROVIDERS_KUBERNETESCRD_DISABLECLUSTERSCOPERESOURCES`: +Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```) + `TRAEFIK_PROVIDERS_KUBERNETESCRD_ENDPOINT`: Kubernetes server endpoint (required for external cluster client). @@ -798,8 +801,11 @@ Allow ExternalName services. (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`: Kubernetes certificate authority file path (not needed for in-cluster client). +`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLECLUSTERSCOPERESOURCES`: +Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```) + `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLEINGRESSCLASSLOOKUP`: -Disables the lookup of IngressClasses. (Default: ```false```) +Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`: Kubernetes server endpoint (required for external cluster client). diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index f171b9e86..8a033d2f2 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -124,6 +124,7 @@ allowEmptyServices = true allowExternalNameServices = true disableIngressClassLookup = true + disableClusterScopeResources = true nativeLBByDefault = true [providers.kubernetesIngress.ingressEndpoint] ip = "foobar" @@ -141,6 +142,7 @@ throttleDuration = "42s" allowEmptyServices = true nativeLBByDefault = true + disableClusterScopeResources = true [providers.kubernetesGateway] endpoint = "foobar" token = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 1c053dfaf..d8a9b1362 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -141,6 +141,7 @@ providers: allowEmptyServices: true allowExternalNameServices: true disableIngressClassLookup: true + disableClusterScopeResources: true nativeLBByDefault: true kubernetesCRD: endpoint: foobar @@ -156,6 +157,7 @@ providers: throttleDuration: 42s allowEmptyServices: true nativeLBByDefault: true + disableClusterScopeResources: true kubernetesGateway: endpoint: foobar token: foobar diff --git a/pkg/cli/deprecation.go b/pkg/cli/deprecation.go index 60bb0adac..0e7af5d05 100644 --- a/pkg/cli/deprecation.go +++ b/pkg/cli/deprecation.go @@ -204,16 +204,17 @@ func (c *configuration) deprecationNotice(logger zerolog.Logger) bool { } type providers struct { - Docker *docker `json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty"` - Swarm *swarm `json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty"` - Consul *consul `json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty"` - ConsulCatalog *consulCatalog `json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty"` - Nomad *nomad `json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"` - Marathon map[string]any `json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" label:"allowEmpty" file:"allowEmpty"` - Rancher map[string]any `json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" label:"allowEmpty" file:"allowEmpty"` - ETCD *etcd `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"` - Redis *redis `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"` - HTTP *http `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"` + Docker *docker `json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty"` + Swarm *swarm `json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty"` + Consul *consul `json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty"` + ConsulCatalog *consulCatalog `json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty"` + Nomad *nomad `json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"` + Marathon map[string]any `json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" label:"allowEmpty" file:"allowEmpty"` + Rancher map[string]any `json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" label:"allowEmpty" file:"allowEmpty"` + ETCD *etcd `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"` + Redis *redis `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"` + HTTP *http `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"` + KubernetesIngress *ingress `json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" file:"allowEmpty"` } func (p *providers) deprecationNotice(logger zerolog.Logger) bool { @@ -243,6 +244,7 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool { etcdIncompatible := p.ETCD.deprecationNotice(logger) redisIncompatible := p.Redis.deprecationNotice(logger) httpIncompatible := p.HTTP.deprecationNotice(logger) + p.KubernetesIngress.deprecationNotice(logger) return incompatible || dockerIncompatible || consulIncompatible || @@ -457,6 +459,22 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool { return incompatible } +type ingress struct { + DisableIngressClassLookup *bool `json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty"` +} + +func (i *ingress) deprecationNotice(logger zerolog.Logger) { + if i == nil { + return + } + + if i.DisableIngressClassLookup != nil { + logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." + + "Please use the `disableClusterScopeResources` option instead." + + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.1/migration/v3/#ingressclasslookup") + } +} + type experimental struct { HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"` KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"` diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go index a52f76497..f82852e9f 100644 --- a/pkg/provider/kubernetes/crd/client.go +++ b/pkg/provider/kubernetes/crd/client.go @@ -57,10 +57,12 @@ type clientWrapper struct { csCrd traefikclientset.Interface csKube kclientset.Interface - factoryClusterScope kinformers.SharedInformerFactory - factoriesCrd map[string]traefikinformers.SharedInformerFactory - factoriesKube map[string]kinformers.SharedInformerFactory - factoriesSecret map[string]kinformers.SharedInformerFactory + clusterScopeFactory kinformers.SharedInformerFactory + disableClusterScopeInformer bool + + factoriesCrd map[string]traefikinformers.SharedInformerFactory + factoriesKube map[string]kinformers.SharedInformerFactory + factoriesSecret map[string]kinformers.SharedInformerFactory labelSelector string @@ -237,18 +239,11 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< c.factoriesSecret[ns] = factorySecret } - c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod) - _, err := c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - for _, ns := range namespaces { c.factoriesCrd[ns].Start(stopCh) c.factoriesKube[ns].Start(stopCh) c.factoriesSecret[ns].Start(stopCh) } - c.factoryClusterScope.Start(stopCh) for _, ns := range namespaces { for t, ok := range c.factoriesCrd[ns].WaitForCacheSync(stopCh) { @@ -270,9 +265,19 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } } - for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String()) + if !c.disableClusterScopeInformer { + c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod) + _, err := c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + + c.clusterScopeFactory.Start(stopCh) + + for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) { + if !ok { + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String()) + } } } @@ -474,7 +479,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, } func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) { - nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything()) + nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything()) exist, err := translateNotFoundError(err) return nodes, exist, err } diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 89efa3502..752ba88a7 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -50,17 +50,18 @@ const ( // Provider holds configurations of the provider. type Provider struct { - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` - Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` - AllowCrossNamespace bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"` - AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"` - LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` - IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` - ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` - AllowEmptyServices bool `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"` - NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"` + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Token types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` + Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` + AllowCrossNamespace bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"` + AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"` + LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` + IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` + ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` + AllowEmptyServices bool `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"` + NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"` + DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"` lastConfiguration safe.Safe @@ -112,6 +113,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { } client.labelSelector = p.LabelSelector + client.disableClusterScopeInformer = p.DisableClusterScopeResources return client, nil } diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 85c9e0ab5..d81a02058 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -51,11 +51,12 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli } cb := configBuilder{ - client: client, - allowCrossNamespace: p.AllowCrossNamespace, - allowExternalNameServices: p.AllowExternalNameServices, - allowEmptyServices: p.AllowEmptyServices, - NativeLBByDefault: p.NativeLBByDefault, + client: client, + allowCrossNamespace: p.AllowCrossNamespace, + allowExternalNameServices: p.AllowExternalNameServices, + allowEmptyServices: p.AllowEmptyServices, + nativeLBByDefault: p.NativeLBByDefault, + disableClusterScopeResources: p.DisableClusterScopeResources, } for _, route := range ingressRoute.Spec.Routes { @@ -199,11 +200,12 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str } type configBuilder struct { - client Client - allowCrossNamespace bool - allowExternalNameServices bool - allowEmptyServices bool - NativeLBByDefault bool + client Client + allowCrossNamespace bool + allowExternalNameServices bool + allowEmptyServices bool + nativeLBByDefault bool + disableClusterScopeResources bool } // buildTraefikService creates the configuration for the traefik service defined in tService, @@ -428,7 +430,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, hostPort)}}, nil } - nativeLB := c.NativeLBByDefault + nativeLB := c.nativeLBByDefault if svc.NativeLB != nil { nativeLB = *svc.NativeLB } @@ -448,6 +450,10 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L var servers []dynamic.Server if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB { + if c.disableClusterScopeResources { + return nil, errors.New("nodes lookup is disabled") + } + nodes, nodesExists, nodesErr := c.client.GetNodes() if nodesErr != nil { return nil, nodesErr diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 5cc35343d..63795c0a3 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -239,6 +239,10 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc traefikv1 var servers []dynamic.TCPServer if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB { + if p.DisableClusterScopeResources { + return nil, errors.New("nodes lookup is disabled") + } + nodes, nodesExists, nodesErr := client.GetNodes() if nodesErr != nil { return nil, nodesErr diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 8f9f5c2c5..49b7bc686 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -7446,10 +7446,10 @@ func TestNativeLB(t *testing.T) { func TestNodePortLB(t *testing.T) { testCases := []struct { - desc string - ingressClass string - paths []string - expected *dynamic.Configuration + desc string + paths []string + disableClusterScope bool + expected *dynamic.Configuration }{ { desc: "Empty", @@ -7594,6 +7594,90 @@ func TestNodePortLB(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "HTTP with node port LB, cluster scope resources disabled", + paths: []string{"services.yml", "with_node_port_lb.yml"}, + disableClusterScope: true, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + 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: "TCP with native Service LB, cluster scope resources disabled", + paths: []string{"tcp/services.yml", "tcp/with_node_port_service_lb.yml"}, + disableClusterScope: true, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + 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{}, + }, + 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: "UDP with native Service LB, cluster scope resources disabled", + paths: []string{"udp/services.yml", "udp/with_node_port_service_lb.yml"}, + disableClusterScope: true, + 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{}, + }, + TCP: &dynamic.TCPConfiguration{ + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + 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{}, + }, + }, } for _, test := range testCases { @@ -7617,7 +7701,9 @@ func TestNodePortLB(t *testing.T) { <-eventCh } - p := Provider{} + p := Provider{ + DisableClusterScopeResources: test.disableClusterScope, + } conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go index 8ab7a3838..5b0e01e78 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_udp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go @@ -123,6 +123,10 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc traefikv1 var servers []dynamic.UDPServer if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB { + if p.DisableClusterScopeResources { + return nil, errors.New("nodes lookup is disabled") + } + nodes, nodesExists, nodesErr := client.GetNodes() if nodesErr != nil { return nil, nodesErr diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go index 0e16aea9e..7eaff4307 100644 --- a/pkg/provider/kubernetes/ingress/client.go +++ b/pkg/provider/kubernetes/ingress/client.go @@ -49,14 +49,14 @@ type Client interface { type clientWrapper struct { clientset kclientset.Interface - factoryClusterScope kinformers.SharedInformerFactory + clusterScopeFactory kinformers.SharedInformerFactory factoriesKube map[string]kinformers.SharedInformerFactory factoriesSecret map[string]kinformers.SharedInformerFactory factoriesIngress map[string]kinformers.SharedInformerFactory - clusterFactory kinformers.SharedInformerFactory ingressLabelSelector string isNamespaceAll bool - disableIngressClassInformer bool + disableIngressClassInformer bool // Deprecated. + disableClusterScopeInformer bool watchedNamespaces []string serverVersion *version.Version } @@ -201,58 +201,52 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< c.factoriesSecret[ns] = factorySecret } - c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod) - _, err = c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - for _, ns := range namespaces { c.factoriesIngress[ns].Start(stopCh) c.factoriesKube[ns].Start(stopCh) c.factoriesSecret[ns].Start(stopCh) } - c.factoryClusterScope.Start(stopCh) for _, ns := range namespaces { - for typ, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) { + for t, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) { if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns) + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) } } - for typ, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) { + for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) { if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns) + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) } } - for typ, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) { + for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) { if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns) + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) } } } - for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String()) - } - } + if !c.disableIngressClassInformer || !c.disableClusterScopeInformer { + c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod) - if !c.disableIngressClassInformer { - c.clusterFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod) - - _, err = c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) + _, err = c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } - c.clusterFactory.Start(stopCh) + if !c.disableClusterScopeInformer { + _, err = c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + } - for typ, ok := range c.clusterFactory.WaitForCacheSync(stopCh) { + c.clusterScopeFactory.Start(stopCh) + + for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) { if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", typ) + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String()) } } } @@ -370,18 +364,18 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, } func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) { - nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything()) + nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything()) exist, err := translateNotFoundError(err) return nodes, exist, err } func (c *clientWrapper) GetIngressClasses() ([]*netv1.IngressClass, error) { - if c.clusterFactory == nil { + if c.clusterScopeFactory == nil { return nil, errors.New("cluster factory not loaded") } var ics []*netv1.IngressClass - ingressClasses, err := c.clusterFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything()) + ingressClasses, err := c.clusterScopeFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything()) if err != nil { return nil, err } diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml new file mode 100644 index 000000000..46e360bf6 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml @@ -0,0 +1,45 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: "" + namespace: testing + +spec: + rules: + - host: traefik.tchouk + http: + paths: + - path: /bar + backend: + service: + name: service1 + port: + number: 8080 + pathType: Prefix + +--- +kind: Service +apiVersion: v1 +metadata: + name: service1 + namespace: testing + annotations: + traefik.ingress.kubernetes.io/service.nodeportlb: "true" + +spec: + ports: + - port: 8080 + nodePort: 32456 + clusterIP: 10.0.0.1 + type: NodePort + externalName: traefik.wtf + +--- +kind: Node +apiVersion: v1 +metadata: + name: traefik-node +status: + addresses: + - type: InternalIP + address: 172.16.4.4 diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 7ae9c6b8b..490271b8c 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -51,8 +51,10 @@ type Provider struct { ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"` AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"` - DisableIngressClassLookup bool `description:"Disables the lookup of IngressClasses." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"` - NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"` + // Deprecated: please use DisableClusterScopeResources. + DisableIngressClassLookup bool `description:"Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources)." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"` + DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"` + NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"` lastConfiguration safe.Safe @@ -114,7 +116,8 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { } cl.ingressLabelSelector = p.LabelSelector - cl.disableIngressClassInformer = p.DisableIngressClassLookup + cl.disableIngressClassInformer = p.DisableIngressClassLookup || p.DisableClusterScopeResources + cl.disableClusterScopeInformer = p.DisableClusterScopeResources return cl, nil } @@ -212,7 +215,6 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, }, - TCP: &dynamic.TCPConfiguration{}, } var ingressClasses []*netv1.IngressClass @@ -591,6 +593,10 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In } if svcConfig.Service.NodePortLB && service.Spec.Type == corev1.ServiceTypeNodePort { + if p.DisableClusterScopeResources { + return nil, errors.New("nodes lookup is disabled") + } + nodes, nodesExists, nodesErr := client.GetNodes() if nodesErr != nil { return nil, nodesErr diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index c7c5a67eb..5a4e3dd2b 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -35,7 +35,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Empty ingresses", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, @@ -46,7 +45,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress one rule host only", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, @@ -57,7 +55,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with a basic rule on one path", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -90,7 +87,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with annotations", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -148,7 +144,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with two different rules with one path", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -185,7 +180,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with conflicting routers on host", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -222,7 +216,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with conflicting routers on path", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -259,7 +252,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress one rule with two paths", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -296,7 +288,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress one rule with one path and one host", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -329,7 +320,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with one host without path", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -359,7 +349,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress one rule with one host and two paths", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -396,7 +385,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress Two rules with one host and one path", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -433,7 +421,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with two services", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -487,7 +474,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with one service without endpoints subset", allowEmptyServices: true, expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -512,7 +498,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with one service without endpoint", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -523,7 +508,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Single Service Ingress (without any rules)", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -558,7 +542,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with port value in backend and no pod replica", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -591,7 +574,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with port name in backend and no pod replica", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -624,7 +606,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with port name in backend and 2 pod replica", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -657,7 +638,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with two paths using same service and different port name", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -710,7 +690,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with a named port matching subset of service pods", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -743,7 +722,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "2 ingresses in different namespace with same service name", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -796,7 +774,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with unknown service port name", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -807,7 +784,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with unknown service port", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -818,7 +794,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with port invalid for one service", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -848,7 +823,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "TLS support", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -889,7 +863,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with a basic rule on one path with https (port == 443)", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -922,7 +895,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with a basic rule on one path with https (portname == https)", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -955,7 +927,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with a basic rule on one path with https (portname starts with https)", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, @@ -989,7 +960,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Double Single Service Ingress", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1024,7 +994,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with default traefik ingressClass", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1054,7 +1023,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress without provider traefik ingressClass and unknown annotation", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1066,7 +1034,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with non matching provider traefik ingressClass and annotation", ingressClass: "tchouk", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1078,7 +1045,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with ingressClass without annotation", ingressClass: "tchouk", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1090,7 +1056,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with ingressClass without annotation", ingressClass: "toto", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1101,7 +1066,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with wildcard host", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1133,7 +1097,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with multiple ingressClasses", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1168,7 +1131,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with ingressClasses filter", ingressClass: "traefik-lb2", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1198,7 +1160,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with prefix pathType", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1228,7 +1189,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with empty pathType", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1258,7 +1218,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with exact pathType", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1288,7 +1247,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with implementationSpecific pathType", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1318,7 +1276,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with ingress annotation", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1351,7 +1308,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with ingress annotation", disableIngressClassLookup: true, expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1381,7 +1337,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with ingressClass", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1414,7 +1369,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { desc: "Ingress with ingressClass", disableIngressClassLookup: true, expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1425,7 +1379,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with named port", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1455,7 +1408,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with missing ingressClass", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1466,7 +1418,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { { desc: "Ingress with defaultbackend", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1524,7 +1475,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { { desc: "Ingress with service with externalName", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{}, @@ -1536,7 +1486,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { desc: "Ingress with service with externalName enabled", allowExternalNameServices: true, expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1566,7 +1515,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { { desc: "Ingress with IPv6 endpoints", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1597,7 +1545,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { desc: "Ingress with IPv6 endpoints externalname enabled", allowExternalNameServices: true, expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1650,7 +1597,6 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { { 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{ @@ -1693,14 +1639,13 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { testCases := []struct { - desc string - ingressClass string - expected *dynamic.Configuration + desc string + clusterScopeDisabled bool + expected *dynamic.Configuration }{ { desc: "Ingress with node port lb", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{ @@ -1725,6 +1670,17 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { }, }, }, + { + desc: "Ingress with node port lb cluster scope disabled", + clusterScopeDisabled: true, + expected: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, } for _, test := range testCases { @@ -1733,7 +1689,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { clientMock := newClientMock(generateTestFilename(test.desc)) - p := Provider{IngressClass: test.ingressClass} + p := Provider{DisableClusterScopeResources: test.clusterScopeDisabled} conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) @@ -1927,7 +1883,6 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { { 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{ @@ -1955,7 +1910,6 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { { desc: "Ingress with native lb by default", expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, HTTP: &dynamic.HTTPConfiguration{ Middlewares: map[string]*dynamic.Middleware{}, Routers: map[string]*dynamic.Router{