add ServersTransport on services

This commit is contained in:
Julien Salleyron 2020-09-11 15:40:03 +02:00 committed by GitHub
parent 6075f7e8fd
commit 76f42a3013
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 2359 additions and 242 deletions

View file

@ -213,7 +213,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
accessLog := setupAccessLog(staticConfiguration.AccessLog)
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry)
roundTripperManager := service.NewRoundTripperManager()
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager)
client, plgs, devPlugin, err := initPlugins(staticConfiguration)
if err != nil {
@ -259,6 +260,10 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
})
watcher.AddListener(func(conf dynamic.Configuration) {
roundTripperManager.Update(conf.HTTP.ServersTransports)
})
watcher.AddListener(switchRouter(routerFactory, acmeProviders, serverEntryPointsTCP, serverEntryPointsUDP, aviator))
watcher.AddListener(func(conf dynamic.Configuration) {

View file

@ -57,7 +57,7 @@ Previous versions of Traefik used a [KV store](https://docs.traefik.io/v1.7/conf
If you require LetsEncrypt with HA in a kubernetes environment, we recommend using [TraefikEE](https://containo.us/traefikee/) where distributed LetsEncrypt is a supported feature.
If you are wanting to continue to run Traefik Community Edition, LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html).
If you want to continue to run Traefik Community Edition, LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html).
When using Cert-Manager to manage certificates, it will create secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls).
When using the Traefik Kubernetes CRD Provider, unfortunately Cert-Manager cannot interface directly with the CRDs _yet_, but this is being worked on by our team.
A workaround is to enable the [Kubernetes Ingress provider](./kubernetes-ingress.md) to allow Cert-Manager to create ingress objects to complete the challenges.

View file

@ -38,6 +38,7 @@
[http.services.Service01]
[http.services.Service01.loadBalancer]
passHostHeader = true
serversTransport = "foobar"
[http.services.Service01.loadBalancer.sticky]
[http.services.Service01.loadBalancer.sticky.cookie]
name = "foobar"
@ -264,6 +265,41 @@
[http.middlewares.Middleware22]
[http.middlewares.Middleware22.stripPrefixRegex]
regex = ["foobar", "foobar"]
[http.serversTransports]
[http.serversTransports.ServersTransport0]
serverName = "foobar"
insecureSkipVerify = true
rootCAs = ["foobar", "foobar"]
maxIdleConnsPerHost = 42
[[http.serversTransports.ServersTransport0.certificates]]
certFile = "foobar"
keyFile = "foobar"
[[http.serversTransports.ServersTransport0.certificates]]
certFile = "foobar"
keyFile = "foobar"
[http.serversTransports.ServersTransport0.forwardingTimeouts]
dialTimeout = "42s"
responseHeaderTimeout = "42s"
idleConnTimeout = "42s"
[http.serversTransports.ServersTransport1]
serverName = "foobar"
insecureSkipVerify = true
rootCAs = ["foobar", "foobar"]
maxIdleConnsPerHost = 42
[[http.serversTransports.ServersTransport1.certificates]]
certFile = "foobar"
keyFile = "foobar"
[[http.serversTransports.ServersTransport1.certificates]]
certFile = "foobar"
keyFile = "foobar"
[http.serversTransports.ServersTransport1.forwardingTimeouts]
dialTimeout = "42s"
responseHeaderTimeout = "42s"
idleConnTimeout = "42s"
[tcp]
[tcp.routers]

View file

@ -70,6 +70,7 @@ http:
passHostHeader: true
responseForwarding:
flushInterval: foobar
serversTransport: foobar
Service02:
mirroring:
service: foobar
@ -301,6 +302,39 @@ http:
regex:
- foobar
- foobar
serversTransports:
ServersTransport0:
serverName: foobar
insecureSkipVerify: true
rootCAs:
- foobar
- foobar
certificates:
- certFile: foobar
keyFile: foobar
- certFile: foobar
keyFile: foobar
maxIdleConnsPerHost: 42
forwardingTimeouts:
dialTimeout: 42s
responseHeaderTimeout: 42s
idleConnTimeout: 42s
ServersTransport1:
serverName: foobar
insecureSkipVerify: true
rootCAs:
- foobar
- foobar
certificates:
- certFile: foobar
keyFile: foobar
- certFile: foobar
keyFile: foobar
maxIdleConnsPerHost: 42
forwardingTimeouts:
dialTimeout: 42s
responseHeaderTimeout: 42s
idleConnTimeout: 42s
tcp:
routers:
TCPRouter0:

View file

@ -101,3 +101,18 @@ spec:
plural: traefikservices
singular: traefikservice
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: serverstransports.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: ServersTransport
plural: serverstransports
singular: serverstransport
scope: Namespaced

View file

@ -40,6 +40,7 @@ rules:
- ingressrouteudps
- tlsoptions
- tlsstores
- serverstransports
verbs:
- get
- list

View file

@ -186,3 +186,25 @@ spec:
clientAuthType: foobar
sniStrict: true
preferServerCipherSuites: true
---
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
serverName: foobar
insecureSkipVerify: true
rootCAsSecrets:
- foobar
- foobar
certificatesSecrets:
- foobar
- foobar
maxIdleConnsPerHost: 1
forwardingTimeouts:
dialTimeout: 42s
responseHeaderTimeout: 42s
idleConnTimeout: 42s

View file

@ -159,6 +159,30 @@
| `traefik/http/routers/Router1/tls/domains/1/sans/0` | `foobar` |
| `traefik/http/routers/Router1/tls/domains/1/sans/1` | `foobar` |
| `traefik/http/routers/Router1/tls/options` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/certificates/0/certFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/certificates/0/keyFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/certificates/1/certFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/certificates/1/keyFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/dialTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/idleConnTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/responseHeaderTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport0/insecureSkipVerify` | `true` |
| `traefik/http/serversTransports/ServersTransport0/maxIdleConnsPerHost` | `42` |
| `traefik/http/serversTransports/ServersTransport0/rootCAs/0` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/rootCAs/1` | `foobar` |
| `traefik/http/serversTransports/ServersTransport0/serverName` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/certificates/0/certFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/certificates/0/keyFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/certificates/1/certFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/certificates/1/keyFile` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/dialTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/idleConnTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/responseHeaderTimeout` | `42s` |
| `traefik/http/serversTransports/ServersTransport1/insecureSkipVerify` | `true` |
| `traefik/http/serversTransports/ServersTransport1/maxIdleConnsPerHost` | `42` |
| `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/serverName` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/followRedirects` | `true` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` |
@ -172,6 +196,7 @@
| `traefik/http/services/Service01/loadBalancer/responseForwarding/flushInterval` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/servers/0/url` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/servers/1/url` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/serversTransport` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/httpOnly` | `true` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/name` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/sameSite` | `foobar` |

View file

@ -1489,7 +1489,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
```
| Ref | Attribute | Purpose |
|-----|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|-----|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `secretName` | The name of the referenced Kubernetes [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) that holds the default certificate for the store. |
??? example "Declaring and referencing a TLSStore"
@ -1537,6 +1537,84 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
```
### Kind: `ServersTransport`
`ServersTransport` is the CRD implementation of a [ServersTransport](../services/index.md#serverstransport).
!!! important "Default serversTransport"
If no `serversTransport` is specified, the `default@internal` will be used.
The `default@internal` serversTransport is created from the [static configuration](../overview.md#transport-configuration).
!!! info "ServersTransport Attributes"
```yaml tab="TLSStore"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
serverName: foobar # [1]
insecureSkipVerify: true # [2]
rootCAsSecrets: # [3]
- foobar
- foobar
certificatesSecrets: # [4]
- foobar
- foobar
maxIdleConnsPerHost: 1 # [5]
forwardingTimeouts: # [6]
dialTimeout: 42s # [7]
responseHeaderTimeout: 42s # [8]
idleConnTimeout: 42s # [9]
```
| Ref | Attribute | Purpose |
|-----|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `serverName` | ServerName used to contact the server. |
| [2] | `insecureSkipVerify` | Disable SSL certificate verification. |
| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. |
| [4] | `certificatesSecrets` | Certificates for mTLS. |
| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. |
| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. |
| [7] | `dialTimeout` | The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. |
| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. |
| [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. |
??? example "Declaring and referencing a ServersTransport"
```yaml tab="ServersTransport"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
serverName: example.org
insecureSkipVerify: true
```
```yaml tab="IngressRoute"
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: testroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`example.com`)
kind: Rule
services:
- name: whoami
port: 80
serversTransport: mytransport
```
## Further
Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.

View file

@ -460,6 +460,33 @@ By default, `passHostHeader` is true.
passHostHeader: false
```
#### ServersTransport
`serversTransport` allows to reference a ServersTransport configuration for the communication between Traefik and your servers.
??? example "Specify a transport -- Using the [File Provider](../../providers/file.md)"
```toml tab="TOML"
## Dynamic configuration
[http.services]
[http.services.Service01]
[http.services.Service01.loadBalancer]
serversTransport = "mytransport"
```
```yaml tab="YAML"
## Dynamic configuration
http:
services:
Service01:
loadBalancer:
serversTransport = "mytransport"
```
!!! info default serversTransport
If no serversTransport is specified, the `default@internal` will be used.
The `default@internal` serversTransport is created from the [static configuration](../overview.md#transport-configuration).
#### Response Forwarding
This section is about configuring how Traefik forwards the response from the backend server to the client.
@ -492,6 +519,301 @@ Below are the available options for the Response Forwarding mechanism:
flushInterval: 1s
```
### ServersTransport
ServersTransport allows to configure the transport between Traefik and your servers.
#### `ServerName`
_Optional_
`serverName` configure the server name that will be used for SNI.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport]
serverName = "myhost"
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
serverName: "myhost"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
serverName: "test"
```
#### `Certificates`
_Optional_
`certificates` is the list of certificates (as file paths, or data bytes)
that will be set as client certificates for mTLS.
```toml tab="File (TOML)"
## Dynamic configuration
[[http.serversTransports.mytransport.certificates]]
certFile = "foo.crt"
keyFile = "bar.crt"
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
certficates:
- certFile: foo.crt
keyFile: bar.crt
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
certificatesSecrets:
- mycert
---
apiVersion: v1
kind: Secret
metadata:
name: mycert
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
```
#### `insecureSkipVerify`
_Optional_
`insecureSkipVerify` disables SSL certificate verification.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport]
insecureSkipVerify = true
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
insecureSkipVerify: true
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
insecureSkipVerify: true
```
#### `rootCAs`
_Optional_
`rootCAs` is the list of certificates (as file paths, or data bytes)
that will be set as Root Certificate Authorities when using a self-signed TLS certificate.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport]
rootCAs = ["foo.crt", "bar.crt"]
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
rootCAs:
- foo.crt
- bar.crt
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
rootCAsSecrets:
- myca
---
apiVersion: v1
kind: Secret
metadata:
name: myca
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
```
#### `maxIdleConnsPerHost`
_Optional, Default=2_
If non-zero, `maxIdleConnsPerHost` controls the maximum idle (keep-alive) connections to keep per-host.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport]
maxIdleConnsPerHost = 7
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
maxIdleConnsPerHost: 7
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
maxIdleConnsPerHost: 7
```
#### `forwardingTimeouts`
`forwardingTimeouts` is about a number of timeouts relevant to when forwarding requests to the backend servers.
##### `forwardingTimeouts.dialTimeout`
_Optional, Default=30s_
`dialTimeout` is the maximum duration allowed for a connection to a backend server to be established.
Zero means no timeout.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport.forwardingTimeouts]
dialTimeout = "1s"
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
forwardingTimeouts:
dialTimeout: "1s"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
forwardingTimeouts:
dialTimeout: "1s"
```
##### `forwardingTimeouts.responseHeaderTimeout`
_Optional, Default=0s_
`responseHeaderTimeout`, if non-zero, specifies the amount of time to wait for a server's response headers
after fully writing the request (including its body, if any).
This time does not include the time to read the response body.
Zero means no timeout.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport.forwardingTimeouts]
responseHeaderTimeout = "1s"
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
forwardingTimeouts:
responseHeaderTimeout: "1s"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
forwardingTimeouts:
responseHeaderTimeout: "1s"
```
##### `forwardingTimeouts.idleConnTimeout`
_Optional, Default=90s_
`idleConnTimeout`, is the maximum amount of time an idle (keep-alive) connection
will remain idle before closing itself.
Zero means no limit.
```toml tab="File (TOML)"
## Dynamic configuration
[http.serversTransports.mytransport.forwardingTimeouts]
idleConnTimeout = "1s"
```
```yaml tab="File (YAML)"
## Dynamic configuration
http:
serversTransports:
mytransport:
forwardingTimeouts:
idleConnTimeout: "1s"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
forwardingTimeouts:
idleConnTimeout: "1s"
```
### Weighted Round Robin (service)
The WRR is able to load balance the requests between multiple services based on weights.

View file

@ -72,6 +72,21 @@ spec:
singular: tlsoption
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: serverstransports.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: ServersTransport
plural: serverstransports
singular: serverstransport
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition

View file

@ -0,0 +1,28 @@
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: testst.route
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`foo.com`) && PathPrefix(`/serverstransport`)
kind: Rule
services:
- name: whoami
port: 80
serversTransport: mytransport
---
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: mytransport
namespace: default
spec:
serverName: "test"
insecureSkipVerify: true

View file

@ -97,6 +97,10 @@ func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *
c.Check(err, checker.IsNil)
defer cmd.Process.Kill()
// Wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Duration(1)*time.Second, try.StatusCodeIs(200), try.BodyContains("PathPrefix(`/keepalive`)"))
c.Check(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/keepalive", time.Duration(1)*time.Second, try.StatusCodeIs(200))
c.Check(err, checker.IsNil)

View file

@ -50,6 +50,17 @@
"using": [
"web"
]
},
"default-testst-route-60ad45fcb5fc1f5f3629@kubernetescrd": {
"entryPoints": [
"web"
],
"service": "default-testst-route-60ad45fcb5fc1f5f3629",
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/serverstransport`)",
"status": "enabled",
"using": [
"web"
]
}
},
"middlewares": {
@ -98,10 +109,10 @@
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.2:80"
"url": "http://10.42.0.6:80"
},
{
"url": "http://10.42.0.3:80"
"url": "http://10.42.0.7:80"
}
],
"passHostHeader": true
@ -111,18 +122,18 @@
"default-test-route-6b204d94623b3df4370c@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.2:80": "UP",
"http://10.42.0.3:80": "UP"
"http://10.42.0.6:80": "UP",
"http://10.42.0.7:80": "UP"
}
},
"default-test2-route-23c7f4c450289ee29016@kubernetescrd": {
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.2:80"
"url": "http://10.42.0.6:80"
},
{
"url": "http://10.42.0.3:80"
"url": "http://10.42.0.7:80"
}
],
"passHostHeader": true
@ -132,26 +143,48 @@
"default-test2-route-23c7f4c450289ee29016@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.2:80": "UP",
"http://10.42.0.3:80": "UP"
"http://10.42.0.6:80": "UP",
"http://10.42.0.7:80": "UP"
}
},
"default-testst-route-60ad45fcb5fc1f5f3629@kubernetescrd": {
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.6:80"
},
{
"url": "http://10.42.0.7:80"
}
],
"passHostHeader": true,
"serversTransport": "mytransport@kubernetescrd"
},
"status": "enabled",
"usedBy": [
"default-testst-route-60ad45fcb5fc1f5f3629@kubernetescrd"
],
"serverStatus": {
"http://10.42.0.6:80": "UP",
"http://10.42.0.7:80": "UP"
}
},
"default-whoami-80@kubernetescrd": {
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.2:80"
"url": "http://10.42.0.6:80"
},
{
"url": "http://10.42.0.3:80"
"url": "http://10.42.0.7:80"
}
],
"passHostHeader": true
},
"status": "enabled",
"serverStatus": {
"http://10.42.0.2:80": "UP",
"http://10.42.0.3:80": "UP"
"http://10.42.0.6:80": "UP",
"http://10.42.0.7:80": "UP"
}
},
"default-wrr1@kubernetescrd": {
@ -210,7 +243,7 @@
"terminationDelay": 100,
"servers": [
{
"address": "10.42.0.3:8080"
"address": "10.42.0.10:8080"
},
{
"address": "10.42.0.8:8080"
@ -255,7 +288,7 @@
"loadBalancer": {
"servers": [
{
"address": "10.42.0.10:8090"
"address": "10.42.0.4:8090"
},
{
"address": "10.42.0.9:8090"

View file

@ -93,10 +93,10 @@
"loadBalancer": {
"servers": [
{
"url": "http://10.42.0.3:80"
"url": "http://10.42.0.10:80"
},
{
"url": "http://10.42.0.5:80"
"url": "http://10.42.0.8:80"
}
],
"passHostHeader": true
@ -107,8 +107,8 @@
"test-ingress-https-default-whoami-test-https-whoami@kubernetes"
],
"serverStatus": {
"http://10.42.0.3:80": "UP",
"http://10.42.0.5:80": "UP"
"http://10.42.0.10:80": "UP",
"http://10.42.0.8:80": "UP"
}
},
"noop@internal": {

View file

@ -2,8 +2,11 @@ package dynamic
import (
"reflect"
"time"
"github.com/containous/traefik/v2/pkg/tls"
"github.com/containous/traefik/v2/pkg/types"
ptypes "github.com/traefik/paerser/types"
)
// +k8s:deepcopy-gen=true
@ -14,6 +17,7 @@ type HTTPConfiguration struct {
Services map[string]*Service `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"`
Middlewares map[string]*Middleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"`
Models map[string]*Model `json:"models,omitempty" toml:"models,omitempty" yaml:"models,omitempty"`
ServersTransports map[string]*ServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-"`
}
// +k8s:deepcopy-gen=true
@ -125,6 +129,7 @@ type ServersLoadBalancer struct {
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
ResponseForwarding *ResponseForwarding `json:"responseForwarding,omitempty" toml:"responseForwarding,omitempty" yaml:"responseForwarding,omitempty"`
ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty"`
}
// Mergeable tells if the given service is mergeable.
@ -192,3 +197,30 @@ func (h *HealthCheck) SetDefaults() {
fr := true
h.FollowRedirects = &fr
}
// +k8s:deepcopy-gen=true
// ServersTransport options to configure communication between Traefik and the servers.
type ServersTransport struct {
ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty" export:"true"`
InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
RootCAs []tls.FileOrContent `description:"Add cert file for self-signed certificate." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"`
Certificates tls.Certificates `description:"Certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty"`
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
DialTimeout ptypes.Duration `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." json:"dialTimeout,omitempty" toml:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty" export:"true"`
ResponseHeaderTimeout ptypes.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
IdleConnTimeout ptypes.Duration `description:"The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself" json:"idleConnTimeout,omitempty" toml:"idleConnTimeout,omitempty" yaml:"idleConnTimeout,omitempty" export:"true"`
}
// SetDefaults sets the default values.
func (f *ForwardingTimeouts) SetDefaults() {
f.DialTimeout = ptypes.Duration(30 * time.Second)
f.IdleConnTimeout = ptypes.Duration(90 * time.Second)
}

View file

@ -357,6 +357,22 @@ func (in *ForwardAuth) DeepCopy() *ForwardAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ForwardingTimeouts) DeepCopyInto(out *ForwardingTimeouts) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardingTimeouts.
func (in *ForwardingTimeouts) DeepCopy() *ForwardingTimeouts {
if in == nil {
return nil
}
out := new(ForwardingTimeouts)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPConfiguration) DeepCopyInto(out *HTTPConfiguration) {
*out = *in
@ -420,6 +436,21 @@ func (in *HTTPConfiguration) DeepCopyInto(out *HTTPConfiguration) {
(*out)[key] = outVal
}
}
if in.ServersTransports != nil {
in, out := &in.ServersTransports, &out.ServersTransports
*out = make(map[string]*ServersTransport, len(*in))
for key, val := range *in {
var outVal *ServersTransport
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(ServersTransport)
(*in).DeepCopyInto(*out)
}
(*out)[key] = outVal
}
}
return
}
@ -1090,6 +1121,37 @@ func (in *ServersLoadBalancer) DeepCopy() *ServersLoadBalancer {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersTransport) DeepCopyInto(out *ServersTransport) {
*out = *in
if in.RootCAs != nil {
in, out := &in.RootCAs, &out.RootCAs
*out = make([]tls.FileOrContent, len(*in))
copy(*out, *in)
}
if in.Certificates != nil {
in, out := &in.Certificates, &out.Certificates
*out = make(tls.Certificates, len(*in))
copy(*out, *in)
}
if in.ForwardingTimeouts != nil {
in, out := &in.ForwardingTimeouts, &out.ForwardingTimeouts
*out = new(ForwardingTimeouts)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersTransport.
func (in *ServersTransport) DeepCopy() *ServersTransport {
if in == nil {
return nil
}
out := new(ServersTransport)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Service) DeepCopyInto(out *Service) {
*out = *in

View file

@ -13,4 +13,5 @@ const (
TracingProviderName = "tracingProviderName"
ServerName = "serverName"
TLSStoreName = "tlsStoreName"
ServersTransportName = "serversTransport"
)

View file

@ -208,6 +208,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
Routers: make(map[string]*dynamic.Router),
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),
@ -274,6 +275,14 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
}
}
for name, conf := range c.HTTP.ServersTransports {
if _, exists := configuration.HTTP.ServersTransports[name]; exists {
logger.WithField(log.ServersTransportName, name).Warn("HTTP servers transport already configured, skipping")
} else {
configuration.HTTP.ServersTransports[name] = conf
}
}
for name, conf := range c.TCP.Routers {
if _, exists := configuration.TCP.Routers[name]; exists {
logger.WithField(log.RouterName, name).Warn("TCP router already configured, skipping")
@ -401,6 +410,7 @@ func (p *Provider) decodeConfiguration(filePath, content string) (*dynamic.Confi
Routers: make(map[string]*dynamic.Router),
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),

View file

@ -149,6 +149,7 @@ func decodeConfiguration(data []byte) (*dynamic.Configuration, error) {
Routers: make(map[string]*dynamic.Router),
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),

View file

@ -137,6 +137,7 @@ func TestProvider_decodeConfiguration(t *testing.T) {
Routers: make(map[string]*dynamic.Router),
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
@ -195,6 +196,7 @@ func TestProvider_Provide(t *testing.T) {
Routers: make(map[string]*dynamic.Router),
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),

View file

@ -52,6 +52,7 @@ type Client interface {
GetTraefikService(namespace, name string) (*v1alpha1.TraefikService, bool, error)
GetTraefikServices() []*v1alpha1.TraefikService
GetTLSOptions() []*v1alpha1.TLSOption
GetServersTransports() []*v1alpha1.ServersTransport
GetTLSStores() []*v1alpha1.TLSStore
GetService(namespace, name string) (*corev1.Service, bool, error)
@ -162,6 +163,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteUDPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TLSOptions().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().ServersTransports().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TLSStores().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TraefikServices().Informer().AddEventHandler(eventHandler)
@ -279,6 +281,21 @@ func (c *clientWrapper) GetTraefikServices() []*v1alpha1.TraefikService {
return result
}
// GetServersTransport returns all ServersTransport.
func (c *clientWrapper) GetServersTransports() []*v1alpha1.ServersTransport {
var result []*v1alpha1.ServersTransport
for ns, factory := range c.factoriesCrd {
serversTransports, err := factory.Traefik().V1alpha1().ServersTransports().Lister().List(c.labelSelector)
if err != nil {
log.Errorf("Failed to list servers transport in namespace %s: %v", ns, err)
}
result = append(result, serversTransports...)
}
return result
}
// GetTLSOptions returns all TLS options.
func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
var result []*v1alpha1.TLSOption

View file

@ -37,6 +37,7 @@ type clientMock struct {
tlsOptions []*v1alpha1.TLSOption
tlsStores []*v1alpha1.TLSStore
traefikServices []*v1alpha1.TraefikService
serversTransport []*v1alpha1.ServersTransport
watchChan chan interface{}
}
@ -69,6 +70,8 @@ func newClientMock(paths ...string) clientMock {
c.traefikServices = append(c.traefikServices, o)
case *v1alpha1.TLSOption:
c.tlsOptions = append(c.tlsOptions, o)
case *v1alpha1.ServersTransport:
c.serversTransport = append(c.serversTransport, o)
case *v1alpha1.TLSStore:
c.tlsStores = append(c.tlsStores, o)
case *corev1.Secret:
@ -120,6 +123,10 @@ func (c clientMock) GetTLSStores() []*v1alpha1.TLSStore {
return c.tlsStores
}
func (c clientMock) GetServersTransports() []*v1alpha1.ServersTransport {
return c.serversTransport
}
func (c clientMock) GetTLSOption(namespace, name string) (*v1alpha1.TLSOption, bool, error) {
for _, option := range c.tlsOptions {
if option.Namespace == namespace && option.Name == name {

View file

@ -0,0 +1,62 @@
apiVersion: v1
kind: Secret
metadata:
name: rootCas1
namespace: foo
data:
tls.ca: VEVTVFJPT1RDQVM=
---
apiVersion: v1
kind: Secret
metadata:
name: rootCas2
namespace: foo
data:
tls.ca: VEVTVFJPT1RDQVMy
---
apiVersion: v1
kind: Secret
metadata:
name: mtls1
namespace: foo
data:
tls.crt: VEVTVENFUlQx
tls.key: VEVTVEtFWTE=
---
apiVersion: v1
kind: Secret
metadata:
name: mtls2
namespace: foo
data:
tls.crt: VEVTVENFUlQy
tls.key: VEVTVEtFWTI=
---
apiVersion: traefik.containo.us/v1alpha1
kind: ServersTransport
metadata:
name: test
namespace: foo
spec:
serverName: "test"
insecureSkipVerify: true
maxIdleConnsPerHost: 42
rootCAsSecrets:
- rootCas1
- rootCas2
certificatesSecrets:
- mtls1
- mtls2
forwardingTimeouts:
dialTimeout: 42
responseHeaderTimeout: 42s
idleConnTimeout: 42ms

View file

@ -35,14 +35,12 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var (
scheme = runtime.NewScheme()
codecs = serializer.NewCodecFactory(scheme)
parameterCodec = runtime.NewParameterCodec(scheme)
localSchemeBuilder = runtime.SchemeBuilder{
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
traefikv1alpha1.AddToScheme,
}
)
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:

View file

@ -35,14 +35,12 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var (
Scheme = runtime.NewScheme()
Codecs = serializer.NewCodecFactory(Scheme)
ParameterCodec = runtime.NewParameterCodec(Scheme)
localSchemeBuilder = runtime.SchemeBuilder{
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
traefikv1alpha1.AddToScheme,
}
)
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:

View file

@ -85,6 +85,7 @@ func (c *FakeIngressRoutes) List(ctx context.Context, opts v1.ListOptions) (resu
func (c *FakeIngressRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(ingressroutesResource, c.ns, opts))
}
// Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any.

View file

@ -85,6 +85,7 @@ func (c *FakeIngressRouteTCPs) List(ctx context.Context, opts v1.ListOptions) (r
func (c *FakeIngressRouteTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(ingressroutetcpsResource, c.ns, opts))
}
// Create takes the representation of a ingressRouteTCP and creates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any.

View file

@ -85,6 +85,7 @@ func (c *FakeIngressRouteUDPs) List(ctx context.Context, opts v1.ListOptions) (r
func (c *FakeIngressRouteUDPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(ingressrouteudpsResource, c.ns, opts))
}
// Create takes the representation of a ingressRouteUDP and creates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any.

View file

@ -85,6 +85,7 @@ func (c *FakeMiddlewares) List(ctx context.Context, opts v1.ListOptions) (result
func (c *FakeMiddlewares) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(middlewaresResource, c.ns, opts))
}
// Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any.

View file

@ -0,0 +1,138 @@
/*
The MIT License (MIT)
Copyright (c) 2016-2020 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
"context"
v1alpha1 "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeServersTransports implements ServersTransportInterface
type FakeServersTransports struct {
Fake *FakeTraefikV1alpha1
ns string
}
var serverstransportsResource = schema.GroupVersionResource{Group: "traefik.containo.us", Version: "v1alpha1", Resource: "serverstransports"}
var serverstransportsKind = schema.GroupVersionKind{Group: "traefik.containo.us", Version: "v1alpha1", Kind: "ServersTransport"}
// Get takes name of the serversTransport, and returns the corresponding serversTransport object, and an error if there is any.
func (c *FakeServersTransports) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransport, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(serverstransportsResource, c.ns, name), &v1alpha1.ServersTransport{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ServersTransport), err
}
// List takes label and field selectors, and returns the list of ServersTransports that match those selectors.
func (c *FakeServersTransports) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(serverstransportsResource, serverstransportsKind, c.ns, opts), &v1alpha1.ServersTransportList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.ServersTransportList{ListMeta: obj.(*v1alpha1.ServersTransportList).ListMeta}
for _, item := range obj.(*v1alpha1.ServersTransportList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested serversTransports.
func (c *FakeServersTransports) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(serverstransportsResource, c.ns, opts))
}
// Create takes the representation of a serversTransport and creates it. Returns the server's representation of the serversTransport, and an error, if there is any.
func (c *FakeServersTransports) Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (result *v1alpha1.ServersTransport, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(serverstransportsResource, c.ns, serversTransport), &v1alpha1.ServersTransport{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ServersTransport), err
}
// Update takes the representation of a serversTransport and updates it. Returns the server's representation of the serversTransport, and an error, if there is any.
func (c *FakeServersTransports) Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (result *v1alpha1.ServersTransport, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(serverstransportsResource, c.ns, serversTransport), &v1alpha1.ServersTransport{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ServersTransport), err
}
// Delete takes name of the serversTransport and deletes it. Returns an error if one occurs.
func (c *FakeServersTransports) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(serverstransportsResource, c.ns, name), &v1alpha1.ServersTransport{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeServersTransports) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(serverstransportsResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.ServersTransportList{})
return err
}
// Patch applies the patch and returns the patched serversTransport.
func (c *FakeServersTransports) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(serverstransportsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServersTransport{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ServersTransport), err
}

View file

@ -85,6 +85,7 @@ func (c *FakeTLSOptions) List(ctx context.Context, opts v1.ListOptions) (result
func (c *FakeTLSOptions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(tlsoptionsResource, c.ns, opts))
}
// Create takes the representation of a tLSOption and creates it. Returns the server's representation of the tLSOption, and an error, if there is any.

View file

@ -85,6 +85,7 @@ func (c *FakeTLSStores) List(ctx context.Context, opts v1.ListOptions) (result *
func (c *FakeTLSStores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(tlsstoresResource, c.ns, opts))
}
// Create takes the representation of a tLSStore and creates it. Returns the server's representation of the tLSStore, and an error, if there is any.

View file

@ -52,6 +52,10 @@ func (c *FakeTraefikV1alpha1) Middlewares(namespace string) v1alpha1.MiddlewareI
return &FakeMiddlewares{c, namespace}
}
func (c *FakeTraefikV1alpha1) ServersTransports(namespace string) v1alpha1.ServersTransportInterface {
return &FakeServersTransports{c, namespace}
}
func (c *FakeTraefikV1alpha1) TLSOptions(namespace string) v1alpha1.TLSOptionInterface {
return &FakeTLSOptions{c, namespace}
}

View file

@ -85,6 +85,7 @@ func (c *FakeTraefikServices) List(ctx context.Context, opts v1.ListOptions) (re
func (c *FakeTraefikServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(traefikservicesResource, c.ns, opts))
}
// Create takes the representation of a traefikService and creates it. Returns the server's representation of the traefikService, and an error, if there is any.

View file

@ -34,6 +34,8 @@ type IngressRouteUDPExpansion interface{}
type MiddlewareExpansion interface{}
type ServersTransportExpansion interface{}
type TLSOptionExpansion interface{}
type TLSStoreExpansion interface{}

View file

@ -0,0 +1,186 @@
/*
The MIT License (MIT)
Copyright (c) 2016-2020 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"context"
"time"
scheme "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme"
v1alpha1 "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ServersTransportsGetter has a method to return a ServersTransportInterface.
// A group's client should implement this interface.
type ServersTransportsGetter interface {
ServersTransports(namespace string) ServersTransportInterface
}
// ServersTransportInterface has methods to work with ServersTransport resources.
type ServersTransportInterface interface {
Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (*v1alpha1.ServersTransport, error)
Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (*v1alpha1.ServersTransport, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServersTransport, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServersTransportList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error)
ServersTransportExpansion
}
// serversTransports implements ServersTransportInterface
type serversTransports struct {
client rest.Interface
ns string
}
// newServersTransports returns a ServersTransports
func newServersTransports(c *TraefikV1alpha1Client, namespace string) *serversTransports {
return &serversTransports{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the serversTransport, and returns the corresponding serversTransport object, and an error if there is any.
func (c *serversTransports) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransport, err error) {
result = &v1alpha1.ServersTransport{}
err = c.client.Get().
Namespace(c.ns).
Resource("serverstransports").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of ServersTransports that match those selectors.
func (c *serversTransports) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.ServersTransportList{}
err = c.client.Get().
Namespace(c.ns).
Resource("serverstransports").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested serversTransports.
func (c *serversTransports) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("serverstransports").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a serversTransport and creates it. Returns the server's representation of the serversTransport, and an error, if there is any.
func (c *serversTransports) Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (result *v1alpha1.ServersTransport, err error) {
result = &v1alpha1.ServersTransport{}
err = c.client.Post().
Namespace(c.ns).
Resource("serverstransports").
VersionedParams(&opts, scheme.ParameterCodec).
Body(serversTransport).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a serversTransport and updates it. Returns the server's representation of the serversTransport, and an error, if there is any.
func (c *serversTransports) Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (result *v1alpha1.ServersTransport, err error) {
result = &v1alpha1.ServersTransport{}
err = c.client.Put().
Namespace(c.ns).
Resource("serverstransports").
Name(serversTransport.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(serversTransport).
Do(ctx).
Into(result)
return
}
// Delete takes name of the serversTransport and deletes it. Returns an error if one occurs.
func (c *serversTransports) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("serverstransports").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *serversTransports) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("serverstransports").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched serversTransport.
func (c *serversTransports) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error) {
result = &v1alpha1.ServersTransport{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("serverstransports").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View file

@ -38,6 +38,7 @@ type TraefikV1alpha1Interface interface {
IngressRouteTCPsGetter
IngressRouteUDPsGetter
MiddlewaresGetter
ServersTransportsGetter
TLSOptionsGetter
TLSStoresGetter
TraefikServicesGetter
@ -64,6 +65,10 @@ func (c *TraefikV1alpha1Client) Middlewares(namespace string) MiddlewareInterfac
return newMiddlewares(c, namespace)
}
func (c *TraefikV1alpha1Client) ServersTransports(namespace string) ServersTransportInterface {
return newServersTransports(c, namespace)
}
func (c *TraefikV1alpha1Client) TLSOptions(namespace string) TLSOptionInterface {
return newTLSOptions(c, namespace)
}

View file

@ -69,6 +69,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().IngressRouteUDPs().Informer()}, nil
case v1alpha1.SchemeGroupVersion.WithResource("middlewares"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().Middlewares().Informer()}, nil
case v1alpha1.SchemeGroupVersion.WithResource("serverstransports"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().ServersTransports().Informer()}, nil
case v1alpha1.SchemeGroupVersion.WithResource("tlsoptions"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().TLSOptions().Informer()}, nil
case v1alpha1.SchemeGroupVersion.WithResource("tlsstores"):

View file

@ -40,6 +40,8 @@ type Interface interface {
IngressRouteUDPs() IngressRouteUDPInformer
// Middlewares returns a MiddlewareInformer.
Middlewares() MiddlewareInformer
// ServersTransports returns a ServersTransportInformer.
ServersTransports() ServersTransportInformer
// TLSOptions returns a TLSOptionInformer.
TLSOptions() TLSOptionInformer
// TLSStores returns a TLSStoreInformer.
@ -79,6 +81,11 @@ func (v *version) Middlewares() MiddlewareInformer {
return &middlewareInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// ServersTransports returns a ServersTransportInformer.
func (v *version) ServersTransports() ServersTransportInformer {
return &serversTransportInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// TLSOptions returns a TLSOptionInformer.
func (v *version) TLSOptions() TLSOptionInformer {
return &tLSOptionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View file

@ -0,0 +1,98 @@
/*
The MIT License (MIT)
Copyright (c) 2016-2020 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
"context"
time "time"
versioned "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned"
internalinterfaces "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
v1alpha1 "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/generated/listers/traefik/v1alpha1"
traefikv1alpha1 "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ServersTransportInformer provides access to a shared informer and lister for
// ServersTransports.
type ServersTransportInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.ServersTransportLister
}
type serversTransportInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewServersTransportInformer constructs a new informer for ServersTransport type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewServersTransportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredServersTransportInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredServersTransportInformer constructs a new informer for ServersTransport type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredServersTransportInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.TraefikV1alpha1().ServersTransports(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.TraefikV1alpha1().ServersTransports(namespace).Watch(context.TODO(), options)
},
},
&traefikv1alpha1.ServersTransport{},
resyncPeriod,
indexers,
)
}
func (f *serversTransportInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredServersTransportInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *serversTransportInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&traefikv1alpha1.ServersTransport{}, f.defaultInformer)
}
func (f *serversTransportInformer) Lister() v1alpha1.ServersTransportLister {
return v1alpha1.NewServersTransportLister(f.Informer().GetIndexer())
}

View file

@ -58,6 +58,14 @@ type MiddlewareListerExpansion interface{}
// MiddlewareNamespaceLister.
type MiddlewareNamespaceListerExpansion interface{}
// ServersTransportListerExpansion allows custom methods to be added to
// ServersTransportLister.
type ServersTransportListerExpansion interface{}
// ServersTransportNamespaceListerExpansion allows custom methods to be added to
// ServersTransportNamespaceLister.
type ServersTransportNamespaceListerExpansion interface{}
// TLSOptionListerExpansion allows custom methods to be added to
// TLSOptionLister.
type TLSOptionListerExpansion interface{}

View file

@ -0,0 +1,102 @@
/*
The MIT License (MIT)
Copyright (c) 2016-2020 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ServersTransportLister helps list ServersTransports.
type ServersTransportLister interface {
// List lists all ServersTransports in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error)
// ServersTransports returns an object that can list and get ServersTransports.
ServersTransports(namespace string) ServersTransportNamespaceLister
ServersTransportListerExpansion
}
// serversTransportLister implements the ServersTransportLister interface.
type serversTransportLister struct {
indexer cache.Indexer
}
// NewServersTransportLister returns a new ServersTransportLister.
func NewServersTransportLister(indexer cache.Indexer) ServersTransportLister {
return &serversTransportLister{indexer: indexer}
}
// List lists all ServersTransports in the indexer.
func (s *serversTransportLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ServersTransport))
})
return ret, err
}
// ServersTransports returns an object that can list and get ServersTransports.
func (s *serversTransportLister) ServersTransports(namespace string) ServersTransportNamespaceLister {
return serversTransportNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ServersTransportNamespaceLister helps list and get ServersTransports.
type ServersTransportNamespaceLister interface {
// List lists all ServersTransports in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error)
// Get retrieves the ServersTransport from the indexer for a given namespace and name.
Get(name string) (*v1alpha1.ServersTransport, error)
ServersTransportNamespaceListerExpansion
}
// serversTransportNamespaceLister implements the ServersTransportNamespaceLister
// interface.
type serversTransportNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all ServersTransports in the indexer for a given namespace.
func (s serversTransportNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ServersTransport))
})
return ret, err
}
// Get retrieves the ServersTransport from the indexer for a given namespace and name.
func (s serversTransportNamespaceLister) Get(name string) (*v1alpha1.ServersTransport, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("serverstransport"), name)
}
return obj.(*v1alpha1.ServersTransport), nil
}

View file

@ -237,6 +237,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
}
cb := configBuilder{client}
for _, service := range client.GetTraefikServices() {
err := cb.buildTraefikService(ctx, service, conf.HTTP.Services)
if err != nil {
@ -246,6 +247,70 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
}
}
for _, serversTransport := range client.GetServersTransports() {
logger := log.FromContext(ctx).WithField(log.ServersTransportName, serversTransport.Name)
var rootCAs []tls.FileOrContent
for _, secret := range serversTransport.Spec.RootCAsSecrets {
caSecret, err := loadCASecret(serversTransport.Namespace, secret, client)
if err != nil {
logger.Errorf("Error while loading rootCAs %s: %v", secret, err)
continue
}
rootCAs = append(rootCAs, tls.FileOrContent(caSecret))
}
var certs tls.Certificates
for _, secret := range serversTransport.Spec.CertificatesSecrets {
tlsSecret, tlsKey, err := loadAuthTLSSecret(serversTransport.Namespace, secret, client)
if err != nil {
logger.Errorf("Error while loading certificates %s: %v", secret, err)
continue
}
certs = append(certs, tls.Certificate{
CertFile: tls.FileOrContent(tlsSecret),
KeyFile: tls.FileOrContent(tlsKey),
})
}
forwardingTimeout := &dynamic.ForwardingTimeouts{}
forwardingTimeout.SetDefaults()
if serversTransport.Spec.ForwardingTimeouts != nil {
if serversTransport.Spec.ForwardingTimeouts.DialTimeout != nil {
err := forwardingTimeout.DialTimeout.Set(serversTransport.Spec.ForwardingTimeouts.DialTimeout.String())
if err != nil {
logger.Errorf("Error while reading DialTimeout: %v", err)
}
}
if serversTransport.Spec.ForwardingTimeouts.ResponseHeaderTimeout != nil {
err := forwardingTimeout.ResponseHeaderTimeout.Set(serversTransport.Spec.ForwardingTimeouts.ResponseHeaderTimeout.String())
if err != nil {
logger.Errorf("Error while reading ResponseHeaderTimeout: %v", err)
}
}
if serversTransport.Spec.ForwardingTimeouts.IdleConnTimeout != nil {
err := forwardingTimeout.IdleConnTimeout.Set(serversTransport.Spec.ForwardingTimeouts.IdleConnTimeout.String())
if err != nil {
logger.Errorf("Error while reading IdleConnTimeout: %v", err)
}
}
}
conf.HTTP.ServersTransports[serversTransport.Name] = &dynamic.ServersTransport{
ServerName: serversTransport.Spec.ServerName,
InsecureSkipVerify: serversTransport.Spec.InsecureSkipVerify,
RootCAs: rootCAs,
Certificates: certs,
MaxIdleConnsPerHost: serversTransport.Spec.MaxIdleConnsPerHost,
ForwardingTimeouts: forwardingTimeout,
}
}
return conf
}

View file

@ -25,6 +25,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
}
for _, ingressRoute := range client.GetIngressRoutes() {
@ -264,6 +265,7 @@ func (c configBuilder) buildServersLB(namespace string, svc v1alpha1.LoadBalance
lb.ResponseForwarding = conf.ResponseForwarding
lb.Sticky = svc.Sticky
lb.ServersTransport = svc.ServersTransport
return &dynamic.Service{LoadBalancer: lb}, nil
}

View file

@ -3,11 +3,13 @@ package crd
import (
"context"
"testing"
"time"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/tls"
"github.com/stretchr/testify/assert"
"github.com/traefik/paerser/types"
corev1 "k8s.io/api/core/v1"
)
@ -35,6 +37,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -51,6 +54,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -136,6 +140,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -177,6 +182,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -242,6 +248,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -323,6 +330,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -344,6 +352,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -364,6 +373,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -416,6 +426,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -459,6 +470,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -523,6 +535,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -585,6 +598,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -646,6 +660,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -696,6 +711,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -746,6 +762,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -787,6 +804,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -830,6 +848,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -881,6 +900,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -917,6 +937,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -954,6 +975,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -980,6 +1002,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
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{},
@ -1025,6 +1048,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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{},
@ -1045,6 +1069,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
@ -1086,6 +1111,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test2-route-23c7f4c450289ee29016": {
EntryPoints: []string{"web"},
@ -1140,6 +1166,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test2-route-23c7f4c450289ee29016": {
EntryPoints: []string{"web"},
@ -1192,6 +1219,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -1253,6 +1281,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1321,6 +1350,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1372,6 +1402,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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{
@ -1416,6 +1447,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1538,6 +1570,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1603,6 +1636,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1693,6 +1727,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1869,6 +1904,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -1931,6 +1967,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -2013,6 +2050,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-77c62dfe9517144aeeaa": {
EntryPoints: []string{"web"},
@ -2082,6 +2120,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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{},
@ -2102,6 +2141,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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{},
@ -2122,6 +2162,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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{},
@ -2151,6 +2192,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2212,6 +2254,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2258,6 +2301,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2321,6 +2365,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2383,6 +2428,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2444,6 +2490,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2494,6 +2541,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2544,6 +2592,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2588,6 +2637,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2630,6 +2680,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
@ -2671,6 +2722,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
@ -2712,6 +2764,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{
"default-basicauth": {
@ -2753,6 +2806,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{
"default-errorpage": {
@ -2794,6 +2848,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
@ -2846,6 +2901,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2890,6 +2946,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{
"default-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"web"},
@ -2934,6 +2991,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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"},
@ -2971,6 +3029,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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"},
@ -3008,6 +3067,7 @@ func TestLoadIngressRoutes(t *testing.T) {
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"},
@ -3045,6 +3105,44 @@ func TestLoadIngressRoutes(t *testing.T) {
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: "ServersTransport",
paths: []string{"services.yml", "with_servers_transport.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{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{
"test": {
ServerName: "test",
InsecureSkipVerify: true,
RootCAs: []tls.FileOrContent{"TESTROOTCAS", "TESTROOTCAS2"},
Certificates: tls.Certificates{
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
},
MaxIdleConnsPerHost: 42,
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
DialTimeout: types.Duration(42 * time.Second),
ResponseHeaderTimeout: types.Duration(42 * time.Second),
IdleConnTimeout: types.Duration(42 * time.Millisecond),
},
},
},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -3056,7 +3154,6 @@ func TestLoadIngressRoutes(t *testing.T) {
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
@ -3090,6 +3187,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
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{},
@ -3126,6 +3224,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
@ -3188,6 +3287,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
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{},
@ -3252,6 +3352,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
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{},
@ -3332,6 +3433,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
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{},
@ -3353,6 +3455,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
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{},

View file

@ -72,6 +72,7 @@ type LoadBalancerSpec struct {
Strategy string `json:"strategy,omitempty"`
PassHostHeader *bool `json:"passHostHeader,omitempty"`
ResponseForwarding *dynamic.ResponseForwarding `json:"responseForwarding,omitempty"`
ServersTransport string `json:"serversTransport,omitempty"`
// Weight should only be specified when Name references a TraefikService object
// (and to be precise, one that embeds a Weighted Round Robin).

View file

@ -47,6 +47,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&TLSStoreList{},
&TraefikService{},
&TraefikServiceList{},
&ServersTransport{},
&ServersTransportList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View file

@ -0,0 +1,48 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServersTransport is a specification for a ServersTransport resource.
type ServersTransport struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec ServersTransportSpec `json:"spec"`
}
// +k8s:deepcopy-gen=true
// ServersTransportSpec options to configure communication between Traefik and the servers.
type ServersTransportSpec struct {
ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty" export:"true"`
InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
RootCAsSecrets []string `description:"Add cert file for self-signed certificate." json:"rootCAsSecrets,omitempty" toml:"rootCAsSecrets,omitempty" yaml:"rootCAsSecrets,omitempty"`
CertificatesSecrets []string `description:"Certificates for mTLS." json:"certificatesSecrets,omitempty" toml:"certificatesSecrets,omitempty" yaml:"certificatesSecrets,omitempty"`
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
DialTimeout *intstr.IntOrString `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." json:"dialTimeout,omitempty" toml:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty" export:"true"`
ResponseHeaderTimeout *intstr.IntOrString `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"`
IdleConnTimeout *intstr.IntOrString `description:"The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself" json:"idleConnTimeout,omitempty" toml:"idleConnTimeout,omitempty" yaml:"idleConnTimeout,omitempty" export:"true"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServersTransportList is a list of ServersTransport resources.
type ServersTransportList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []ServersTransport `json:"items"`
}

View file

@ -32,6 +32,7 @@ import (
dynamic "github.com/containous/traefik/v2/pkg/config/dynamic"
types "github.com/containous/traefik/v2/pkg/types"
runtime "k8s.io/apimachinery/pkg/runtime"
intstr "k8s.io/apimachinery/pkg/util/intstr"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -188,6 +189,37 @@ func (in *ForwardAuth) DeepCopy() *ForwardAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ForwardingTimeouts) DeepCopyInto(out *ForwardingTimeouts) {
*out = *in
if in.DialTimeout != nil {
in, out := &in.DialTimeout, &out.DialTimeout
*out = new(intstr.IntOrString)
**out = **in
}
if in.ResponseHeaderTimeout != nil {
in, out := &in.ResponseHeaderTimeout, &out.ResponseHeaderTimeout
*out = new(intstr.IntOrString)
**out = **in
}
if in.IdleConnTimeout != nil {
in, out := &in.IdleConnTimeout, &out.IdleConnTimeout
*out = new(intstr.IntOrString)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardingTimeouts.
func (in *ForwardingTimeouts) DeepCopy() *ForwardingTimeouts {
if in == nil {
return nil
}
out := new(ForwardingTimeouts)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressRoute) DeepCopyInto(out *IngressRoute) {
*out = *in
@ -827,6 +859,97 @@ func (in *RouteUDP) DeepCopy() *RouteUDP {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersTransport) DeepCopyInto(out *ServersTransport) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersTransport.
func (in *ServersTransport) DeepCopy() *ServersTransport {
if in == nil {
return nil
}
out := new(ServersTransport)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServersTransport) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersTransportList) DeepCopyInto(out *ServersTransportList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ServersTransport, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersTransportList.
func (in *ServersTransportList) DeepCopy() *ServersTransportList {
if in == nil {
return nil
}
out := new(ServersTransportList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServersTransportList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) {
*out = *in
if in.RootCAsSecrets != nil {
in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.CertificatesSecrets != nil {
in, out := &in.CertificatesSecrets, &out.CertificatesSecrets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ForwardingTimeouts != nil {
in, out := &in.ForwardingTimeouts, &out.ForwardingTimeouts
*out = new(ForwardingTimeouts)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersTransportSpec.
func (in *ServersTransportSpec) DeepCopy() *ServersTransportSpec {
if in == nil {
return nil
}
out := new(ServersTransportSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Service) DeepCopyInto(out *Service) {
*out = *in

View file

@ -12,7 +12,7 @@ import (
// MustParseYaml parses a YAML to objects.
func MustParseYaml(content []byte) []runtime.Object {
acceptedK8sTypes := regexp.MustCompile(`^(Deployment|Endpoints|Service|Ingress|IngressRoute|IngressRouteTCP|IngressRouteUDP|Middleware|Secret|TLSOption|TLSStore|TraefikService|IngressClass)$`)
acceptedK8sTypes := regexp.MustCompile(`^(Deployment|Endpoints|Service|Ingress|IngressRoute|IngressRouteTCP|IngressRouteUDP|Middleware|Secret|TLSOption|TLSStore|TraefikService|IngressClass|ServersTransport)$`)
files := strings.Split(string(content), "---")
retVal := make([]runtime.Object, 0, len(files))

View file

@ -53,6 +53,7 @@ func (i *Provider) createConfiguration(ctx context.Context) *dynamic.Configurati
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
Models: make(map[string]*dynamic.Model),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),
@ -70,6 +71,7 @@ func (i *Provider) createConfiguration(ctx context.Context) *dynamic.Configurati
i.prometheusConfiguration(cfg)
i.entryPointModels(cfg)
i.redirection(ctx, cfg)
i.serverTransport(cfg)
cfg.HTTP.Services["noop"] = &dynamic.Service{}
@ -274,3 +276,25 @@ func (i *Provider) prometheusConfiguration(cfg *dynamic.Configuration) {
cfg.HTTP.Services["prometheus"] = &dynamic.Service{}
}
func (i *Provider) serverTransport(cfg *dynamic.Configuration) {
if i.staticCfg.ServersTransport == nil {
return
}
st := &dynamic.ServersTransport{
InsecureSkipVerify: i.staticCfg.ServersTransport.InsecureSkipVerify,
RootCAs: i.staticCfg.ServersTransport.RootCAs,
MaxIdleConnsPerHost: i.staticCfg.ServersTransport.MaxIdleConnsPerHost,
}
if i.staticCfg.ServersTransport.ForwardingTimeouts != nil {
st.ForwardingTimeouts = &dynamic.ForwardingTimeouts{
DialTimeout: i.staticCfg.ServersTransport.ForwardingTimeouts.DialTimeout,
ResponseHeaderTimeout: i.staticCfg.ServersTransport.ForwardingTimeouts.ResponseHeaderTimeout,
IdleConnTimeout: i.staticCfg.ServersTransport.ForwardingTimeouts.IdleConnTimeout,
}
}
cfg.HTTP.ServersTransports["default"] = st
}

View file

@ -14,6 +14,7 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
Models: make(map[string]*dynamic.Model),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),
@ -52,6 +53,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
for modelName, model := range configuration.HTTP.Models {
conf.HTTP.Models[provider.MakeQualifiedName(pvd, modelName)] = model
}
for serversTransportName, serversTransport := range configuration.HTTP.ServersTransports {
conf.HTTP.ServersTransports[provider.MakeQualifiedName(pvd, serversTransportName)] = serversTransport
}
}
if configuration.TCP != nil {

View file

@ -22,6 +22,7 @@ func Test_mergeConfiguration(t *testing.T) {
Middlewares: make(map[string]*dynamic.Middleware),
Services: make(map[string]*dynamic.Service),
Models: make(map[string]*dynamic.Model),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
},
{
@ -54,6 +55,7 @@ func Test_mergeConfiguration(t *testing.T) {
"service-1@provider-1": {},
},
Models: make(map[string]*dynamic.Model),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
},
{
@ -104,6 +106,7 @@ func Test_mergeConfiguration(t *testing.T) {
"service-1@provider-2": {},
},
Models: make(map[string]*dynamic.Model),
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
},
}

View file

@ -287,7 +287,9 @@ func TestRouterManager_Get(t *testing.T) {
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
@ -391,7 +393,9 @@ func TestAccessLog(t *testing.T) {
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
@ -678,7 +682,9 @@ func TestRuntimeConfiguration(t *testing.T) {
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
@ -759,7 +765,9 @@ func TestProviderOnMiddlewares(t *testing.T) {
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
chainBuilder := middleware.NewChainBuilder(staticCfg, nil, nil)
@ -773,6 +781,14 @@ func TestProviderOnMiddlewares(t *testing.T) {
assert.Equal(t, []string{"m1@docker", "m2@docker", "m1@file"}, rtConf.Middlewares["chain@docker"].Chain.Middlewares)
}
type staticRoundTripperGetter struct {
res *http.Response
}
func (s staticRoundTripperGetter) Get(name string) (http.RoundTripper, error) {
return &staticTransport{res: s.res}, nil
}
type staticTransport struct {
res *http.Response
}
@ -819,7 +835,7 @@ func BenchmarkRouterServe(b *testing.B) {
},
})
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
@ -861,7 +877,7 @@ func BenchmarkService(b *testing.B) {
},
})
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
w := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)

View file

@ -48,7 +48,9 @@ func TestReuseService(t *testing.T) {
),
)
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
tlsManager := tls.NewManager()
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
@ -182,7 +184,9 @@ func TestServerResponseEmptyBackend(t *testing.T) {
},
}
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
tlsManager := tls.NewManager()
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
@ -221,7 +225,9 @@ func TestInternalServices(t *testing.T) {
),
)
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
roundTripperManager := service.NewRoundTripperManager()
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
tlsManager := tls.NewManager()
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)

View file

@ -14,7 +14,7 @@ import (
type ManagerFactory struct {
metricsRegistry metrics.Registry
defaultRoundTripper http.RoundTripper
roundTripperManager *RoundTripperManager
api func(configuration *runtime.Configuration) http.Handler
restHandler http.Handler
@ -26,11 +26,11 @@ type ManagerFactory struct {
}
// NewManagerFactory creates a new ManagerFactory.
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry) *ManagerFactory {
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry, roundTripperManager *RoundTripperManager) *ManagerFactory {
factory := &ManagerFactory{
metricsRegistry: metricsRegistry,
defaultRoundTripper: setupDefaultRoundTripper(staticConfiguration.ServersTransport),
routinesPool: routinesPool,
roundTripperManager: roundTripperManager,
}
if staticConfiguration.API != nil {
@ -61,6 +61,6 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
// Build creates a service manager.
func (f *ManagerFactory) Build(configuration *runtime.Configuration) *InternalHandlers {
svcManager := NewManager(configuration.Services, f.defaultRoundTripper, f.metricsRegistry, f.routinesPool)
svcManager := NewManager(configuration.Services, f.metricsRegistry, f.routinesPool, f.roundTripperManager)
return NewInternalHandlers(f.api, configuration, f.restHandler, f.metricsHandler, f.pingHandler, f.dashboardHandler, svcManager)
}

View file

@ -21,7 +21,7 @@ const StatusClientClosedRequest = 499
// StatusClientClosedRequestText non-standard HTTP status for client disconnection.
const StatusClientClosedRequestText = "Client Closed Request"
func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwarding, defaultRoundTripper http.RoundTripper, bufferPool httputil.BufferPool) (http.Handler, error) {
func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwarding, roundTripper http.RoundTripper, bufferPool httputil.BufferPool) (http.Handler, error) {
var flushInterval ptypes.Duration
if responseForwarding != nil {
err := flushInterval.Set(responseForwarding.FlushInterval)
@ -76,7 +76,7 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
delete(outReq.Header, "Sec-Websocket-Protocol")
delete(outReq.Header, "Sec-Websocket-Version")
},
Transport: defaultRoundTripper,
Transport: roundTripper,
FlushInterval: time.Duration(flushInterval),
BufferPool: bufferPool,
ErrorHandler: func(w http.ResponseWriter, request *http.Request, err error) {

View file

@ -4,11 +4,14 @@ import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/http"
"reflect"
"sync"
"time"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
traefiktls "github.com/containous/traefik/v2/pkg/tls"
"golang.org/x/net/http2"
@ -23,30 +26,100 @@ func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, erro
return t.Transport.RoundTrip(req)
}
// createRoundtripper creates an http.Roundtripper configured with the Transport configuration settings.
// NewRoundTripperManager creates a new RoundTripperManager.
func NewRoundTripperManager() *RoundTripperManager {
return &RoundTripperManager{
roundTrippers: make(map[string]http.RoundTripper),
configs: make(map[string]*dynamic.ServersTransport),
}
}
// RoundTripperManager handles roundtripper for the reverse proxy.
type RoundTripperManager struct {
rtLock sync.RWMutex
roundTrippers map[string]http.RoundTripper
configs map[string]*dynamic.ServersTransport
}
// Update updates the roundtrippers configurations.
func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTransport) {
r.rtLock.Lock()
defer r.rtLock.Unlock()
for configName, config := range r.configs {
newConfig, ok := newConfigs[configName]
if !ok {
delete(r.configs, configName)
delete(r.roundTrippers, configName)
continue
}
if reflect.DeepEqual(newConfig, config) {
continue
}
var err error
r.roundTrippers[configName], err = createRoundTripper(newConfig)
if err != nil {
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", configName, err)
r.roundTrippers[configName] = http.DefaultTransport
}
}
for newConfigName, newConfig := range newConfigs {
if _, ok := r.configs[newConfigName]; ok {
continue
}
var err error
r.roundTrippers[newConfigName], err = createRoundTripper(newConfig)
if err != nil {
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", newConfigName, err)
r.roundTrippers[newConfigName] = http.DefaultTransport
}
}
r.configs = newConfigs
}
// Get get a roundtripper by name.
func (r *RoundTripperManager) Get(name string) (http.RoundTripper, error) {
if len(name) == 0 {
name = "default@internal"
}
r.rtLock.RLock()
defer r.rtLock.RUnlock()
if rt, ok := r.roundTrippers[name]; ok {
return rt, nil
}
return nil, fmt.Errorf("servers transport not found %s", name)
}
// createRoundTripper creates an http.RoundTripper configured with the Transport configuration settings.
// For the settings that can't be configured in Traefik it uses the default http.Transport settings.
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHost
// in Traefik at this point in time. Setting this value to the default of 100 could lead to confusing
// behavior and backwards compatibility issues.
func createRoundtripper(transportConfiguration *static.ServersTransport) (http.RoundTripper, error) {
if transportConfiguration == nil {
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHostin Traefik at this point in time.
// Setting this value to the default of 100 could lead to confusing behavior and backwards compatibility issues.
func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error) {
if cfg == nil {
return nil, errors.New("no transport configuration given")
}
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
if transportConfiguration.ForwardingTimeouts != nil {
dialer.Timeout = time.Duration(transportConfiguration.ForwardingTimeouts.DialTimeout)
if cfg.ForwardingTimeouts != nil {
dialer.Timeout = time.Duration(cfg.ForwardingTimeouts.DialTimeout)
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
MaxIdleConnsPerHost: transportConfiguration.MaxIdleConnsPerHost,
MaxIdleConnsPerHost: cfg.MaxIdleConnsPerHost,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
@ -61,24 +134,21 @@ func createRoundtripper(transportConfiguration *static.ServersTransport) (http.R
},
})
if transportConfiguration.ForwardingTimeouts != nil {
transport.ResponseHeaderTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
transport.IdleConnTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.IdleConnTimeout)
if cfg.ForwardingTimeouts != nil {
transport.ResponseHeaderTimeout = time.Duration(cfg.ForwardingTimeouts.ResponseHeaderTimeout)
transport.IdleConnTimeout = time.Duration(cfg.ForwardingTimeouts.IdleConnTimeout)
}
if transportConfiguration.InsecureSkipVerify || len(transportConfiguration.RootCAs) > 0 {
if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 {
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: transportConfiguration.InsecureSkipVerify,
RootCAs: createRootCACertPool(transportConfiguration.RootCAs),
ServerName: cfg.ServerName,
InsecureSkipVerify: cfg.InsecureSkipVerify,
RootCAs: createRootCACertPool(cfg.RootCAs),
Certificates: cfg.Certificates.GetCertificates(),
}
}
smartTransport, err := newSmartRoundTripper(transport)
if err != nil {
return nil, err
}
return smartTransport, nil
return newSmartRoundTripper(transport)
}
func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
@ -99,13 +169,3 @@ func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
return roots
}
func setupDefaultRoundTripper(conf *static.ServersTransport) http.RoundTripper {
transport, err := createRoundtripper(conf)
if err != nil {
log.WithoutContext().Errorf("Could not configure HTTP Transport, fallbacking on default transport: %v", err)
return http.DefaultTransport
}
return transport
}

View file

@ -0,0 +1,229 @@
package service
import (
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"github.com/containous/traefik/v2/pkg/config/dynamic"
traefiktls "github.com/containous/traefik/v2/pkg/tls"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Int32(i int32) *int32 {
return &i
}
// LocalhostCert is a PEM-encoded TLS cert
// for host example.com, www.example.com
// expiring at Jan 29 16:00:00 2084 GMT.
// go run $GOROOT/src/crypto/tls/generate_cert.go --rsa-bits 1024 --host example.com,www.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIICDDCCAXWgAwIBAgIQH20JmcOlcRWHNuf62SYwszANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQC0qINy3F4oq6viDnlpDDE5J08iSRGggg6EylJKBKZfphEG2ufgK78Dufl3
+7b0LlEY2AeZHwviHODqC9a6ihj1ZYQk0/djAh+OeOhFEWu+9T/VP8gVFarFqT8D
Opy+hrG7YJivUIzwb4fmJQRI7FajzsnGyM6LiXLU+0qzb7ZO/QIDAQABo2EwXzAO
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
AwEB/zAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUuY29tMA0G
CSqGSIb3DQEBCwUAA4GBAB+eluoQYzyyMfeEEAOtlldevx5MtDENT05NB0WI+91R
we7mX8lv763u0XuCWPxbHszhclI6FFjoQef0Z1NYLRm8ZRq58QqWDFZ3E6wdDK+B
+OWvkW+hRavo6R9LzIZPfbv8yBo4M9PK/DXw8hLqH7VkkI+Gh793iH7Ugd4A7wvT
-----END CERTIFICATE-----`)
// LocalhostKey is the private key for localhostCert.
var LocalhostKey = []byte(`-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALSog3LcXiirq+IO
eWkMMTknTyJJEaCCDoTKUkoEpl+mEQba5+ArvwO5+Xf7tvQuURjYB5kfC+Ic4OoL
1rqKGPVlhCTT92MCH4546EURa771P9U/yBUVqsWpPwM6nL6GsbtgmK9QjPBvh+Yl
BEjsVqPOycbIzouJctT7SrNvtk79AgMBAAECgYB1wMT1MBgbkFIXpXGTfAP1id61
rUTVBxCpkypx3ngHLjo46qRq5Hi72BN4FlTY8fugIudI8giP2FztkMvkiLDc4m0p
Gn+QMJzjlBjjTuNLvLy4aSmNRLIC3mtbx9PdU71DQswEpJHFj/vmsxbuSrG1I1YE
r1reuSo2ow6fOAjXLQJBANpz+RkOiPSPuvl+gi1sp2pLuynUJVDVqWZi386YRpfg
DiKCLpqwqYDkOozm/fwFALvwXKGmsyyL43HO8eI+2NsCQQDTtY32V+02GPecdsyq
msK06EPVTSaYwj9Mm+q709KsmYFHLXDqXjcKV4UgKYKRPz7my1fXodMmGmfuh1a3
/HMHAkEAmOQKN0tA90mRJwUvvvMIyRBv0fq0kzq28P3KfiF9ZtZdjjFmxMVYHOmf
QPZ6VGR7+w1jB5BQXqEZcpHQIPSzeQJBAIy9tZJ/AYNlNbcegxEnsSjy/6VdlLsY
51vWi0Yym2uC4R6gZuBnoc+OP0ISVmqY0Qg9RjhjrCs4gr9f2ZaWjSECQCxqZMq1
3viJ8BGCC0m/5jv1EHur3YgwphYCkf4Li6DKwIdMLk1WXkTcPIY3V2Jqj8rPEB5V
rqPRSAtd/h6oZbs=
-----END PRIVATE KEY-----`)
// openssl req -newkey rsa:2048 \
// -new -nodes -x509 \
// -days 3650 \
// -out cert.pem \
// -keyout key.pem \
// -subj "/CN=example.com"
// -addext "subjectAltName = DNS:example.com"
var mTLSCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDJTCCAg2gAwIBAgIUYKnGcLnmMosOSKqTn4ydAMURE4gwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjAwODEzMDkyNzIwWhcNMzAw
ODExMDkyNzIwWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAOAe+QM1c9lZ2TPRgoiuPAq2A3Pfu+i82lmqrTJ0
PR2Cx1fPbccCUTFJPlxSDiaMrwtvqw1yP9L2Pu/vJK5BY4YDVDtFGKjpRBau1otJ
iY50O5qMo3sfLqR4/1VsQGlLVZYLD3dyc4ZTmOp8+7tJ2SyGorojbIKfimZT7XD7
dzrVr4h4Gn+SzzOnoKyx29uaNRP+XuMYHmHyQcJE03pUGhkTOvMwBlF96QdQ9WG0
D+1CxRciEsZXE+imKBHoaTgrTkpnFHzsrIEw+OHQYf30zuT/k/lkgv1vqEwINHjz
W2VeTur5eqVvA7zZdoEXMRy7BUvh/nZk5AXkXAmZLn0eUg8CAwEAAaNrMGkwHQYD
VR0OBBYEFEDrbhPDt+hi3ZOzk6S/CFAVHwk0MB8GA1UdIwQYMBaAFEDrbhPDt+hi
3ZOzk6S/CFAVHwk0MA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILZXhhbXBs
ZS5jb20wDQYJKoZIhvcNAQELBQADggEBAG/JRJWeUNx2mDJAk8W7Syq3gmQB7s9f
+yY/XVRJZGahOPilABqFpC6GVn2HWuvuOqy8/RGk9ja5abKVXqE6YKrljqo3XfzB
KQcOz4SFirpkHvNCiEcK3kggN3wJWqL2QyXAxWldBBBCO9yx7a3cux31C//sTUOG
xq4JZDg171U1UOpfN1t0BFMdt05XZFEM247N7Dcf7HoXwAa7eyLKgtKWqPDqGrFa
fvGDDKK9X/KVsU2x9V3pG+LsJg7ogUnSyD2r5G1F3Y8OVs2T/783PaN0M35fDL38
09VbsxA2GasOHZrghUzT4UvZWWZbWEmG975hFYvdj6DlK9K0s5TdKIs=
-----END CERTIFICATE-----`)
var mTLSKey = []byte(`-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDgHvkDNXPZWdkz
0YKIrjwKtgNz37vovNpZqq0ydD0dgsdXz23HAlExST5cUg4mjK8Lb6sNcj/S9j7v
7ySuQWOGA1Q7RRio6UQWrtaLSYmOdDuajKN7Hy6keP9VbEBpS1WWCw93cnOGU5jq
fPu7SdkshqK6I2yCn4pmU+1w+3c61a+IeBp/ks8zp6CssdvbmjUT/l7jGB5h8kHC
RNN6VBoZEzrzMAZRfekHUPVhtA/tQsUXIhLGVxPopigR6Gk4K05KZxR87KyBMPjh
0GH99M7k/5P5ZIL9b6hMCDR481tlXk7q+XqlbwO82XaBFzEcuwVL4f52ZOQF5FwJ
mS59HlIPAgMBAAECggEAAKLV3hZ2v7UrkqQTlMO50+X0WI3YAK8Yh4yedTgzPDQ0
0KD8FMaC6HrmvGhXNfDMRmIIwD8Ew1qDjzbEieIRoD2+LXTivwf6c34HidmplEfs
K2IezKin/zuArgNio2ndUlGxt4sRnN373x5/sGZjQWcYayLSmgRN5kByuhFco0Qa
oSrXcXNUlb+KgRQXPDU4+M35tPHvLdyg+tko/m/5uK9dc9MNvGZHOMBKg0VNURJb
V1l3dR+evwvpqHzBvWiqN/YOiUUvIxlFKA35hJkfCl7ivFs4CLqqFNCKDao95fWe
s0UR9iMakT48jXV76IfwZnyX10OhIWzKls5trjDL8QKBgQD3thQJ8e0FL9y1W+Ph
mCdEaoffSPkgSn64wIsQ9bMmv4y+KYBK5AhqaHgYm4LgW4x1+CURNFu+YFEyaNNA
kNCXFyRX3Em3vxcShP5jIqg+f07mtXPKntWP/zBeKQWgdHX371oFTfaAlNuKX/7S
n0jBYjr4Iof1bnquMQvUoHCYWwKBgQDnntFU9/AQGaQIvhfeU1XKFkQ/BfhIsd27
RlmiCi0ee9Ce74cMAhWr/9yg0XUxzrh+Ui1xnkMVTZ5P8tWIxROokznLUTGJA5rs
zB+ovCPFZcquTwNzn7SBnpHTR0OqJd8sd89P5ST2SqufeSF/gGi5sTs4EocOLCpZ
EPVIfm47XQKBgB4d5RHQeCDJUOw739jtxthqm1pqZN+oLwAHaOEG/mEXqOT15sM0
NlG5oeBcB+1/M/Sj1t3gn8blrvmSBR00fifgiGqmPdA5S3TU9pjW/d2bXNxv80QP
S6fWPusz0ZtQjYc3cppygCXh808/nJu/AfmBF+pTSHRumjvTery/RPFBAoGBAMi/
zCta4cTylEvHhqR5kiefePMu120aTFYeuV1KeKStJ7o5XNE5lVMIZk80e+D5jMpf
q2eIhhgWuBoPHKh4N3uqbzMbYlWgvEx09xOmTVKv0SWW8iTqzOZza2y1nZ4BSRcf
mJ1ku86EFZAYysHZp+saA3usA0ZzXRjpK87zVdM5AoGBAOSqI+t48PnPtaUDFdpd
taNNVDbcecJatm3w8VDWnarahfWe66FIqc9wUkqekqAgwZLa0AGdUalvXfGrHfNs
PtvuNc5EImfSkuPBYLBslNxtjbBvAYgacEdY+gRhn2TeIUApnND58lCWsKbNHLFZ
ajIPbTY+Fe9OTOFTN48ujXNn
-----END PRIVATE KEY-----`)
func TestKeepConnectionWhenSameConfiguration(t *testing.T) {
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
connCount := Int32(0)
srv.Config.ConnState = func(conn net.Conn, state http.ConnState) {
if state == http.StateNew {
atomic.AddInt32(connCount, 1)
}
}
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
require.NoError(t, err)
srv.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
srv.StartTLS()
rtManager := NewRoundTripperManager()
dynamicConf := map[string]*dynamic.ServersTransport{
"test": {
ServerName: "example.com",
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
},
}
for i := 0; i < 10; i++ {
rtManager.Update(dynamicConf)
tr, err := rtManager.Get("test")
require.NoError(t, err)
client := http.Client{Transport: tr}
resp, err := client.Get(srv.URL)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
}
count := atomic.LoadInt32(connCount)
require.EqualValues(t, 1, count)
dynamicConf = map[string]*dynamic.ServersTransport{
"test": {
ServerName: "www.example.com",
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
},
}
rtManager.Update(dynamicConf)
tr, err := rtManager.Get("test")
require.NoError(t, err)
client := http.Client{Transport: tr}
resp, err := client.Get(srv.URL)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
count = atomic.LoadInt32(connCount)
assert.EqualValues(t, 2, count)
}
func TestMTLS(t *testing.T) {
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
require.NoError(t, err)
clientPool := x509.NewCertPool()
clientPool.AppendCertsFromPEM(mTLSCert)
srv.TLS = &tls.Config{
// For TLS
Certificates: []tls.Certificate{cert},
// For mTLS
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientPool,
}
srv.StartTLS()
rtManager := NewRoundTripperManager()
dynamicConf := map[string]*dynamic.ServersTransport{
"test": {
ServerName: "example.com",
// For TLS
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
// For mTLS
Certificates: traefiktls.Certificates{
traefiktls.Certificate{
CertFile: traefiktls.FileOrContent(mTLSCert),
KeyFile: traefiktls.FileOrContent(mTLSKey),
},
},
},
}
rtManager.Update(dynamicConf)
tr, err := rtManager.Get("test")
require.NoError(t, err)
client := http.Client{Transport: tr}
resp, err := client.Get(srv.URL)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}

View file

@ -35,13 +35,18 @@ const (
const defaultMaxBodySize int64 = -1
// RoundTripperGetter is a roundtripper getter interface.
type RoundTripperGetter interface {
Get(name string) (http.RoundTripper, error)
}
// NewManager creates a new Manager.
func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper http.RoundTripper, metricsRegistry metrics.Registry, routinePool *safe.Pool) *Manager {
func NewManager(configs map[string]*runtime.ServiceInfo, metricsRegistry metrics.Registry, routinePool *safe.Pool, roundTripperManager RoundTripperGetter) *Manager {
return &Manager{
routinePool: routinePool,
metricsRegistry: metricsRegistry,
bufferPool: newBufferPool(),
defaultRoundTripper: defaultRoundTripper,
roundTripperManager: roundTripperManager,
balancers: make(map[string]healthcheck.Balancers),
configs: configs,
}
@ -52,7 +57,7 @@ type Manager struct {
routinePool *safe.Pool
metricsRegistry metrics.Registry
bufferPool httputil.BufferPool
defaultRoundTripper http.RoundTripper
roundTripperManager RoundTripperGetter
// balancers is the map of all Balancers, keyed by service name.
// There is one Balancer per service handler, and there is one service handler per reference to a service
// (e.g. if 2 routers refer to the same service name, 2 service handlers are created),
@ -168,7 +173,16 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
service.PassHostHeader = &defaultPassHostHeader
}
fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, m.defaultRoundTripper, m.bufferPool)
if len(service.ServersTransport) > 0 {
service.ServersTransport = provider.GetQualifiedName(ctx, service.ServersTransport)
}
roundTripper, err := m.roundTripperManager.Get(service.ServersTransport)
if err != nil {
return nil, err
}
fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, roundTripper, m.bufferPool)
if err != nil {
return nil, err
}
@ -213,7 +227,7 @@ func (m *Manager) LaunchHealthCheck() {
if hcOpts := buildHealthCheckOptions(ctx, balancers, serviceName, service.HealthCheck); hcOpts != nil {
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
hcOpts.Transport = m.defaultRoundTripper
hcOpts.Transport, _ = m.roundTripperManager.Get(service.ServersTransport)
backendHealthCheck = healthcheck.NewBackendConfig(*hcOpts, serviceName)
}

View file

@ -80,7 +80,11 @@ func TestGetLoadBalancer(t *testing.T) {
}
func TestGetLoadBalancerServiceHandler(t *testing.T) {
sm := NewManager(nil, http.DefaultTransport, nil, nil)
sm := NewManager(nil, nil, nil, &RoundTripperManager{
roundTrippers: map[string]http.RoundTripper{
"default@internal": http.DefaultTransport,
},
})
server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-From", "first")
@ -332,7 +336,11 @@ func TestManager_Build(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
manager := NewManager(test.configs, http.DefaultTransport, nil, nil)
manager := NewManager(test.configs, nil, nil, &RoundTripperManager{
roundTrippers: map[string]http.RoundTripper{
"default@internal": http.DefaultTransport,
},
})
ctx := context.Background()
if len(test.providerName) > 0 {
@ -355,7 +363,11 @@ func TestMultipleTypeOnBuildHTTP(t *testing.T) {
},
}
manager := NewManager(services, http.DefaultTransport, nil, nil)
manager := NewManager(services, nil, nil, &RoundTripperManager{
roundTrippers: map[string]http.RoundTripper{
"default@internal": http.DefaultTransport,
},
})
_, err := manager.BuildHTTP(context.Background(), "test@file")
assert.Error(t, err, "cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")

View file

@ -8,6 +8,7 @@ import (
func BuildConfiguration(dynamicConfigBuilders ...func(*dynamic.HTTPConfiguration)) *dynamic.HTTPConfiguration {
conf := &dynamic.HTTPConfiguration{
Models: map[string]*dynamic.Model{},
ServersTransports: map[string]*dynamic.ServersTransport{},
}
for _, build := range dynamicConfigBuilders {

View file

@ -56,6 +56,23 @@ type Certificate struct {
// Certs and Keys could be either a file path, or the file content itself.
type Certificates []Certificate
// GetCertificates retrieves the certificates as slice of tls.Certificate.
func (c Certificates) GetCertificates() []tls.Certificate {
var certs []tls.Certificate
for _, certificate := range c {
cert, err := certificate.GetCertificate()
if err != nil {
log.WithoutContext().Debugf("Error while getting certificate: %v", err)
continue
}
certs = append(certs, cert)
}
return certs
}
// FileOrContent hold a file path or content.
type FileOrContent string
@ -190,6 +207,26 @@ func (c *Certificate) AppendCertificate(certs map[string]map[string]*tls.Certifi
return err
}
// GetCertificate retrieves Certificate as tls.Certificate.
func (c *Certificate) GetCertificate() (tls.Certificate, error) {
certContent, err := c.CertFile.Read()
if err != nil {
return tls.Certificate{}, fmt.Errorf("unable to read CertFile : %w", err)
}
keyContent, err := c.KeyFile.Read()
if err != nil {
return tls.Certificate{}, fmt.Errorf("unable to read KeyFile : %w", err)
}
cert, err := tls.X509KeyPair(certContent, keyContent)
if err != nil {
return tls.Certificate{}, fmt.Errorf("unable to generate TLS certificate : %w", err)
}
return cert, nil
}
// GetTruncatedCertificateName truncates the certificate name.
func (c *Certificate) GetTruncatedCertificateName() string {
certName := c.CertFile.String()