Add option to the Ingress provider to disable IngressClass lookup
This commit is contained in:
parent
d046af2e91
commit
8c98234c07
11 changed files with 263 additions and 17 deletions
|
@ -344,6 +344,35 @@ providers:
|
||||||
--providers.kubernetesingress.ingressclass=traefik-internal
|
--providers.kubernetesingress.ingressclass=traefik-internal
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `disableIngressClassLookup`
|
||||||
|
|
||||||
|
_Optional, Default: false_
|
||||||
|
|
||||||
|
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.
|
||||||
|
Furthermore, when this option is set to `true`,
|
||||||
|
Traefik is not able to handle Ingresses with IngressClass references,
|
||||||
|
therefore such Ingresses will be ignored.
|
||||||
|
Please note that annotations are not affected by this option.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
providers:
|
||||||
|
kubernetesIngress:
|
||||||
|
disableIngressClassLookup: true
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[providers.kubernetesIngress]
|
||||||
|
disableIngressClassLookup = true
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--providers.kubernetesingress.disableingressclasslookup=true
|
||||||
|
```
|
||||||
|
|
||||||
### `ingressEndpoint`
|
### `ingressEndpoint`
|
||||||
|
|
||||||
#### `hostname`
|
#### `hostname`
|
||||||
|
|
|
@ -735,6 +735,9 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`--providers.kubernetesingress.certauthfilepath`:
|
`--providers.kubernetesingress.certauthfilepath`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
|
`--providers.kubernetesingress.disableingressclasslookup`:
|
||||||
|
Disables the lookup of IngressClasses. (Default: ```false```)
|
||||||
|
|
||||||
`--providers.kubernetesingress.endpoint`:
|
`--providers.kubernetesingress.endpoint`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
|
||||||
|
|
|
@ -735,6 +735,9 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`:
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLEINGRESSCLASSLOOKUP`:
|
||||||
|
Disables the lookup of IngressClasses. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`:
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@
|
||||||
throttleDuration = "42s"
|
throttleDuration = "42s"
|
||||||
allowEmptyServices = true
|
allowEmptyServices = true
|
||||||
allowExternalNameServices = true
|
allowExternalNameServices = true
|
||||||
|
disableIngressClassLookup = true
|
||||||
[providers.kubernetesIngress.ingressEndpoint]
|
[providers.kubernetesIngress.ingressEndpoint]
|
||||||
ip = "foobar"
|
ip = "foobar"
|
||||||
hostname = "foobar"
|
hostname = "foobar"
|
||||||
|
|
|
@ -117,6 +117,7 @@ providers:
|
||||||
throttleDuration: 42s
|
throttleDuration: 42s
|
||||||
allowEmptyServices: true
|
allowEmptyServices: true
|
||||||
allowExternalNameServices: true
|
allowExternalNameServices: true
|
||||||
|
disableIngressClassLookup: true
|
||||||
ingressEndpoint:
|
ingressEndpoint:
|
||||||
ip: foobar
|
ip: foobar
|
||||||
hostname: foobar
|
hostname: foobar
|
||||||
|
|
18
integration/fixtures/k8s_ingressclass_disabled.toml
Normal file
18
integration/fixtures/k8s_ingressclass_disabled.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
noColor = true
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[providers.kubernetesIngress]
|
||||||
|
ingressClass = "traefik-keep"
|
||||||
|
disableIngressClassLookup = true
|
|
@ -128,6 +128,17 @@ func (s *K8sSuite) TestIngressclass(c *check.C) {
|
||||||
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
|
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *K8sSuite) TestDisableIngressclassLookup(c *check.C) {
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass_disabled.toml"))
|
||||||
|
defer display(c)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer s.killCmd(cmd)
|
||||||
|
|
||||||
|
testConfiguration(c, "testdata/rawdata-ingressclass-disabled.json", "8080")
|
||||||
|
}
|
||||||
|
|
||||||
func testConfiguration(c *check.C, path, apiPort string) {
|
func testConfiguration(c *check.C, path, apiPort string) {
|
||||||
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
|
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
74
integration/testdata/rawdata-ingressclass-disabled.json
vendored
Normal file
74
integration/testdata/rawdata-ingressclass-disabled.json
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"routers": {
|
||||||
|
"api@internal": {
|
||||||
|
"entryPoints": [
|
||||||
|
"traefik"
|
||||||
|
],
|
||||||
|
"service": "api@internal",
|
||||||
|
"rule": "PathPrefix(`/api`)",
|
||||||
|
"priority": 2147483646,
|
||||||
|
"status": "enabled",
|
||||||
|
"using": [
|
||||||
|
"traefik"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dashboard@internal": {
|
||||||
|
"entryPoints": [
|
||||||
|
"traefik"
|
||||||
|
],
|
||||||
|
"middlewares": [
|
||||||
|
"dashboard_redirect@internal",
|
||||||
|
"dashboard_stripprefix@internal"
|
||||||
|
],
|
||||||
|
"service": "dashboard@internal",
|
||||||
|
"rule": "PathPrefix(`/`)",
|
||||||
|
"priority": 2147483645,
|
||||||
|
"status": "enabled",
|
||||||
|
"using": [
|
||||||
|
"traefik"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"middlewares": {
|
||||||
|
"dashboard_redirect@internal": {
|
||||||
|
"redirectRegex": {
|
||||||
|
"regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$",
|
||||||
|
"replacement": "${1}/dashboard/",
|
||||||
|
"permanent": true
|
||||||
|
},
|
||||||
|
"status": "enabled",
|
||||||
|
"usedBy": [
|
||||||
|
"dashboard@internal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dashboard_stripprefix@internal": {
|
||||||
|
"stripPrefix": {
|
||||||
|
"prefixes": [
|
||||||
|
"/dashboard/",
|
||||||
|
"/dashboard"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "enabled",
|
||||||
|
"usedBy": [
|
||||||
|
"dashboard@internal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"api@internal": {
|
||||||
|
"status": "enabled",
|
||||||
|
"usedBy": [
|
||||||
|
"api@internal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dashboard@internal": {
|
||||||
|
"status": "enabled",
|
||||||
|
"usedBy": [
|
||||||
|
"dashboard@internal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noop@internal": {
|
||||||
|
"status": "enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,15 +50,16 @@ type Client interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientWrapper struct {
|
type clientWrapper struct {
|
||||||
clientset kubernetes.Interface
|
clientset kubernetes.Interface
|
||||||
factoriesKube map[string]informers.SharedInformerFactory
|
factoriesKube map[string]informers.SharedInformerFactory
|
||||||
factoriesSecret map[string]informers.SharedInformerFactory
|
factoriesSecret map[string]informers.SharedInformerFactory
|
||||||
factoriesIngress map[string]informers.SharedInformerFactory
|
factoriesIngress map[string]informers.SharedInformerFactory
|
||||||
clusterFactory informers.SharedInformerFactory
|
clusterFactory informers.SharedInformerFactory
|
||||||
ingressLabelSelector string
|
ingressLabelSelector string
|
||||||
isNamespaceAll bool
|
isNamespaceAll bool
|
||||||
watchedNamespaces []string
|
disableIngressClassInformer bool
|
||||||
serverVersion *version.Version
|
watchedNamespaces []string
|
||||||
|
serverVersion *version.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
// newInClusterClient returns a new Provider client that is expected to run
|
// newInClusterClient returns a new Provider client that is expected to run
|
||||||
|
@ -214,7 +215,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if supportsIngressClass(serverVersion) {
|
if !c.disableIngressClassInformer && supportsIngressClass(serverVersion) {
|
||||||
c.clusterFactory = informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
|
c.clusterFactory = informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
|
||||||
|
|
||||||
if supportsNetworkingV1Ingress(serverVersion) {
|
if supportsNetworkingV1Ingress(serverVersion) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ type Provider struct {
|
||||||
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,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 creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,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"`
|
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"`
|
||||||
lastConfiguration safe.Safe
|
lastConfiguration safe.Safe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +92,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cl.ingressLabelSelector = p.LabelSelector
|
cl.ingressLabelSelector = p.LabelSelector
|
||||||
|
cl.disableIngressClassInformer = p.DisableIngressClassLookup
|
||||||
return cl, nil
|
return cl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +197,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||||
|
|
||||||
var ingressClasses []*networkingv1.IngressClass
|
var ingressClasses []*networkingv1.IngressClass
|
||||||
|
|
||||||
if supportsIngressClass(serverVersion) {
|
if !p.DisableIngressClassLookup && supportsIngressClass(serverVersion) {
|
||||||
ics, err := client.GetIngressClasses()
|
ics, err := client.GetIngressClasses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Ctx(ctx).Warn().Err(err).Msg("Failed to list ingress classes")
|
log.Ctx(ctx).Warn().Err(err).Msg("Failed to list ingress classes")
|
||||||
|
|
|
@ -26,11 +26,12 @@ func Bool(v bool) *bool { return &v }
|
||||||
|
|
||||||
func TestLoadConfigurationFromIngresses(t *testing.T) {
|
func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
ingressClass string
|
ingressClass string
|
||||||
serverVersion string
|
serverVersion string
|
||||||
expected *dynamic.Configuration
|
expected *dynamic.Configuration
|
||||||
allowEmptyServices bool
|
allowEmptyServices bool
|
||||||
|
disableIngressClassLookup bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Empty ingresses",
|
desc: "Empty ingresses",
|
||||||
|
@ -1392,6 +1393,40 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Duplicate test case with the same fixture as the one above, but with the disableIngressClassLookup option to true.
|
||||||
|
// Showing that disabling the ingressClass discovery still allow the discovery of ingresses with ingress annotation.
|
||||||
|
desc: "v18 Ingress with ingress annotation",
|
||||||
|
serverVersion: "v1.18",
|
||||||
|
disableIngressClassLookup: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"testing-bar": {
|
||||||
|
Rule: "PathPrefix(`/bar`)",
|
||||||
|
Service: "testing-service1-80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"testing-service1-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
||||||
|
},
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "v18 Ingress with ingressClasses filter",
|
desc: "v18 Ingress with ingressClasses filter",
|
||||||
serverVersion: "v1.18",
|
serverVersion: "v1.18",
|
||||||
|
@ -1424,6 +1459,22 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Duplicate test case with the same fixture as the one above, but with the disableIngressClassLookup option to true.
|
||||||
|
// Showing that disabling the ingressClass discovery avoid discovering Ingresses with an IngressClass.
|
||||||
|
desc: "v18 Ingress with ingressClasses filter",
|
||||||
|
serverVersion: "v1.18",
|
||||||
|
ingressClass: "traefik-lb2",
|
||||||
|
disableIngressClassLookup: true,
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "v19 Ingress with prefix pathType",
|
desc: "v19 Ingress with prefix pathType",
|
||||||
serverVersion: "v1.19",
|
serverVersion: "v1.19",
|
||||||
|
@ -1610,6 +1661,39 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Duplicate test case with the same fixture as the one above, but with the disableIngressClassLookup option to true.
|
||||||
|
// Showing that disabling the ingressClass discovery still allow the discovery of ingresses with ingress annotation.
|
||||||
|
desc: "v19 Ingress with ingress annotation",
|
||||||
|
serverVersion: "v1.19",
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Routers: map[string]*dynamic.Router{
|
||||||
|
"testing-bar": {
|
||||||
|
Rule: "PathPrefix(`/bar`)",
|
||||||
|
Service: "testing-service1-80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.Service{
|
||||||
|
"testing-service1-80": {
|
||||||
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
|
PassHostHeader: Bool(true),
|
||||||
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
||||||
|
},
|
||||||
|
Servers: []dynamic.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "v19 Ingress with ingressClass",
|
desc: "v19 Ingress with ingressClass",
|
||||||
serverVersion: "v1.19",
|
serverVersion: "v1.19",
|
||||||
|
@ -1641,6 +1725,21 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Duplicate test case with the same fixture as the one above, but with the disableIngressClassLookup option to true.
|
||||||
|
// Showing that disabling the ingressClass discovery avoid discovering Ingresses with an IngressClass.
|
||||||
|
desc: "v19 Ingress with ingressClass",
|
||||||
|
disableIngressClassLookup: true,
|
||||||
|
serverVersion: "v1.19",
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "v19 Ingress with ingressClassv1",
|
desc: "v19 Ingress with ingressClassv1",
|
||||||
serverVersion: "v1.19",
|
serverVersion: "v1.19",
|
||||||
|
@ -1783,7 +1882,11 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
clientMock := newClientMock(serverVersion, paths...)
|
clientMock := newClientMock(serverVersion, paths...)
|
||||||
p := Provider{IngressClass: test.ingressClass, AllowEmptyServices: test.allowEmptyServices}
|
p := Provider{
|
||||||
|
IngressClass: test.ingressClass,
|
||||||
|
AllowEmptyServices: test.allowEmptyServices,
|
||||||
|
DisableIngressClassLookup: test.disableIngressClassLookup,
|
||||||
|
}
|
||||||
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
|
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
|
|
Loading…
Reference in a new issue