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

View file

@ -31,42 +31,24 @@ global_job_config:
- cache restore traefik-$(checksum go.sum) - cache restore traefik-$(checksum go.sum)
blocks: blocks:
- name: Test Integration Container - name: Test Integration
dependencies: [] dependencies: []
run: run:
when: "branch =~ '.*' OR pull_request =~'.*'" when: "branch =~ '.*' OR pull_request =~'.*'"
task: task:
jobs: jobs:
- name: Test Integration Container - name: Test Integration
commands: commands:
- make pull-images - make pull-images
- mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui - mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui
- PRE_TARGET="" make binary - PRE_TARGET="" make binary
- make test-integration-container - make test-integration
- df -h - df -h
epilogue: epilogue:
always: always:
commands: commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod - 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 - name: Release
dependencies: [] dependencies: []
run: 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:]') REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik") 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)",) DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
TRAEFIK_ENVS := \ TRAEFIK_ENVS := \
@ -32,7 +32,8 @@ TRAEFIK_ENVS := \
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/traefik/traefik/$(BIND_DIR)" 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_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_NON_INTERACTIVE ?= false 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) DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
PRE_TARGET ?= build-dev-image PRE_TARGET ?= build-dev-image
@ -81,30 +82,27 @@ crossbinary-default-parallel:
$(MAKE) build-dev-image crossbinary-default $(MAKE) build-dev-image crossbinary-default
## Run the unit and integration tests ## Run the unit and integration tests
test: build-dev-image test: $(PRE_TARGET)
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration -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 ## Run the unit tests
test-unit: $(PRE_TARGET) 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 all images for integration tests
pull-images: pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull 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 code and docs
validate-files: $(PRE_TARGET) validate-files: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell $(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell

View file

@ -349,12 +349,16 @@ http:
### `tls` ### `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, #### `ca`
defaults to the system bundle.
_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" ```yaml tab="Docker"
labels: labels:
@ -417,13 +421,15 @@ http:
ca = "path/to/local.crt" 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 "" !!! 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. 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 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" ```yaml tab="Docker"
labels: 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. 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" ```yaml tab="Docker"
labels: 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. 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. 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) - [Prometheus](./prometheus.md)
- [StatsD](./statsd.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 ## Server Metrics
| Metric | DataDog | InfluxDB | Prometheus | StatsD | | Metric | DataDog | InfluxDB | Prometheus | StatsD |

View file

@ -362,14 +362,14 @@ providers:
_Optional_ _Optional_
Defines TLS options for Consul server endpoint. Defines the TLS configuration used for the secure connection to Consul Catalog.
##### `ca` ##### `ca`
_Optional_ _Optional_
Certificate Authority used for the secure connection to Consul, `ca` is the path to the certificate authority used for the secure connection to Consul Catalog,
defaults to the system bundle. it defaults to the system bundle.
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"
providers: providers:
@ -392,11 +392,11 @@ providers:
_Optional_ _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 "" !!! 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. 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_ _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. When using this option, setting the `key` option is required.
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"
@ -451,8 +450,7 @@ providers:
_Optional_ _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. When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)" ```yaml tab="File (YAML)"
@ -477,7 +475,7 @@ providers:
##### `insecureSkipVerify` ##### `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. 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_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to Consul.
Certificate Authority used for the secure connection to Consul, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -125,13 +129,15 @@ providers:
--providers.consul.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -176,9 +185,12 @@ providers:
--providers.consul.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -199,7 +211,9 @@ providers:
--providers.consul.tls.key=path/to/foo.key --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. 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_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to Docker.
Certificate Authority used for the secure connection to Docker, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -634,13 +638,15 @@ providers:
--providers.docker.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -685,9 +692,12 @@ providers:
--providers.docker.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -708,7 +718,9 @@ providers:
--providers.docker.tls.key=path/to/foo.key --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. 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_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to etcd.
Certificate Authority used for the secure connection to etcd, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -125,13 +129,15 @@ providers:
--providers.etcd.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -176,9 +185,12 @@ providers:
--providers.etcd.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -199,7 +211,9 @@ providers:
--providers.etcd.tls.key=path/to/foo.key --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. 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"_ _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)" ```yaml tab="File (YAML)"
providers: providers:
@ -76,10 +76,14 @@ providers:
_Optional_ _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, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -97,13 +101,15 @@ providers:
--providers.http.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -148,9 +157,12 @@ providers:
--providers.http.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -171,7 +183,9 @@ providers:
--providers.http.tls.key=path/to/foo.key --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. 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_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to Marathon.
Certificate Authority used for the secure connection to Marathon, #### `ca`
defaults to the system bundle.
`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)" ```yaml tab="File (YAML)"
providers: providers:
@ -425,13 +427,15 @@ providers:
--providers.marathon.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -476,9 +483,12 @@ providers:
--providers.marathon.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -499,7 +509,9 @@ providers:
--providers.marathon.tls.key=path/to/foo.key --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. 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)" ```yaml tab="File (YAML)"
providers: providers:
marathon: marathon:
responseHeaderTimeout: "10s" tlsHandshakeTimeout: "10s"
# ... # ...
``` ```
```toml tab="File (TOML)" ```toml tab="File (TOML)"
[providers.marathon] [providers.marathon]
responseHeaderTimeout = "10s" tlsHandshakeTimeout = "10s"
# ... # ...
``` ```
```bash tab="CLI" ```bash tab="CLI"
--providers.marathon.responseHeaderTimeout=10s --providers.marathon.tlsHandshakeTimeout=10s
# ... # ...
``` ```

View file

@ -104,10 +104,14 @@ providers:
_Optional_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to Redis.
Certificate Authority used for the secure connection to Redis, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -125,13 +129,15 @@ providers:
--providers.redis.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -176,9 +185,12 @@ providers:
--providers.redis.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -199,7 +211,9 @@ providers:
--providers.redis.tls.key=path/to/foo.key --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. 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_ _Optional_
#### `tls.ca` Defines the TLS configuration used for the secure connection to ZooKeeper.
Certificate Authority used for the secure connection to ZooKeeper, #### `ca`
defaults to the system bundle.
_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)" ```yaml tab="File (YAML)"
providers: providers:
@ -125,13 +129,15 @@ providers:
--providers.zookeeper.tls.ca=path/to/ca.crt --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 "" !!! 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. 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 --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -176,9 +185,12 @@ providers:
--providers.zookeeper.tls.key=path/to/foo.key --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)" ```yaml tab="File (YAML)"
providers: providers:
@ -199,7 +211,9 @@ providers:
--providers.zookeeper.tls.key=path/to/foo.key --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. 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. See [tls](../routers/index.md#tls) for more information.
```yaml ```yaml
traefik.http.routers.myrouter>.tls=true traefik.http.routers.myrouter.tls=true
``` ```
??? info "`traefik.http.routers.<router_name>.tls.certresolver`" ??? 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. See [serverstransport](../services/index.md#serverstransport) for more information.
```yaml ```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`" ??? 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. `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. 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`). 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" !!! 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. 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. 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 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)).
already has a health check mechanism. As unhealthy pods have no Kubernetes endpoints, Traefik will not forward traffic to them.
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)) Therefore, Traefik health check is not available for `kubernetesCRD` and `kubernetesIngress` providers.
??? example "Custom Interval & Timeout -- Using the [File Provider](../../providers/file.md)" ??? 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/BurntSushi/toml v0.3.1
github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61 github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61
github.com/Masterminds/sprig/v3 v3.2.2 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/Shopify/sarama v1.23.1 // indirect
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000 github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
github.com/abronan/valkeyrie v0.2.0 github.com/abronan/valkeyrie v0.2.0
github.com/aws/aws-sdk-go v1.39.0 github.com/aws/aws-sdk-go v1.39.0
github.com/cenkalti/backoff/v4 v4.1.1 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/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/docker/cli v0.0.0-20200221155518-740919cc7fc0 github.com/docker/cli v20.10.11+incompatible
github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/compose/v2 v2.0.1
github.com/docker/docker v17.12.0-ce-rc1.0.20200204220554-5f6d6f3f2203+incompatible github.com/docker/docker v20.10.7+incompatible
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-connections v0.4.0 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/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e // indirect
github.com/eapache/channels v1.1.0 github.com/eapache/channels v1.1.0
github.com/fatih/structs v1.1.0 github.com/fatih/structs v1.1.0
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 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-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
github.com/golang/protobuf v1.5.2 github.com/golang/protobuf v1.5.2
@ -40,24 +36,17 @@ require (
github.com/hashicorp/consul/api v1.10.0 github.com/hashicorp/consul/api v1.10.0
github.com/hashicorp/go-hclog v0.16.1 github.com/hashicorp/go-hclog v0.16.1
github.com/hashicorp/go-multierror v1.1.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/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
github.com/instana/go-sensor v1.31.1 github.com/instana/go-sensor v1.31.1
github.com/klauspost/compress v1.13.0 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/lucas-clemente/quic-go v0.23.0
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f
github.com/miekg/dns v1.1.43 github.com/miekg/dns v1.1.43
github.com/mitchellh/copystructure v1.0.0 github.com/mitchellh/copystructure v1.0.0
github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/hashstructure v1.0.0
github.com/mitchellh/mapstructure v1.4.1 github.com/mitchellh/mapstructure v1.4.2
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opentracing/opentracing-go v1.2.0
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/openzipkin-contrib/zipkin-go-opentracing v0.4.5 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
github.com/openzipkin/zipkin-go v0.2.2 github.com/openzipkin/zipkin-go v0.2.2
github.com/patrickmn/go-cache v2.1.0+incompatible 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_golang v1.11.0
github.com/prometheus/client_model v0.2.0 github.com/prometheus/client_model v0.2.0
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac 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/stretchr/testify v1.7.0
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/tinylib/msgp v1.0.2 // indirect github.com/tinylib/msgp v1.0.2 // indirect
github.com/traefik/paerser v0.1.4 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-client-go v2.29.1+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2 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/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595
github.com/mailgun/multibuf => github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba 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) { func (s *AccessLogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log") s.createComposeProject(c, "access_log")
s.composeProject.Start(c) s.composeUp(c)
s.composeProject.Container(c, "server0")
s.composeProject.Container(c, "server1")
s.composeProject.Container(c, "server2")
s.composeProject.Container(c, "server3")
} }
func (s *AccessLogSuite) TearDownTest(c *check.C) { func (s *AccessLogSuite) TearDownTest(c *check.C) {
@ -122,7 +117,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
code: "200", code: "200",
user: "test", user: "test",
routerName: "rt-authFrontend", 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) checkStatsForLogFile(c)
s.composeProject.Container(c, "authFrontend")
waitForTraefik(c, "authFrontend") waitForTraefik(c, "authFrontend")
// Verify Traefik started OK // Verify Traefik started OK
@ -193,7 +186,7 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
code: "200", code: "200",
user: "test", user: "test",
routerName: "rt-digestAuthMiddleware", 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) checkStatsForLogFile(c)
s.composeProject.Container(c, "digestAuthMiddleware")
waitForTraefik(c, "digestAuthMiddleware") waitForTraefik(c, "digestAuthMiddleware")
// Verify Traefik started OK // Verify Traefik started OK
@ -322,8 +313,6 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
checkStatsForLogFile(c) checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendRedirect")
waitForTraefik(c, "frontendRedirect") waitForTraefik(c, "frontendRedirect")
// Verify Traefik started OK // Verify Traefik started OK
@ -375,8 +364,6 @@ func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
checkStatsForLogFile(c) checkStatsForLogFile(c)
s.composeProject.Container(c, "rateLimit")
waitForTraefik(c, "rateLimit") waitForTraefik(c, "rateLimit")
// Verify Traefik started OK // Verify Traefik started OK
@ -471,8 +458,6 @@ func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
checkStatsForLogFile(c) checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendWhitelist")
waitForTraefik(c, "frontendWhitelist") waitForTraefik(c, "frontendWhitelist")
// Verify Traefik started OK // Verify Traefik started OK
@ -504,7 +489,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
code: "200", code: "200",
user: "test", user: "test",
routerName: "rt-authFrontend", 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) checkStatsForLogFile(c)
s.composeProject.Container(c, "authFrontend")
waitForTraefik(c, "authFrontend") waitForTraefik(c, "authFrontend")
// Verify Traefik started OK // Verify Traefik started OK
@ -548,7 +531,6 @@ func checkNoOtherTraefikProblems(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) 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) c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) fmt.Printf("%s\n", string(traefikLog))
c.Assert(traefikLog, checker.HasLen, 0)
} }
return traefikLog return traefikLog
} }

View file

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

View file

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

View file

@ -3,6 +3,8 @@ package integration
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -18,20 +20,24 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
// Consul test suites (using libcompose). // Consul test suites.
type ConsulSuite struct { type ConsulSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
consulURL string
} }
func (s *ConsulSuite) setupStore(c *check.C) { func (s *ConsulSuite) setupStore(c *check.C) {
s.createComposeProject(c, "consul") 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() consul.Register()
kv, err := valkeyrie.NewStore( kv, err := valkeyrie.NewStore(
store.CONSUL, store.CONSUL,
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"}, []string{consulAddr},
&store.Config{ &store.Config{
ConnectionTimeout: 10 * time.Second, ConnectionTimeout: 10 * time.Second,
}, },
@ -46,20 +52,10 @@ func (s *ConsulSuite) setupStore(c *check.C) {
c.Assert(err, checker.IsNil) 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) { func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(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 }{s.consulURL})
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{address})
defer os.Remove(file) defer os.Remove(file)
data := map[string]string{ data := map[string]string{

View file

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

View file

@ -6,80 +6,24 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"strings"
"time" "time"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/go-check/check" "github.com/go-check/check"
d "github.com/libkermit/docker"
"github.com/libkermit/docker-check"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers" 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. // Docker tests suite.
type DockerSuite struct { type DockerSuite struct {
BaseSuite BaseSuite
project *docker.Project
} }
func (s *DockerSuite) startContainer(c *check.C, image string, args ...string) string { func (s *DockerSuite) SetUpTest(c *check.C) {
return s.startContainerWithConfig(c, image, d.ContainerConfig{ s.createComposeProject(c, "docker")
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) TearDownTest(c *check.C) { 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) { 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) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
s.composeUp(c)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
// TODO validate : run on 80
// Expected a 404 as we did not configure anything // 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)) err = try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) 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) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
name := s.startContainer(c, "swarm:1.0.0", "manage", "token://blablabla") s.composeUp(c, "simple")
// Start traefik // Start traefik
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
c.Assert(err, checker.IsNil) 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 ?) // 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) 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) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
// Start a container with some labels s.composeUp(c, "withtcplabels")
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")
// Start traefik // Start traefik
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
@ -193,17 +134,7 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
// Start a container with some labels s.composeUp(c, "withlabels1", "withlabels2")
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")
// Start traefik // Start traefik
cmd, display := s.traefikCmd(withConfigFile(file)) 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) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
// Start a container with some labels s.composeUp(c, "withonelabelmissing")
labels := map[string]string{
"traefik.random.value": "my.super.host",
}
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
// Start traefik // Start traefik
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
@ -285,16 +213,12 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects) file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file) defer os.Remove(file)
// Start a container with some labels s.composeUp(c, "powpow")
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")
// Start traefik // Start traefik
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
s.stopAndRemoveContainerByName(c, "powpow") s.composeStop(c, "powpow")
defer s.project.Remove(c, "powpow")
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("powpow")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.NotNil) 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")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }

View file

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

View file

@ -3,6 +3,7 @@ package integration
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -18,20 +19,24 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
// etcd test suites (using libcompose). // etcd test suites.
type EtcdSuite struct { type EtcdSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
etcdAddr string
} }
func (s *EtcdSuite) setupStore(c *check.C) { func (s *EtcdSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "etcd") s.createComposeProject(c, "etcd")
s.composeProject.Start(c) s.composeUp(c)
etcdv3.Register() 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, store.ETCDV3,
[]string{s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":2379"}, []string{s.etcdAddr},
&store.Config{ &store.Config{
ConnectionTimeout: 10 * time.Second, ConnectionTimeout: 10 * time.Second,
}, },
@ -39,27 +44,14 @@ func (s *EtcdSuite) setupStore(c *check.C) {
if err != nil { if err != nil {
c.Fatal("Cannot create store etcd") c.Fatal("Cannot create store etcd")
} }
s.kvClient = kv
// wait for etcd // 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) 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) { func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c) file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
address := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":2379"
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{address})
defer os.Remove(file) defer os.Remove(file)
data := map[string]string{ data := map[string]string{

View file

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

View file

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

View file

@ -6,4 +6,4 @@
[http.services] [http.services]
[http.services.service2.loadBalancer] [http.services.service2.loadBalancer]
[[http.services.service2.loadBalancer.servers]] [[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]
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8080" url = "http://{{ .WhoamiIP }}:8080"
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8081" url = "http://{{ .WhoamiIP }}:8081"
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8082" url = "http://{{ .WhoamiIP }}:8082"
[[http.services.service1.loadBalancer.servers]] [[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]
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:8080" url = "http://{{ .WhoamiIP }}:8080"
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoamiEndpoint}}:80" url = "http://{{ .WhoamiIP }}:80"

View file

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

View file

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

View file

@ -38,18 +38,14 @@
[tcp.services] [tcp.services]
[tcp.services.whoami-a.loadBalancer] [tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]] [[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081" address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer] [tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]] [[tcp.services.whoami-b.loadBalancer.servers]]
address = "localhost:8082" address = "whoami-b:8080"
[tcp.middlewares] [tcp.middlewares]
[tcp.middlewares.allowing-ipwhitelist.ipWhiteList] [tcp.middlewares.allowing-ipwhitelist.ipWhiteList]
sourceRange = ["127.0.0.1/32"] sourceRange = ["127.0.0.1/32"]
[tcp.middlewares.blocking-ipwhitelist.ipWhiteList] [tcp.middlewares.blocking-ipwhitelist.ipWhiteList]
sourceRange = ["127.127.127.127/32"] 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/`)" rule = "Path(`/whoami/`)"
service = "whoami" service = "whoami"
[http.routers.my-https-router.tls] [http.routers.my-https-router.tls]
[http.routers.api]
rule = "PathPrefix(`/api`)"
service = "api@internal"
entryPoints = ["traefik"]
[http.services] [http.services]
[http.services.whoami.loadBalancer] [http.services.whoami.loadBalancer]
[[http.services.whoami.loadBalancer.servers]] [[http.services.whoami.loadBalancer.servers]]
url = "http://localhost:8085" url = "http://whoami:80"
[tcp] [tcp]
[tcp.routers] [tcp.routers]
[tcp.routers.to-whoami-a] [tcp.routers.to-whoami-a]
@ -58,15 +62,15 @@
[tcp.services.whoami-a.loadBalancer] [tcp.services.whoami-a.loadBalancer]
[[tcp.services.whoami-a.loadBalancer.servers]] [[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081" address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer] [tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]] [[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]
[[tcp.services.whoami-no-cert.loadBalancer.servers]] [[tcp.services.whoami-no-cert.loadBalancer.servers]]
address = "localhost:8083" address = "whoami-no-cert:8080"
[[tls.certificates]] [[tls.certificates]]
certFile = "fixtures/tcp/whoami-c.crt" certFile = "fixtures/tcp/whoami-c.crt"

View file

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

View file

@ -24,14 +24,14 @@
service = "whoami-a" service = "whoami-a"
entryPoints = [ "tcp" ] entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-a.tls] [tcp.routers.to-whoami-a.tls]
passthrough=true passthrough = true
[tcp.routers.to-whoami-b] [tcp.routers.to-whoami-b]
rule = "HostSNI(`whoami-b.test`)" rule = "HostSNI(`whoami-b.test`)"
service = "whoami-b" service = "whoami-b"
entryPoints = [ "tcp" ] entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-b.tls] [tcp.routers.to-whoami-b.tls]
passthrough=true passthrough = true
[tcp.routers.to-whoami-no-cert] [tcp.routers.to-whoami-no-cert]
rule = "HostSNI(`whoami-c.test`)" rule = "HostSNI(`whoami-c.test`)"
@ -47,16 +47,17 @@
[tcp.services] [tcp.services]
[tcp.services.whoami-no-tls.loadBalancer] [tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]] [[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]
[[tcp.services.whoami-a.loadBalancer.servers]] [[tcp.services.whoami-a.loadBalancer.servers]]
address = "localhost:8081" address = "whoami-a:8080"
[tcp.services.whoami-b.loadBalancer] [tcp.services.whoami-b.loadBalancer]
[[tcp.services.whoami-b.loadBalancer.servers]] [[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]
[[tcp.services.whoami-no-cert.loadBalancer.servers]] [[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]
[tcp.services.whoami-no-tls.loadBalancer] [tcp.services.whoami-no-tls.loadBalancer]
[[tcp.services.whoami-no-tls.loadBalancer.servers]] [[tcp.services.whoami-no-tls.loadBalancer.servers]]
address = "localhost:8084" address = "whoami-no-tls:8080"

View file

@ -18,25 +18,24 @@
## dynamic configuration ## ## dynamic configuration ##
[tcp] [tcp]
[tcp.routers] [tcp.routers]
[tcp.routers.to-whoami-a] [tcp.routers.to-whoami-b]
rule = "HostSNI(`whoami-a.test`)" rule = "HostSNI(`whoami-b.test`)"
service = "whoami" service = "whoami"
entryPoints = [ "tcp" ] entryPoints = [ "tcp" ]
[tcp.routers.to-whoami-a.tls] [tcp.routers.to-whoami-b.tls]
passthrough=true passthrough=true
[[tcp.services.whoami.weighted.services]] [[tcp.services.whoami.weighted.services]]
name="whoami-a" name="whoami-b"
weight=3 weight=3
[[tcp.services.whoami.weighted.services]] [[tcp.services.whoami.weighted.services]]
name="whoami-b" name="whoami-ab"
weight=1 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]
[[tcp.services.whoami-b.loadBalancer.servers]] [[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] [http.services.service1.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}" url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service2] [http.services.service2]
[http.services.service2.loadBalancer] [http.services.service2.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service2.loadBalancer.servers]] [[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}" url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service3] [http.services.service3]
[http.services.service3.loadBalancer] [http.services.service3.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service3.loadBalancer.servers]] [[http.services.service3.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}" url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"

View file

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

View file

@ -50,16 +50,16 @@
[http.services.service1.loadBalancer] [http.services.service1.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service1.loadBalancer.servers]] [[http.services.service1.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}" url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service2] [http.services.service2]
[http.services.service2.loadBalancer] [http.services.service2.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service2.loadBalancer.servers]] [[http.services.service2.loadBalancer.servers]]
url = "http://{{.WhoAmiIP}}:{{.WhoAmiPort}}" url = "http://{{.WhoamiIP}}:{{.WhoamiPort}}"
[http.services.service3] [http.services.service3]
[http.services.service3.loadBalancer] [http.services.service3.loadBalancer]
passHostHeader = true passHostHeader = true
[[http.services.service3.loadBalancer.servers]] [[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" checker "github.com/vdemeester/shakers"
) )
// HealthCheck test suites (using libcompose). // HealthCheck test suites.
type HealthCheckSuite struct { type HealthCheckSuite struct {
BaseSuite BaseSuite
whoami1IP string whoami1IP string
@ -24,12 +24,12 @@ type HealthCheckSuite struct {
func (s *HealthCheckSuite) SetUpSuite(c *check.C) { func (s *HealthCheckSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "healthcheck") s.createComposeProject(c, "healthcheck")
s.composeProject.Start(c) s.composeUp(c)
s.whoami1IP = s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress s.whoami1IP = s.getComposeServiceIP(c, "whoami1")
s.whoami2IP = s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress s.whoami2IP = s.getComposeServiceIP(c, "whoami2")
s.whoami3IP = s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress s.whoami3IP = s.getComposeServiceIP(c, "whoami3")
s.whoami4IP = s.composeProject.Container(c, "whoami4").NetworkSettings.IPAddress s.whoami4IP = s.getComposeServiceIP(c, "whoami4")
} }
func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) { 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. // Check if the service with bad health check (whoami2) never respond.
err = try.Request(frontendReq, 2*time.Second, try.BodyContains(s.whoami2IP)) 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 // TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1:8000/") 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) { func (s *HostResolverSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "hostresolver") s.createComposeProject(c, "hostresolver")
s.composeUp(c)
s.composeProject.Start(c)
s.composeProject.Container(c, "server1")
} }
func (s *HostResolverSuite) TestSimpleConfig(c *check.C) { func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
@ -48,7 +46,7 @@ func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
req.Host = test.host 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) c.Assert(err, checker.IsNil)
} }
} }

View file

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

View file

@ -3,9 +3,9 @@ package integration
import ( import (
"bytes" "bytes"
"context"
"flag" "flag"
"fmt" "fmt"
"net"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -14,17 +14,23 @@ import (
"text/template" "text/template"
"time" "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/fatih/structs"
"github.com/go-check/check" "github.com/go-check/check"
compose "github.com/libkermit/compose/check"
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
var ( var (
integration = flag.Bool("integration", false, "run integration tests") 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") showLog = flag.Bool("tlog", false, "always show Traefik logs")
) )
@ -34,45 +40,39 @@ func Test(t *testing.T) {
return return
} }
if *container {
// tests launched from a container
check.Suite(&AccessLogSuite{}) check.Suite(&AccessLogSuite{})
check.Suite(&AcmeSuite{}) check.Suite(&AcmeSuite{})
check.Suite(&EtcdSuite{})
check.Suite(&ConsulSuite{})
check.Suite(&ConsulCatalogSuite{}) check.Suite(&ConsulCatalogSuite{})
check.Suite(&ConsulSuite{})
check.Suite(&DockerComposeSuite{}) check.Suite(&DockerComposeSuite{})
check.Suite(&DockerSuite{}) check.Suite(&DockerSuite{})
check.Suite(&ErrorPagesSuite{}) check.Suite(&ErrorPagesSuite{})
check.Suite(&EtcdSuite{})
check.Suite(&FileSuite{}) check.Suite(&FileSuite{})
check.Suite(&GRPCSuite{}) check.Suite(&GRPCSuite{})
check.Suite(&HealthCheckSuite{})
check.Suite(&HeadersSuite{}) check.Suite(&HeadersSuite{})
check.Suite(&HealthCheckSuite{})
check.Suite(&HostResolverSuite{}) check.Suite(&HostResolverSuite{})
check.Suite(&HTTPSuite{})
check.Suite(&HTTPSSuite{}) check.Suite(&HTTPSSuite{})
check.Suite(&HTTPSuite{})
check.Suite(&K8sSuite{})
check.Suite(&KeepAliveSuite{}) check.Suite(&KeepAliveSuite{})
check.Suite(&LogRotationSuite{}) check.Suite(&LogRotationSuite{})
check.Suite(&MarathonSuite{})
check.Suite(&MarathonSuite15{}) check.Suite(&MarathonSuite15{})
check.Suite(&MarathonSuite{})
check.Suite(&ProxyProtocolSuite{})
check.Suite(&RateLimitSuite{}) check.Suite(&RateLimitSuite{})
check.Suite(&RedisSuite{}) check.Suite(&RedisSuite{})
check.Suite(&RestSuite{}) check.Suite(&RestSuite{})
check.Suite(&RetrySuite{}) check.Suite(&RetrySuite{})
check.Suite(&SimpleSuite{}) check.Suite(&SimpleSuite{})
check.Suite(&TCPSuite{})
check.Suite(&TimeoutSuite{}) check.Suite(&TimeoutSuite{})
check.Suite(&TLSClientHeadersSuite{}) check.Suite(&TLSClientHeadersSuite{})
check.Suite(&TracingSuite{}) check.Suite(&TracingSuite{})
check.Suite(&UDPSuite{}) check.Suite(&UDPSuite{})
check.Suite(&WebsocketSuite{}) check.Suite(&WebsocketSuite{})
check.Suite(&ZookeeperSuite{}) check.Suite(&ZookeeperSuite{})
}
if *host {
// tests launched from the host
check.Suite(&K8sSuite{})
check.Suite(&ProxyProtocolSuite{})
check.Suite(&TCPSuite{})
}
check.TestingT(t) check.TestingT(t)
} }
@ -80,36 +80,72 @@ func Test(t *testing.T) {
var traefikBinary = "../dist/traefik" var traefikBinary = "../dist/traefik"
type BaseSuite struct { type BaseSuite struct {
composeProject *compose.Project composeProject *types.Project
dockerComposeService composeapi.Service
dockerClient *client.Client
} }
func (s *BaseSuite) TearDownSuite(c *check.C) { func (s *BaseSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project if s.composeProject != nil && s.dockerComposeService != nil {
if s.composeProject != nil { s.composeDown(c)
s.composeProject.Stop(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) { 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) 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) c.Assert(err, checker.IsNil)
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
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) 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)
} }
func withConfigFile(file string) string { // composeUp starts the given services of the current docker compose project, if they are not already started.
return "--configFile=" + file // 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)
}
// 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) { 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) { return cmd, func(c *check.C) {
if c.Failed() || *showLog { if c.Failed() || *showLog {
s.displayLogK3S(c) s.displayLogK3S(c)
s.displayLogCompose(c)
s.displayTraefikLog(c, out) s.displayTraefikLog(c, out)
} }
} }
@ -153,6 +190,25 @@ func (s *BaseSuite) displayLogK3S(c *check.C) {
log.WithoutContext().Println() 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) { func (s *BaseSuite) displayTraefikLog(c *check.C, output *bytes.Buffer) {
if output == nil || output.Len() == 0 { if output == nil || output.Len() == 0 {
log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName()) log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName())
@ -168,6 +224,7 @@ func (s *BaseSuite) getDockerHost() string {
// Default docker socket // Default docker socket
dockerHost = "unix:///var/run/docker.sock" dockerHost = "unix:///var/run/docker.sock"
} }
return dockerHost return dockerHost
} }
@ -192,3 +249,38 @@ func (s *BaseSuite) adaptFile(c *check.C, path string, tempObjects interface{})
return tmpFile.Name() 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) { func (s *K8sSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "k8s") s.createComposeProject(c, "k8s")
s.composeProject.Start(c) s.composeUp(c)
abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml") abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml")
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
@ -44,7 +44,7 @@ func (s *K8sSuite) SetUpSuite(c *check.C) {
} }
func (s *K8sSuite) TearDownSuite(c *check.C) { func (s *K8sSuite) TearDownSuite(c *check.C) {
s.composeProject.Stop(c) s.composeDown(c)
generatedFiles := []string{ generatedFiles := []string{
"./fixtures/k8s/config.skip/kubeconfig.yaml", "./fixtures/k8s/config.skip/kubeconfig.yaml",
@ -56,8 +56,7 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
} }
for _, filename := range generatedFiles { for _, filename := range generatedFiles {
err := os.Remove(filename) if err := os.Remove(filename); err != nil {
if err != nil {
log.WithoutContext().Warning(err) log.WithoutContext().Warning(err)
} }
} }

View file

@ -13,17 +13,38 @@ import (
"github.com/go-check/check" "github.com/go-check/check"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
const (
traefikTestLogFileRotated = traefikTestLogFile + ".rotated"
traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
)
// Log rotation integration test suite. // Log rotation integration test suite.
type LogRotationSuite struct{ BaseSuite } type LogRotationSuite struct{ BaseSuite }
func (s *LogRotationSuite) SetUpSuite(c *check.C) { func (s *LogRotationSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log") 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) { func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
@ -36,8 +57,6 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
defer os.Remove(traefikTestAccessLogFile)
// Verify Traefik started ok // Verify Traefik started ok
verifyEmptyErrorLog(c, "traefik.log") verifyEmptyErrorLog(c, "traefik.log")
@ -52,7 +71,7 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// Rename access log // Rename access log
err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFile+".rotated") err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFileRotated)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// in the midst of the requests, issue SIGUSR1 signal to server process // 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) c.Assert(err, checker.IsNil)
// Verify access.log.rotated output as expected // Verify access.log.rotated output as expected
logAccessLogFile(c, traefikTestAccessLogFile+".rotated") logAccessLogFile(c, traefikTestAccessLogFileRotated)
lineCount := verifyLogLines(c, traefikTestAccessLogFile+".rotated", 0, true) lineCount := verifyLogLines(c, traefikTestAccessLogFileRotated, 0, true)
c.Assert(lineCount, checker.GreaterOrEqualThan, 1) c.Assert(lineCount, checker.GreaterOrEqualThan, 1)
// make sure that the access log file is at least created before we do assertions on it // 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) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) defer s.killCmd(cmd)
defer os.Remove(traefikTestAccessLogFile)
waitForTraefik(c, "server1") waitForTraefik(c, "server1")
// Rename traefik log // Rename traefik log
err = os.Rename(traefikTestLogFile, traefikTestLogFile+".rotated") err = os.Rename(traefikTestLogFile, traefikTestLogFileRotated)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// issue SIGUSR1 signal to server process // issue SIGUSR1 signal to server process
@ -118,7 +135,7 @@ func (s *LogRotationSuite) TestTraefikLogRotation(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// we have at least 6 lines in traefik.log.rotated // 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 // GreaterOrEqualThan used to ensure test doesn't break
// If more log entries are output on startup // If more log entries are output on startup

View file

@ -1,7 +1,6 @@
package integration package integration
import ( import (
"fmt"
"net/http" "net/http"
"os" "os"
"time" "time"
@ -12,7 +11,7 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
// Marathon test suites (using libcompose). // Marathon test suites.
type MarathonSuite15 struct { type MarathonSuite15 struct {
BaseSuite BaseSuite
marathonURL string marathonURL string
@ -20,53 +19,15 @@ type MarathonSuite15 struct {
func (s *MarathonSuite15) SetUpSuite(c *check.C) { func (s *MarathonSuite15) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon15") s.createComposeProject(c, "marathon15")
s.composeProject.Start(c) s.composeUp(c)
marathonIPAddr := s.composeProject.Container(c, containerNameMarathon).NetworkSettings.IPAddress s.marathonURL = "http://" + containerNameMarathon + ":8080"
c.Assert(marathonIPAddr, checker.Not(checker.HasLen), 0)
s.marathonURL = "http://" + marathonIPAddr + ":8080"
// Wait for Marathon readiness prior to creating the client so that we // 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 // don't run into the "all cluster members down" state right from the
// start. // start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) 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) { func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {

View file

@ -1,7 +1,6 @@
package integration package integration
import ( import (
"fmt"
"net/http" "net/http"
"os" "os"
"time" "time"
@ -12,12 +11,9 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
const ( const containerNameMarathon = "marathon"
containerNameMesosSlave = "mesos-slave"
containerNameMarathon = "marathon"
)
// Marathon test suites (using libcompose). // Marathon test suites.
type MarathonSuite struct { type MarathonSuite struct {
BaseSuite BaseSuite
marathonURL string marathonURL string
@ -25,53 +21,15 @@ type MarathonSuite struct {
func (s *MarathonSuite) SetUpSuite(c *check.C) { func (s *MarathonSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon") s.createComposeProject(c, "marathon")
s.composeProject.Start(c) s.composeUp(c)
marathonIPAddr := s.composeProject.Container(c, containerNameMarathon).NetworkSettings.IPAddress s.marathonURL = "http://" + containerNameMarathon + ":8080"
c.Assert(marathonIPAddr, checker.Not(checker.HasLen), 0)
s.marathonURL = "http://" + marathonIPAddr + ":8080"
// Wait for Marathon readiness prior to creating the client so that we // 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 // don't run into the "all cluster members down" state right from the
// start. // start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) 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) { func deployApplication(c *check.C, client marathon.Marathon, application *marathon.Application) {

View file

@ -10,22 +10,27 @@ import (
checker "github.com/vdemeester/shakers" 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) { func (s *ProxyProtocolSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "proxy-protocol") 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) { 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 { file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP}) }{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
@ -34,21 +39,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+gatewayIP)) try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }
func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) { 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 { file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP}) }{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
@ -57,20 +58,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+gatewayIP)) try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) { 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 { file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP}) }{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
@ -79,20 +77,17 @@ func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+haproxyIP)) try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }
func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) { 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 { file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: haproxyIP, WhoamiIP: whoamiIP}) }{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
@ -101,8 +96,8 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+haproxyIP)) try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }

View file

@ -17,9 +17,9 @@ type RateLimitSuite struct {
func (s *RateLimitSuite) SetUpSuite(c *check.C) { func (s *RateLimitSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "ratelimit") 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) { func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,10 @@
etcd: version: "3.8"
services:
etcd:
image: quay.io/coreos/etcd:v3.3.18 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 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 @@
whoami1: version: "3.8"
services:
whoami1:
image: traefik/whoami image: traefik/whoami
ports:
- "8881:80" whoami2:
whoami2:
image: traefik/whoami image: traefik/whoami
ports:
- "8882:80" whoami3:
whoami3:
image: traefik/whoami image: traefik/whoami
ports:
- "8883:80" whoami4:
whoami4:
image: traefik/whoami image: traefik/whoami
ports:
- "8884:80" whoami5:
whoami5:
image: traefik/whoami image: traefik/whoami
ports:
- "8885:80" networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

@ -1,21 +1,24 @@
server: version: "3.8"
services:
server:
image: rancher/k3s:v1.18.20-k3s1 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: environment:
- K3S_CLUSTER_SECRET=somethingtotallyrandom K3S_CLUSTER_SECRET: somethingtotallyrandom
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
- K3S_KUBECONFIG_MODE=666 K3S_KUBECONFIG_MODE: 666
volumes: volumes:
- ../../fixtures/k8s/config.skip:/output - ./fixtures/k8s/config.skip:/output
- ../../fixtures/k8s:/var/lib/rancher/k3s/server/manifests - ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
ports:
- 6443:6443
node: node:
image: rancher/k3s:v1.18.20-k3s1 image: rancher/k3s:v1.18.20-k3s1
privileged: true privileged: true
links:
- server
environment: environment:
- K3S_URL=https://server:6443 K3S_URL: https://server:6443
- K3S_CLUSTER_SECRET=somethingtotallyrandom K3S_CLUSTER_SECRET: somethingtotallyrandom
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

View file

@ -1,10 +1,15 @@
pebble: version: "3.8"
services:
pebble:
image: letsencrypt/pebble:v2.3.1 image: letsencrypt/pebble:v2.3.1
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053 command: pebble --dnsserver traefik:5053
ports:
- 14000:14000
environment: environment:
# https://github.com/letsencrypt/pebble#testing-at-full-speed # 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 # 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 @@
haproxy: version: "3.8"
services:
haproxy:
image: haproxy:2.2 image: haproxy:2.2
volumes: volumes:
- ../haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg - ./resources/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
whoami: whoami:
image: traefik/whoami image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,34 +1,41 @@
noOverrideWhitelist: version: "3.8"
services:
noOverrideWhitelist:
image: traefik/whoami image: traefik/whoami
labels: labels:
- traefik.enable=true traefik.enable: true
- traefik.http.routers.rt1.rule=Host("no.override.whitelist.docker.local") traefik.http.routers.rt1.rule: Host(`no.override.whitelist.docker.local`)
- traefik.http.routers.rt1.middlewares=wl1 traefik.http.routers.rt1.middlewares: wl1
- traefik.http.middlewares.wl1.ipwhiteList.sourceRange=8.8.8.8 traefik.http.middlewares.wl1.ipwhiteList.sourceRange: 8.8.8.8
overrideIPStrategyRemoteAddrWhitelist: overrideIPStrategyRemoteAddrWhitelist:
image: traefik/whoami image: traefik/whoami
labels: labels:
- traefik.enable=true traefik.enable: true
- traefik.http.routers.rt2.rule=Host("override.remoteaddr.whitelist.docker.local") traefik.http.routers.rt2.rule: Host(`override.remoteaddr.whitelist.docker.local`)
- traefik.http.routers.rt2.middlewares=wl2 traefik.http.routers.rt2.middlewares: wl2
- traefik.http.middlewares.wl2.ipwhitelist.sourceRange=8.8.8.8 traefik.http.middlewares.wl2.ipwhitelist.sourceRange: 8.8.8.8
- traefik.http.middlewares.wl2.ipwhitelist.ipStrategy=true traefik.http.middlewares.wl2.ipwhitelist.ipStrategy: true
overrideIPStrategyDepthWhitelist: overrideIPStrategyDepthWhitelist:
image: traefik/whoami image: traefik/whoami
labels: labels:
- traefik.enable=true traefik.enable: true
- traefik.http.routers.rt3.rule=Host("override.depth.whitelist.docker.local") traefik.http.routers.rt3.rule: Host(`override.depth.whitelist.docker.local`)
- traefik.http.routers.rt3.middlewares=wl3 traefik.http.routers.rt3.middlewares: wl3
- traefik.http.middlewares.wl3.ipwhitelist.sourceRange=8.8.8.8 traefik.http.middlewares.wl3.ipwhitelist.sourceRange: 8.8.8.8
- traefik.http.middlewares.wl3.ipwhitelist.ipStrategy.depth=3 traefik.http.middlewares.wl3.ipwhitelist.ipStrategy.depth: 3
overrideIPStrategyExcludedIPsWhitelist: overrideIPStrategyExcludedIPsWhitelist:
image: traefik/whoami image: traefik/whoami
labels: labels:
- traefik.enable=true traefik.enable: true
- traefik.http.routers.rt4.rule=Host("override.excludedips.whitelist.docker.local") traefik.http.routers.rt4.rule: Host(`override.excludedips.whitelist.docker.local`)
- traefik.http.routers.rt4.middlewares=wl4 traefik.http.routers.rt4.middlewares: wl4
- traefik.http.middlewares.wl4.ipwhitelist.sourceRange=8.8.8.8 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.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 @@
zookeeper: version: "3.8"
services:
zookeeper:
image: zookeeper:3.5 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 backend TestServerNodes
mode tcp mode tcp
server TestServer01 172.17.0.1:8000 send-proxy server TestServer01 traefik:8000 send-proxy
backend TestServerNodesV2 backend TestServerNodesV2
mode tcp 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 ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"net"
"net/http" "net/http"
"os" "os"
"strings" "strings"
@ -14,12 +15,16 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
type RestSuite struct{ BaseSuite } type RestSuite struct {
BaseSuite
whoamiAddr string
}
func (s *RestSuite) SetUpSuite(c *check.C) { func (s *RestSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "rest") 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) { func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
@ -60,7 +65,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
LoadBalancer: &dynamic.ServersLoadBalancer{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ 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{ LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{ 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{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ 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{ LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{ 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(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) 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) 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) c.Assert(err, checker.IsNil)
} }
} }

View file

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

View file

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

View file

@ -2,6 +2,9 @@ package integration
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -18,7 +21,7 @@ type TCPSuite struct{ BaseSuite }
func (s *TCPSuite) SetUpSuite(c *check.C) { func (s *TCPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tcp") s.createComposeProject(c, "tcp")
s.composeProject.Start(c) s.composeUp(c)
} }
func (s *TCPSuite) TestMixed(c *check.C) { func (s *TCPSuite) TestMixed(c *check.C) {
@ -36,12 +39,12 @@ func (s *TCPSuite) TestMixed(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a // 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(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a") c.Assert(out, checker.Contains, "whoami-a")
// Traefik passes through, termination handled by whoami-b // 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(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b") c.Assert(out, checker.Contains, "whoami-b")
@ -116,12 +119,12 @@ func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a // 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(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a") c.Assert(out, checker.Contains, "whoami-a")
// Traefik passes through, termination handled by whoami-b // 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(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b") c.Assert(out, checker.Contains, "whoami-b")
@ -211,19 +214,52 @@ func (s *TCPSuite) TestMiddlewareWhiteList(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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) c.Assert(err, checker.IsNil)
// Traefik not passes through, ipWhitelist closes connection // Traefik not passes through, ipWhitelist closes connection
_, err = guessWho("127.0.0.1:8093", "whoami-a.test", true) _, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.NotNil) c.Assert(err, checker.ErrorMatches, "EOF")
// Traefik passes through, termination handled by whoami-b // 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(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b") 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) { func welcome(addr string) (string, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr) tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil { if err != nil {
@ -291,34 +327,54 @@ func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion
return string(out[:n]), nil return string(out[:n]), nil
} }
func (s *TCPSuite) TestWRR(c *check.C) { // guessWhoTLSPassthrough guesses service identity and ensures that the
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{}) // certificate is valid for the given server name.
defer os.Remove(file) func guessWhoTLSPassthrough(addr, serverName string) (string, error) {
var conn net.Conn
var err error
cmd, display := s.traefikCmd(withConfigFile(file)) conn, err = tls.Dial("tcp", addr, &tls.Config{
defer display(c) ServerName: serverName,
InsecureSkipVerify: true,
err := cmd.Start() MinVersion: 0,
c.Assert(err, checker.IsNil) MaxVersion: 0,
defer s.killCmd(cmd) VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rawCerts) > 1 {
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI")) return errors.New("tls: more than one certificates from peer")
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"]++
}
} }
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) { func (s *TimeoutSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "timeout") s.createComposeProject(c, "timeout")
s.composeProject.Start(c) s.composeUp(c)
} }
func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) { func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
httpTimeoutEndpoint := s.composeProject.Container(c, "timeoutEndpoint").NetworkSettings.IPAddress timeoutEndpointIP := s.getComposeServiceIP(c, "timeoutEndpoint")
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct { file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct{ TimeoutEndpoint string }{timeoutEndpointIP})
TimeoutEndpoint string
}{httpTimeoutEndpoint})
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(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) c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
// Check that timeout service is available // 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) c.Assert(try.GetRequest(statusURL, 60*time.Second, try.StatusCodeIs(http.StatusOK)), checker.IsNil)
// This simulates a ResponseHeaderTimeout. // This simulates a ResponseHeaderTimeout.

View file

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

View file

@ -12,47 +12,49 @@ import (
type TracingSuite struct { type TracingSuite struct {
BaseSuite BaseSuite
WhoAmiIP string whoamiIP string
WhoAmiPort int whoamiPort int
IP string tracerIP string
} }
type TracingTemplate struct { type TracingTemplate struct {
WhoAmiIP string WhoamiIP string
WhoAmiPort int WhoamiPort int
IP string IP string
TraceContextHeaderName string TraceContextHeaderName string
} }
func (s *TracingSuite) SetUpSuite(c *check.C) { func (s *TracingSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tracing") s.createComposeProject(c, "tracing")
s.composeProject.Start(c, "whoami") s.composeUp(c)
s.WhoAmiIP = s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress s.whoamiIP = s.getComposeServiceIP(c, "whoami")
s.WhoAmiPort = 80 s.whoamiPort = 80
} }
func (s *TracingSuite) startZipkin(c *check.C) { func (s *TracingSuite) startZipkin(c *check.C) {
s.composeProject.Start(c, "zipkin") s.composeUp(c, "zipkin")
s.IP = s.composeProject.Container(c, "zipkin").NetworkSettings.IPAddress s.tracerIP = s.getComposeServiceIP(c, "zipkin")
// Wait for Zipkin to turn ready. // 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestZipkinRateLimit(c *check.C) { func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
s.startZipkin(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{ file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
}) })
defer os.Remove(file) defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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)) err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestZipkinRetry(c *check.C) { func (s *TracingSuite) TestZipkinRetry(c *check.C) {
s.startZipkin(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{ file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: 81, WhoamiPort: 81,
IP: s.IP, IP: s.tracerIP,
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestZipkinAuth(c *check.C) { func (s *TracingSuite) TestZipkinAuth(c *check.C) {
s.startZipkin(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{ file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) startJaeger(c *check.C) { func (s *TracingSuite) startJaeger(c *check.C) {
s.composeProject.Start(c, "jaeger") s.composeUp(c, "jaeger", "whoami")
s.IP = s.composeProject.Container(c, "jaeger").NetworkSettings.IPAddress s.tracerIP = s.getComposeServiceIP(c, "jaeger")
// Wait for Jaeger to turn ready. // 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestJaegerRateLimit(c *check.C) { func (s *TracingSuite) TestJaegerRateLimit(c *check.C) {
s.startJaeger(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{ file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id", TraceContextHeaderName: "uber-trace-id",
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestJaegerRetry(c *check.C) { func (s *TracingSuite) TestJaegerRetry(c *check.C) {
s.startJaeger(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{ file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: 81, WhoamiPort: 81,
IP: s.IP, IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id", TraceContextHeaderName: "uber-trace-id",
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestJaegerAuth(c *check.C) { func (s *TracingSuite) TestJaegerAuth(c *check.C) {
s.startJaeger(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{ file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
TraceContextHeaderName: "uber-trace-id", TraceContextHeaderName: "uber-trace-id",
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestJaegerCustomHeader(c *check.C) { func (s *TracingSuite) TestJaegerCustomHeader(c *check.C) {
s.startJaeger(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{ file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
TraceContextHeaderName: "powpow", TraceContextHeaderName: "powpow",
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }
func (s *TracingSuite) TestJaegerAuthCollector(c *check.C) { func (s *TracingSuite) TestJaegerAuthCollector(c *check.C) {
s.startJaeger(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{ file := s.adaptFile(c, "fixtures/tracing/simple-jaeger-collector.toml", TracingTemplate{
WhoAmiIP: s.WhoAmiIP, WhoamiIP: s.whoamiIP,
WhoAmiPort: s.WhoAmiPort, WhoamiPort: s.whoamiPort,
IP: s.IP, IP: s.tracerIP,
}) })
defer os.Remove(file) 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)) err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
c.Assert(err, checker.IsNil) 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) c.Assert(err, checker.IsNil)
} }

View file

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

View file

@ -3,6 +3,7 @@ package integration
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -18,20 +19,25 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
// Zk test suites (using libcompose). // Zk test suites.
type ZookeeperSuite struct { type ZookeeperSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
zookeeperAddr string
} }
func (s *ZookeeperSuite) setupStore(c *check.C) { func (s *ZookeeperSuite) setupStore(c *check.C) {
s.createComposeProject(c, "zookeeper") s.createComposeProject(c, "zookeeper")
s.composeProject.Start(c) s.composeUp(c)
zookeeper.Register() 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, store.ZK,
[]string{s.composeProject.Container(c, "zookeeper").NetworkSettings.IPAddress + ":2181"}, []string{s.zookeeperAddr},
&store.Config{ &store.Config{
ConnectionTimeout: 10 * time.Second, ConnectionTimeout: 10 * time.Second,
}, },
@ -39,27 +45,16 @@ func (s *ZookeeperSuite) setupStore(c *check.C) {
if err != nil { if err != nil {
c.Fatal("Cannot create store zookeeper") c.Fatal("Cannot create store zookeeper")
} }
s.kvClient = kv
// wait for zk // 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) 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) { func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c) s.setupStore(c)
address := s.composeProject.Container(c, "zookeeper").NetworkSettings.IPAddress + ":2181" file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{address})
defer os.Remove(file) defer os.Remove(file)
data := map[string]string{ 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] [global]
checkNewVersion = true checkNewVersion = true
sendAnonymousUsage = true sendAnonymousUsage = true

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"os"
"github.com/traefik/yaegi/interp" "github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib" "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) 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) err = i.Use(stdlib.Symbols)
if err != nil { 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) 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) err = i.Use(stdlib.Symbols)
if err != nil { if err != nil {

View file

@ -9,6 +9,24 @@ spec:
prefixes: prefixes:
- /tobestripped - /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 apiVersion: traefik.containo.us/v1alpha1
kind: Middleware kind: Middleware
@ -40,5 +58,6 @@ spec:
port: 80 port: 80
middlewares: middlewares:
- name: stripprefix - name: stripprefix
- name: ratelimit
- name: addprefix - name: addprefix
namespace: foo 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 return rl, nil
} }

View file

@ -1472,10 +1472,22 @@ func TestLoadIngressRoutes(t *testing.T) {
Service: "default-test2-route-23c7f4c450289ee29016", Service: "default-test2-route-23c7f4c450289ee29016",
Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)", Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)",
Priority: 12, Priority: 12,
Middlewares: []string{"default-stripprefix", "foo-addprefix"}, Middlewares: []string{"default-stripprefix", "default-ratelimit", "foo-addprefix"},
}, },
}, },
Middlewares: map[string]*dynamic.Middleware{ 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": { "default-stripprefix": {
StripPrefix: &dynamic.StripPrefix{ StripPrefix: &dynamic.StripPrefix{
Prefixes: []string{"/tobestripped"}, Prefixes: []string{"/tobestripped"},

View file

@ -8,7 +8,8 @@ import (
"time" "time"
) )
const receiveMTU = 8192 // maxDatagramSize is the maximum size of a UDP datagram.
const maxDatagramSize = 65535
const closeRetryInterval = 500 * time.Millisecond const closeRetryInterval = 500 * time.Millisecond
@ -135,7 +136,8 @@ func (l *Listener) readLoop() {
// Allocating a new buffer for every read avoids // Allocating a new buffer for every read avoids
// overwriting data in c.msgs in case the next packet is received // overwriting data in c.msgs in case the next packet is received
// before c.msgs is emptied via Read() // before c.msgs is emptied via Read()
buf := make([]byte, receiveMTU) buf := make([]byte, maxDatagramSize)
n, raddr, err := l.pConn.ReadFrom(buf) n, raddr, err := l.pConn.ReadFrom(buf)
if err != nil { if err != nil {
return return
@ -144,6 +146,7 @@ func (l *Listener) readLoop() {
if err != nil { if err != nil {
continue continue
} }
select { select {
case conn.receiveCh <- buf[:n]: case conn.receiveCh <- buf[:n]:
case <-conn.doneCh: 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) { func (c *Conn) Read(p []byte) (int, error) {
select { select {
case c.readCh <- p: case c.readCh <- p:
@ -258,22 +263,21 @@ func (c *Conn) Read(p []byte) (int, error) {
c.lastActivity = time.Now() c.lastActivity = time.Now()
c.muActivity.Unlock() c.muActivity.Unlock()
return n, nil return n, nil
case <-c.doneCh: case <-c.doneCh:
return 0, io.EOF 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) { func (c *Conn) Write(p []byte) (n int, err error) {
l := c.listener
if l == nil {
return 0, io.EOF
}
c.muActivity.Lock() c.muActivity.Lock()
c.lastActivity = time.Now() c.lastActivity = time.Now()
c.muActivity.Unlock() c.muActivity.Unlock()
return l.pConn.WriteTo(p, c.rAddr)
return c.listener.pConn.WriteTo(p, c.rAddr)
} }
func (c *Conn) close() { func (c *Conn) close() {

View file

@ -1,9 +1,11 @@
package udp package udp
import ( import (
"crypto/rand"
"errors" "errors"
"io" "io"
"net" "net"
"runtime"
"testing" "testing"
"time" "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, // 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. // 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, // 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. // ServeUDP implements the Handler interface.
func (p *Proxy) ServeUDP(conn *Conn) { 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 // needed because of e.g. server.trackedConnection
defer conn.Close() defer conn.Close()
connBackend, err := net.Dial("udp", p.target) connBackend, err := net.Dial("udp", p.target)
if err != nil { if err != nil {
log.Errorf("Error while connecting to backend: %v", err) log.WithoutContext().Errorf("Error while connecting to backend: %v", err)
return return
} }
@ -35,8 +35,8 @@ func (p *Proxy) ServeUDP(conn *Conn) {
defer connBackend.Close() defer connBackend.Close()
errChan := make(chan error) errChan := make(chan error)
go p.connCopy(conn, connBackend, errChan) go connCopy(conn, connBackend, errChan)
go p.connCopy(connBackend, conn, errChan) go connCopy(connBackend, conn, errChan)
err = <-errChan err = <-errChan
if err != nil { if err != nil {
@ -46,8 +46,12 @@ func (p *Proxy) ServeUDP(conn *Conn) {
<-errChan <-errChan
} }
func (p Proxy) connCopy(dst io.WriteCloser, src io.Reader, errCh chan error) { func connCopy(dst io.WriteCloser, src io.Reader, errCh chan error) {
_, err := io.Copy(dst, src) // 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 errCh <- err
if err := dst.Close(); err != nil { if err := dst.Close(); err != nil {

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