Merge current v2.5 into master

This commit is contained in:
Tom Moulard 2021-12-06 17:19:29 +01:00
commit 89cd9e8ddd
No known key found for this signature in database
GPG key ID: 521ABE0C1A0DEAF6
102 changed files with 2402 additions and 1429 deletions

View file

@ -48,6 +48,7 @@
extensionsv1beta1 = "k8s.io/api/extensions/v1beta1"
metav1 = "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeerror = "k8s.io/apimachinery/pkg/api/errors"
composeapi = "github.com/docker/compose/v2/pkg/api"
[linters-settings.gomoddirectives]
replace-allow-list = [
@ -56,6 +57,7 @@
"github.com/gorilla/mux",
"github.com/mailgun/minheap",
"github.com/mailgun/multibuf",
"github.com/jaguilar/vt100",
]
[linters]

View file

@ -31,42 +31,24 @@ global_job_config:
- cache restore traefik-$(checksum go.sum)
blocks:
- name: Test Integration Container
- name: Test Integration
dependencies: []
run:
when: "branch =~ '.*' OR pull_request =~'.*'"
task:
jobs:
- name: Test Integration Container
- name: Test Integration
commands:
- make pull-images
- mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui
- PRE_TARGET="" make binary
- make test-integration-container
- make test-integration
- df -h
epilogue:
always:
commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
- name: Test Integration Host
dependencies: []
run:
when: "branch =~ '.*' OR pull_request =~'.*'"
task:
env_vars:
- name: PRE_TARGET
value: ""
jobs:
- name: Test Integration Host
commands:
- mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui
- make test-integration-host
epilogue:
always:
commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
- name: Release
dependencies: []
run:

View file

@ -15,7 +15,7 @@ TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)",-v "/var/run/docker.sock:/var/run/docker.sock")
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
TRAEFIK_ENVS := \
@ -32,7 +32,8 @@ TRAEFIK_ENVS := \
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/traefik/traefik/$(BIND_DIR)"
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_NON_INTERACTIVE ?= false
DOCKER_RUN_TRAEFIK := docker run --add-host=host.docker.internal:127.0.0.1 $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_TEST := docker run --add-host=host.docker.internal:127.0.0.1 --rm --name=traefik --network traefik-test-network -v $(PWD):$(PWD) -w $(PWD) $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
PRE_TARGET ?= build-dev-image
@ -81,30 +82,27 @@ crossbinary-default-parallel:
$(MAKE) build-dev-image crossbinary-default
## Run the unit and integration tests
test: build-dev-image
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration
test: $(PRE_TARGET)
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_TEST),) ./script/make.sh generate test-unit binary test-integration
## Run the unit tests
test-unit: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate test-unit
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
## Run the integration tests
test-integration: $(PRE_TARGET)
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_TEST),) ./script/make.sh generate binary test-integration
## Pull all images for integration tests
pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
## Run the integration tests
test-integration: $(PRE_TARGET) binary
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh test-integration
TEST_HOST=1 ./script/make.sh test-integration
## Run the container integration tests
test-integration-container: $(PRE_TARGET) binary
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh test-integration
## Run the host integration tests
test-integration-host: $(PRE_TARGET) binary
TEST_HOST=1 ./script/make.sh test-integration
## Validate code and docs
validate-files: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell

View file

@ -349,12 +349,16 @@ http:
### `tls`
The `tls` option is the TLS configuration from Traefik to the authentication server.
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to the authentication server.
Certificate Authority used for the secured connection to the authentication server,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secured connection to the authentication server,
it defaults to the system bundle.
```yaml tab="Docker"
labels:
@ -417,13 +421,15 @@ http:
ca = "path/to/local.crt"
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to the authentication server.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to the authentication server.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -479,9 +485,12 @@ http:
caOptional = true
```
#### `tls.cert`
#### `cert`
The public certificate used for the secure connection to the authentication server.
_Optional_
`cert` is the path to the public certificate used for the secure connection to the authentication server.
When using this option, setting the `key` option is required.
```yaml tab="Docker"
labels:
@ -554,9 +563,12 @@ http:
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
#### `tls.key`
#### `key`
The private certificate used for the secure connection to the authentication server.
_Optional_
`key` is the path to the private key used for the secure connection to the authentication server.
When using this option, setting the `cert` option is required.
```yaml tab="Docker"
labels:
@ -629,7 +641,9 @@ http:
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to the authentication server accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -7,22 +7,6 @@ Traefik supports 4 metrics backends:
- [Prometheus](./prometheus.md)
- [StatsD](./statsd.md)
## Configuration
To enable metrics:
```yaml tab="File (YAML)"
metrics: {}
```
```toml tab="File (TOML)"
[metrics]
```
```bash tab="CLI"
--metrics=true
```
## Server Metrics
| Metric | DataDog | InfluxDB | Prometheus | StatsD |

View file

@ -362,14 +362,14 @@ providers:
_Optional_
Defines TLS options for Consul server endpoint.
Defines the TLS configuration used for the secure connection to Consul Catalog.
##### `ca`
_Optional_
Certificate Authority used for the secure connection to Consul,
defaults to the system bundle.
`ca` is the path to the certificate authority used for the secure connection to Consul Catalog,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -392,11 +392,11 @@ providers:
_Optional_
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Consul.
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Consul Catalog.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -423,8 +423,7 @@ providers:
_Optional_
`cert` is the path to the public certificate to use for Consul communication.
`cert` is the path to the public certificate used for the secure connection to Consul Catalog.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
@ -451,8 +450,7 @@ providers:
_Optional_
`key` is the path to the private key for Consul communication.
`key` is the path to the private key used for the secure connection to Consul Catalog.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
@ -477,7 +475,7 @@ providers:
##### `insecureSkipVerify`
_Optional_
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Consul accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -104,10 +104,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to Consul.
Certificate Authority used for the secure connection to Consul,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to Consul,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -125,13 +129,15 @@ providers:
--providers.consul.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Consul.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Consul.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -153,9 +159,12 @@ providers:
--providers.consul.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to Consul.
_Optional_
`cert` is the path to the public certificate used for the secure connection to Consul.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -176,9 +185,12 @@ providers:
--providers.consul.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to Consul.
_Optional_
`key` is the path to the private key used for the secure connection to Consul.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -199,7 +211,9 @@ providers:
--providers.consul.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Consul accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -613,10 +613,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to Docker.
Certificate Authority used for the secure connection to Docker,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to Docker,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -634,13 +638,15 @@ providers:
--providers.docker.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Docker.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Docker.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -662,9 +668,10 @@ providers:
--providers.docker.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to Docker.
`cert` is the path to the public certificate used for the secure connection to Docker.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -685,9 +692,12 @@ providers:
--providers.docker.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to Docker.
_Optional_
`key` is the path to the private key used for the secure connection Docker.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -708,7 +718,9 @@ providers:
--providers.docker.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Docker accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -104,10 +104,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to etcd.
Certificate Authority used for the secure connection to etcd,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to etcd,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -125,13 +129,15 @@ providers:
--providers.etcd.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to etcd.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to etcd.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -153,9 +159,12 @@ providers:
--providers.etcd.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to etcd.
_Optional_
`cert` is the path to the public certificate used for the secure connection to etcd.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -176,9 +185,12 @@ providers:
--providers.etcd.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to etcd.
_Optional_
`key` is the path to the private key used for the secure connection to etcd.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -199,7 +211,9 @@ providers:
--providers.etcd.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to etcd accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -55,7 +55,7 @@ providers:
_Optional, Default="5s"_
Defines the polling timeout when connecting to the configured endpoint.
Defines the polling timeout when connecting to the endpoint.
```yaml tab="File (YAML)"
providers:
@ -76,10 +76,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to the endpoint.
Certificate Authority used for the secure connection to the configured endpoint,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to the endpoint,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -97,13 +101,15 @@ providers:
--providers.http.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to the configured endpoint.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to the endpoint.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -125,9 +131,12 @@ providers:
--providers.http.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to the configured endpoint.
_Optional_
`cert` is the path to the public certificate used for the secure connection to the endpoint.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -148,9 +157,12 @@ providers:
--providers.http.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to the configured endpoint.
_Optional_
`key` is the path to the private key used for the secure connection to the endpoint.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -171,7 +183,9 @@ providers:
--providers.http.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to the endpoint accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -404,10 +404,12 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to Marathon.
Certificate Authority used for the secure connection to Marathon,
defaults to the system bundle.
#### `ca`
`ca` is the path to the certificate authority used for the secure connection to Marathon,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -425,13 +427,15 @@ providers:
--providers.marathon.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Marathon.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Marathon.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -453,9 +457,12 @@ providers:
--providers.marathon.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to Marathon.
_Optional_
`cert` is the path to the public certificate used for the secure connection to Marathon.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -476,9 +483,12 @@ providers:
--providers.marathon.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to Marathon.
_Optional_
`key` is the path to the private key used for the secure connection to Marathon.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -499,7 +509,9 @@ providers:
--providers.marathon.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Marathon accepts any certificate presented by the server regardless of the hostnames it covers.
@ -532,18 +544,18 @@ see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
```yaml tab="File (YAML)"
providers:
marathon:
responseHeaderTimeout: "10s"
tlsHandshakeTimeout: "10s"
# ...
```
```toml tab="File (TOML)"
[providers.marathon]
responseHeaderTimeout = "10s"
tlsHandshakeTimeout = "10s"
# ...
```
```bash tab="CLI"
--providers.marathon.responseHeaderTimeout=10s
--providers.marathon.tlsHandshakeTimeout=10s
# ...
```

View file

@ -104,10 +104,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to Redis.
Certificate Authority used for the secure connection to Redis,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to Redis,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -125,13 +129,15 @@ providers:
--providers.redis.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Redis.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Redis.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -153,9 +159,12 @@ providers:
--providers.redis.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to Redis.
_Optional_
`cert` is the path to the public certificate used for the secure connection to Redis.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -176,9 +185,12 @@ providers:
--providers.redis.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to Redis.
_Optional_
`key` is the path to the private key used for the secure connection to Redis.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -199,7 +211,9 @@ providers:
--providers.redis.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Redis accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -104,10 +104,14 @@ providers:
_Optional_
#### `tls.ca`
Defines the TLS configuration used for the secure connection to ZooKeeper.
Certificate Authority used for the secure connection to ZooKeeper,
defaults to the system bundle.
#### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to ZooKeeper,
it defaults to the system bundle.
```yaml tab="File (YAML)"
providers:
@ -125,13 +129,15 @@ providers:
--providers.zookeeper.tls.ca=path/to/ca.crt
```
#### `tls.caOptional`
#### `caOptional`
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Zookeeper.
_Optional_
The value of `caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to Zookeeper.
!!! warning ""
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
If `ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
@ -153,9 +159,12 @@ providers:
--providers.zookeeper.tls.caOptional=true
```
#### `tls.cert`
#### `cert`
Public certificate used for the secure connection to ZooKeeper.
_Optional_
`cert` is the path to the public certificate used for the secure connection to ZooKeeper.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
providers:
@ -176,9 +185,12 @@ providers:
--providers.zookeeper.tls.key=path/to/foo.key
```
#### `tls.key`
#### `key`
Private certificate used for the secure connection to ZooKeeper.
_Optional_
`key` is the path to the private key used for the secure connection to ZooKeeper.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
providers:
@ -199,7 +211,9 @@ providers:
--providers.zookeeper.tls.key=path/to/foo.key
```
#### `tls.insecureSkipVerify`
#### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`, the TLS connection to Zookeeper accepts any certificate presented by the server regardless of the hostnames it covers.

View file

@ -63,7 +63,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m
See [tls](../routers/index.md#tls) for more information.
```yaml
traefik.http.routers.myrouter>.tls=true
traefik.http.routers.myrouter.tls=true
```
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
@ -136,7 +136,7 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass
See [serverstransport](../services/index.md#serverstransport) for more information.
```yaml
traefik.http.services.<service_name>.loadbalancer.serverstransport=foobar@file
traefik.http.services.myservice.loadbalancer.serverstransport=foobar@file
```
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"

View file

@ -251,7 +251,7 @@ The table below lists all the available matchers:
`HostRegexp` and `Path` accept an expression with zero or more groups enclosed by curly braces.
Named groups can be like `{name:pattern}` that matches the given regexp pattern or like `{name}` that matches anything until the next dot.
The group name (`name` is the above examples) is an arbitrary value.
The group name (`name` in the above examples) is an arbitrary value.
Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used (example: `{subdomain:[a-z]+}.{domain}.com`).
!!! info "Combining Matchers Using Operators and Parenthesis"

View file

@ -336,11 +336,11 @@ Below are the available options for the health check mechanism:
Traefik keeps monitoring the health of unhealthy servers.
If a server has recovered (returning `2xx` -> `3xx` responses again), it will be added back to the load balancer rotation pool.
!!! warning "Health check in Kubernetes"
!!! warning "Health check with Kubernetes"
The Traefik health check is not available for `kubernetesCRD` and `kubernetesIngress` providers because Kubernetes
already has a health check mechanism.
Unhealthy pods will be removed by kubernetes. (cf [liveness documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request))
Kubernetes has an health check mechanism to remove unhealthy pods from Kubernetes services (cf [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes)).
As unhealthy pods have no Kubernetes endpoints, Traefik will not forward traffic to them.
Therefore, Traefik health check is not available for `kubernetesCRD` and `kubernetesIngress` providers.
??? example "Custom Interval & Timeout -- Using the [File Provider](../../providers/file.md)"

36
go.mod
View file

@ -7,29 +7,25 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61
github.com/Masterminds/sprig/v3 v3.2.2
github.com/Microsoft/hcsshim v0.8.7 // indirect
github.com/Shopify/sarama v1.23.1 // indirect
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
github.com/abronan/valkeyrie v0.2.0
github.com/aws/aws-sdk-go v1.39.0
github.com/cenkalti/backoff/v4 v4.1.1
github.com/containerd/containerd v1.3.2 // indirect
github.com/compose-spec/compose-go v1.0.3
github.com/containerd/containerd v1.5.8 // indirect
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/davecgh/go-spew v1.1.1
github.com/docker/cli v0.0.0-20200221155518-740919cc7fc0
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v17.12.0-ce-rc1.0.20200204220554-5f6d6f3f2203+incompatible
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/cli v20.10.11+incompatible
github.com/docker/compose/v2 v2.0.1
github.com/docker/docker v20.10.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect
github.com/docker/libcompose v0.0.0-20190805081528-eac9fe1b8b03 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e // indirect
github.com/eapache/channels v1.1.0
github.com/fatih/structs v1.1.0
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
github.com/go-acme/lego/v4 v4.5.0
github.com/go-acme/lego/v4 v4.5.3
github.com/go-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
github.com/golang/protobuf v1.5.2
@ -40,24 +36,17 @@ require (
github.com/hashicorp/consul/api v1.10.0
github.com/hashicorp/go-hclog v0.16.1
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.2.1
github.com/hashicorp/go-version v1.3.0
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
github.com/instana/go-sensor v1.31.1
github.com/klauspost/compress v1.13.0
github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad
github.com/libkermit/docker v0.0.0-20171122101128-e6674d32b807
github.com/libkermit/docker-check v0.0.0-20171122104347-1113af38e591
github.com/lucas-clemente/quic-go v0.23.0
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f
github.com/miekg/dns v1.1.43
github.com/mitchellh/copystructure v1.0.0
github.com/mitchellh/hashstructure v1.0.0
github.com/mitchellh/mapstructure v1.4.1
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v1.0.0-rc10 // indirect
github.com/opentracing/opentracing-go v1.1.0
github.com/mitchellh/mapstructure v1.4.2
github.com/opentracing/opentracing-go v1.2.0
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
github.com/openzipkin/zipkin-go v0.2.2
github.com/patrickmn/go-cache v2.1.0+incompatible
@ -67,12 +56,12 @@ require (
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/client_model v0.2.0
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac
github.com/sirupsen/logrus v1.7.0
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/tinylib/msgp v1.0.2 // indirect
github.com/traefik/paerser v0.1.4
github.com/traefik/yaegi v0.10.0
github.com/traefik/yaegi v0.11.1
github.com/uber/jaeger-client-go v2.29.1+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2
@ -108,3 +97,6 @@ replace (
github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595
github.com/mailgun/multibuf => github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba
)
// https://github.com/docker/compose/blob/e44222664abd07ce1d1fe6796d84d93cbc7468c3/go.mod#L131
replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305

680
go.sum

File diff suppressed because it is too large Load diff

View file

@ -36,12 +36,7 @@ type accessLogValue struct {
func (s *AccessLogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log")
s.composeProject.Start(c)
s.composeProject.Container(c, "server0")
s.composeProject.Container(c, "server1")
s.composeProject.Container(c, "server2")
s.composeProject.Container(c, "server3")
s.composeUp(c)
}
func (s *AccessLogSuite) TearDownTest(c *check.C) {
@ -122,7 +117,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
code: "200",
user: "test",
routerName: "rt-authFrontend",
serviceURL: "http://172.17.0",
serviceURL: "http://172.31.42",
},
}
@ -136,8 +131,6 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "authFrontend")
waitForTraefik(c, "authFrontend")
// Verify Traefik started OK
@ -193,7 +186,7 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
code: "200",
user: "test",
routerName: "rt-digestAuthMiddleware",
serviceURL: "http://172.17.0",
serviceURL: "http://172.31.42",
},
}
@ -207,8 +200,6 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "digestAuthMiddleware")
waitForTraefik(c, "digestAuthMiddleware")
// Verify Traefik started OK
@ -322,8 +313,6 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendRedirect")
waitForTraefik(c, "frontendRedirect")
// Verify Traefik started OK
@ -375,8 +364,6 @@ func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "rateLimit")
waitForTraefik(c, "rateLimit")
// Verify Traefik started OK
@ -471,8 +458,6 @@ func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendWhitelist")
waitForTraefik(c, "frontendWhitelist")
// Verify Traefik started OK
@ -504,7 +489,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
code: "200",
user: "test",
routerName: "rt-authFrontend",
serviceURL: "http://172.17.0",
serviceURL: "http://172.31.42",
},
}
@ -518,8 +503,6 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
checkStatsForLogFile(c)
s.composeProject.Container(c, "authFrontend")
waitForTraefik(c, "authFrontend")
// Verify Traefik started OK
@ -548,7 +531,6 @@ func checkNoOtherTraefikProblems(c *check.C) {
c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog))
c.Assert(traefikLog, checker.HasLen, 0)
}
}
@ -616,7 +598,6 @@ func checkTraefikStarted(c *check.C) []byte {
c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog))
c.Assert(traefikLog, checker.HasLen, 0)
}
return traefikLog
}

View file

@ -4,6 +4,7 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
@ -19,7 +20,7 @@ import (
checker "github.com/vdemeester/shakers"
)
// ACME test suites (using libcompose).
// ACME test suites.
type AcmeSuite struct {
BaseSuite
pebbleIP string
@ -54,7 +55,8 @@ const (
)
func (s *AcmeSuite) getAcmeURL() string {
return fmt.Sprintf("https://%s:14000/dir", s.pebbleIP)
return fmt.Sprintf("https://%s/dir",
net.JoinHostPort(s.pebbleIP, "14000"))
}
func setupPebbleRootCA() (*http.Transport, error) {
@ -86,11 +88,10 @@ func setupPebbleRootCA() (*http.Transport, error) {
func (s *AcmeSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "pebble")
s.composeProject.Start(c)
s.composeUp(c)
s.fakeDNSServer = startFakeDNSServer()
s.pebbleIP = s.composeProject.Container(c, "pebble").NetworkSettings.IPAddress
s.fakeDNSServer = startFakeDNSServer(s.getContainerIP(c, "traefik"))
s.pebbleIP = s.getComposeServiceIP(c, "pebble")
pebbleTransport, err := setupPebbleRootCA()
if err != nil {
@ -115,15 +116,14 @@ func (s *AcmeSuite) SetUpSuite(c *check.C) {
}
func (s *AcmeSuite) TearDownSuite(c *check.C) {
if s.fakeDNSServer != nil {
err := s.fakeDNSServer.Shutdown()
if err != nil {
c.Log(err)
}
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
s.composeDown(c)
}
func (s *AcmeSuite) TestHTTP01Domains(c *check.C) {

View file

@ -2,6 +2,7 @@ package integration
import (
"fmt"
"net"
"net/http"
"os"
"time"
@ -16,30 +17,29 @@ type ConsulCatalogSuite struct {
BaseSuite
consulClient *api.Client
consulAgentClient *api.Client
consulAddress string
consulAgentAddress string
consulURL string
}
func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "consul_catalog")
s.composeProject.Start(c)
s.consulAddress = "http://" + s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"
client, err := api.NewClient(&api.Config{
Address: s.consulAddress,
s.composeUp(c)
s.consulURL = "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "consul"), "8500")
var err error
s.consulClient, err = api.NewClient(&api.Config{
Address: s.consulURL,
})
c.Check(err, check.IsNil)
s.consulClient = client
// Wait for consul to elect itself leader
err = s.waitToElectConsulLeader()
c.Assert(err, checker.IsNil)
s.consulAgentAddress = "http://" + s.composeProject.Container(c, "consul-agent").NetworkSettings.IPAddress + ":8500"
clientAgent, err := api.NewClient(&api.Config{
Address: s.consulAgentAddress,
s.consulAgentClient, err = api.NewClient(&api.Config{
Address: "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "consul-agent"), "8500"),
})
c.Check(err, check.IsNil)
s.consulAgentClient = clientAgent
}
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
@ -66,13 +66,6 @@ func (s *ConsulCatalogSuite) waitForConnectCA() error {
})
}
func (s *ConsulCatalogSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *ConsulCatalogSuite) registerService(reg *api.AgentServiceRegistration, onAgent bool) error {
client := s.consulClient
if onAgent {
@ -96,7 +89,7 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
@ -106,7 +99,7 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami2"),
}
err = s.registerService(reg2, false)
c.Assert(err, checker.IsNil)
@ -116,7 +109,7 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami3"),
}
err = s.registerService(reg3, false)
c.Assert(err, checker.IsNil)
@ -124,7 +117,7 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
@ -163,7 +156,7 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
}
func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
containerIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
containerIP := s.getComposeServiceIP(c, "whoami1")
reg := &api.AgentServiceRegistration{
ID: "whoami1",
@ -183,7 +176,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
@ -195,7 +188,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
err = try.GetRequest("http://127.0.0.1:8000/whoami", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
c.Assert(err, checker.IsNil)
err = s.deregisterService("whoami1", false)
@ -207,7 +200,7 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -219,7 +212,7 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
@ -246,7 +239,7 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -285,7 +278,7 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -296,7 +289,7 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
ID: "whoami1",
Name: "whoami",
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
@ -324,7 +317,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -341,7 +334,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
"traefik.tcp.Services.Super.Loadbalancer.server.port=8080",
},
Port: 8080,
Address: s.composeProject.Container(c, "whoamitcp").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoamitcp"),
}
err := s.registerService(reg, false)
@ -371,7 +364,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -386,7 +379,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
"traefik.http.Routers.Super.Rule=Host(`my.super.host`)",
},
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg1, false)
@ -400,7 +393,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
"traefik.http.Routers.SuperHost.Rule=Host(`my-super.host`)",
},
Port: 80,
Address: s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami2"),
}
err = s.registerService(reg2, false)
c.Assert(err, checker.IsNil)
@ -438,7 +431,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -457,7 +450,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
Name: "whoami",
Tags: tags,
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
@ -467,7 +460,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
Name: "whoami",
Tags: tags,
Port: 80,
Address: s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami2"),
}
err = s.registerService(reg2, true)
c.Assert(err, checker.IsNil)
@ -490,8 +483,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
c.Assert(err, checker.IsNil)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200),
try.BodyContainsOr(s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress))
try.BodyContainsOr(s.getComposeServiceIP(c, "whoami1"), s.getComposeServiceIP(c, "whoami2")))
c.Assert(err, checker.IsNil)
err = s.deregisterService("whoami", false)
@ -506,7 +498,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
ConsulAddress string
DefaultRule string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
@ -521,7 +513,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
"traefik.random.value=my.super.host",
},
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: s.getComposeServiceIP(c, "whoami1"),
}
err := s.registerService(reg, false)
@ -546,11 +538,12 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
}
func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
whoamiIP := s.getComposeServiceIP(c, "whoami1")
tags := []string{
"traefik.enable=true",
"traefik.http.routers.router1.rule=Path(`/whoami`)",
"traefik.http.routers.router1.service=service1",
"traefik.http.services.service1.loadBalancer.server.url=http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
"traefik.http.services.service1.loadBalancer.server.url=http://" + whoamiIP,
}
reg1 := &api.AgentServiceRegistration{
@ -558,7 +551,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
Name: "whoami",
Tags: tags,
Port: 80,
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress,
Address: whoamiIP,
Check: &api.AgentServiceCheck{
CheckID: "some-failed-check",
TCP: "127.0.0.1:1234",
@ -574,7 +567,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
@ -592,17 +585,16 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
err = s.deregisterService("whoami1", false)
c.Assert(err, checker.IsNil)
containerIP := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
whoami2IP := s.getComposeServiceIP(c, "whoami2")
reg2 := &api.AgentServiceRegistration{
ID: "whoami2",
Name: "whoami",
Tags: tags,
Port: 80,
Address: containerIP,
Address: whoami2IP,
Check: &api.AgentServiceCheck{
CheckID: "some-ok-check",
TCP: containerIP + ":80",
TCP: whoami2IP + ":80",
Name: "some-ok-check",
Interval: "1s",
Timeout: "1s",
@ -629,7 +621,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
err := s.waitForConnectCA()
c.Assert(err, checker.IsNil)
connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress
connectIP := s.getComposeServiceIP(c, "connect")
reg := &api.AgentServiceRegistration{
ID: "uuid-api1",
Name: "uuid-api",
@ -649,7 +641,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
err = s.registerService(reg, false)
c.Assert(err, checker.IsNil)
whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
whoamiIP := s.getComposeServiceIP(c, "whoami1")
regWhoami := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
@ -667,7 +659,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect.toml", tempObjects)
defer os.Remove(file)
@ -695,7 +687,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
err := s.waitForConnectCA()
c.Assert(err, checker.IsNil)
connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress
connectIP := s.getComposeServiceIP(c, "connect")
reg := &api.AgentServiceRegistration{
ID: "uuid-api1",
Name: "uuid-api",
@ -714,7 +706,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
err = s.registerService(reg, false)
c.Assert(err, checker.IsNil)
whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
whoamiIP := s.getComposeServiceIP(c, "whoami1")
regWhoami := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami1",
@ -729,7 +721,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
err = s.registerService(regWhoami, false)
c.Assert(err, checker.IsNil)
whoami2IP := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
whoami2IP := s.getComposeServiceIP(c, "whoami2")
regWhoami2 := &api.AgentServiceRegistration{
ID: "whoami2",
Name: "whoami2",
@ -748,7 +740,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect_by_default.toml", tempObjects)
defer os.Remove(file)
@ -781,7 +773,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
err := s.waitForConnectCA()
c.Assert(err, checker.IsNil)
connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress
connectIP := s.getComposeServiceIP(c, "connect")
reg := &api.AgentServiceRegistration{
ID: "uuid-api1",
Name: "uuid-api",
@ -801,7 +793,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
err = s.registerService(reg, false)
c.Assert(err, checker.IsNil)
whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
whoamiIP := s.getComposeServiceIP(c, "whoami1")
regWhoami := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
@ -819,7 +811,7 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulAddress,
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect_not_aware.toml", tempObjects)
defer os.Remove(file)

View file

@ -3,6 +3,8 @@ package integration
import (
"bytes"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
@ -18,20 +20,24 @@ import (
checker "github.com/vdemeester/shakers"
)
// Consul test suites (using libcompose).
// Consul test suites.
type ConsulSuite struct {
BaseSuite
kvClient store.Store
consulURL string
}
func (s *ConsulSuite) setupStore(c *check.C) {
s.createComposeProject(c, "consul")
s.composeProject.Start(c)
s.composeUp(c)
consulAddr := net.JoinHostPort(s.getComposeServiceIP(c, "consul"), "8500")
s.consulURL = fmt.Sprintf("http://%s", consulAddr)
consul.Register()
kv, err := valkeyrie.NewStore(
store.CONSUL,
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"},
[]string{consulAddr},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
@ -46,20 +52,10 @@ func (s *ConsulSuite) setupStore(c *check.C) {
c.Assert(err, checker.IsNil)
}
func (s *ConsulSuite) TearDownTest(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *ConsulSuite) TearDownSuite(c *check.C) {}
func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c)
address := "http://" + s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{address})
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
defer os.Remove(file)
data := map[string]string{

View file

@ -14,33 +14,17 @@ import (
checker "github.com/vdemeester/shakers"
)
const (
composeProject = "minimal"
)
// Docker tests suite.
type DockerComposeSuite struct {
BaseSuite
}
func (s *DockerComposeSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, composeProject)
s.composeProject.Start(c)
}
func (s *DockerComposeSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
s.createComposeProject(c, "minimal")
s.composeUp(c)
}
func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
serviceCount := 2
composeService := "whoami1"
s.composeProject.Scale(c, composeService, serviceCount)
tempObjects := struct {
DockerHost string
DefaultRule string
@ -81,8 +65,8 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
if strings.HasSuffix(name, "@internal") {
continue
}
c.Assert(name, checker.Equals, composeService+"-integrationtest"+composeProject+"@docker")
c.Assert(service.LoadBalancer.Servers, checker.HasLen, serviceCount)
c.Assert(name, checker.Equals, "whoami1-"+s.composeProject.Name+"@docker")
c.Assert(service.LoadBalancer.Servers, checker.HasLen, 2)
// We could break here, but we don't just to keep us honest.
}
}

View file

@ -6,80 +6,24 @@ import (
"io"
"net/http"
"os"
"strings"
"time"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/go-check/check"
d "github.com/libkermit/docker"
"github.com/libkermit/docker-check"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// Images to have or pull before the build in order to make it work.
// FIXME handle this offline but loading them before build.
var RequiredImages = map[string]string{
"swarm": "1.0.0",
"traefik/whoami": "latest",
}
// Docker tests suite.
type DockerSuite struct {
BaseSuite
project *docker.Project
}
func (s *DockerSuite) startContainer(c *check.C, image string, args ...string) string {
return s.startContainerWithConfig(c, image, d.ContainerConfig{
Cmd: args,
})
}
func (s *DockerSuite) startContainerWithLabels(c *check.C, image string, labels map[string]string, args ...string) string {
return s.startContainerWithConfig(c, image, d.ContainerConfig{
Cmd: args,
Labels: labels,
})
}
func (s *DockerSuite) startContainerWithNameAndLabels(c *check.C, name, image string, labels map[string]string, args ...string) string {
return s.startContainerWithConfig(c, image, d.ContainerConfig{
Name: name,
Cmd: args,
Labels: labels,
})
}
func (s *DockerSuite) startContainerWithConfig(c *check.C, image string, config d.ContainerConfig) string {
if config.Name == "" {
config.Name = namesgenerator.GetRandomName(10)
}
container := s.project.StartWithConfig(c, image, config)
// FIXME(vdemeester) this is ugly (it's because of the / in front of the name in docker..)
return strings.SplitAfter(container.Name, "/")[1]
}
func (s *DockerSuite) stopAndRemoveContainerByName(c *check.C, name string) {
s.project.Stop(c, name)
s.project.Remove(c, name)
}
func (s *DockerSuite) SetUpSuite(c *check.C) {
project := docker.NewProjectFromEnv(c)
s.project = project
// Pull required images
for repository, tag := range RequiredImages {
image := fmt.Sprintf("%s:%s", repository, tag)
s.project.Pull(c, image)
}
func (s *DockerSuite) SetUpTest(c *check.C) {
s.createComposeProject(c, "docker")
}
func (s *DockerSuite) TearDownTest(c *check.C) {
s.project.Clean(c, os.Getenv("CIRCLECI") != "") // FIXME
s.composeDown(c)
}
func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
@ -94,13 +38,15 @@ func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
s.composeUp(c)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// TODO validate : run on 80
// Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
@ -118,18 +64,19 @@ func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
name := s.startContainer(c, "swarm:1.0.0", "manage", "token://blablabla")
s.composeUp(c, "simple")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
c.Assert(err, checker.IsNil)
req.Host = fmt.Sprintf("%s.docker.localhost", strings.ReplaceAll(name, "_", "-"))
req.Host = fmt.Sprintf("simple-%s.docker.localhost", s.composeProject.Name)
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
@ -156,18 +103,12 @@ func (s *DockerSuite) TestDockerContainersWithTCPLabels(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
// Start a container with some labels
labels := map[string]string{
"traefik.tcp.Routers.Super.Rule": "HostSNI(`my.super.host`)",
"traefik.tcp.Routers.Super.tls": "true",
"traefik.tcp.Services.Super.Loadbalancer.server.port": "8080",
}
s.startContainerWithLabels(c, "traefik/whoamitcp", labels, "-name", "my.super.host")
s.composeUp(c, "withtcplabels")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
@ -193,17 +134,7 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
// Start a container with some labels
labels := map[string]string{
"traefik.http.Routers.Super.Rule": "Host(`my.super.host`)",
}
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
// Start another container by replacing a '.' by a '-'
labels = map[string]string{
"traefik.http.Routers.SuperHost.Rule": "Host(`my-super.host`)",
}
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blablabla")
s.composeUp(c, "withlabels1", "withlabels2")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
@ -249,15 +180,12 @@ func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
// Start a container with some labels
labels := map[string]string{
"traefik.random.value": "my.super.host",
}
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
s.composeUp(c, "withonelabelmissing")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
@ -285,16 +213,12 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
// Start a container with some labels
labels := map[string]string{
"traefik.http.Routers.Super.Rule": "Host(`my.super.host`)",
"traefik.http.Services.powpow.LoadBalancer.server.Port": "2375",
}
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
s.composeUp(c, "powpow")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
@ -318,16 +242,14 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.IsNil)
s.stopAndRemoveContainerByName(c, "powpow")
defer s.project.Remove(c, "powpow")
s.composeStop(c, "powpow")
time.Sleep(5 * time.Second)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.NotNil)
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
s.composeUp(c, "powpow")
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.IsNil)
}

View file

@ -10,7 +10,7 @@ import (
checker "github.com/vdemeester/shakers"
)
// ErrorPagesSuite test suites (using libcompose).
// ErrorPagesSuite test suites.
type ErrorPagesSuite struct {
BaseSuite
ErrorPageIP string
@ -19,10 +19,10 @@ type ErrorPagesSuite struct {
func (s *ErrorPagesSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "error_pages")
s.composeProject.Start(c)
s.composeUp(c)
s.ErrorPageIP = s.composeProject.Container(c, "nginx2").NetworkSettings.IPAddress
s.BackendIP = s.composeProject.Container(c, "nginx1").NetworkSettings.IPAddress
s.ErrorPageIP = s.getComposeServiceIP(c, "nginx2")
s.BackendIP = s.getComposeServiceIP(c, "nginx1")
}
func (s *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {

View file

@ -3,6 +3,7 @@ package integration
import (
"bytes"
"encoding/json"
"net"
"net/http"
"os"
"path/filepath"
@ -18,20 +19,24 @@ import (
checker "github.com/vdemeester/shakers"
)
// etcd test suites (using libcompose).
// etcd test suites.
type EtcdSuite struct {
BaseSuite
kvClient store.Store
etcdAddr string
}
func (s *EtcdSuite) setupStore(c *check.C) {
func (s *EtcdSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "etcd")
s.composeProject.Start(c)
s.composeUp(c)
etcdv3.Register()
kv, err := valkeyrie.NewStore(
var err error
s.etcdAddr = net.JoinHostPort(s.getComposeServiceIP(c, "etcd"), "2379")
s.kvClient, err = valkeyrie.NewStore(
store.ETCDV3,
[]string{s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":2379"},
[]string{s.etcdAddr},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
@ -39,27 +44,14 @@ func (s *EtcdSuite) setupStore(c *check.C) {
if err != nil {
c.Fatal("Cannot create store etcd")
}
s.kvClient = kv
// wait for etcd
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
c.Assert(err, checker.IsNil)
}
func (s *EtcdSuite) TearDownTest(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *EtcdSuite) TearDownSuite(c *check.C) {}
func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c)
address := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":2379"
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{address})
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
defer os.Remove(file)
data := map[string]string{

View file

@ -9,7 +9,9 @@ import (
"github.com/traefik/traefik/v2/pkg/log"
)
type handler struct{}
type handler struct {
traefikIP string
}
// ServeDNS a fake DNS server
// Simplified version of the Challenge Test Server from Boulder
@ -21,11 +23,6 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
m.SetReply(r)
m.Compress = false
fakeDNS := os.Getenv("DOCKER_HOST_IP")
if fakeDNS == "" {
fakeDNS = "127.0.0.1"
}
for _, q := range r.Question {
logger.Infof("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype])
@ -38,7 +35,7 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
Class: dns.ClassINET,
Ttl: 0,
}
record.A = net.ParseIP(fakeDNS)
record.A = net.ParseIP(s.traefikIP)
m.Answer = append(m.Answer, record)
case dns.TypeCAA:
@ -101,11 +98,11 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}
}
func startFakeDNSServer() *dns.Server {
func startFakeDNSServer(traefikIP string) *dns.Server {
srv := &dns.Server{
Addr: ":5053",
Net: "udp",
Handler: &handler{},
Handler: &handler{traefikIP},
}
go func() {

View file

@ -15,7 +15,7 @@ type FileSuite struct{ BaseSuite }
func (s *FileSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "file")
s.composeProject.Start(c)
s.composeUp(c)
}
func (s *FileSuite) TestSimpleConfiguration(c *check.C) {

View file

@ -6,4 +6,4 @@
[http.services]
[http.services.service2.loadBalancer]
[[http.services.service2.loadBalancer.servers]]
url = "http://172.17.0.123:80"
url = "http://127.0.0.1:80"

View file

@ -33,13 +33,13 @@
[http.services.service1.loadBalancer]
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8080"
url = "http://{{ .WhoamiIP }}:8080"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8081"
url = "http://{{ .WhoamiIP }}:8081"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8082"
url = "http://{{ .WhoamiIP }}:8082"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:80"
url = "http://{{ .WhoamiIP }}:80"

View file

@ -31,7 +31,7 @@
[http.services.service1.loadBalancer]
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8080"
url = "http://{{ .WhoamiIP }}:8080"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:80"
url = "http://{{ .WhoamiIP }}:80"

View file

@ -27,7 +27,7 @@
[tcp.services]
[tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]]
address = "localhost:8086"
address = "whoami-no-tls:8080"
[http]
[http.routers]
@ -40,4 +40,4 @@
[http.services]
[http.services.whoami.loadBalancer]
[[http.services.whoami.loadBalancer.servers]]
url = "http://localhost:8085"
url = "http://whoami:80"

View file

@ -27,4 +27,4 @@
[tcp.services]
[tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]]
address = "localhost:8086"
address = "whoami-banner:8080"

View file

@ -38,18 +38,14 @@
[tcp.services]
[tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081"
address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]]
address = "localhost:8082"
address = "whoami-b:8080"
[tcp.middlewares]
[tcp.middlewares.allowing-ipwhitelist.ipWhiteList]
sourceRange = ["127.0.0.1/32"]
[tcp.middlewares.blocking-ipwhitelist.ipWhiteList]
sourceRange = ["127.127.127.127/32"]
[[tls.certificates]]
certFile = "fixtures/tcp/whoami-c.crt"
keyFile = "fixtures/tcp/whoami-c.key"

View file

@ -29,11 +29,15 @@
rule = "Path(`/whoami/`)"
service = "whoami"
[http.routers.my-https-router.tls]
[http.routers.api]
rule = "PathPrefix(`/api`)"
service = "api@internal"
entryPoints = ["traefik"]
[http.services]
[http.services.whoami.loadBalancer]
[[http.services.whoami.loadBalancer.servers]]
url = "http://localhost:8085"
url = "http://whoami:80"
[tcp]
[tcp.routers]
[tcp.routers.to-whoami-a]
@ -58,15 +62,15 @@
[tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081"
address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]]
address = "localhost:8082"
address = "whoami-b:8080"
[tcp.services.whoami-no-cert.loadBalancer]
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
address = "localhost:8083"
address = "whoami-no-cert:8080"
[[tls.certificates]]
certFile = "fixtures/tcp/whoami-c.crt"

View file

@ -36,7 +36,7 @@
[tcp.services.whoami-no-cert]
[tcp.services.whoami-no-cert.loadBalancer]
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
address = "localhost:8083"
address = "whoami-no-cert:8080"
[tls.options]

View file

@ -47,16 +47,17 @@
[tcp.services]
[tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]]
address = "localhost:8084"
address = "whoami-no-tls:8080"
[tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081"
address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]]
address = "localhost:8082"
address = "whoami-b:8080"
[tcp.services.whoami-no-cert.loadBalancer]
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
address = "localhost:8083"
address = "whoami-no-cert:8080"

View file

@ -27,4 +27,4 @@
[tcp.services]
[tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]]
address = "localhost:8084"
address = "whoami-no-tls:8080"

View file

@ -18,25 +18,24 @@
## dynamic configuration ##
[tcp]
[tcp.routers]
[tcp.routers.to-whoami-a]
rule = "HostSNI(`whoami-a.test`)"
[tcp.routers.to-whoami-b]
rule = "HostSNI(`whoami-b.test`)"
service = "whoami"
entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-a.tls]
[tcp.routers.to-whoami-b.tls]
passthrough=true
[[tcp.services.whoami.weighted.services]]
name="whoami-a"
name="whoami-b"
weight=3
[[tcp.services.whoami.weighted.services]]
name="whoami-b"
name="whoami-ab"
weight=1
[tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081"
[tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]]
address = "localhost:8082"
address = "whoami-b:8080"
[tcp.services.whoami-ab.loadBalancer]
[[tcp.services.whoami-ab.loadBalancer.servers]]
address = "whoami-ab:8080"

View file

@ -55,16 +55,16 @@
[http.services.service1.loadBalancer]
passHostHeader = true
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service2]
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service3]
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"

View file

@ -54,16 +54,16 @@
[http.services.service1.loadBalancer]
passHostHeader = true
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service2]
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service3]
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"

View file

@ -50,16 +50,16 @@
[http.services.service1.loadBalancer]
passHostHeader = true
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service2]
[http.services.service2.loadBalancer]
passHostHeader = true
[[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service3]
[http.services.service3.loadBalancer]
passHostHeader = true
[[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}"
url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"

View file

@ -13,7 +13,7 @@ import (
checker "github.com/vdemeester/shakers"
)
// HealthCheck test suites (using libcompose).
// HealthCheck test suites.
type HealthCheckSuite struct {
BaseSuite
whoami1IP string
@ -24,12 +24,12 @@ type HealthCheckSuite struct {
func (s *HealthCheckSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "healthcheck")
s.composeProject.Start(c)
s.composeUp(c)
s.whoami1IP = s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
s.whoami2IP = s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
s.whoami3IP = s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress
s.whoami4IP = s.composeProject.Container(c, "whoami4").NetworkSettings.IPAddress
s.whoami1IP = s.getComposeServiceIP(c, "whoami1")
s.whoami2IP = s.getComposeServiceIP(c, "whoami2")
s.whoami3IP = s.getComposeServiceIP(c, "whoami3")
s.whoami4IP = s.getComposeServiceIP(c, "whoami4")
}
func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
@ -90,7 +90,7 @@ func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
// Check if the service with bad health check (whoami2) never respond.
err = try.Request(frontendReq, 2*time.Second, try.BodyContains(s.whoami2IP))
c.Assert(err, checker.Not(checker.IsNil))
c.Assert(err, checker.NotNil)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1:8000/")

View file

@ -13,9 +13,7 @@ type HostResolverSuite struct{ BaseSuite }
func (s *HostResolverSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "hostresolver")
s.composeProject.Start(c)
s.composeProject.Container(c, "server1")
s.composeUp(c)
}
func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
@ -48,7 +46,7 @@ func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
c.Assert(err, checker.IsNil)
req.Host = test.host
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(test.status), try.HasBody())
err = try.Request(req, 1*time.Second, try.StatusCodeIs(test.status), try.HasBody())
c.Assert(err, checker.IsNil)
}
}

View file

@ -1063,13 +1063,13 @@ func (s *HTTPSSuite) TestEntryPointHttpsRedirectAndPathModification(c *check.C)
for _, test := range testCases {
sourceURL := fmt.Sprintf("http://127.0.0.1:8888%s", test.path)
for _, host := range test.hosts {
req, err := http.NewRequest("GET", sourceURL, nil)
req, err := http.NewRequest(http.MethodGet, sourceURL, nil)
c.Assert(err, checker.IsNil)
req.Host = host
resp, err := client.Do(req)
c.Assert(err, checker.IsNil)
defer resp.Body.Close()
resp.Body.Close()
location := resp.Header.Get("Location")
expected := fmt.Sprintf("https://%s:8443%s", host, test.path)

View file

@ -3,9 +3,9 @@ package integration
import (
"bytes"
"context"
"flag"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
@ -14,17 +14,23 @@ import (
"text/template"
"time"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/compose/v2/cmd/formatter"
composeapi "github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/fatih/structs"
"github.com/go-check/check"
compose "github.com/libkermit/compose/check"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
)
var (
integration = flag.Bool("integration", false, "run integration tests")
container = flag.Bool("container", false, "run container integration tests")
host = flag.Bool("host", false, "run host integration tests")
showLog = flag.Bool("tlog", false, "always show Traefik logs")
)
@ -34,45 +40,39 @@ func Test(t *testing.T) {
return
}
if *container {
// tests launched from a container
check.Suite(&AccessLogSuite{})
check.Suite(&AcmeSuite{})
check.Suite(&EtcdSuite{})
check.Suite(&ConsulSuite{})
check.Suite(&ConsulCatalogSuite{})
check.Suite(&ConsulSuite{})
check.Suite(&DockerComposeSuite{})
check.Suite(&DockerSuite{})
check.Suite(&ErrorPagesSuite{})
check.Suite(&EtcdSuite{})
check.Suite(&FileSuite{})
check.Suite(&GRPCSuite{})
check.Suite(&HealthCheckSuite{})
check.Suite(&HeadersSuite{})
check.Suite(&HealthCheckSuite{})
check.Suite(&HostResolverSuite{})
check.Suite(&HTTPSuite{})
check.Suite(&HTTPSSuite{})
check.Suite(&HTTPSuite{})
check.Suite(&K8sSuite{})
check.Suite(&KeepAliveSuite{})
check.Suite(&LogRotationSuite{})
check.Suite(&MarathonSuite{})
check.Suite(&MarathonSuite15{})
check.Suite(&MarathonSuite{})
check.Suite(&ProxyProtocolSuite{})
check.Suite(&RateLimitSuite{})
check.Suite(&RedisSuite{})
check.Suite(&RestSuite{})
check.Suite(&RetrySuite{})
check.Suite(&SimpleSuite{})
check.Suite(&TCPSuite{})
check.Suite(&TimeoutSuite{})
check.Suite(&TLSClientHeadersSuite{})
check.Suite(&TracingSuite{})
check.Suite(&UDPSuite{})
check.Suite(&WebsocketSuite{})
check.Suite(&ZookeeperSuite{})
}
if *host {
// tests launched from the host
check.Suite(&K8sSuite{})
check.Suite(&ProxyProtocolSuite{})
check.Suite(&TCPSuite{})
}
check.TestingT(t)
}
@ -80,36 +80,72 @@ func Test(t *testing.T) {
var traefikBinary = "../dist/traefik"
type BaseSuite struct {
composeProject *compose.Project
composeProject *types.Project
dockerComposeService composeapi.Service
dockerClient *client.Client
}
func (s *BaseSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
if s.composeProject != nil && s.dockerComposeService != nil {
s.composeDown(c)
}
}
// createComposeProject creates the docker compose project stored as a field in the BaseSuite.
// This method should be called before starting and/or stopping compose services.
func (s *BaseSuite) createComposeProject(c *check.C, name string) {
projectName := fmt.Sprintf("integration-test-%s", name)
projectName := fmt.Sprintf("traefik-integration-test-%s", name)
composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
addrs, err := net.InterfaceAddrs()
var err error
s.dockerClient, err = client.NewClientWithOpts()
c.Assert(err, checker.IsNil)
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
s.dockerComposeService = compose.NewComposeService(s.dockerClient, &configfile.ConfigFile{})
ops, err := cli.NewProjectOptions([]string{composeFile}, cli.WithName(projectName))
c.Assert(err, checker.IsNil)
s.composeProject, err = cli.ProjectFromOptions(ops)
c.Assert(err, checker.IsNil)
if !ip.IsLoopback() && ip.To4() != nil {
_ = os.Setenv("DOCKER_HOST_IP", ip.String())
break
}
}
s.composeProject = compose.CreateProject(c, projectName, composeFile)
// composeUp starts the given services of the current docker compose project, if they are not already started.
// Already running services are not affected (i.e. not stopped).
func (s *BaseSuite) composeUp(c *check.C, services ...string) {
c.Assert(s.composeProject, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
// We use Create and Restart instead of Up, because the only option that actually works to control which containers
// are started is within the RestartOptions.
err := s.dockerComposeService.Create(context.Background(), s.composeProject, composeapi.CreateOptions{})
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Restart(context.Background(), s.composeProject, composeapi.RestartOptions{Services: services})
c.Assert(err, checker.IsNil)
}
func withConfigFile(file string) string {
return "--configFile=" + file
// composeStop stops the given services of the current docker compose project and removes the corresponding containers.
func (s *BaseSuite) composeStop(c *check.C, services ...string) {
c.Assert(s.dockerComposeService, check.NotNil)
c.Assert(s.composeProject, check.NotNil)
err := s.dockerComposeService.Stop(context.Background(), s.composeProject, composeapi.StopOptions{Services: services})
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Remove(context.Background(), s.composeProject, composeapi.RemoveOptions{
Services: services,
Force: true,
})
c.Assert(err, checker.IsNil)
}
// composeDown stops all compose project services and removes the corresponding containers.
func (s *BaseSuite) composeDown(c *check.C) {
c.Assert(s.dockerComposeService, check.NotNil)
c.Assert(s.composeProject, check.NotNil)
err := s.dockerComposeService.Down(context.Background(), s.composeProject.Name, composeapi.DownOptions{})
c.Assert(err, checker.IsNil)
}
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
@ -134,6 +170,7 @@ func (s *BaseSuite) traefikCmd(args ...string) (*exec.Cmd, func(*check.C)) {
return cmd, func(c *check.C) {
if c.Failed() || *showLog {
s.displayLogK3S(c)
s.displayLogCompose(c)
s.displayTraefikLog(c, out)
}
}
@ -153,6 +190,25 @@ func (s *BaseSuite) displayLogK3S(c *check.C) {
log.WithoutContext().Println()
}
func (s *BaseSuite) displayLogCompose(c *check.C) {
if s.dockerComposeService == nil || s.composeProject == nil {
log.WithoutContext().Infof("%s: No docker compose logs.", c.TestName())
return
}
log.WithoutContext().Infof("%s: docker compose logs: ", c.TestName())
logWriter := log.WithoutContext().WriterLevel(log.GetLevel())
logConsumer := formatter.NewLogConsumer(context.Background(), logWriter, false, true)
err := s.dockerComposeService.Logs(context.Background(), s.composeProject.Name, logConsumer, composeapi.LogOptions{})
c.Assert(err, checker.IsNil)
log.WithoutContext().Println()
log.WithoutContext().Println("################################")
log.WithoutContext().Println()
}
func (s *BaseSuite) displayTraefikLog(c *check.C, output *bytes.Buffer) {
if output == nil || output.Len() == 0 {
log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName())
@ -168,6 +224,7 @@ func (s *BaseSuite) getDockerHost() string {
// Default docker socket
dockerHost = "unix:///var/run/docker.sock"
}
return dockerHost
}
@ -192,3 +249,38 @@ func (s *BaseSuite) adaptFile(c *check.C, path string, tempObjects interface{})
return tmpFile.Name()
}
func (s *BaseSuite) getComposeServiceIP(c *check.C, name string) string {
filter := filters.NewArgs(
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ProjectLabel, s.composeProject.Name)),
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ServiceLabel, name)),
)
containers, err := s.dockerClient.ContainerList(context.Background(), dockertypes.ContainerListOptions{Filters: filter})
c.Assert(err, checker.IsNil)
c.Assert(containers, checker.HasLen, 1)
networkNames := s.composeProject.NetworkNames()
c.Assert(networkNames, checker.HasLen, 1)
network := s.composeProject.Networks[networkNames[0]]
return containers[0].NetworkSettings.Networks[network.Name].IPAddress
}
func (s *BaseSuite) getContainerIP(c *check.C, name string) string {
container, err := s.dockerClient.ContainerInspect(context.Background(), name)
c.Assert(err, checker.IsNil)
c.Assert(container.NetworkSettings.Networks, check.NotNil)
for _, network := range container.NetworkSettings.Networks {
return network.IPAddress
}
// Should never happen.
c.Error("No network found")
return ""
}
func withConfigFile(file string) string {
return "--configFile=" + file
}

View file

@ -28,7 +28,7 @@ type K8sSuite struct{ BaseSuite }
func (s *K8sSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "k8s")
s.composeProject.Start(c)
s.composeUp(c)
abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml")
c.Assert(err, checker.IsNil)
@ -44,7 +44,7 @@ func (s *K8sSuite) SetUpSuite(c *check.C) {
}
func (s *K8sSuite) TearDownSuite(c *check.C) {
s.composeProject.Stop(c)
s.composeDown(c)
generatedFiles := []string{
"./fixtures/k8s/config.skip/kubeconfig.yaml",
@ -56,8 +56,7 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
}
for _, filename := range generatedFiles {
err := os.Remove(filename)
if err != nil {
if err := os.Remove(filename); err != nil {
log.WithoutContext().Warning(err)
}
}

View file

@ -13,17 +13,38 @@ import (
"github.com/go-check/check"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
)
const (
traefikTestLogFileRotated = traefikTestLogFile + ".rotated"
traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
)
// Log rotation integration test suite.
type LogRotationSuite struct{ BaseSuite }
func (s *LogRotationSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log")
s.composeProject.Start(c)
s.composeUp(c)
}
s.composeProject.Container(c, "server1")
func (s *LogRotationSuite) TearDownSuite(c *check.C) {
s.composeDown(c)
generatedFiles := []string{
traefikTestLogFile,
traefikTestLogFileRotated,
traefikTestAccessLogFile,
traefikTestAccessLogFileRotated,
}
for _, filename := range generatedFiles {
if err := os.Remove(filename); err != nil {
log.WithoutContext().Warning(err)
}
}
}
func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
@ -36,8 +57,6 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
defer os.Remove(traefikTestAccessLogFile)
// Verify Traefik started ok
verifyEmptyErrorLog(c, "traefik.log")
@ -52,7 +71,7 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
c.Assert(err, checker.IsNil)
// Rename access log
err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFile+".rotated")
err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFileRotated)
c.Assert(err, checker.IsNil)
// in the midst of the requests, issue SIGUSR1 signal to server process
@ -66,8 +85,8 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
c.Assert(err, checker.IsNil)
// Verify access.log.rotated output as expected
logAccessLogFile(c, traefikTestAccessLogFile+".rotated")
lineCount := verifyLogLines(c, traefikTestAccessLogFile+".rotated", 0, true)
logAccessLogFile(c, traefikTestAccessLogFileRotated)
lineCount := verifyLogLines(c, traefikTestAccessLogFileRotated, 0, true)
c.Assert(lineCount, checker.GreaterOrEqualThan, 1)
// make sure that the access log file is at least created before we do assertions on it
@ -95,12 +114,10 @@ func (s *LogRotationSuite) TestTraefikLogRotation(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
defer os.Remove(traefikTestAccessLogFile)
waitForTraefik(c, "server1")
// Rename traefik log
err = os.Rename(traefikTestLogFile, traefikTestLogFile+".rotated")
err = os.Rename(traefikTestLogFile, traefikTestLogFileRotated)
c.Assert(err, checker.IsNil)
// issue SIGUSR1 signal to server process
@ -118,7 +135,7 @@ func (s *LogRotationSuite) TestTraefikLogRotation(c *check.C) {
c.Assert(err, checker.IsNil)
// we have at least 6 lines in traefik.log.rotated
lineCount := verifyLogLines(c, traefikTestLogFile+".rotated", 0, false)
lineCount := verifyLogLines(c, traefikTestLogFileRotated, 0, false)
// GreaterOrEqualThan used to ensure test doesn't break
// If more log entries are output on startup

View file

@ -1,7 +1,6 @@
package integration
import (
"fmt"
"net/http"
"os"
"time"
@ -12,7 +11,7 @@ import (
checker "github.com/vdemeester/shakers"
)
// Marathon test suites (using libcompose).
// Marathon test suites.
type MarathonSuite15 struct {
BaseSuite
marathonURL string
@ -20,53 +19,15 @@ type MarathonSuite15 struct {
func (s *MarathonSuite15) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon15")
s.composeProject.Start(c)
s.composeUp(c)
marathonIPAddr := s.composeProject.Container(c, containerNameMarathon).NetworkSettings.IPAddress
c.Assert(marathonIPAddr, checker.Not(checker.HasLen), 0)
s.marathonURL = "http://" + marathonIPAddr + ":8080"
s.marathonURL = "http://" + containerNameMarathon + ":8080"
// Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the
// start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
// Add entry for Mesos slave container IP address in the hosts file so
// that Traefik can properly forward traffic.
// This is necessary as long as we are still using the docker-compose v1
// spec. Once we switch to v2 or higher, we can have both the test/builder
// container and the Mesos slave container join the same custom network and
// enjoy DNS-discoverable container host names.
mesosSlaveIPAddr := s.composeProject.Container(c, containerNameMesosSlave).NetworkSettings.IPAddress
c.Assert(mesosSlaveIPAddr, checker.Not(checker.HasLen), 0)
err = s.extendDockerHostsFile(containerNameMesosSlave, mesosSlaveIPAddr)
c.Assert(err, checker.IsNil)
}
// extendDockerHostsFile extends the hosts file (/etc/hosts) by the given
// host/IP address mapping if we are running inside a container.
func (s *MarathonSuite15) extendDockerHostsFile(host, ipAddr string) error {
const hostsFile = "/etc/hosts"
// Determine if the run inside a container. The most reliable way to
// do this is to inject an indicator, which we do in terms of an
// environment variable.
// (See also https://groups.google.com/d/topic/docker-user/JOGE7AnJ3Gw/discussion.)
if os.Getenv("CONTAINER") == "DOCKER" {
// We are running inside a container -- extend the hosts file.
file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(fmt.Sprintf("%s\t%s\n", ipAddr, host)); err != nil {
return err
}
}
return nil
}
func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {

View file

@ -1,7 +1,6 @@
package integration
import (
"fmt"
"net/http"
"os"
"time"
@ -12,12 +11,9 @@ import (
checker "github.com/vdemeester/shakers"
)
const (
containerNameMesosSlave = "mesos-slave"
containerNameMarathon = "marathon"
)
const containerNameMarathon = "marathon"
// Marathon test suites (using libcompose).
// Marathon test suites.
type MarathonSuite struct {
BaseSuite
marathonURL string
@ -25,53 +21,15 @@ type MarathonSuite struct {
func (s *MarathonSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon")
s.composeProject.Start(c)
s.composeUp(c)
marathonIPAddr := s.composeProject.Container(c, containerNameMarathon).NetworkSettings.IPAddress
c.Assert(marathonIPAddr, checker.Not(checker.HasLen), 0)
s.marathonURL = "http://" + marathonIPAddr + ":8080"
s.marathonURL = "http://" + containerNameMarathon + ":8080"
// Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the
// start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
// Add entry for Mesos slave container IP address in the hosts file so
// that Traefik can properly forward traffic.
// This is necessary as long as we are still using the docker-compose v1
// spec. Once we switch to v2 or higher, we can have both the test/builder
// container and the Mesos slave container join the same custom network and
// enjoy DNS-discoverable container host names.
mesosSlaveIPAddr := s.composeProject.Container(c, containerNameMesosSlave).NetworkSettings.IPAddress
c.Assert(mesosSlaveIPAddr, checker.Not(checker.HasLen), 0)
err = s.extendDockerHostsFile(containerNameMesosSlave, mesosSlaveIPAddr)
c.Assert(err, checker.IsNil)
}
// extendDockerHostsFile extends the hosts file (/etc/hosts) by the given
// host/IP address mapping if we are running inside a container.
func (s *MarathonSuite) extendDockerHostsFile(host, ipAddr string) error {
const hostsFile = "/etc/hosts"
// Determine if the run inside a container. The most reliable way to
// do this is to inject an indicator, which we do in terms of an
// environment variable.
// (See also https://groups.google.com/d/topic/docker-user/JOGE7AnJ3Gw/discussion.)
if os.Getenv("CONTAINER") == "DOCKER" {
// We are running inside a container -- extend the hosts file.
file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(fmt.Sprintf("%s\t%s\n", ipAddr, host)); err != nil {
return err
}
}
return nil
}
func deployApplication(c *check.C, client marathon.Marathon, application *marathon.Application) {

View file

@ -10,22 +10,27 @@ import (
checker "github.com/vdemeester/shakers"
)
type ProxyProtocolSuite struct{ BaseSuite }
type ProxyProtocolSuite struct {
BaseSuite
gatewayIP string
haproxyIP string
whoamiIP string
}
func (s *ProxyProtocolSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "proxy-protocol")
s.composeProject.Start(c)
s.composeUp(c)
s.gatewayIP = s.getContainerIP(c, "traefik")
s.haproxyIP = s.getComposeServiceIP(c, "haproxy")
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
}
func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) {
gatewayIP := s.composeProject.Container(c, "haproxy").NetworkSettings.Gateway
haproxyIP := s.composeProject.Container(c, "haproxy").NetworkSettings.IPAddress
whoamiIP := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP})
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -34,21 +39,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+haproxyIP+"/whoami", 1*time.Second,
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+gatewayIP))
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil)
}
func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) {
gatewayIP := s.composeProject.Container(c, "haproxy").NetworkSettings.Gateway
haproxyIP := s.composeProject.Container(c, "haproxy").NetworkSettings.IPAddress
whoamiIP := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP})
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -57,20 +58,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 1*time.Second,
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+gatewayIP))
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil)
}
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) {
haproxyIP := s.composeProject.Container(c, "haproxy").NetworkSettings.IPAddress
whoamiIP := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP})
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -79,20 +77,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+haproxyIP+"/whoami", 1*time.Second,
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+haproxyIP))
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil)
}
func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
haproxyIP := s.composeProject.Container(c, "haproxy").NetworkSettings.IPAddress
whoamiIP := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP})
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -101,8 +96,8 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 1*time.Second,
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+haproxyIP))
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil)
}

View file

@ -17,9 +17,9 @@ type RateLimitSuite struct {
func (s *RateLimitSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "ratelimit")
s.composeProject.Start(c)
s.composeUp(c)
s.ServerIP = s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
s.ServerIP = s.getComposeServiceIP(c, "whoami1")
}
func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {

View file

@ -3,6 +3,7 @@ package integration
import (
"bytes"
"encoding/json"
"net"
"net/http"
"os"
"path/filepath"
@ -18,20 +19,22 @@ import (
checker "github.com/vdemeester/shakers"
)
// Redis test suites (using libcompose).
// Redis test suites.
type RedisSuite struct {
BaseSuite
kvClient store.Store
redisAddr string
}
func (s *RedisSuite) setupStore(c *check.C) {
s.createComposeProject(c, "redis")
s.composeProject.Start(c)
s.composeUp(c)
s.redisAddr = net.JoinHostPort(s.getComposeServiceIP(c, "redis"), "6379")
redis.Register()
kv, err := valkeyrie.NewStore(
store.REDIS,
[]string{s.composeProject.Container(c, "redis").NetworkSettings.IPAddress + ":6379"},
[]string{s.redisAddr},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
@ -46,20 +49,10 @@ func (s *RedisSuite) setupStore(c *check.C) {
c.Assert(err, checker.IsNil)
}
func (s *RedisSuite) TearDownTest(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *RedisSuite) TearDownSuite(c *check.C) {}
func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c)
address := s.composeProject.Container(c, "redis").NetworkSettings.IPAddress + ":6379"
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{address})
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{s.redisAddr})
defer os.Remove(file)
data := map[string]string{

View file

@ -1,77 +1,91 @@
version: "3.8"
services:
server0:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-server0.entryPoints=web
- traefik.http.routers.rt-server0.rule=Path("/test")
- traefik.http.services.service1.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-server0.entryPoints: web
traefik.http.routers.rt-server0.rule: Path(`/test`)
traefik.http.services.service1.loadbalancer.server.port: 80
server1:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-server1.entryPoints=web
- traefik.http.routers.rt-server1.rule=Host("frontend1.docker.local")
- traefik.http.routers.rt-server1.service=service1
- traefik.http.services.service1.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-server1.entryPoints: web
traefik.http.routers.rt-server1.rule: Host(`frontend1.docker.local`)
traefik.http.routers.rt-server1.service: service1
traefik.http.services.service1.loadbalancer.server.port: 80
server2:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-server2.entryPoints=web
- traefik.http.routers.rt-server2.rule=Host("frontend2.docker.local")
- traefik.http.services.service2.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-server2.entryPoints: web
traefik.http.routers.rt-server2.rule: Host(`frontend2.docker.local`)
traefik.http.services.service2.loadbalancer.server.port: 80
server3:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-server3.entryPoints=web
- traefik.http.routers.rt-server3.rule=Host("frontend2.docker.local")
- traefik.http.services.service2.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-server3.entryPoints: web
traefik.http.routers.rt-server3.rule: Host(`frontend2.docker.local`)
traefik.http.services.service2.loadbalancer.server.port: 80
authFrontend:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-authFrontend.entryPoints=httpFrontendAuth
- traefik.http.routers.rt-authFrontend.rule=Host("frontend.auth.docker.local")
- traefik.http.routers.rt-authFrontend.middlewares=basicauth
- traefik.http.middlewares.basicauth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/
- traefik.http.services.service3.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-authFrontend.entryPoints: httpFrontendAuth
traefik.http.routers.rt-authFrontend.rule: Host(`frontend.auth.docker.local`)
traefik.http.routers.rt-authFrontend.middlewares: basicauth
traefik.http.middlewares.basicauth.basicauth.users: test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/
traefik.http.services.service3.loadbalancer.server.port: 80
digestAuthMiddleware:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-digestAuthMiddleware.entryPoints=digestAuth
- traefik.http.routers.rt-digestAuthMiddleware.rule=Host("entrypoint.digest.auth.docker.local")
- traefik.http.routers.rt-digestAuthMiddleware.middlewares=digestauth
- traefik.http.middlewares.digestauth.digestauth.users=test:traefik:a2688e031edb4be6a3797f3882655c05, test2:traefik:518845800f9e2bfb1f1f740ec24f074e
- traefik.http.services.service3.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-digestAuthMiddleware.entryPoints: digestAuth
traefik.http.routers.rt-digestAuthMiddleware.rule: Host(`entrypoint.digest.auth.docker.local`)
traefik.http.routers.rt-digestAuthMiddleware.middlewares: digestauth
traefik.http.middlewares.digestauth.digestauth.users: test:traefik:a2688e031edb4be6a3797f3882655c05, test2:traefik:518845800f9e2bfb1f1f740ec24f074e
traefik.http.services.service3.loadbalancer.server.port: 80
frontendRedirect:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-frontendRedirect.entryPoints=frontendRedirect
- traefik.http.routers.rt-frontendRedirect.rule=Path("/test")
- traefik.http.routers.rt-frontendRedirect.middlewares=redirecthttp
- traefik.http.middlewares.redirecthttp.redirectScheme.scheme=http
- traefik.http.middlewares.redirecthttp.redirectScheme.port=8000
- traefik.http.services.service3.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-frontendRedirect.entryPoints: frontendRedirect
traefik.http.routers.rt-frontendRedirect.rule: Path(`/test`)
traefik.http.routers.rt-frontendRedirect.middlewares: redirecthttp
traefik.http.middlewares.redirecthttp.redirectScheme.scheme: http
traefik.http.middlewares.redirecthttp.redirectScheme.port: 8000
traefik.http.services.service3.loadbalancer.server.port: 80
rateLimit:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-rateLimit.entryPoints=httpRateLimit
- traefik.http.routers.rt-rateLimit.rule=Host("ratelimit.docker.local")
- traefik.http.routers.rt-rateLimit.middlewares=rate
- traefik.http.middlewares.rate.ratelimit
- traefik.http.middlewares.rate.ratelimit.average=1
- traefik.http.middlewares.rate.ratelimit.burst=2
- traefik.http.services.service3.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-rateLimit.entryPoints: httpRateLimit
traefik.http.routers.rt-rateLimit.rule: Host(`ratelimit.docker.local`)
traefik.http.routers.rt-rateLimit.middlewares: rate
traefik.http.middlewares.rate.ratelimit.average: 1
traefik.http.middlewares.rate.ratelimit.burst: 2
traefik.http.services.service3.loadbalancer.server.port: 80
frontendWhitelist:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt-frontendWhitelist.entryPoints=web
- traefik.http.routers.rt-frontendWhitelist.rule=Host("frontend.whitelist.docker.local")
- traefik.http.routers.rt-frontendWhitelist.middlewares=wl
- traefik.http.middlewares.wl.ipwhitelist.sourcerange=8.8.8.8/32
- traefik.http.services.service3.loadbalancer.server.port=80
traefik.enable: true
traefik.http.routers.rt-frontendWhitelist.entryPoints: web
traefik.http.routers.rt-frontendWhitelist.rule: Host(`frontend.whitelist.docker.local`)
traefik.http.routers.rt-frontendWhitelist.middlewares: wl
traefik.http.middlewares.wl.ipwhitelist.sourcerange: 8.8.8.8/32
traefik.http.services.service3.loadbalancer.server.port: 80
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,11 +1,18 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.router1.rule=PathPrefix("/whoami")
- traefik.http.routers.router2.rule=PathPrefix("/whoami2")
traefik.enable: true
traefik.http.routers.router1.rule: PathPrefix(`/whoami`)
traefik.http.routers.router2.rule: PathPrefix(`/whoami2`)
whoami2:
image: traefik/whoami
labels:
- traefik.enable=false
traefik.enable: false
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,9 @@
version: "3.8"
services:
consul:
image: consul:1.6
ports:
- "8500:8500"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,32 +1,37 @@
version: "3.8"
services:
consul:
image: consul:1.6.2
ports:
- 8500:8500
command: "agent -server -bootstrap -ui -client 0.0.0.0 -hcl 'connect { enabled = true }'"
command: agent -server -bootstrap -ui -client 0.0.0.0 -hcl 'connect { enabled = true }'
consul-agent:
image: consul:1.6.2
ports:
- 8501:8500
command: "agent -retry-join consul -client 0.0.0.0"
links:
- consul
command: agent -retry-join consul -client 0.0.0.0
whoami1:
image: traefik/whoami
hostname: whoami1
whoami2:
image: traefik/whoami
hostname: whoami2
whoami3:
image: traefik/whoami
hostname: whoami3
whoamitcp:
image: traefik/whoamitcp
hostname: whoamitcp
connect:
image: hashicorpnomad/uuid-api:v5
links:
- consul
environment:
PORT: 443
BIND: 0.0.0.0
CONSUL_HTTP_ADDR: http://consul:8500
networks:
default:
name: traefik-test-network
external: true

View file

@ -0,0 +1,43 @@
version: "3.8"
services:
simple:
image: swarm:1.0.0
command: [ "manage", "token://blablabla" ]
withtcplabels:
image: traefik/whoamitcp
command: [ "-name", "my.super.host" ]
labels:
traefik.tcp.Routers.Super.Rule: HostSNI(`my.super.host`)
traefik.tcp.Routers.Super.tls: true
traefik.tcp.Services.Super.Loadbalancer.server.port: 8080
withlabels1:
image: swarm:1.0.0
command: [ "manage", "token://blabla" ]
labels:
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
withlabels2:
image: swarm:1.0.0
command: [ "manage", "token://blablabla" ]
labels:
traefik.http.Routers.SuperHost.Rule: Host(`my-super.host`)
withonelabelmissing:
image: swarm:1.0.0
command: [ "manage", "token://blabla" ]
labels:
traefik.random.value: my.super.host
powpow:
image: swarm:1.0.0
command: [ "manage", "token://blabla" ]
labels:
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,12 @@
version: "3.8"
services:
nginx1:
image: nginx:1.13.8-alpine
nginx2:
image: nginx:1.13.8-alpine
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,5 +1,10 @@
version: "3.8"
services:
etcd:
image: quay.io/coreos/etcd:v3.3.18
command: etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2380
ports:
- "2379:2379"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,20 +1,21 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
ports:
- "8881:80"
whoami2:
image: traefik/whoami
ports:
- "8882:80"
whoami3:
image: traefik/whoami
ports:
- "8883:80"
whoami4:
image: traefik/whoami
ports:
- "8884:80"
whoami5:
image: traefik/whoami
ports:
- "8885:80"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,3 +1,5 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
@ -9,3 +11,8 @@ whoami3:
whoami4:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,6 +1,13 @@
version: "3.8"
services:
server1:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.services.service1.loadbalancer.server.port=80
- traefik.http.routers.router1.rule=Host("github.com")
traefik.enable: true
traefik.http.services.service1.loadbalancer.server.port: 80
traefik.http.routers.router1.rule: Host(`github.com`)
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,21 +1,24 @@
version: "3.8"
services:
server:
image: rancher/k3s:v1.18.20-k3s1
command: server --disable-agent --no-deploy coredns --no-deploy servicelb --no-deploy traefik --no-deploy local-storage --no-deploy metrics-server --log /output/k3s.log
command: server --disable-agent --no-deploy coredns --no-deploy servicelb --no-deploy traefik --no-deploy local-storage --no-deploy metrics-server --log /output/k3s.log --bind-address=server --tls-san=server
environment:
- K3S_CLUSTER_SECRET=somethingtotallyrandom
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
- K3S_KUBECONFIG_MODE=666
K3S_CLUSTER_SECRET: somethingtotallyrandom
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
K3S_KUBECONFIG_MODE: 666
volumes:
- ../../fixtures/k8s/config.skip:/output
- ../../fixtures/k8s:/var/lib/rancher/k3s/server/manifests
ports:
- 6443:6443
- ./fixtures/k8s/config.skip:/output
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
node:
image: rancher/k3s:v1.18.20-k3s1
privileged: true
links:
- server
environment:
- K3S_URL=https://server:6443
- K3S_CLUSTER_SECRET=somethingtotallyrandom
K3S_URL: https://server:6443
K3S_CLUSTER_SECRET: somethingtotallyrandom
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,54 +1,60 @@
version: "3.8"
services:
zookeeper:
image: zookeeper:3.4.10
mesos-master:
links:
- zookeeper
image: mesosphere/mesos-master:1.0.1-2.0.93.ubuntu1404
# Uncomment published ports for interactive debugging.
# ports:
# - "5050:5050"
environment:
- MESOS_HOSTNAME=mesos-master
- MESOS_CLUSTER=local
- MESOS_REGISTRY=in_memory
- MESOS_LOG_DIR=/var/log
- MESOS_WORK_DIR=/var/lib/mesos
- MESOS_ZK=zk://zookeeper:2181/mesos
MESOS_HOSTNAME: mesos-master
MESOS_CLUSTER: local
MESOS_REGISTRY: in_memory
MESOS_LOG_DIR: /var/log
MESOS_WORK_DIR: /var/lib/mesos
MESOS_ZK: zk://zookeeper:2181/mesos
mesos-slave:
links:
- zookeeper
- mesos-master
image: mesosphere/mesos-slave-dind:0.3.0_mesos-1.0.1_docker-1.10.3_ubuntu-14.04.5
image: docker:dind
privileged: true
# Uncomment published ports for interactive debugging.
# ports:
# - "5051:5051"
environment:
- MESOS_HOSTNAME=mesos-slave
- MESOS_CONTAINERIZERS=docker,mesos
- MESOS_ISOLATOR=cgroups/cpu,cgroups/mem
- MESOS_LOG_DIR=/var/log
- MESOS_MASTER=zk://zookeeper:2181/mesos
- MESOS_PORT=5051
- MESOS_WORK_DIR=/var/lib/mesos
- MESOS_EXECUTOR_REGISTRATION_TIMEOUT=5mins
- MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD=90secs
- MESOS_DOCKER_STOP_TIMEOUT=60secs
- MESOS_RESOURCES=cpus:2;mem:2048;disk:20480;ports(*):[12000-12999]
# docker version in mesosphere/mesos-slave-dind:0.3.0_mesos-1.0.1_docker-1.10.3_ubuntu-14.04.5 is too old and can't
# pull images on new kernels.
command:
- "/bin/sh"
- "-c"
- "(/usr/local/bin/dockerd-entrypoint.sh &); sleep 10; set -x; \
docker -H unix:///var/run/docker.sock run -d --net=host --privileged \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /cgroup:/cgroup -v /sys:/sys \
-v /usr/local/bin/docker:/usr/local/bin/docker \
-e MESOS_HOSTNAME=mesos-slave \
-e MESOS_CONTAINERIZERS=docker,mesos \
-e MESOS_ISOLATOR=cgroups/cpu,cgroups/mem \
-e MESOS_LOG_DIR=/var/log \
-e MESOS_MASTER=zk://zookeeper:2181/mesos \
-e MESOS_PORT=5051 \
-e MESOS_WORK_DIR=/var/lib/mesos \
-e MESOS_EXECUTOR_REGISTRATION_TIMEOUT=5mins \
-e MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD=90secs \
-e MESOS_DOCKER_STOP_TIMEOUT=60secs \
-e MESOS_RESOURCES='cpus:2;mem:2048;disk:20480;ports(*):[12000-12999]' \
mesosphere/mesos-slave:1.0.3; sleep 600"
marathon:
links:
- zookeeper
- mesos-master
- mesos-slave
image: mesosphere/marathon:v1.3.12
# Uncomment published ports for interactive debugging.
# ports:
# - "8080:8080"
extra_hosts:
- "mesos-slave:172.17.0.1"
environment:
- MARATHON_ZK=zk://zookeeper:2181/marathon
- MARATHON_MASTER=zk://zookeeper:2181/mesos
MARATHON_ZK: zk://zookeeper:2181/marathon
MARATHON_MASTER: zk://zookeeper:2181/mesos
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,55 +1,51 @@
version: "3.8"
services:
zookeeper:
image: zookeeper:3.4.10
mesos-master:
links:
- zookeeper
image: mesosphere/mesos-master:1.4.1
# Uncomment published ports for interactive debugging.
# ports:
# - "5050:5050"
environment:
- MESOS_HOSTNAME=mesos-master
- MESOS_CLUSTER=local
- MESOS_REGISTRY=in_memory
- MESOS_LOG_DIR=/var/log
- MESOS_WORK_DIR=/var/lib/mesos
- MESOS_ZK=zk://zookeeper:2181/mesos
MESOS_HOSTNAME: mesos-master
MESOS_CLUSTER: local
MESOS_REGISTRY: in_memory
MESOS_LOG_DIR: /var/log
MESOS_WORK_DIR: /var/lib/mesos
MESOS_ZK: zk://zookeeper:2181/mesos
mesos-slave:
links:
- zookeeper
- mesos-master
image: mesosphere/mesos-slave-dind:0.4.0_mesos-1.4.1_docker-17.05.0_ubuntu-16.04.3
privileged: true
# Uncomment published ports for interactive debugging.
# ports:
# - "5051:5051"
environment:
- MESOS_HOSTNAME=mesos-slave
- MESOS_CONTAINERIZERS=docker,mesos
- MESOS_ISOLATOR=cgroups/cpu,cgroups/mem
- MESOS_LOG_DIR=/var/log
- MESOS_MASTER=zk://zookeeper:2181/mesos
- MESOS_PORT=5051
- MESOS_WORK_DIR=/var/lib/mesos
- MESOS_EXECUTOR_REGISTRATION_TIMEOUT=5mins
- MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD=90secs
- MESOS_DOCKER_STOP_TIMEOUT=60secs
- MESOS_RESOURCES=cpus:2;mem:2048;disk:20480;ports(*):[12000-12999]
- MESOS_SYSTEMD_ENABLE_SUPPORT=false
MESOS_HOSTNAME: mesos-slave
MESOS_CONTAINERIZERS: docker,mesos
MESOS_ISOLATOR: cgroups/cpu,cgroups/mem
MESOS_LOG_DIR: /var/log
MESOS_MASTER: zk://zookeeper:2181/mesos
MESOS_PORT: 5051
MESOS_WORK_DIR: /var/lib/mesos
MESOS_EXECUTOR_REGISTRATION_TIMEOUT: 5mins
MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD: 90secs
MESOS_DOCKER_STOP_TIMEOUT: 60secs
MESOS_RESOURCES: cpus:2;mem:2048;disk:20480;ports(*):[12000-12999]
MESOS_SYSTEMD_ENABLE_SUPPORT: false
marathon:
links:
- zookeeper
- mesos-master
- mesos-slave
image: mesosphere/marathon:v1.5.9
# Uncomment published ports for interactive debugging.
# ports:
# - "8080:8080"
extra_hosts:
- "mesos-slave:172.17.0.1"
environment:
- MARATHON_ZK=zk://zookeeper:2181/marathon
- MARATHON_MASTER=zk://zookeeper:2181/mesos
MARATHON_ZK: zk://zookeeper:2181/marathon
MARATHON_MASTER: zk://zookeeper:2181/mesos
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,6 +1,14 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
labels:
- traefik.http.Routers.RouterMini.Rule=PathPrefix("/whoami")
- traefik.enable=true
traefik.http.Routers.RouterMini.Rule: PathPrefix(`/whoami`)
traefik.enable: true
deploy:
replicas: 2
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,10 +1,15 @@
version: "3.8"
services:
pebble:
image: letsencrypt/pebble:v2.3.1
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
ports:
- 14000:14000
command: pebble --dnsserver traefik:5053
environment:
# https://github.com/letsencrypt/pebble#testing-at-full-speed
- PEBBLE_VA_NOSLEEP=1
PEBBLE_VA_NOSLEEP: 1
# https://github.com/letsencrypt/pebble#invalid-anti-replay-nonce-errors
- PEBBLE_WFE_NONCEREJECT=0
PEBBLE_WFE_NONCEREJECT: 0
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,7 +1,14 @@
version: "3.8"
services:
haproxy:
image: haproxy:2.2
volumes:
- ../haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
- ./resources/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,2 +1,9 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,9 @@
version: "3.8"
services:
redis:
image: redis:5.0
ports:
- "6379:6379"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,2 +1,9 @@
version: "3.8"
services:
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,9 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
ports:
- "8881:80"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,2 +1,9 @@
version: "3.8"
services:
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,12 @@
version: "3.8"
services:
whoami1:
image: traefik/whoami
whoami2:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,38 +1,39 @@
version: "3.8"
services:
whoami-a:
image: traefik/whoamitcp
command: -name whoami-a -certFile /certs/whoami-a.crt -keyFile /certs/whoami-a.key
volumes:
- ../../fixtures/tcp:/certs
ports:
- "8081:8080"
- ./fixtures/tcp:/certs
whoami-b:
image: traefik/whoamitcp
command: -name whoami-b -certFile /certs/whoami-b.crt -keyFile /certs/whoami-b.key
volumes:
- ../../fixtures/tcp:/certs
ports:
- "8082:8080"
- ./fixtures/tcp:/certs
whoami-ab:
image: traefik/whoamitcp
command: -name whoami-ab -certFile /certs/whoami-b.crt -keyFile /certs/whoami-b.key
volumes:
- ./fixtures/tcp:/certs
whoami-no-cert:
image: traefik/whoamitcp
command: -name whoami-no-cert
ports:
- "8083:8080"
whoami-no-tls:
image: traefik/whoamitcp
command: -name whoami-no-tls
ports:
- "8084:8080"
whoami:
image: traefik/whoami
ports:
- "8085:80"
whoami-banner:
image: traefik/whoamitcp
command: -name whoami-banner --banner
ports:
- "8086:8080"
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,7 +1,12 @@
version: "3.8"
services:
timeoutEndpoint:
image: yaman/timeout
environment:
- PROTO=http
- PORT=9000
ports:
- "9000:9000"
PROTO: http
PORT: 9000
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,7 +1,14 @@
version: "3.8"
services:
whoami:
image: traefik/whoami
labels:
- traefik.http.routers.route1.rule=PathPrefix(`/foo`)
- traefik.http.routers.route1.middlewares=passtls
- traefik.http.routers.route1.tls=true
- traefik.http.middlewares.passtls.passtlsclientcert.pem=true
traefik.http.routers.route1.rule: PathPrefix(`/foo`)
traefik.http.routers.route1.middlewares: passtls
traefik.http.routers.route1.tls: true
traefik.http.middlewares.passtls.passtlsclientcert.pem: true
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,21 +1,20 @@
version: "3.8"
services:
zipkin:
image: openzipkin/zipkin:2.16.2
environment:
STORAGE_TYPE: mem
JAVA_OPTS: -Dlogging.level.zipkin=DEBUG
ports:
- 9411:9411
jaeger:
image: jaegertracing/all-in-one:1.14
environment:
COLLECTOR_ZIPKIN_HTTP_PORT: 9411
ports:
- "5775:5775/udp"
- "6831:6831/udp"
- "6832:6832/udp"
- "5778:5778"
- "16686:16686"
- "14268:14268"
- "9411:9411"
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,3 +1,5 @@
version: "3.8"
services:
whoami-a:
image: traefik/whoamiudp:latest
command: -name whoami-a
@ -12,3 +14,8 @@ whoami-c:
whoami-d:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,34 +1,41 @@
version: "3.8"
services:
noOverrideWhitelist:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt1.rule=Host("no.override.whitelist.docker.local")
- traefik.http.routers.rt1.middlewares=wl1
- traefik.http.middlewares.wl1.ipwhiteList.sourceRange=8.8.8.8
traefik.enable: true
traefik.http.routers.rt1.rule: Host(`no.override.whitelist.docker.local`)
traefik.http.routers.rt1.middlewares: wl1
traefik.http.middlewares.wl1.ipwhiteList.sourceRange: 8.8.8.8
overrideIPStrategyRemoteAddrWhitelist:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt2.rule=Host("override.remoteaddr.whitelist.docker.local")
- traefik.http.routers.rt2.middlewares=wl2
- traefik.http.middlewares.wl2.ipwhitelist.sourceRange=8.8.8.8
- traefik.http.middlewares.wl2.ipwhitelist.ipStrategy=true
traefik.enable: true
traefik.http.routers.rt2.rule: Host(`override.remoteaddr.whitelist.docker.local`)
traefik.http.routers.rt2.middlewares: wl2
traefik.http.middlewares.wl2.ipwhitelist.sourceRange: 8.8.8.8
traefik.http.middlewares.wl2.ipwhitelist.ipStrategy: true
overrideIPStrategyDepthWhitelist:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt3.rule=Host("override.depth.whitelist.docker.local")
- traefik.http.routers.rt3.middlewares=wl3
- traefik.http.middlewares.wl3.ipwhitelist.sourceRange=8.8.8.8
- traefik.http.middlewares.wl3.ipwhitelist.ipStrategy.depth=3
traefik.enable: true
traefik.http.routers.rt3.rule: Host(`override.depth.whitelist.docker.local`)
traefik.http.routers.rt3.middlewares: wl3
traefik.http.middlewares.wl3.ipwhitelist.sourceRange: 8.8.8.8
traefik.http.middlewares.wl3.ipwhitelist.ipStrategy.depth: 3
overrideIPStrategyExcludedIPsWhitelist:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.rt4.rule=Host("override.excludedips.whitelist.docker.local")
- traefik.http.routers.rt4.middlewares=wl4
- traefik.http.middlewares.wl4.ipwhitelist.sourceRange=8.8.8.8
- traefik.http.middlewares.wl4.ipwhitelist.ipStrategy.excludedIPs=10.0.0.1,10.0.0.2
traefik.enable: true
traefik.http.routers.rt4.rule: Host(`override.excludedips.whitelist.docker.local`)
traefik.http.routers.rt4.middlewares: wl4
traefik.http.middlewares.wl4.ipwhitelist.sourceRange: 8.8.8.8
traefik.http.middlewares.wl4.ipwhitelist.ipStrategy.excludedIPs: 10.0.0.1,10.0.0.2
networks:
default:
name: traefik-test-network
external: true

View file

@ -1,4 +1,9 @@
version: "3.8"
services:
zookeeper:
image: zookeeper:3.5
ports:
- "2181:2181"
networks:
default:
name: traefik-test-network
external: true

View file

@ -23,8 +23,8 @@ frontend TestServerTestV2
backend TestServerNodes
mode tcp
server TestServer01 172.17.0.1:8000 send-proxy
server TestServer01 traefik:8000 send-proxy
backend TestServerNodesV2
mode tcp
server TestServer01 172.17.0.1:8000 send-proxy-v2
server TestServer01 traefik:8000 send-proxy-v2

View file

@ -3,6 +3,7 @@ package integration
import (
"bytes"
"encoding/json"
"net"
"net/http"
"os"
"strings"
@ -14,12 +15,16 @@ import (
checker "github.com/vdemeester/shakers"
)
type RestSuite struct{ BaseSuite }
type RestSuite struct {
BaseSuite
whoamiAddr string
}
func (s *RestSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "rest")
s.composeUp(c)
s.composeProject.Start(c)
s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
}
func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
@ -60,7 +65,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
URL: "http://" + s.whoamiAddr,
},
},
},
@ -86,7 +91,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
Address: s.whoamiAddr,
},
},
},
@ -164,7 +169,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
URL: "http://" + s.whoamiAddr,
},
},
},
@ -190,7 +195,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
Address: s.whoamiAddr,
},
},
},
@ -213,10 +218,10 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains(test.ruleMatch))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains(test.ruleMatch))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK))
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
}

View file

@ -11,18 +11,20 @@ import (
checker "github.com/vdemeester/shakers"
)
type RetrySuite struct{ BaseSuite }
type RetrySuite struct {
BaseSuite
whoamiIP string
}
func (s *RetrySuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "retry")
s.composeProject.Start(c)
s.composeUp(c)
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
}
func (s *RetrySuite) TestRetry(c *check.C) {
whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct {
WhoamiEndpoint string
}{whoamiEndpoint})
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -44,10 +46,7 @@ func (s *RetrySuite) TestRetry(c *check.C) {
}
func (s *RetrySuite) TestRetryBackoff(c *check.C) {
whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/retry/backoff.toml", struct {
WhoamiEndpoint string
}{whoamiEndpoint})
file := s.adaptFile(c, "fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -72,10 +71,7 @@ func (s *RetrySuite) TestRetryBackoff(c *check.C) {
}
func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct {
WhoamiEndpoint string
}{whoamiEndpoint})
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))

View file

@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"os"
@ -50,7 +51,6 @@ func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// TODO validate : run on 80
// Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
@ -93,17 +93,20 @@ func (s *SimpleSuite) TestPrintHelp(c *check.C) {
func (s *SimpleSuite) TestRequestAcceptGraceTimeout(c *check.C) {
s.createComposeProject(c, "reqacceptgrace")
s.composeProject.Start(c)
whoami := "http://" + s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress + ":80"
s.composeUp(c)
defer s.composeDown(c)
whoamiURL := "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "whoami"), "80")
file := s.adaptFile(c, "fixtures/reqacceptgrace.toml", struct {
Server string
}{whoami})
}{whoamiURL})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
@ -193,15 +196,17 @@ func (s *SimpleSuite) TestCustomPingTerminationStatusCode(c *check.C) {
func (s *SimpleSuite) TestStatsWithMultipleEntryPoint(c *check.C) {
c.Skip("Stats is missing")
s.createComposeProject(c, "stats")
s.composeProject.Start(c)
whoami1 := "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80"
whoami2 := "http://" + s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress + ":80"
s.composeUp(c)
defer s.composeDown(c)
whoami1URL := "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
whoami2URL := "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "whoami2"), "80")
file := s.adaptFile(c, "fixtures/simple_stats.toml", struct {
Server1 string
Server2 string
}{whoami1, whoami2})
}{whoami1URL, whoami2URL})
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
@ -229,7 +234,9 @@ func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) {
c.Skip("Waiting for new api handler implementation")
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
file := s.adaptFile(c, "./fixtures/simple_auth.toml", struct{}{})
defer os.Remove(file)
@ -249,7 +256,9 @@ func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) {
func (s *SimpleSuite) TestDefaultEntryPointHTTP(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--log.level=DEBUG", "--providers.docker", "--api.insecure")
defer output(c)
@ -267,7 +276,9 @@ func (s *SimpleSuite) TestDefaultEntryPointHTTP(c *check.C) {
func (s *SimpleSuite) TestWithNonExistingEntryPoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--log.level=DEBUG", "--providers.docker", "--api.insecure")
defer output(c)
@ -285,7 +296,9 @@ func (s *SimpleSuite) TestWithNonExistingEntryPoint(c *check.C) {
func (s *SimpleSuite) TestMetricsPrometheusDefaultEntryPoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api.insecure", "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0", "--providers.docker", "--metrics.prometheus.addrouterslabels=true", "--log.level=DEBUG")
defer output(c)
@ -315,7 +328,9 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntryPoint(c *check.C) {
func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd("--entryPoints.http.Address=:8000", "--api.insecure", "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0", "--providers.docker", "--metrics.prometheus.addentrypointslabels=false", "--metrics.prometheus.addrouterslabels=true", "--log.level=DEBUG")
defer output(c)
@ -346,22 +361,22 @@ func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService(c *check.C) {
c.Assert(err, checker.IsNil)
// Reqs count of 1 for both routers
c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router1@docker\",service=\"whoami1-integrationtestbase@docker\"} 1")
c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router2@docker\",service=\"whoami1-integrationtestbase@docker\"} 1")
c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router1@docker\",service=\"whoami1-traefik-integration-test-base@docker\"} 1")
c.Assert(string(body), checker.Contains, "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router2@docker\",service=\"whoami1-traefik-integration-test-base@docker\"} 1")
// Reqs count of 2 for service behind both routers
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"whoami1-integrationtestbase@docker\"} 2")
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"whoami1-traefik-integration-test-base@docker\"} 2")
}
}
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
ipWhoami01 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
ipWhoami02 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{
IP: ipWhoami02,
})
s.composeUp(c)
defer s.composeDown(c)
whoami1IP := s.getComposeServiceIP(c, "whoami1")
whoami2IP := s.getComposeServiceIP(c, "whoami2")
file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{IP: whoami2IP})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
@ -374,16 +389,18 @@ func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.BodyContains(ipWhoami01))
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.BodyContains(whoami1IP))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/file", 1*time.Second, try.BodyContains(ipWhoami02))
err = try.GetRequest("http://127.0.0.1:8000/file", 1*time.Second, try.BodyContains(whoami2IP))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestIPStrategyWhitelist(c *check.C) {
s.createComposeProject(c, "whitelist")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd(withConfigFile("fixtures/simple_whitelist.toml"))
defer output(c)
@ -451,7 +468,9 @@ func (s *SimpleSuite) TestIPStrategyWhitelist(c *check.C) {
func (s *SimpleSuite) TestXForwardedHeaders(c *check.C) {
s.createComposeProject(c, "whitelist")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
cmd, output := s.traefikCmd(withConfigFile("fixtures/simple_whitelist.toml"))
defer output(c)
@ -479,13 +498,13 @@ func (s *SimpleSuite) TestXForwardedHeaders(c *check.C) {
func (s *SimpleSuite) TestMultiProvider(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server := "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
s.composeUp(c)
defer s.composeDown(c)
file := s.adaptFile(c, "fixtures/multiprovider.toml", struct {
Server string
}{Server: server})
whoamiURL := "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
file := s.adaptFile(c, "fixtures/multiprovider.toml", struct{ Server string }{Server: whoamiURL})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
@ -530,13 +549,13 @@ func (s *SimpleSuite) TestMultiProvider(c *check.C) {
func (s *SimpleSuite) TestSimpleConfigurationHostRequestTrailingPeriod(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server := "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
s.composeUp(c)
defer s.composeDown(c)
file := s.adaptFile(c, "fixtures/file/simple-hosts.toml", struct {
Server string
}{Server: server})
whoamiURL := "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
file := s.adaptFile(c, "fixtures/file/simple-hosts.toml", struct{ Server string }{Server: whoamiURL})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
@ -707,15 +726,17 @@ func (s *SimpleSuite) TestUDPServiceConfigErrors(c *check.C) {
func (s *SimpleSuite) TestWRR(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
s.composeUp(c)
defer s.composeDown(c)
whoami1IP := s.getComposeServiceIP(c, "whoami1")
whoami2IP := s.getComposeServiceIP(c, "whoami2")
file := s.adaptFile(c, "fixtures/wrr.toml", struct {
Server1 string
Server2 string
}{Server1: "http://" + server1, Server2: "http://" + server2})
}{Server1: "http://" + whoami1IP, Server2: "http://" + whoami2IP})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
@ -740,29 +761,31 @@ func (s *SimpleSuite) TestWRR(c *check.C) {
body, err := io.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if strings.Contains(string(body), server1) {
repartition[server1]++
if strings.Contains(string(body), whoami1IP) {
repartition[whoami1IP]++
}
if strings.Contains(string(body), server2) {
repartition[server2]++
if strings.Contains(string(body), whoami2IP) {
repartition[whoami2IP]++
}
}
c.Assert(repartition[server1], checker.Equals, 3)
c.Assert(repartition[server2], checker.Equals, 1)
c.Assert(repartition[whoami1IP], checker.Equals, 3)
c.Assert(repartition[whoami2IP], checker.Equals, 1)
}
func (s *SimpleSuite) TestWRRSticky(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
s.composeUp(c)
defer s.composeDown(c)
whoami1IP := s.getComposeServiceIP(c, "whoami1")
whoami2IP := s.getComposeServiceIP(c, "whoami2")
file := s.adaptFile(c, "fixtures/wrr_sticky.toml", struct {
Server1 string
Server2 string
}{Server1: "http://" + server1, Server2: "http://" + server2})
}{Server1: "http://" + whoami1IP, Server2: "http://" + whoami2IP})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
@ -791,16 +814,16 @@ func (s *SimpleSuite) TestWRRSticky(c *check.C) {
body, err := io.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if strings.Contains(string(body), server1) {
repartition[server1]++
if strings.Contains(string(body), whoami1IP) {
repartition[whoami1IP]++
}
if strings.Contains(string(body), server2) {
repartition[server2]++
if strings.Contains(string(body), whoami2IP) {
repartition[whoami2IP]++
}
}
c.Assert(repartition[server1], checker.Equals, 4)
c.Assert(repartition[server2], checker.Equals, 0)
c.Assert(repartition[whoami1IP], checker.Equals, 4)
c.Assert(repartition[whoami2IP], checker.Equals, 0)
}
func (s *SimpleSuite) TestMirror(c *check.C) {
@ -1037,7 +1060,9 @@ func (s *SimpleSuite) TestMirrorCanceled(c *check.C) {
func (s *SimpleSuite) TestSecureAPI(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
s.composeUp(c)
defer s.composeDown(c)
file := s.adaptFile(c, "./fixtures/simple_secure_api.toml", struct{}{})
defer os.Remove(file)

View file

@ -2,6 +2,9 @@ package integration
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/http"
"net/http/httptest"
@ -18,7 +21,7 @@ type TCPSuite struct{ BaseSuite }
func (s *TCPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tcp")
s.composeProject.Start(c)
s.composeUp(c)
}
func (s *TCPSuite) TestMixed(c *check.C) {
@ -36,12 +39,12 @@ func (s *TCPSuite) TestMixed(c *check.C) {
c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a
out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true)
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a")
// Traefik passes through, termination handled by whoami-b
out, err = guessWho("127.0.0.1:8093", "whoami-b.test", true)
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
@ -116,12 +119,12 @@ func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a
out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true)
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a")
// Traefik passes through, termination handled by whoami-b
out, err = guessWho("127.0.0.1:8093", "whoami-b.test", true)
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
@ -211,19 +214,52 @@ func (s *TCPSuite) TestMiddlewareWhiteList(c *check.C) {
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 50*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
c.Assert(err, checker.IsNil)
// Traefik not passes through, ipWhitelist closes connection
_, err = guessWho("127.0.0.1:8093", "whoami-a.test", true)
c.Assert(err, checker.NotNil)
_, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.ErrorMatches, "EOF")
// Traefik passes through, termination handled by whoami-b
out, err := guessWho("127.0.0.1:8093", "whoami-b.test", true)
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
}
func (s *TCPSuite) TestWRR(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-b.test`)"))
c.Assert(err, checker.IsNil)
call := map[string]int{}
for i := 0; i < 4; i++ {
// Traefik passes through, termination handled by whoami-b or whoami-bb
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
switch {
case strings.Contains(out, "whoami-b"):
call["whoami-b"]++
case strings.Contains(out, "whoami-ab"):
call["whoami-ab"]++
default:
call["unknown"]++
}
time.Sleep(time.Second)
}
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-b": 3, "whoami-ab": 1})
}
func welcome(addr string) (string, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
@ -291,34 +327,54 @@ func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion
return string(out[:n]), nil
}
func (s *TCPSuite) TestWRR(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{})
defer os.Remove(file)
// guessWhoTLSPassthrough guesses service identity and ensures that the
// certificate is valid for the given server name.
func guessWhoTLSPassthrough(addr, serverName string) (string, error) {
var conn net.Conn
var err error
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI"))
c.Assert(err, checker.IsNil)
call := map[string]int{}
for i := 0; i < 4; i++ {
// Traefik passes through, termination handled by whoami-a
out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true)
c.Assert(err, checker.IsNil)
switch {
case strings.Contains(out, "whoami-a"):
call["whoami-a"]++
case strings.Contains(out, "whoami-b"):
call["whoami-b"]++
default:
call["unknown"]++
}
conn, err = tls.Dial("tcp", addr, &tls.Config{
ServerName: serverName,
InsecureSkipVerify: true,
MinVersion: 0,
MaxVersion: 0,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rawCerts) > 1 {
return errors.New("tls: more than one certificates from peer")
}
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 3, "whoami-b": 1})
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return fmt.Errorf("tls: failed to parse certificate from peer: %w", err)
}
if cert.Subject.CommonName == serverName {
return nil
}
if err = cert.VerifyHostname(serverName); err == nil {
return nil
}
return fmt.Errorf("tls: no valid certificate for serverName %s", serverName)
},
})
if err != nil {
return "", err
}
defer conn.Close()
_, err = conn.Write([]byte("WHO"))
if err != nil {
return "", err
}
out := make([]byte, 2048)
n, err := conn.Read(out)
if err != nil {
return "", err
}
return string(out[:n]), nil
}

View file

@ -15,14 +15,12 @@ type TimeoutSuite struct{ BaseSuite }
func (s *TimeoutSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "timeout")
s.composeProject.Start(c)
s.composeUp(c)
}
func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
httpTimeoutEndpoint := s.composeProject.Container(c, "timeoutEndpoint").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct {
TimeoutEndpoint string
}{httpTimeoutEndpoint})
timeoutEndpointIP := s.getComposeServiceIP(c, "timeoutEndpoint")
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct{ TimeoutEndpoint string }{timeoutEndpointIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
@ -40,7 +38,7 @@ func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
// Check that timeout service is available
statusURL := fmt.Sprintf("http://%s:9000/statusTest?status=200", httpTimeoutEndpoint)
statusURL := fmt.Sprintf("http://%s:9000/statusTest?status=200", timeoutEndpointIP)
c.Assert(try.GetRequest(statusURL, 60*time.Second, try.StatusCodeIs(http.StatusOK)), checker.IsNil)
// This simulates a ResponseHeaderTimeout.

View file

@ -21,7 +21,7 @@ type TLSClientHeadersSuite struct{ BaseSuite }
func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tlsclientheaders")
s.composeProject.Start(c)
s.composeUp(c)
}
func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {

View file

@ -12,47 +12,49 @@ import (
type TracingSuite struct {
BaseSuite
WhoAmiIP string
WhoAmiPort int
IP string
whoamiIP string
whoamiPort int
tracerIP string
}
type TracingTemplate struct {
WhoAmiIP string
WhoAmiPort int
WhoamiIP string
WhoamiPort int
IP string
TraceContextHeaderName string
}
func (s *TracingSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tracing")
s.composeProject.Start(c, "whoami")
s.composeUp(c)
s.WhoAmiIP = s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
s.WhoAmiPort = 80
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
s.whoamiPort = 80
}
func (s *TracingSuite) startZipkin(c *check.C) {
s.composeProject.Start(c, "zipkin")
s.IP = s.composeProject.Container(c, "zipkin").NetworkSettings.IPAddress
s.composeUp(c, "zipkin")
s.tracerIP = s.getComposeServiceIP(c, "zipkin")
// Wait for Zipkin to turn ready.
err := try.GetRequest("http://"+s.IP+":9411/api/v2/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
err := try.GetRequest("http://"+s.tracerIP+":9411/api/v2/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
s.startZipkin(c)
defer s.composeProject.Stop(c, "zipkin")
// defer s.composeStop(c, "zipkin")
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
@ -87,17 +89,18 @@ func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestZipkinRetry(c *check.C) {
s.startZipkin(c)
defer s.composeProject.Stop(c, "zipkin")
defer s.composeStop(c, "zipkin")
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: 81,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: 81,
IP: s.tracerIP,
})
defer os.Remove(file)
@ -114,17 +117,18 @@ func (s *TracingSuite) TestZipkinRetry(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestZipkinAuth(c *check.C) {
s.startZipkin(c)
defer s.composeProject.Stop(c, "zipkin")
defer s.composeStop(c, "zipkin")
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
})
defer os.Remove(file)
@ -141,26 +145,27 @@ func (s *TracingSuite) TestZipkinAuth(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) startJaeger(c *check.C) {
s.composeProject.Start(c, "jaeger")
s.IP = s.composeProject.Container(c, "jaeger").NetworkSettings.IPAddress
s.composeUp(c, "jaeger", "whoami")
s.tracerIP = s.getComposeServiceIP(c, "jaeger")
// Wait for Jaeger to turn ready.
err := try.GetRequest("http://"+s.IP+":16686/api/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
err := try.GetRequest("http://"+s.tracerIP+":16686/api/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestJaegerRateLimit(c *check.C) {
s.startJaeger(c)
defer s.composeProject.Stop(c, "jaeger")
defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
@ -200,17 +205,18 @@ func (s *TracingSuite) TestJaegerRateLimit(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestJaegerRetry(c *check.C) {
s.startJaeger(c)
defer s.composeProject.Stop(c, "jaeger")
defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: 81,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: 81,
IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
@ -228,17 +234,18 @@ func (s *TracingSuite) TestJaegerRetry(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestJaegerAuth(c *check.C) {
s.startJaeger(c)
defer s.composeProject.Stop(c, "jaeger")
defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
@ -256,17 +263,18 @@ func (s *TracingSuite) TestJaegerAuth(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestJaegerCustomHeader(c *check.C) {
s.startJaeger(c)
defer s.composeProject.Stop(c, "jaeger")
defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
TraceContextHeaderName: "powpow",
})
defer os.Remove(file)
@ -284,17 +292,18 @@ func (s *TracingSuite) TestJaegerCustomHeader(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
}
func (s *TracingSuite) TestJaegerAuthCollector(c *check.C) {
s.startJaeger(c)
defer s.composeProject.Stop(c, "jaeger")
defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger-collector.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP,
WhoAmiPort: s.WhoAmiPort,
IP: s.IP,
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
})
defer os.Remove(file)
@ -311,6 +320,6 @@ func (s *TracingSuite) TestJaegerAuthCollector(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.IP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
}

View file

@ -16,7 +16,7 @@ type UDPSuite struct{ BaseSuite }
func (s *UDPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "udp")
s.composeProject.Start(c)
s.composeUp(c)
}
func guessWhoUDP(addr string) (string, error) {
@ -47,21 +47,16 @@ func guessWhoUDP(addr string) (string, error) {
}
func (s *UDPSuite) TestWRR(c *check.C) {
whoamiAIP := s.composeProject.Container(c, "whoami-a").NetworkSettings.IPAddress
whoamiBIP := s.composeProject.Container(c, "whoami-b").NetworkSettings.IPAddress
whoamiCIP := s.composeProject.Container(c, "whoami-c").NetworkSettings.IPAddress
whoamiDIP := s.composeProject.Container(c, "whoami-d").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/udp/wrr.toml", struct {
WhoamiAIP string
WhoamiBIP string
WhoamiCIP string
WhoamiDIP string
}{
WhoamiAIP: whoamiAIP,
WhoamiBIP: whoamiBIP,
WhoamiCIP: whoamiCIP,
WhoamiDIP: whoamiDIP,
WhoamiAIP: s.getComposeServiceIP(c, "whoami-a"),
WhoamiBIP: s.getComposeServiceIP(c, "whoami-b"),
WhoamiCIP: s.getComposeServiceIP(c, "whoami-c"),
WhoamiDIP: s.getComposeServiceIP(c, "whoami-d"),
})
defer os.Remove(file)

View file

@ -3,6 +3,7 @@ package integration
import (
"bytes"
"encoding/json"
"net"
"net/http"
"os"
"path/filepath"
@ -18,20 +19,25 @@ import (
checker "github.com/vdemeester/shakers"
)
// Zk test suites (using libcompose).
// Zk test suites.
type ZookeeperSuite struct {
BaseSuite
kvClient store.Store
zookeeperAddr string
}
func (s *ZookeeperSuite) setupStore(c *check.C) {
s.createComposeProject(c, "zookeeper")
s.composeProject.Start(c)
s.composeUp(c)
zookeeper.Register()
kv, err := valkeyrie.NewStore(
s.zookeeperAddr = net.JoinHostPort(s.getComposeServiceIP(c, "zookeeper"), "2181")
var err error
s.kvClient, err = valkeyrie.NewStore(
store.ZK,
[]string{s.composeProject.Container(c, "zookeeper").NetworkSettings.IPAddress + ":2181"},
[]string{s.zookeeperAddr},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
@ -39,27 +45,16 @@ func (s *ZookeeperSuite) setupStore(c *check.C) {
if err != nil {
c.Fatal("Cannot create store zookeeper")
}
s.kvClient = kv
// wait for zk
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
c.Assert(err, checker.IsNil)
}
func (s *ZookeeperSuite) TearDownTest(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *ZookeeperSuite) TearDownSuite(c *check.C) {}
func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c)
address := s.composeProject.Container(c, "zookeeper").NetworkSettings.IPAddress + ":2181"
file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{address})
file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
defer os.Remove(file)
data := map[string]string{

View file

@ -1,3 +1,5 @@
# This file is not meant as a usable Traefik configuration.
# It is only used in tests as a sample for serialization.
[global]
checkNewVersion = true
sendAnonymousUsage = true

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"os"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
@ -46,7 +47,7 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[
return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
}
i := interp.New(interp.Options{GoPath: client.GoPath()})
i := interp.New(interp.Options{GoPath: client.GoPath(), Env: os.Environ()})
err = i.Use(stdlib.Symbols)
if err != nil {
@ -89,7 +90,7 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[
return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
}
i := interp.New(interp.Options{GoPath: localGoPath})
i := interp.New(interp.Options{GoPath: localGoPath, Env: os.Environ()})
err = i.Use(stdlib.Symbols)
if err != nil {

View file

@ -9,6 +9,24 @@ spec:
prefixes:
- /tobestripped
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: ratelimit
namespace: default
spec:
rateLimit:
period: 1m
average: 6
burst: 12
sourceCriterion:
ipStrategy:
excludedIPs:
- 127.0.0.1/32
- 192.168.1.7
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
@ -40,5 +58,6 @@ spec:
port: 80
middlewares:
- name: stripprefix
- name: ratelimit
- name: addprefix
namespace: foo

View file

@ -443,6 +443,10 @@ func createRateLimitMiddleware(rateLimit *v1alpha1.RateLimit) (*dynamic.RateLimi
}
}
if rateLimit.SourceCriterion != nil {
rl.SourceCriterion = rateLimit.SourceCriterion
}
return rl, nil
}

View file

@ -1472,10 +1472,22 @@ func TestLoadIngressRoutes(t *testing.T) {
Service: "default-test2-route-23c7f4c450289ee29016",
Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)",
Priority: 12,
Middlewares: []string{"default-stripprefix", "foo-addprefix"},
Middlewares: []string{"default-stripprefix", "default-ratelimit", "foo-addprefix"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"default-ratelimit": {
RateLimit: &dynamic.RateLimit{
Average: 6,
Burst: 12,
Period: ptypes.Duration(60 * time.Second),
SourceCriterion: &dynamic.SourceCriterion{
IPStrategy: &dynamic.IPStrategy{
ExcludedIPs: []string{"127.0.0.1/32", "192.168.1.7"},
},
},
},
},
"default-stripprefix": {
StripPrefix: &dynamic.StripPrefix{
Prefixes: []string{"/tobestripped"},

View file

@ -8,7 +8,8 @@ import (
"time"
)
const receiveMTU = 8192
// maxDatagramSize is the maximum size of a UDP datagram.
const maxDatagramSize = 65535
const closeRetryInterval = 500 * time.Millisecond
@ -135,7 +136,8 @@ func (l *Listener) readLoop() {
// Allocating a new buffer for every read avoids
// overwriting data in c.msgs in case the next packet is received
// before c.msgs is emptied via Read()
buf := make([]byte, receiveMTU)
buf := make([]byte, maxDatagramSize)
n, raddr, err := l.pConn.ReadFrom(buf)
if err != nil {
return
@ -144,6 +146,7 @@ func (l *Listener) readLoop() {
if err != nil {
continue
}
select {
case conn.receiveCh <- buf[:n]:
case <-conn.doneCh:
@ -249,7 +252,9 @@ func (c *Conn) readLoop() {
}
}
// Read implements io.Reader for a Conn.
// Read reads up to len(p) bytes into p from the connection.
// Each call corresponds to at most one datagram.
// If p is smaller than the datagram, the extra bytes will be discarded.
func (c *Conn) Read(p []byte) (int, error) {
select {
case c.readCh <- p:
@ -258,22 +263,21 @@ func (c *Conn) Read(p []byte) (int, error) {
c.lastActivity = time.Now()
c.muActivity.Unlock()
return n, nil
case <-c.doneCh:
return 0, io.EOF
}
}
// Write implements io.Writer for a Conn.
// Write writes len(p) bytes from p to the underlying connection.
// Each call sends at most one datagram.
// It is an error to send a message larger than the system's max UDP datagram size.
func (c *Conn) Write(p []byte) (n int, err error) {
l := c.listener
if l == nil {
return 0, io.EOF
}
c.muActivity.Lock()
c.lastActivity = time.Now()
c.muActivity.Unlock()
return l.pConn.WriteTo(p, c.rAddr)
return c.listener.pConn.WriteTo(p, c.rAddr)
}
func (c *Conn) close() {

View file

@ -1,9 +1,11 @@
package udp
import (
"crypto/rand"
"errors"
"io"
"net"
"runtime"
"testing"
"time"
@ -317,6 +319,61 @@ func TestShutdown(t *testing.T) {
}
}
func TestReadLoopMaxDataSize(t *testing.T) {
if runtime.GOOS == "darwin" {
// sudo sysctl -w net.inet.udp.maxdgram=65507
t.Skip("Skip test on darwin as the maximum dgram size is set to 9216 bytes by default")
}
// Theoretical maximum size of data in a UDP datagram.
// 65535 8 (UDP header) 20 (IP header).
dataSize := 65507
doneCh := make(chan struct{})
addr, err := net.ResolveUDPAddr("udp", ":0")
require.NoError(t, err)
l, err := Listen("udp", addr, 3*time.Second)
require.NoError(t, err)
defer func() {
err := l.Close()
require.NoError(t, err)
}()
go func() {
defer close(doneCh)
conn, err := l.Accept()
require.NoError(t, err)
buffer := make([]byte, dataSize)
n, err := conn.Read(buffer)
require.NoError(t, err)
assert.Equal(t, dataSize, n)
}()
c, err := net.Dial("udp", l.Addr().String())
require.NoError(t, err)
data := make([]byte, dataSize)
_, err = rand.Read(data)
require.NoError(t, err)
_, err = c.Write(data)
require.NoError(t, err)
select {
case <-doneCh:
case <-time.Tick(5 * time.Second):
t.Fatal("Timeout waiting for datagram read")
}
}
// requireEcho tests that the conn session is live and functional,
// by writing data through it, and expecting the same data as a response when reading on it.
// It fatals if the read blocks longer than timeout,

View file

@ -20,14 +20,14 @@ func NewProxy(address string) (*Proxy, error) {
// ServeUDP implements the Handler interface.
func (p *Proxy) ServeUDP(conn *Conn) {
log.Debugf("Handling connection from %s", conn.rAddr)
log.WithoutContext().Debugf("Handling connection from %s", conn.rAddr)
// needed because of e.g. server.trackedConnection
defer conn.Close()
connBackend, err := net.Dial("udp", p.target)
if err != nil {
log.Errorf("Error while connecting to backend: %v", err)
log.WithoutContext().Errorf("Error while connecting to backend: %v", err)
return
}
@ -35,8 +35,8 @@ func (p *Proxy) ServeUDP(conn *Conn) {
defer connBackend.Close()
errChan := make(chan error)
go p.connCopy(conn, connBackend, errChan)
go p.connCopy(connBackend, conn, errChan)
go connCopy(conn, connBackend, errChan)
go connCopy(connBackend, conn, errChan)
err = <-errChan
if err != nil {
@ -46,8 +46,12 @@ func (p *Proxy) ServeUDP(conn *Conn) {
<-errChan
}
func (p Proxy) connCopy(dst io.WriteCloser, src io.Reader, errCh chan error) {
_, err := io.Copy(dst, src)
func connCopy(dst io.WriteCloser, src io.Reader, errCh chan error) {
// The buffer is initialized to the maximum UDP datagram size,
// to make sure that the whole UDP datagram is read or written atomically (no data is discarded).
buffer := make([]byte, maxDatagramSize)
_, err := io.CopyBuffer(dst, src, buffer)
errCh <- err
if err := dst.Close(); err != nil {

Some files were not shown because too many files have changed in this diff Show more