Merge v2.11 into v3.0
This commit is contained in:
commit
3bbc560283
85 changed files with 3456 additions and 5204 deletions
27
.github/workflows/build.yaml
vendored
27
.github/workflows/build.yaml
vendored
|
@ -8,7 +8,6 @@ on:
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.21'
|
GO_VERSION: '1.21'
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
IN_DOCKER: ""
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
@ -17,7 +16,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
@ -39,38 +38,22 @@ jobs:
|
||||||
os: [ ubuntu-20.04, macos-latest, windows-latest ]
|
os: [ ubuntu-20.04, macos-latest, windows-latest ]
|
||||||
needs:
|
needs:
|
||||||
- build-webui
|
- build-webui
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: go/src/github.com/traefik/traefik
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
go-version: ${{ env.GO_VERSION }}
|
||||||
~/go/pkg/mod
|
|
||||||
~/.cache/go-build
|
|
||||||
~/Library/Caches/go-build
|
|
||||||
'%LocalAppData%\go-build'
|
|
||||||
key: ${{ runner.os }}-build-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: ${{ runner.os }}-build-go-
|
|
||||||
|
|
||||||
- name: Artifact webui
|
- name: Artifact webui
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: webui.tar.gz
|
name: webui.tar.gz
|
||||||
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
|
||||||
|
|
||||||
- name: Untar webui
|
- name: Untar webui
|
||||||
run: tar xvf webui.tar.gz
|
run: tar xvf webui.tar.gz
|
||||||
|
|
2
.github/workflows/check_doc.yml
vendored
2
.github/workflows/check_doc.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
2
.github/workflows/experimental.yaml
vendored
2
.github/workflows/experimental.yaml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/checkout
|
# https://github.com/marketplace/actions/checkout
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
74
.github/workflows/test-integration.yaml
vendored
Normal file
74
.github/workflows/test-integration.yaml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
name: Test Integration
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'gh-actions'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: '1.21'
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
|
||||||
|
- name: Avoid generating webui
|
||||||
|
run: touch webui/static/index.html
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
run: make binary
|
||||||
|
|
||||||
|
test-integration:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
parallel: [12]
|
||||||
|
index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GO_VERSION }}
|
||||||
|
|
||||||
|
- name: Avoid generating webui
|
||||||
|
run: touch webui/static/index.html
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
run: make binary
|
||||||
|
|
||||||
|
- name: Generate go test Slice
|
||||||
|
id: test_split
|
||||||
|
uses: hashicorp-forge/go-test-split-action@v1
|
||||||
|
with:
|
||||||
|
packages: ./integration
|
||||||
|
total: ${{ matrix.parallel }}
|
||||||
|
index: ${{ matrix.index }}
|
||||||
|
|
||||||
|
- name: Run Integration tests
|
||||||
|
run: |
|
||||||
|
go test ./integration -test.timeout=20m -failfast -v -run "${{ steps.test_split.outputs.run}}"
|
23
.github/workflows/test-unit.yaml
vendored
23
.github/workflows/test-unit.yaml
vendored
|
@ -7,37 +7,22 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.21'
|
GO_VERSION: '1.21'
|
||||||
IN_DOCKER: ""
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: go/src/github.com/traefik/traefik
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
go-version: ${{ env.GO_VERSION }}
|
||||||
~/go/pkg/mod
|
|
||||||
~/.cache/go-build
|
|
||||||
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: ${{ runner.os }}-test-unit-go-
|
|
||||||
|
|
||||||
- name: Avoid generating webui
|
- name: Avoid generating webui
|
||||||
run: touch webui/static/index.html
|
run: touch webui/static/index.html
|
||||||
|
|
47
.github/workflows/validate.yaml
vendored
47
.github/workflows/validate.yaml
vendored
|
@ -8,38 +8,23 @@ on:
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.21'
|
GO_VERSION: '1.21'
|
||||||
GOLANGCI_LINT_VERSION: v1.55.2
|
GOLANGCI_LINT_VERSION: v1.55.2
|
||||||
MISSSPELL_VERSION: v0.4.0
|
MISSSPELL_VERSION: v0.4.1
|
||||||
IN_DOCKER: ""
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
validate:
|
validate:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: go/src/github.com/traefik/traefik
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
go-version: ${{ env.GO_VERSION }}
|
||||||
~/go/pkg/mod
|
|
||||||
~/.cache/go-build
|
|
||||||
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: ${{ runner.os }}-validate-go-
|
|
||||||
|
|
||||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||||
|
@ -56,30 +41,16 @@ jobs:
|
||||||
validate-generate:
|
validate-generate:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: go/src/github.com/traefik/traefik
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
go-version: ${{ env.GO_VERSION }}
|
||||||
~/go/pkg/mod
|
|
||||||
~/.cache/go-build
|
|
||||||
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: ${{ runner.os }}-validate-generate-go-
|
|
||||||
|
|
||||||
- name: go generate
|
- name: go generate
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -144,11 +144,11 @@ linters-settings:
|
||||||
gomoddirectives:
|
gomoddirectives:
|
||||||
replace-allow-list:
|
replace-allow-list:
|
||||||
- github.com/abbot/go-http-auth
|
- github.com/abbot/go-http-auth
|
||||||
- github.com/go-check/check
|
|
||||||
- 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
|
- github.com/jaguilar/vt100
|
||||||
|
- github.com/cucumber/godog
|
||||||
testifylint:
|
testifylint:
|
||||||
enable:
|
enable:
|
||||||
- bool-compare
|
- bool-compare
|
||||||
|
@ -159,7 +159,6 @@ linters-settings:
|
||||||
- expected-actual
|
- expected-actual
|
||||||
- float-compare
|
- float-compare
|
||||||
- len
|
- len
|
||||||
- suite-dont-use-pkg
|
|
||||||
- suite-extra-assert-call
|
- suite-extra-assert-call
|
||||||
- suite-thelper
|
- suite-thelper
|
||||||
|
|
||||||
|
|
|
@ -31,24 +31,6 @@ global_job_config:
|
||||||
- cache restore traefik-$(checksum go.sum)
|
- cache restore traefik-$(checksum go.sum)
|
||||||
|
|
||||||
blocks:
|
blocks:
|
||||||
- name: Test Integration
|
|
||||||
dependencies: []
|
|
||||||
run:
|
|
||||||
when: "branch =~ '.*' OR pull_request =~'.*'"
|
|
||||||
task:
|
|
||||||
jobs:
|
|
||||||
- name: Test Integration
|
|
||||||
commands:
|
|
||||||
- make pull-images
|
|
||||||
- touch webui/static/index.html # Avoid generating webui
|
|
||||||
- IN_DOCKER="" make binary
|
|
||||||
- make test-integration
|
|
||||||
- df -h
|
|
||||||
epilogue:
|
|
||||||
always:
|
|
||||||
commands:
|
|
||||||
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
|
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
dependencies: []
|
dependencies: []
|
||||||
run:
|
run:
|
||||||
|
@ -65,8 +47,6 @@ blocks:
|
||||||
value: 2.32.1
|
value: 2.32.1
|
||||||
- name: CODENAME
|
- name: CODENAME
|
||||||
value: "beaufort"
|
value: "beaufort"
|
||||||
- name: IN_DOCKER
|
|
||||||
value: ""
|
|
||||||
prologue:
|
prologue:
|
||||||
commands:
|
commands:
|
||||||
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
||||||
|
|
103
Makefile
103
Makefile
|
@ -6,34 +6,6 @@ VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||||
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
||||||
|
|
||||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
|
||||||
|
|
||||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
|
||||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik")
|
|
||||||
|
|
||||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)",-v "/var/run/docker.sock:/var/run/docker.sock")
|
|
||||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
|
||||||
|
|
||||||
# only used when running in docker
|
|
||||||
TRAEFIK_ENVS := \
|
|
||||||
-e OS_ARCH_ARG \
|
|
||||||
-e OS_PLATFORM_ARG \
|
|
||||||
-e TESTFLAGS \
|
|
||||||
-e VERBOSE \
|
|
||||||
-e VERSION \
|
|
||||||
-e CODENAME \
|
|
||||||
-e TESTDIRS \
|
|
||||||
-e CI \
|
|
||||||
-e IN_DOCKER=true # Indicator for integration tests that we are running inside a container.
|
|
||||||
|
|
||||||
TRAEFIK_MOUNT := -v "$(CURDIR)/dist:/go/src/github.com/traefik/traefik/dist"
|
|
||||||
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
|
||||||
DOCKER_NON_INTERACTIVE ?= false
|
|
||||||
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)
|
|
||||||
|
|
||||||
IN_DOCKER ?= true
|
|
||||||
|
|
||||||
.PHONY: default
|
.PHONY: default
|
||||||
default: binary
|
default: binary
|
||||||
|
@ -42,20 +14,6 @@ default: binary
|
||||||
dist:
|
dist:
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|
||||||
## Build Dev Docker image
|
|
||||||
.PHONY: build-dev-image
|
|
||||||
build-dev-image: dist
|
|
||||||
ifneq ("$(IN_DOCKER)", "")
|
|
||||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
|
||||||
endif
|
|
||||||
|
|
||||||
## Build Dev Docker image without cache
|
|
||||||
.PHONY: build-dev-image-no-cache
|
|
||||||
build-dev-image-no-cache: dist
|
|
||||||
ifneq ("$(IN_DOCKER)", "")
|
|
||||||
docker build $(DOCKER_BUILD_ARGS) --no-cache -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
|
||||||
endif
|
|
||||||
|
|
||||||
## Build WebUI Docker image
|
## Build WebUI Docker image
|
||||||
.PHONY: build-webui-image
|
.PHONY: build-webui-image
|
||||||
build-webui-image:
|
build-webui-image:
|
||||||
|
@ -79,8 +37,8 @@ generate-webui: webui/static/index.html
|
||||||
|
|
||||||
## Build the binary
|
## Build the binary
|
||||||
.PHONY: binary
|
.PHONY: binary
|
||||||
binary: generate-webui build-dev-image
|
binary: generate-webui
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
./script/make.sh generate binary
|
||||||
|
|
||||||
## Build the linux binary locally
|
## Build the linux binary locally
|
||||||
.PHONY: binary-debug
|
.PHONY: binary-debug
|
||||||
|
@ -89,35 +47,29 @@ binary-debug: generate-webui
|
||||||
|
|
||||||
## Build the binary for the standard platforms (linux, darwin, windows)
|
## Build the binary for the standard platforms (linux, darwin, windows)
|
||||||
.PHONY: crossbinary-default
|
.PHONY: crossbinary-default
|
||||||
crossbinary-default: generate-webui build-dev-image
|
crossbinary-default: generate-webui
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
./script/make.sh generate crossbinary-default
|
||||||
|
|
||||||
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
||||||
.PHONY: crossbinary-default-parallel
|
.PHONY: crossbinary-default-parallel
|
||||||
crossbinary-default-parallel:
|
crossbinary-default-parallel:
|
||||||
$(MAKE) generate-webui
|
$(MAKE) generate-webui
|
||||||
$(MAKE) build-dev-image crossbinary-default
|
$(MAKE) crossbinary-default
|
||||||
|
|
||||||
## Run the unit and integration tests
|
## Run the unit and integration tests
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: build-dev-image
|
test:
|
||||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
./script/make.sh generate test-unit binary test-integration
|
||||||
trap 'docker network rm traefik-test-network' EXIT; \
|
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit binary test-integration
|
|
||||||
|
|
||||||
## Run the unit tests
|
## Run the unit tests
|
||||||
.PHONY: test-unit
|
.PHONY: test-unit
|
||||||
test-unit: build-dev-image
|
test-unit:
|
||||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
./script/make.sh generate test-unit
|
||||||
trap 'docker network rm traefik-test-network' EXIT; \
|
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
|
|
||||||
|
|
||||||
## Run the integration tests
|
## Run the integration tests
|
||||||
.PHONY: test-integration
|
.PHONY: test-integration
|
||||||
test-integration: build-dev-image
|
test-integration:
|
||||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
./script/make.sh generate binary test-integration
|
||||||
trap 'docker network rm traefik-test-network' EXIT; \
|
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate binary test-integration
|
|
||||||
|
|
||||||
## Pull all images for integration tests
|
## Pull all images for integration tests
|
||||||
.PHONY: pull-images
|
.PHONY: pull-images
|
||||||
|
@ -128,16 +80,22 @@ pull-images:
|
||||||
| uniq \
|
| uniq \
|
||||||
| xargs -P 6 -n 1 docker pull
|
| xargs -P 6 -n 1 docker pull
|
||||||
|
|
||||||
|
EXECUTABLES = misspell shellcheck
|
||||||
|
|
||||||
## Validate code and docs
|
## Validate code and docs
|
||||||
.PHONY: validate-files
|
.PHONY: validate-files
|
||||||
validate-files: build-dev-image
|
validate-files:
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
|
$(foreach exec,$(EXECUTABLES),\
|
||||||
|
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
|
||||||
|
./script/make.sh generate validate-lint validate-misspell
|
||||||
bash $(CURDIR)/script/validate-shell-script.sh
|
bash $(CURDIR)/script/validate-shell-script.sh
|
||||||
|
|
||||||
## Validate code, docs, and vendor
|
## Validate code, docs, and vendor
|
||||||
.PHONY: validate
|
.PHONY: validate
|
||||||
validate: build-dev-image
|
validate:
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
|
$(foreach exec,$(EXECUTABLES),\
|
||||||
|
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
|
||||||
|
./script/make.sh generate validate-lint validate-misspell validate-vendor
|
||||||
bash $(CURDIR)/script/validate-shell-script.sh
|
bash $(CURDIR)/script/validate-shell-script.sh
|
||||||
|
|
||||||
## Clean up static directory and build a Docker Traefik image
|
## Clean up static directory and build a Docker Traefik image
|
||||||
|
@ -155,11 +113,6 @@ build-image-dirty: binary
|
||||||
build-image-debug: binary-debug
|
build-image-debug: binary-debug
|
||||||
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
|
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
|
||||||
|
|
||||||
## Start a shell inside the build env
|
|
||||||
.PHONY: shell
|
|
||||||
shell: build-dev-image
|
|
||||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
|
||||||
|
|
||||||
## Build documentation site
|
## Build documentation site
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
docs:
|
docs:
|
||||||
|
@ -187,23 +140,23 @@ generate-genconf:
|
||||||
|
|
||||||
## Create packages for the release
|
## Create packages for the release
|
||||||
.PHONY: release-packages
|
.PHONY: release-packages
|
||||||
release-packages: generate-webui build-dev-image
|
release-packages: generate-webui
|
||||||
rm -rf dist
|
rm -rf dist
|
||||||
@- $(foreach os, linux darwin windows freebsd openbsd, \
|
@- $(foreach os, linux darwin windows freebsd openbsd, \
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \
|
goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) go clean -cache; \
|
go clean -cache; \
|
||||||
)
|
)
|
||||||
|
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
|
cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) rm dist/**/*_checksums.txt
|
rm dist/**/*_checksums.txt
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
--exclude .idea \
|
--exclude .idea \
|
||||||
--exclude .travis \
|
--exclude .travis \
|
||||||
--exclude .semaphoreci \
|
--exclude .semaphoreci \
|
||||||
--exclude .github \
|
--exclude .github \
|
||||||
--exclude dist .
|
--exclude dist .
|
||||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) chown -R $(shell id -u):$(shell id -g) dist/
|
chown -R $(shell id -u):$(shell id -g) dist/
|
||||||
|
|
||||||
## Format the Code
|
## Format the Code
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
|
|
|
@ -13,67 +13,13 @@ Let's see how.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
You need either [Docker](https://github.com/docker/docker "Link to website of Docker") and `make` (Method 1), or [Go](https://go.dev/ "Link to website of Go") (Method 2) in order to build Traefik.
|
You need:
|
||||||
For changes to its dependencies, the `dep` dependency management tool is required.
|
- [Docker](https://github.com/docker/docker "Link to website of Docker")
|
||||||
|
- `make`
|
||||||
### Method 1: Using `Docker` and `Makefile`
|
- [Go](https://go.dev/ "Link to website of Go")
|
||||||
|
- [misspell](https://github.com/golangci/misspell)
|
||||||
Run make with the `binary` target.
|
- [shellcheck](https://github.com/koalaman/shellcheck)
|
||||||
|
- [Tailscale](https://tailscale.com/) if you are using Docker Desktop
|
||||||
```bash
|
|
||||||
make binary
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create binaries for the Linux platform in the `dist` folder.
|
|
||||||
|
|
||||||
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ make binary
|
|
||||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
|
||||||
Sending build context to Docker daemon 2.686MB
|
|
||||||
Step 1/11 : FROM node:8.15.0
|
|
||||||
---> 1f6c34f7921c
|
|
||||||
[...]
|
|
||||||
Successfully built ce4ff439c06a
|
|
||||||
Successfully tagged traefik-webui:latest
|
|
||||||
[...]
|
|
||||||
docker build -t "traefik-dev:4475--feature-documentation" -f build.Dockerfile .
|
|
||||||
Sending build context to Docker daemon 279MB
|
|
||||||
Step 1/10 : FROM golang:1.16-alpine
|
|
||||||
---> f4bfb3d22bda
|
|
||||||
[...]
|
|
||||||
Successfully built 5c3c1a911277
|
|
||||||
Successfully tagged traefik-dev:4475--feature-documentation
|
|
||||||
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
|
|
||||||
---> Making bundle: generate (in .)
|
|
||||||
removed 'autogen/genstatic/gen.go'
|
|
||||||
|
|
||||||
---> Making bundle: binary (in .)
|
|
||||||
|
|
||||||
$ ls dist/
|
|
||||||
traefik*
|
|
||||||
```
|
|
||||||
|
|
||||||
The following targets can be executed outside Docker by setting the variable `IN_DOCKER` to an empty string (although be aware that some of the tests might fail in that context):
|
|
||||||
|
|
||||||
- `test-unit`
|
|
||||||
- `test-integration`
|
|
||||||
- `validate`
|
|
||||||
- `binary` (the webUI is still generated by using Docker)
|
|
||||||
|
|
||||||
ex:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
IN_DOCKER= make test-unit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Method 2: Using `go`
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- `go` v1.16+
|
|
||||||
- environment variable `GO111MODULE=on`
|
|
||||||
|
|
||||||
!!! tip "Source Directory"
|
!!! tip "Source Directory"
|
||||||
|
|
||||||
|
@ -106,41 +52,33 @@ Requirements:
|
||||||
## ... and the list goes on
|
## ... and the list goes on
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Build Traefik
|
### Build Traefik
|
||||||
|
|
||||||
Once you've set up your go environment and cloned the source repository, you can build Traefik.
|
Once you've set up your go environment and cloned the source repository, you can build Traefik.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate UI static files
|
$ make binary
|
||||||
make clean-webui generate-webui
|
./script/make.sh generate binary
|
||||||
|
---> Making bundle: generate (in .)
|
||||||
|
|
||||||
# required to merge non-code components into the final binary,
|
---> Making bundle: binary (in .)
|
||||||
# such as the web dashboard/UI
|
|
||||||
go generate
|
$ ls dist/
|
||||||
|
traefik*
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
You will find the Traefik executable (`traefik`) in the `./dist` directory.
|
||||||
# Standard go build
|
|
||||||
go build ./cmd/traefik
|
|
||||||
```
|
|
||||||
|
|
||||||
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
### Method 1: `Docker` and `make`
|
|
||||||
|
|
||||||
Run unit tests using the `test-unit` target.
|
Run unit tests using the `test-unit` target.
|
||||||
Run integration tests using the `test-integration` target.
|
Run integration tests using the `test-integration` target.
|
||||||
Run all tests (unit and integration) using the `test` target.
|
Run all tests (unit and integration) using the `test` target.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ make test-unit
|
$ make test-unit
|
||||||
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
./script/make.sh generate test-unit
|
||||||
# […]
|
|
||||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
|
||||||
---> Making bundle: generate (in .)
|
---> Making bundle: generate (in .)
|
||||||
removed 'gen.go'
|
|
||||||
|
|
||||||
---> Making bundle: test-unit (in .)
|
---> Making bundle: test-unit (in .)
|
||||||
+ go test -cover -coverprofile=cover.out .
|
+ go test -cover -coverprofile=cover.out .
|
||||||
|
@ -151,28 +89,30 @@ Test success
|
||||||
|
|
||||||
For development purposes, you can specify which tests to run by using (only works the `test-integration` target):
|
For development purposes, you can specify which tests to run by using (only works the `test-integration` target):
|
||||||
|
|
||||||
|
??? note "Configuring Tailscale for Docker Desktop user"
|
||||||
|
|
||||||
|
Create `tailscale.secret` file in `integration` directory.
|
||||||
|
|
||||||
|
This file need to contains a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
|
||||||
|
(an ephemeral, but reusable, one is recommended).
|
||||||
|
|
||||||
|
Add this section to your tailscale ACLs to auto-approve the routes for the
|
||||||
|
containers in the docker subnet:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"autoApprovers": {
|
||||||
|
// Allow myself to automatically
|
||||||
|
// advertize routes for docker networks
|
||||||
|
"routes": {
|
||||||
|
"172.31.42.0/24": ["your_tailscale_identity"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run every tests in the MyTest suite
|
# Run every tests in the MyTest suite
|
||||||
TESTFLAGS="-check.f MyTestSuite" make test-integration
|
TESTFLAGS="-test.run TestAccessLogSuite" make test-integration
|
||||||
|
|
||||||
# Run the test "MyTest" in the MyTest suite
|
# Run the test "MyTest" in the MyTest suite
|
||||||
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
|
TESTFLAGS="-test.run TestAccessLogSuite -testify.m ^TestAccessLog$" make test-integration
|
||||||
|
|
||||||
# Run every tests starting with "My", in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
|
||||||
|
|
||||||
# Run every tests ending with "Test", in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Check [gocheck](https://labix.org/gocheck "Link to website of gocheck") for more information.
|
|
||||||
|
|
||||||
### Method 2: `go`
|
|
||||||
|
|
||||||
Unit tests can be run from the cloned directory using `$ go test ./...` which should return `ok`, similar to:
|
|
||||||
|
|
||||||
```test
|
|
||||||
ok _/home/user/go/src/github/traefik/traefik 0.004s
|
|
||||||
```
|
|
||||||
|
|
||||||
Integration tests must be run from the `integration/` directory and require the `-integration` switch: `$ cd integration && go test -integration ./...`.
|
|
||||||
|
|
|
@ -8,21 +8,7 @@ description: "Learn how to use IPAllowList in HTTP middleware for limiting clien
|
||||||
Limiting Clients to Specific IPs
|
Limiting Clients to Specific IPs
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
<<<<<<<< HEAD:docs/content/middlewares/http/ipallowlist.md
|
|
||||||
IPAllowList accepts / refuses requests based on the client IP.
|
IPAllowList accepts / refuses requests based on the client IP.
|
||||||
|||||||| dae0491b6:docs/content/middlewares/http/ipwhitelist.md
|
|
||||||
![IpWhiteList](../../assets/img/middleware/ipwhitelist.png)
|
|
||||||
|
|
||||||
IPWhitelist accepts / refuses requests based on the client IP.
|
|
||||||
========
|
|
||||||
![IPWhiteList](../../assets/img/middleware/ipwhitelist.png)
|
|
||||||
|
|
||||||
IPWhiteList accepts / refuses requests based on the client IP.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
|
|
||||||
This middleware is deprecated, please use the [IPAllowList](./ipallowlist.md) middleware instead.
|
|
||||||
>>>>>>>> upstream/v2.11:docs/content/middlewares/http/ipwhitelist.md
|
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
|
@ -207,45 +193,3 @@ http:
|
||||||
[http.middlewares.test-ipallowlist.ipAllowList.ipStrategy]
|
[http.middlewares.test-ipallowlist.ipAllowList.ipStrategy]
|
||||||
excludedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
excludedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `rejectStatusCode`
|
|
||||||
|
|
||||||
The `rejectStatusCode` option sets HTTP status code for refused requests. If not set, the default is 403 (Forbidden).
|
|
||||||
|
|
||||||
```yaml tab="Docker & Swarm"
|
|
||||||
# Reject requests with a 404 rather than a 403
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.rejectstatuscode=404"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
|
||||||
# Reject requests with a 404 rather than a 403
|
|
||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: test-ipallowlist
|
|
||||||
spec:
|
|
||||||
ipAllowList:
|
|
||||||
rejectStatusCode: 404
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
|
||||||
# Reject requests with a 404 rather than a 403
|
|
||||||
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.rejectstatuscode=404"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
# Reject requests with a 404 rather than a 403
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
test-ipallowlist:
|
|
||||||
ipAllowList:
|
|
||||||
rejectStatusCode: 404
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
# Reject requests with a 404 rather than a 403
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.test-ipallowlist.ipAllowList]
|
|
||||||
rejectStatusCode = 404
|
|
||||||
```
|
|
||||||
|
|
102
go.mod
102
go.mod
|
@ -9,18 +9,15 @@ require (
|
||||||
github.com/andybalholm/brotli v1.0.6
|
github.com/andybalholm/brotli v1.0.6
|
||||||
github.com/aws/aws-sdk-go v1.44.327
|
github.com/aws/aws-sdk-go v1.44.327
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1
|
github.com/cenkalti/backoff/v4 v4.2.1
|
||||||
github.com/compose-spec/compose-go v1.0.3
|
|
||||||
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.2-0.20180830191138-d8f796af33cc
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||||
github.com/docker/cli v20.10.11+incompatible
|
github.com/docker/cli v24.0.7+incompatible
|
||||||
github.com/docker/compose/v2 v2.0.1
|
github.com/docker/docker v24.0.7+incompatible
|
||||||
github.com/docker/docker v20.10.21+incompatible
|
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/fatih/structs v1.1.0
|
github.com/fatih/structs v1.1.0
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-acme/lego/v4 v4.14.0
|
github.com/go-acme/lego/v4 v4.14.0
|
||||||
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.3
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/google/go-github/v28 v28.1.1
|
github.com/google/go-github/v28 v28.1.1
|
||||||
|
@ -35,7 +32,7 @@ require (
|
||||||
github.com/http-wasm/http-wasm-host-go v0.5.2
|
github.com/http-wasm/http-wasm-host-go v0.5.2
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
||||||
github.com/klauspost/compress v1.17.1
|
github.com/klauspost/compress v1.17.2
|
||||||
github.com/kvtools/consul v1.0.2
|
github.com/kvtools/consul v1.0.2
|
||||||
github.com/kvtools/etcdv3 v1.0.2
|
github.com/kvtools/etcdv3 v1.0.2
|
||||||
github.com/kvtools/redis v1.1.0
|
github.com/kvtools/redis v1.1.0
|
||||||
|
@ -59,6 +56,7 @@ require (
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
|
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
|
||||||
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2
|
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2
|
||||||
|
github.com/testcontainers/testcontainers-go v0.27.0
|
||||||
github.com/tetratelabs/wazero v1.5.0
|
github.com/tetratelabs/wazero v1.5.0
|
||||||
github.com/tidwall/gjson v1.17.0
|
github.com/tidwall/gjson v1.17.0
|
||||||
github.com/traefik/grpc-web v0.16.0
|
github.com/traefik/grpc-web v0.16.0
|
||||||
|
@ -66,7 +64,6 @@ require (
|
||||||
github.com/traefik/yaegi v0.15.1
|
github.com/traefik/yaegi v0.15.1
|
||||||
github.com/unrolled/render v1.0.2
|
github.com/unrolled/render v1.0.2
|
||||||
github.com/unrolled/secure v1.0.9
|
github.com/unrolled/secure v1.0.9
|
||||||
github.com/vdemeester/shakers v0.1.0
|
|
||||||
github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c
|
github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c
|
||||||
github.com/vulcand/predicate v1.2.0
|
github.com/vulcand/predicate v1.2.0
|
||||||
go.opentelemetry.io/collector/pdata v0.66.0
|
go.opentelemetry.io/collector/pdata v0.66.0
|
||||||
|
@ -100,8 +97,8 @@ require (
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.23.0 // indirect
|
cloud.google.com/go/compute v1.23.0 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.3 // indirect
|
|
||||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
|
||||||
|
@ -121,12 +118,11 @@ require (
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.8.25 // indirect
|
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||||
|
@ -147,72 +143,59 @@ require (
|
||||||
github.com/aws/smithy-go v1.14.2 // indirect
|
github.com/aws/smithy-go v1.14.2 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/buger/goterm v1.0.0 // indirect
|
github.com/bytedance/sonic v1.10.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/civo/civogo v0.3.11 // indirect
|
github.com/civo/civogo v0.3.11 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
|
||||||
github.com/compose-spec/godotenv v1.0.0 // indirect
|
github.com/containerd/containerd v1.7.11 // indirect
|
||||||
github.com/containerd/cgroups v1.0.3 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
|
||||||
github.com/containerd/containerd v1.5.17 // indirect
|
|
||||||
github.com/containerd/continuity v0.3.0 // indirect
|
|
||||||
github.com/containerd/typeurl v1.0.2 // indirect
|
|
||||||
github.com/coreos/go-semver v0.3.1 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e // indirect
|
|
||||||
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
|
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
|
||||||
github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc // indirect
|
|
||||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
|
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
|
||||||
github.com/exoscale/egoscale v0.100.1 // indirect
|
github.com/exoscale/egoscale v0.100.1 // indirect
|
||||||
github.com/fatih/color v1.15.0 // indirect
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
|
||||||
github.com/ghodss/yaml v1.0.0 // indirect
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.7.7 // indirect
|
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||||
github.com/go-errors/errors v1.0.1 // indirect
|
github.com/go-errors/errors v1.0.1 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||||
github.com/go-logr/logr v1.3.0 // indirect
|
github.com/go-logr/logr v1.3.0 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||||
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||||
github.com/gofrs/flock v0.8.0 // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gogo/googleapis v1.4.0 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
|
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
|
||||||
github.com/google/s2a-go v0.1.5 // indirect
|
github.com/google/s2a-go v0.1.5 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/uuid v1.4.0 // indirect
|
||||||
github.com/google/uuid v1.3.1 // indirect
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
|
||||||
github.com/hashicorp/cronexpr v1.1.1 // indirect
|
github.com/hashicorp/cronexpr v1.1.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
@ -223,16 +206,14 @@ require (
|
||||||
github.com/huandu/xstrings v1.4.0 // indirect
|
github.com/huandu/xstrings v1.4.0 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect
|
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
|
@ -241,26 +222,22 @@ require (
|
||||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||||
github.com/magiconair/properties v1.8.6 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
|
github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
|
||||||
github.com/mailgun/multibuf v0.1.2 // indirect
|
github.com/mailgun/multibuf v0.1.2 // indirect
|
||||||
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect
|
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
|
||||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
|
||||||
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf // indirect
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
github.com/moby/locker v1.0.1 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/sys/mount v0.2.0 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/moby/sys/mountinfo v0.5.0 // indirect
|
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
@ -279,13 +256,14 @@ require (
|
||||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||||
github.com/onsi/gomega v1.27.10 // indirect
|
github.com/onsi/gomega v1.27.10 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
|
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
||||||
github.com/opencontainers/runc v1.1.5 // indirect
|
github.com/opencontainers/runc v1.1.5 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||||
github.com/ovh/go-ovh v1.4.1 // indirect
|
github.com/ovh/go-ovh v1.4.1 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/pquerna/otp v1.4.0 // indirect
|
github.com/pquerna/otp v1.4.0 // indirect
|
||||||
github.com/prometheus/common v0.45.0 // indirect
|
github.com/prometheus/common v0.45.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
|
@ -297,33 +275,30 @@ require (
|
||||||
github.com/sacloud/go-http v0.1.6 // indirect
|
github.com/sacloud/go-http v0.1.6 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.11.1 // indirect
|
github.com/sacloud/iaas-api-go v1.11.1 // indirect
|
||||||
github.com/sacloud/packages-go v0.0.9 // indirect
|
github.com/sacloud/packages-go v0.0.9 // indirect
|
||||||
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b // indirect
|
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.11 // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
|
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||||
github.com/softlayer/softlayer-go v1.1.2 // indirect
|
github.com/softlayer/softlayer-go v1.1.2 // indirect
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/cobra v1.7.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.1 // indirect
|
github.com/stretchr/objx v0.5.1 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
||||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
github.com/transip/gotransip/v6 v6.20.0 // indirect
|
||||||
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
github.com/zeebo/errs v1.2.2 // indirect
|
github.com/zeebo/errs v1.2.2 // indirect
|
||||||
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
|
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
|
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
|
||||||
|
@ -335,10 +310,10 @@ require (
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/ratelimit v0.2.0 // indirect
|
go.uber.org/ratelimit v0.2.0 // indirect
|
||||||
go.uber.org/zap v1.26.0 // indirect
|
go.uber.org/zap v1.26.0 // indirect
|
||||||
|
golang.org/x/arch v0.4.0 // indirect
|
||||||
golang.org/x/crypto v0.14.0 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
golang.org/x/oauth2 v0.13.0 // indirect
|
golang.org/x/oauth2 v0.13.0 // indirect
|
||||||
golang.org/x/sync v0.4.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
|
||||||
golang.org/x/term v0.13.0 // indirect
|
golang.org/x/term v0.13.0 // indirect
|
||||||
google.golang.org/api v0.128.0 // indirect
|
google.golang.org/api v0.128.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
|
@ -363,7 +338,7 @@ require (
|
||||||
// Containous forks
|
// Containous forks
|
||||||
replace (
|
replace (
|
||||||
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e
|
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e
|
||||||
github.com/go-check/check => github.com/containous/check v0.0.0-20170915194414-ca0bf163426a
|
github.com/gorilla/mux => github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -373,3 +348,6 @@ replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402
|
||||||
// ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules
|
// ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules
|
||||||
// tencentcloud uses monorepo with multimodule but the go.mod files are incomplete.
|
// tencentcloud uses monorepo with multimodule but the go.mod files are incomplete.
|
||||||
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
||||||
|
|
||||||
|
// https://github.com/docker/compose/blob/v2.19.0/go.mod#L12
|
||||||
|
replace github.com/cucumber/godog => github.com/cucumber/godog v0.13.0
|
||||||
|
|
|
@ -11,13 +11,15 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -28,6 +30,10 @@ const (
|
||||||
// AccessLogSuite tests suite.
|
// AccessLogSuite tests suite.
|
||||||
type AccessLogSuite struct{ BaseSuite }
|
type AccessLogSuite struct{ BaseSuite }
|
||||||
|
|
||||||
|
func TestAccessLogSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(AccessLogSuite))
|
||||||
|
}
|
||||||
|
|
||||||
type accessLogValue struct {
|
type accessLogValue struct {
|
||||||
formatOnly bool
|
formatOnly bool
|
||||||
code string
|
code string
|
||||||
|
@ -36,67 +42,67 @@ type accessLogValue struct {
|
||||||
serviceURL string
|
serviceURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) SetUpSuite(c *check.C) {
|
func (s *AccessLogSuite) SetupSuite() {
|
||||||
s.createComposeProject(c, "access_log")
|
s.BaseSuite.SetupSuite()
|
||||||
s.composeUp(c)
|
s.createComposeProject("access_log")
|
||||||
|
s.composeUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TearDownTest(c *check.C) {
|
func (s *AccessLogSuite) TearDownSuite() {
|
||||||
displayTraefikLogFile(c, traefikTestLogFile)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccessLogSuite) TearDownTest() {
|
||||||
|
s.displayTraefikLogFile(traefikTestLogFile)
|
||||||
_ = os.Remove(traefikTestAccessLogFile)
|
_ = os.Remove(traefikTestAccessLogFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLog(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLog() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
log.Info().Msg(string(traefikLog))
|
log.Info().Msg(string(traefikLog))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := cmd.Start()
|
s.waitForTraefik("server1")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
waitForTraefik(c, "server1")
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Make some requests
|
// Make some requests
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend1.docker.local"
|
req.Host = "frontend1.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend2.docker.local"
|
req.Host = "frontend2.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogOutput(c)
|
count := s.checkAccessLogOutput()
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, 3)
|
assert.Equal(s.T(), 3, count)
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogAuthFrontend() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -124,48 +130,43 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("authFrontend")
|
||||||
|
|
||||||
waitForTraefik(c, "authFrontend")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test auth entrypoint
|
// Test auth entrypoint
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend.auth.docker.local"
|
req.Host = "frontend.auth.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req.SetBasicAuth("test", "")
|
req.SetBasicAuth("test", "")
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req.SetBasicAuth("test", "test")
|
req.SetBasicAuth("test", "test")
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -193,27 +194,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("digestAuthMiddleware")
|
||||||
|
|
||||||
waitForTraefik(c, "digestAuthMiddleware")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test auth entrypoint
|
// Test auth entrypoint
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8008/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8008/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "entrypoint.digest.auth.docker.local"
|
req.Host = "entrypoint.digest.auth.docker.local"
|
||||||
|
|
||||||
resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized)
|
resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
digest := digestParts(resp)
|
digest := digestParts(resp)
|
||||||
digest["uri"] = "/"
|
digest["uri"] = "/"
|
||||||
|
@ -225,22 +221,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
digest["password"] = "test"
|
digest["password"] = "test"
|
||||||
|
|
||||||
req.Header.Set("Authorization", getDigestAuthorization(digest))
|
req.Header.Set("Authorization", getDigestAuthorization(digest))
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thanks to mvndaai for digest authentication
|
// Thanks to mvndaai for digest authentication
|
||||||
|
@ -291,7 +287,7 @@ func getDigestAuthorization(digestParts map[string]string) string {
|
||||||
return authorization
|
return authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogFrontendRedirect() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -308,38 +304,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("frontendRedirect")
|
||||||
|
|
||||||
waitForTraefik(c, "frontendRedirect")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test frontend redirect
|
// Test frontend redirect
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = ""
|
req.Host = ""
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
type logLine struct {
|
type logLine struct {
|
||||||
|
@ -365,30 +356,25 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("frontendRedirect")
|
||||||
|
|
||||||
waitForTraefik(c, "frontendRedirect")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test frontend redirect
|
// Test frontend redirect
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = ""
|
req.Host = ""
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
lines := extractLines(c)
|
lines := s.extractLines()
|
||||||
c.Assert(len(lines), checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), len(lines), len(expected))
|
||||||
|
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
|
@ -396,15 +382,15 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
|
||||||
}
|
}
|
||||||
var logline logLine
|
var logline logLine
|
||||||
err := json.Unmarshal([]byte(line), &logline)
|
err := json.Unmarshal([]byte(line), &logline)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(logline.DownstreamStatus, checker.Equals, expected[i].DownstreamStatus)
|
assert.Equal(s.T(), expected[i].DownstreamStatus, logline.DownstreamStatus)
|
||||||
c.Assert(logline.OriginStatus, checker.Equals, expected[i].OriginStatus)
|
assert.Equal(s.T(), expected[i].OriginStatus, logline.OriginStatus)
|
||||||
c.Assert(logline.RouterName, checker.Equals, expected[i].RouterName)
|
assert.Equal(s.T(), expected[i].RouterName, logline.RouterName)
|
||||||
c.Assert(logline.ServiceName, checker.Equals, expected[i].ServiceName)
|
assert.Equal(s.T(), expected[i].ServiceName, logline.ServiceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogRateLimit() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -424,42 +410,37 @@ func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("rateLimit")
|
||||||
|
|
||||||
waitForTraefik(c, "rateLimit")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test rate limit
|
// Test rate limit
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8007/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8007/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "ratelimit.docker.local"
|
req.Host = "ratelimit.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogBackendNotFound() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -473,38 +454,33 @@ func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.waitForTraefik("server1")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
waitForTraefik(c, "server1")
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test rate limit
|
// Test rate limit
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "backendnotfound.docker.local"
|
req.Host = "backendnotfound.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogFrontendAllowlist(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogFrontendAllowlist() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -518,38 +494,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendAllowlist(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("frontendAllowlist")
|
||||||
|
|
||||||
waitForTraefik(c, "frontendAllowlist")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test rate limit
|
// Test rate limit
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend.allowlist.docker.local"
|
req.Host = "frontend.allowlist.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -563,39 +534,34 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("authFrontend")
|
||||||
|
|
||||||
waitForTraefik(c, "authFrontend")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test auth entrypoint
|
// Test auth entrypoint
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend.auth.docker.local"
|
req.Host = "frontend.auth.docker.local"
|
||||||
req.SetBasicAuth("test", "test")
|
req.SetBasicAuth("test", "test")
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) {
|
func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
expected := []accessLogValue{
|
expected := []accessLogValue{
|
||||||
|
@ -609,79 +575,74 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.checkStatsForLogFile()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
checkStatsForLogFile(c)
|
s.waitForTraefik("preflightCORS")
|
||||||
|
|
||||||
waitForTraefik(c, "preflightCORS")
|
|
||||||
|
|
||||||
// Verify Traefik started OK
|
// Verify Traefik started OK
|
||||||
checkTraefikStarted(c)
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
// Test preflight response
|
// Test preflight response
|
||||||
req, err := http.NewRequest(http.MethodOptions, "http://127.0.0.1:8009/", nil)
|
req, err := http.NewRequest(http.MethodOptions, "http://127.0.0.1:8009/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "preflight.docker.local"
|
req.Host = "preflight.docker.local"
|
||||||
req.Header.Set("Origin", "whatever")
|
req.Header.Set("Origin", "whatever")
|
||||||
req.Header.Set("Access-Control-Request-Method", "GET")
|
req.Header.Set("Access-Control-Request-Method", "GET")
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
count := checkAccessLogExactValuesOutput(c, expected)
|
count := s.checkAccessLogExactValuesOutput(expected)
|
||||||
|
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
|
assert.GreaterOrEqual(s.T(), count, len(expected))
|
||||||
|
|
||||||
// Verify no other Traefik problems
|
// Verify no other Traefik problems
|
||||||
checkNoOtherTraefikProblems(c)
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNoOtherTraefikProblems(c *check.C) {
|
func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
if len(traefikLog) > 0 {
|
if len(traefikLog) > 0 {
|
||||||
fmt.Printf("%s\n", string(traefikLog))
|
fmt.Printf("%s\n", string(traefikLog))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAccessLogOutput(c *check.C) int {
|
func (s *AccessLogSuite) checkAccessLogOutput() int {
|
||||||
lines := extractLines(c)
|
lines := s.extractLines()
|
||||||
count := 0
|
count := 0
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
count++
|
count++
|
||||||
CheckAccessLogFormat(c, line, i)
|
s.CheckAccessLogFormat(line, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAccessLogExactValuesOutput(c *check.C, values []accessLogValue) int {
|
func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
|
||||||
lines := extractLines(c)
|
lines := s.extractLines()
|
||||||
count := 0
|
count := 0
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
fmt.Println(line)
|
fmt.Println(line)
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
count++
|
count++
|
||||||
if values[i].formatOnly {
|
if values[i].formatOnly {
|
||||||
CheckAccessLogFormat(c, line, i)
|
s.CheckAccessLogFormat(line, i)
|
||||||
} else {
|
} else {
|
||||||
checkAccessLogExactValues(c, line, i, values[i])
|
s.checkAccessLogExactValues(line, i, values[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractLines(c *check.C) []string {
|
func (s *AccessLogSuite) extractLines() []string {
|
||||||
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
|
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
lines := strings.Split(string(accessLog), "\n")
|
lines := strings.Split(string(accessLog), "\n")
|
||||||
|
|
||||||
|
@ -694,14 +655,14 @@ func extractLines(c *check.C) []string {
|
||||||
return clean
|
return clean
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStatsForLogFile(c *check.C) {
|
func (s *AccessLogSuite) checkStatsForLogFile() {
|
||||||
err := try.Do(1*time.Second, func() error {
|
err := try.Do(1*time.Second, func() error {
|
||||||
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
|
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
|
||||||
return fmt.Errorf("could not get stats for log file: %w", errStat)
|
return fmt.Errorf("could not get stats for log file: %w", errStat)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureWorkingDirectoryIsClean() {
|
func ensureWorkingDirectoryIsClean() {
|
||||||
|
@ -709,69 +670,38 @@ func ensureWorkingDirectoryIsClean() {
|
||||||
os.Remove(traefikTestLogFile)
|
os.Remove(traefikTestLogFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkTraefikStarted(c *check.C) []byte {
|
func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
if len(traefikLog) > 0 {
|
if len(traefikLog) > 0 {
|
||||||
fmt.Printf("%s\n", string(traefikLog))
|
fmt.Printf("%s\n", string(traefikLog))
|
||||||
}
|
}
|
||||||
return traefikLog
|
return traefikLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckAccessLogFormat(c *check.C, line string, i int) {
|
func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
|
||||||
results, err := accesslog.ParseAccessLog(line)
|
results, err := accesslog.ParseAccessLog(line)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(results, checker.HasLen, 14)
|
assert.Len(s.T(), results, 14)
|
||||||
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`)
|
assert.Regexp(s.T(), `^(-|\d{3})$`, results[accesslog.OriginStatus])
|
||||||
count, _ := strconv.Atoi(results[accesslog.RequestCount])
|
count, _ := strconv.Atoi(results[accesslog.RequestCount])
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, i+1)
|
assert.GreaterOrEqual(s.T(), count, i+1)
|
||||||
c.Assert(results[accesslog.RouterName], checker.Matches, `"(rt-.+@docker|api@internal)"`)
|
assert.Regexp(s.T(), `"(rt-.+@docker|api@internal)"`, results[accesslog.RouterName])
|
||||||
c.Assert(results[accesslog.ServiceURL], checker.HasPrefix, `"http://`)
|
assert.True(s.T(), strings.HasPrefix(results[accesslog.ServiceURL], `"http://`))
|
||||||
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
|
assert.Regexp(s.T(), `^\d+ms$`, results[accesslog.Duration])
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue) {
|
func (s *AccessLogSuite) checkAccessLogExactValues(line string, i int, v accessLogValue) {
|
||||||
results, err := accesslog.ParseAccessLog(line)
|
results, err := accesslog.ParseAccessLog(line)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(results, checker.HasLen, 14)
|
assert.Len(s.T(), results, 14)
|
||||||
if len(v.user) > 0 {
|
if len(v.user) > 0 {
|
||||||
c.Assert(results[accesslog.ClientUsername], checker.Equals, v.user)
|
assert.Equal(s.T(), v.user, results[accesslog.ClientUsername])
|
||||||
}
|
}
|
||||||
c.Assert(results[accesslog.OriginStatus], checker.Equals, v.code)
|
assert.Equal(s.T(), v.code, results[accesslog.OriginStatus])
|
||||||
count, _ := strconv.Atoi(results[accesslog.RequestCount])
|
count, _ := strconv.Atoi(results[accesslog.RequestCount])
|
||||||
c.Assert(count, checker.GreaterOrEqualThan, i+1)
|
assert.GreaterOrEqual(s.T(), count, i+1)
|
||||||
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?`+v.routerName+`.*(@docker)?$`)
|
assert.Regexp(s.T(), `^"?`+v.routerName+`.*(@docker)?$`, results[accesslog.RouterName])
|
||||||
c.Assert(results[accesslog.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`)
|
assert.Regexp(s.T(), `^"?`+v.serviceURL+`.*$`, results[accesslog.ServiceURL])
|
||||||
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
|
assert.Regexp(s.T(), `^\d+ms$`, results[accesslog.Duration])
|
||||||
}
|
|
||||||
|
|
||||||
func waitForTraefik(c *check.C, containerName string) {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
// Wait for Traefik to turn ready.
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func displayTraefikLogFile(c *check.C, path string) {
|
|
||||||
if c.Failed() {
|
|
||||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
||||||
content, errRead := os.ReadFile(path)
|
|
||||||
fmt.Printf("%s: Traefik logs: \n", c.TestName())
|
|
||||||
if errRead == nil {
|
|
||||||
fmt.Println(content)
|
|
||||||
} else {
|
|
||||||
fmt.Println(errRead)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s: No Traefik logs.\n", c.TestName())
|
|
||||||
}
|
|
||||||
errRemove := os.Remove(path)
|
|
||||||
if errRemove != nil {
|
|
||||||
fmt.Println(errRemove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,19 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v3/pkg/provider/acme"
|
"github.com/traefik/traefik/v3/pkg/provider/acme"
|
||||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||||
"github.com/traefik/traefik/v3/pkg/types"
|
"github.com/traefik/traefik/v3/pkg/types"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACME test suites.
|
// ACME test suites.
|
||||||
|
@ -27,6 +30,10 @@ type AcmeSuite struct {
|
||||||
fakeDNSServer *dns.Server
|
fakeDNSServer *dns.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcmeSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(AcmeSuite))
|
||||||
|
}
|
||||||
|
|
||||||
type subCases struct {
|
type subCases struct {
|
||||||
host string
|
host string
|
||||||
expectedCommonName string
|
expectedCommonName string
|
||||||
|
@ -87,17 +94,18 @@ func setupPebbleRootCA() (*http.Transport, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
func (s *AcmeSuite) SetupSuite() {
|
||||||
s.createComposeProject(c, "pebble")
|
s.BaseSuite.SetupSuite()
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.fakeDNSServer = startFakeDNSServer(s.getContainerIP(c, "traefik"))
|
s.createComposeProject("pebble")
|
||||||
s.pebbleIP = s.getComposeServiceIP(c, "pebble")
|
s.composeUp()
|
||||||
|
|
||||||
|
// Retrieving the Docker host ip.
|
||||||
|
s.fakeDNSServer = startFakeDNSServer(s.hostIP)
|
||||||
|
s.pebbleIP = s.getComposeServiceIP("pebble")
|
||||||
|
|
||||||
pebbleTransport, err := setupPebbleRootCA()
|
pebbleTransport, err := setupPebbleRootCA()
|
||||||
if err != nil {
|
require.NoError(s.T(), err)
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for pebble
|
// wait for pebble
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil)
|
||||||
|
@ -113,21 +121,24 @@ func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
||||||
}
|
}
|
||||||
return try.StatusCodeIs(http.StatusOK)(resp)
|
return try.StatusCodeIs(http.StatusOK)(resp)
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TearDownSuite(c *check.C) {
|
func (s *AcmeSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
|
||||||
if s.fakeDNSServer != nil {
|
if s.fakeDNSServer != nil {
|
||||||
err := s.fakeDNSServer.Shutdown()
|
err := s.fakeDNSServer.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log(err)
|
log.Info().Msg(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.composeDown(c)
|
s.composeDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01Domains(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01Domains() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -147,10 +158,10 @@ func (s *AcmeSuite) TestHTTP01Domains(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01StoreDomains(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01StoreDomains() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_store_domains.toml",
|
traefikConfFilePath: "fixtures/acme/acme_store_domains.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -170,10 +181,10 @@ func (s *AcmeSuite) TestHTTP01StoreDomains(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01DomainsInSAN(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01DomainsInSAN() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -194,10 +205,10 @@ func (s *AcmeSuite) TestHTTP01DomainsInSAN(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01OnHostRule() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -214,10 +225,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestMultipleResolver(c *check.C) {
|
func (s *AcmeSuite) TestMultipleResolver() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml",
|
traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml",
|
||||||
subCases: []subCases{
|
subCases: []subCases{
|
||||||
|
@ -245,10 +256,10 @@ func (s *AcmeSuite) TestMultipleResolver(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -266,10 +277,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -287,10 +298,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -307,10 +318,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard(c
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check.C) {
|
func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -327,10 +338,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP(c *check.C) {
|
func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_tcp.toml",
|
traefikConfFilePath: "fixtures/acme/acme_tcp.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -347,10 +358,10 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
|
func (s *AcmeSuite) TestTLSALPN01OnHostRule() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -367,10 +378,10 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestTLSALPN01Domains(c *check.C) {
|
func (s *AcmeSuite) TestTLSALPN01Domains() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -390,10 +401,10 @@ func (s *AcmeSuite) TestTLSALPN01Domains(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AcmeSuite) TestTLSALPN01DomainsInSAN(c *check.C) {
|
func (s *AcmeSuite) TestTLSALPN01DomainsInSAN() {
|
||||||
testCase := acmeTestCase{
|
testCase := acmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
|
||||||
subCases: []subCases{{
|
subCases: []subCases{{
|
||||||
|
@ -414,12 +425,12 @@ func (s *AcmeSuite) TestTLSALPN01DomainsInSAN(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(testCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test Let's encrypt down.
|
// Test Let's encrypt down.
|
||||||
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
func (s *AcmeSuite) TestNoValidLetsEncryptServer() {
|
||||||
file := s.adaptFile(c, "fixtures/acme/acme_base.toml", templateModel{
|
file := s.adaptFile("fixtures/acme/acme_base.toml", templateModel{
|
||||||
Acme: map[string]static.CertificateResolver{
|
Acme: map[string]static.CertificateResolver{
|
||||||
"default": {ACME: &acme.Configuration{
|
"default": {ACME: &acme.Configuration{
|
||||||
CAServer: "http://wrongurl:4001/directory",
|
CAServer: "http://wrongurl:4001/directory",
|
||||||
|
@ -427,21 +438,16 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Expected traefik works
|
// Expected traefik works
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doing an HTTPS request and test the response certificate.
|
// Doing an HTTPS request and test the response certificate.
|
||||||
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
func (s *AcmeSuite) retrieveAcmeCertificate(testCase acmeTestCase) {
|
||||||
if len(testCase.template.PortHTTP) == 0 {
|
if len(testCase.template.PortHTTP) == 0 {
|
||||||
testCase.template.PortHTTP = ":5002"
|
testCase.template.PortHTTP = ":5002"
|
||||||
}
|
}
|
||||||
|
@ -456,14 +462,10 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.template)
|
file := s.adaptFile(testCase.traefikConfFilePath, testCase.template)
|
||||||
defer os.Remove(file)
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
// A real file is needed to have the right mode on acme.json file
|
// A real file is needed to have the right mode on acme.json file
|
||||||
defer os.Remove("/tmp/acme.json")
|
defer os.Remove("/tmp/acme.json")
|
||||||
|
|
||||||
|
@ -477,11 +479,11 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for traefik (generating acme account take some seconds)
|
// wait for traefik (generating acme account take some seconds)
|
||||||
err = try.Do(60*time.Second, func() error {
|
err := try.Do(60*time.Second, func() error {
|
||||||
_, errGet := client.Get("https://127.0.0.1:5001")
|
_, errGet := client.Get("https://127.0.0.1:5001")
|
||||||
return errGet
|
return errGet
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
for _, sub := range testCase.subCases {
|
for _, sub := range testCase.subCases {
|
||||||
client = &http.Client{
|
client = &http.Client{
|
||||||
|
@ -503,7 +505,7 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
|
||||||
// Retry to send a Request which uses the LE generated certificate
|
// Retry to send a Request which uses the LE generated certificate
|
||||||
err = try.Do(60*time.Second, func() error {
|
err := try.Do(60*time.Second, func() error {
|
||||||
resp, err = client.Do(req)
|
resp, err = client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -517,10 +519,10 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||||
// Check Domain into response certificate
|
// Check Domain into response certificate
|
||||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, sub.expectedCommonName)
|
assert.Equal(s.T(), sub.expectedCommonName, resp.TLS.PeerCertificates[0].Subject.CommonName)
|
||||||
c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, sub.expectedAlgorithm)
|
assert.Equal(s.T(), sub.expectedAlgorithm, resp.TLS.PeerCertificates[0].PublicKeyAlgorithm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,36 +8,38 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ThrottlingSuite struct{ BaseSuite }
|
type ThrottlingSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *ThrottlingSuite) SetUpSuite(c *check.C) {
|
func TestThrottlingSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "rest")
|
suite.Run(t, new(ThrottlingSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
|
func (s *ThrottlingSuite) SetupSuite() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml"))
|
s.BaseSuite.SetupSuite()
|
||||||
|
s.createComposeProject("rest")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
defer display(c)
|
func (s *ThrottlingSuite) TestThrottleConfReload() {
|
||||||
err := cmd.Start()
|
s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml"))
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// 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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
config := &dynamic.Configuration{
|
config := &dynamic.Configuration{
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
@ -47,7 +49,7 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
|
||||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||||
Servers: []dynamic.Server{
|
Servers: []dynamic.Server{
|
||||||
{
|
{
|
||||||
URL: "http://" + s.getComposeServiceIP(c, "whoami1") + ":80",
|
URL: "http://" + s.getComposeServiceIP("whoami1") + ":80",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -68,28 +70,28 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
|
||||||
for i := 0; i < confChanges; i++ {
|
for i := 0; i < confChanges; i++ {
|
||||||
config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router
|
config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router
|
||||||
data, err := json.Marshal(config)
|
data, err := json.Marshal(config)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
|
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.DefaultClient.Do(request)
|
response, err := http.DefaultClient.Do(request)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadsRegexp := regexp.MustCompile(`traefik_config_reloads_total (\d*)\n`)
|
reloadsRegexp := regexp.MustCompile(`traefik_config_reloads_total (\d*)\n`)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/metrics")
|
resp, err := http.Get("http://127.0.0.1:8080/metrics")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
fields := reloadsRegexp.FindStringSubmatch(string(body))
|
fields := reloadsRegexp.FindStringSubmatch(string(body))
|
||||||
c.Assert(len(fields), checker.Equals, 2)
|
assert.Len(s.T(), fields, 2)
|
||||||
|
|
||||||
reloads, err := strconv.Atoi(fields[1])
|
reloads, err := strconv.Atoi(fields[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,5 +103,5 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
|
||||||
// Therefore the throttling (set at 400ms for this test) should only let
|
// Therefore the throttling (set at 400ms for this test) should only let
|
||||||
// (2s / 400 ms =) 5 config reloads happen in theory.
|
// (2s / 400 ms =) 5 config reloads happen in theory.
|
||||||
// In addition, we have to take into account the extra config reload from the internal provider (5 + 1).
|
// In addition, we have to take into account the extra config reload from the internal provider (5 + 1).
|
||||||
c.Assert(reloads, checker.LessOrEqualThan, 6)
|
assert.LessOrEqual(s.T(), reloads, 6)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConsulCatalogSuite struct {
|
type ConsulCatalogSuite struct {
|
||||||
|
@ -20,26 +21,36 @@ type ConsulCatalogSuite struct {
|
||||||
consulURL string
|
consulURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
func TestConsulCatalogSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "consul_catalog")
|
suite.Run(t, new(ConsulCatalogSuite))
|
||||||
s.composeUp(c)
|
}
|
||||||
|
|
||||||
s.consulURL = "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "consul"), "8500")
|
func (s *ConsulCatalogSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("consul_catalog")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.consulURL = "http://" + net.JoinHostPort(s.getComposeServiceIP("consul"), "8500")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.consulClient, err = api.NewClient(&api.Config{
|
s.consulClient, err = api.NewClient(&api.Config{
|
||||||
Address: s.consulURL,
|
Address: s.consulURL,
|
||||||
})
|
})
|
||||||
c.Check(err, check.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// 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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
s.consulAgentClient, err = api.NewClient(&api.Config{
|
s.consulAgentClient, err = api.NewClient(&api.Config{
|
||||||
Address: "http://" + net.JoinHostPort(s.getComposeServiceIP(c, "consul-agent"), "8500"),
|
Address: "http://" + net.JoinHostPort(s.getComposeServiceIP("consul-agent"), "8500"),
|
||||||
})
|
})
|
||||||
c.Check(err, check.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConsulCatalogSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
|
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
|
||||||
|
@ -83,36 +94,36 @@ func (s *ConsulCatalogSuite) deregisterService(id string, onAgent bool) error {
|
||||||
return client.Agent().ServiceDeregister(id)
|
return client.Agent().ServiceDeregister(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *check.C) {
|
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings() {
|
||||||
reg1 := &api.AgentServiceRegistration{
|
reg1 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: []string{"traefik.enable=true"},
|
Tags: []string{"traefik.enable=true"},
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
err := s.registerService(reg1, false)
|
err := s.registerService(reg1, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
reg2 := &api.AgentServiceRegistration{
|
reg2 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami2",
|
ID: "whoami2",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: []string{"traefik.enable=true"},
|
Tags: []string{"traefik.enable=true"},
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami2"),
|
Address: s.getComposeServiceIP("whoami2"),
|
||||||
}
|
}
|
||||||
err = s.registerService(reg2, false)
|
err = s.registerService(reg2, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
reg3 := &api.AgentServiceRegistration{
|
reg3 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami3",
|
ID: "whoami3",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: []string{"traefik.enable=true"},
|
Tags: []string{"traefik.enable=true"},
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami3"),
|
Address: s.getComposeServiceIP("whoami3"),
|
||||||
}
|
}
|
||||||
err = s.registerService(reg3, false)
|
err = s.registerService(reg3, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
|
@ -120,23 +131,18 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "whoami"
|
req.Host = "whoami"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second,
|
err = try.Request(req, 2*time.Second,
|
||||||
try.StatusCodeIs(200),
|
try.StatusCodeIs(200),
|
||||||
try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
try.StatusCodeIs(200),
|
try.StatusCodeIs(200),
|
||||||
|
@ -145,18 +151,18 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
|
||||||
fmt.Sprintf(`"http://%s:80":"UP"`, reg2.Address),
|
fmt.Sprintf(`"http://%s:80":"UP"`, reg2.Address),
|
||||||
fmt.Sprintf(`"http://%s:80":"UP"`, reg3.Address),
|
fmt.Sprintf(`"http://%s:80":"UP"`, reg3.Address),
|
||||||
))
|
))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami2", false)
|
err = s.deregisterService("whoami2", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami3", false)
|
err = s.deregisterService("whoami3", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
|
func (s *ConsulCatalogSuite) TestByLabels() {
|
||||||
containerIP := s.getComposeServiceIP(c, "whoami1")
|
containerIP := s.getComposeServiceIP("whoami1")
|
||||||
|
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
|
@ -171,7 +177,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
|
||||||
Address: containerIP,
|
Address: containerIP,
|
||||||
}
|
}
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
|
@ -179,23 +185,18 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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:8000/whoami", 5*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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *ConsulCatalogSuite) TestSimpleConfiguration() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -204,37 +205,32 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: []string{"traefik.enable=true"},
|
Tags: []string{"traefik.enable=true"},
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "whoami.consul.localhost"
|
req.Host = "whoami.consul.localhost"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
|
func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -243,39 +239,34 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple_watch.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple_watch.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: []string{"traefik.enable=true"},
|
Tags: []string{"traefik.enable=true"},
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "whoami.consul.localhost"
|
req.Host = "whoami.consul.localhost"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoamiIP := s.getComposeServiceIP(c, "whoami1")
|
whoamiIP := s.getComposeServiceIP("whoami1")
|
||||||
reg.Check = &api.AgentServiceCheck{
|
reg.Check = &api.AgentServiceCheck{
|
||||||
CheckID: "some-ok-check",
|
CheckID: "some-ok-check",
|
||||||
TCP: whoamiIP + ":80",
|
TCP: whoamiIP + ":80",
|
||||||
|
@ -285,10 +276,10 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.registerService(reg, false)
|
err = s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
reg.Check = &api.AgentServiceCheck{
|
reg.Check = &api.AgentServiceCheck{
|
||||||
CheckID: "some-failing-check",
|
CheckID: "some-failing-check",
|
||||||
|
@ -299,16 +290,16 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.registerService(reg, false)
|
err = s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
|
func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -317,8 +308,7 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
|
@ -328,25 +318,21 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
|
||||||
Address: "",
|
Address: "",
|
||||||
}
|
}
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/http/services", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/http/services", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("whoami@consulcatalog", "\"http://127.0.0.1:80\": \"UP\""))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("whoami@consulcatalog", "\"http://127.0.0.1:80\": \"UP\""))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
|
func (s *ConsulCatalogSuite) TestDefaultConsulService() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -355,37 +341,32 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "whoami.consul.localhost"
|
req.Host = "whoami.consul.localhost"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -394,8 +375,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
// Start a container with some tags
|
// Start a container with some tags
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
|
@ -407,32 +387,28 @@ 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.getComposeServiceIP(c, "whoamitcp"),
|
Address: s.getComposeServiceIP("whoamitcp"),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 1500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
who, err := guessWho("127.0.0.1:8000", "my.super.host", true)
|
who, err := guessWho("127.0.0.1:8000", "my.super.host", true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
c.Assert(who, checker.Contains, "whoamitcp")
|
assert.Contains(s.T(), who, "whoamitcp")
|
||||||
|
|
||||||
err = s.deregisterService("whoamitcp", false)
|
err = s.deregisterService("whoamitcp", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulServiceWithLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -441,8 +417,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
// Start a container with some tags
|
// Start a container with some tags
|
||||||
reg1 := &api.AgentServiceRegistration{
|
reg1 := &api.AgentServiceRegistration{
|
||||||
|
@ -452,11 +427,11 @@ 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.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService(reg1, false)
|
err := s.registerService(reg1, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start another container by replacing a '.' by a '-'
|
// Start another container by replacing a '.' by a '-'
|
||||||
reg2 := &api.AgentServiceRegistration{
|
reg2 := &api.AgentServiceRegistration{
|
||||||
|
@ -466,40 +441,36 @@ 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.getComposeServiceIP(c, "whoami2"),
|
Address: s.getComposeServiceIP("whoami2"),
|
||||||
}
|
}
|
||||||
err = s.registerService(reg2, false)
|
err = s.registerService(reg2, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my-super.host"
|
req.Host = "my-super.host"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami2", false)
|
err = s.deregisterService("whoami2", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C) {
|
func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -508,8 +479,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
// Start a container with some tags
|
// Start a container with some tags
|
||||||
tags := []string{
|
tags := []string{
|
||||||
|
@ -523,50 +493,46 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
err := s.registerService(reg1, false)
|
err := s.registerService(reg1, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
reg2 := &api.AgentServiceRegistration{
|
reg2 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami",
|
ID: "whoami",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Port: 80,
|
Port: 80,
|
||||||
Address: s.getComposeServiceIP(c, "whoami2"),
|
Address: s.getComposeServiceIP("whoami2"),
|
||||||
}
|
}
|
||||||
err = s.registerService(reg2, true)
|
err = s.registerService(reg2, true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2"))
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200),
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200),
|
||||||
try.BodyContainsOr(s.getComposeServiceIP(c, "whoami1"), s.getComposeServiceIP(c, "whoami2")))
|
try.BodyContainsOr(s.getComposeServiceIP("whoami1"), s.getComposeServiceIP("whoami2")))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami", false)
|
err = s.deregisterService("whoami", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami", true)
|
err = s.deregisterService("whoami", true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -575,8 +541,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
// Start a container with some tags
|
// Start a container with some tags
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
|
@ -586,32 +551,28 @@ 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.getComposeServiceIP(c, "whoami1"),
|
Address: s.getComposeServiceIP("whoami1"),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService(reg, false)
|
err := s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||||
// TODO validate : run on 80
|
// 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.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck() {
|
||||||
whoamiIP := s.getComposeServiceIP(c, "whoami1")
|
whoamiIP := s.getComposeServiceIP("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`)",
|
||||||
|
@ -635,7 +596,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.registerService(reg1, false)
|
err := s.registerService(reg1, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
|
@ -643,22 +604,17 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoami2IP := s.getComposeServiceIP(c, "whoami2")
|
whoami2IP := s.getComposeServiceIP("whoami2")
|
||||||
reg2 := &api.AgentServiceRegistration{
|
reg2 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami2",
|
ID: "whoami2",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
|
@ -675,26 +631,26 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.registerService(reg2, false)
|
err = s.registerService(reg2, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "whoami"
|
req.Host = "whoami"
|
||||||
|
|
||||||
// TODO Need to wait for up to 10 seconds (for consul discovery or traefik to boot up ?)
|
// TODO Need to wait for up to 10 seconds (for consul discovery or traefik to boot up ?)
|
||||||
err = try.Request(req, 10*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
err = try.Request(req, 10*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("whoami2", false)
|
err = s.deregisterService("whoami2", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulConnect() {
|
||||||
// Wait for consul to fully initialize connect CA
|
// Wait for consul to fully initialize connect CA
|
||||||
err := s.waitForConnectCA()
|
err := s.waitForConnectCA()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
connectIP := s.getComposeServiceIP(c, "connect")
|
connectIP := s.getComposeServiceIP("connect")
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "uuid-api1",
|
ID: "uuid-api1",
|
||||||
Name: "uuid-api",
|
Name: "uuid-api",
|
||||||
|
@ -712,9 +668,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
|
||||||
Address: connectIP,
|
Address: connectIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(reg, false)
|
err = s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoamiIP := s.getComposeServiceIP(c, "whoami1")
|
whoamiIP := s.getComposeServiceIP("whoami1")
|
||||||
regWhoami := &api.AgentServiceRegistration{
|
regWhoami := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
|
@ -727,40 +683,35 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
|
||||||
Address: whoamiIP,
|
Address: whoamiIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(regWhoami, false)
|
err = s.registerService(regWhoami, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
}{
|
}{
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/connect.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/connect.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("uuid-api1", false)
|
err = s.deregisterService("uuid-api1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault() {
|
||||||
// Wait for consul to fully initialize connect CA
|
// Wait for consul to fully initialize connect CA
|
||||||
err := s.waitForConnectCA()
|
err := s.waitForConnectCA()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
connectIP := s.getComposeServiceIP(c, "connect")
|
connectIP := s.getComposeServiceIP("connect")
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "uuid-api1",
|
ID: "uuid-api1",
|
||||||
Name: "uuid-api",
|
Name: "uuid-api",
|
||||||
|
@ -777,9 +728,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
|
||||||
Address: connectIP,
|
Address: connectIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(reg, false)
|
err = s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoamiIP := s.getComposeServiceIP(c, "whoami1")
|
whoamiIP := s.getComposeServiceIP("whoami1")
|
||||||
regWhoami := &api.AgentServiceRegistration{
|
regWhoami := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami1",
|
Name: "whoami1",
|
||||||
|
@ -792,9 +743,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
|
||||||
Address: whoamiIP,
|
Address: whoamiIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(regWhoami, false)
|
err = s.registerService(regWhoami, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoami2IP := s.getComposeServiceIP(c, "whoami2")
|
whoami2IP := s.getComposeServiceIP("whoami2")
|
||||||
regWhoami2 := &api.AgentServiceRegistration{
|
regWhoami2 := &api.AgentServiceRegistration{
|
||||||
ID: "whoami2",
|
ID: "whoami2",
|
||||||
Name: "whoami2",
|
Name: "whoami2",
|
||||||
|
@ -808,45 +759,40 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
|
||||||
Address: whoami2IP,
|
Address: whoami2IP,
|
||||||
}
|
}
|
||||||
err = s.registerService(regWhoami2, false)
|
err = s.registerService(regWhoami2, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
}{
|
}{
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/connect_by_default.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/connect_by_default.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami2", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami2", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("uuid-api1", false)
|
err = s.deregisterService("uuid-api1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami2", false)
|
err = s.deregisterService("whoami2", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
|
func (s *ConsulCatalogSuite) TestConsulConnect_NotAware() {
|
||||||
// Wait for consul to fully initialize connect CA
|
// Wait for consul to fully initialize connect CA
|
||||||
err := s.waitForConnectCA()
|
err := s.waitForConnectCA()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
connectIP := s.getComposeServiceIP(c, "connect")
|
connectIP := s.getComposeServiceIP("connect")
|
||||||
reg := &api.AgentServiceRegistration{
|
reg := &api.AgentServiceRegistration{
|
||||||
ID: "uuid-api1",
|
ID: "uuid-api1",
|
||||||
Name: "uuid-api",
|
Name: "uuid-api",
|
||||||
|
@ -864,9 +810,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
|
||||||
Address: connectIP,
|
Address: connectIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(reg, false)
|
err = s.registerService(reg, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
whoamiIP := s.getComposeServiceIP(c, "whoami1")
|
whoamiIP := s.getComposeServiceIP("whoami1")
|
||||||
regWhoami := &api.AgentServiceRegistration{
|
regWhoami := &api.AgentServiceRegistration{
|
||||||
ID: "whoami1",
|
ID: "whoami1",
|
||||||
Name: "whoami",
|
Name: "whoami",
|
||||||
|
@ -879,30 +825,25 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
|
||||||
Address: whoamiIP,
|
Address: whoamiIP,
|
||||||
}
|
}
|
||||||
err = s.registerService(regWhoami, false)
|
err = s.registerService(regWhoami, false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
ConsulAddress string
|
ConsulAddress string
|
||||||
}{
|
}{
|
||||||
ConsulAddress: s.consulURL,
|
ConsulAddress: s.consulURL,
|
||||||
}
|
}
|
||||||
file := s.adaptFile(c, "fixtures/consul_catalog/connect_not_aware.toml", tempObjects)
|
file := s.adaptFile("fixtures/consul_catalog/connect_not_aware.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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:8000/", 10*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = s.deregisterService("uuid-api1", false)
|
err = s.deregisterService("uuid-api1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = s.deregisterService("whoami1", false)
|
err = s.deregisterService("whoami1", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,17 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/kvtools/consul"
|
"github.com/kvtools/consul"
|
||||||
"github.com/kvtools/valkeyrie"
|
"github.com/kvtools/valkeyrie"
|
||||||
"github.com/kvtools/valkeyrie/store"
|
"github.com/kvtools/valkeyrie/store"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Consul test suites.
|
// Consul test suites.
|
||||||
|
@ -29,18 +30,16 @@ type ConsulSuite struct {
|
||||||
consulURL string
|
consulURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulSuite) resetStore(c *check.C) {
|
func TestConsulSuite(t *testing.T) {
|
||||||
err := s.kvClient.DeleteTree(context.Background(), "traefik")
|
suite.Run(t, new(ConsulSuite))
|
||||||
if err != nil && !errors.Is(err, store.ErrKeyNotFound) {
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulSuite) setupStore(c *check.C) {
|
func (s *ConsulSuite) SetupSuite() {
|
||||||
s.createComposeProject(c, "consul")
|
s.BaseSuite.SetupSuite()
|
||||||
s.composeUp(c)
|
s.createComposeProject("consul")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
consulAddr := net.JoinHostPort(s.getComposeServiceIP(c, "consul"), "8500")
|
consulAddr := net.JoinHostPort(s.getComposeServiceIP("consul"), "8500")
|
||||||
s.consulURL = fmt.Sprintf("http://%s", consulAddr)
|
s.consulURL = fmt.Sprintf("http://%s", consulAddr)
|
||||||
|
|
||||||
kv, err := valkeyrie.NewStore(
|
kv, err := valkeyrie.NewStore(
|
||||||
|
@ -51,21 +50,27 @@ func (s *ConsulSuite) setupStore(c *check.C) {
|
||||||
ConnectionTimeout: 10 * time.Second,
|
ConnectionTimeout: 10 * time.Second,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
require.NoError(s.T(), err, "Cannot create store consul")
|
||||||
c.Fatal("Cannot create store consul")
|
|
||||||
}
|
|
||||||
s.kvClient = kv
|
s.kvClient = kv
|
||||||
|
|
||||||
// wait for consul
|
// wait for consul
|
||||||
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *ConsulSuite) TearDownSuite() {
|
||||||
s.setupStore(c)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
func (s *ConsulSuite) TearDownTest() {
|
||||||
defer os.Remove(file)
|
err := s.kvClient.DeleteTree(context.Background(), "traefik")
|
||||||
|
if err != nil && !errors.Is(err, store.ErrKeyNotFound) {
|
||||||
|
require.ErrorIs(s.T(), err, store.ErrKeyNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConsulSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
||||||
|
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
@ -114,39 +119,35 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
try.BodyContains(`"striper@consul":`, `"compressor@consul":`, `"srvcA@consul":`, `"srvcB@consul":`),
|
try.BodyContains(`"striper@consul":`, `"compressor@consul":`, `"srvcA@consul":`, `"srvcB@consul":`),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var obtained api.RunTimeRepresentation
|
var obtained api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
got, err := json.MarshalIndent(obtained, "", " ")
|
got, err := json.MarshalIndent(obtained, "", " ")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash("testdata/rawdata-consul.json")
|
expectedJSON := filepath.FromSlash("testdata/rawdata-consul.json")
|
||||||
|
|
||||||
if *updateExpected {
|
if *updateExpected {
|
||||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := os.ReadFile(expectedJSON)
|
expected, err := os.ReadFile(expectedJSON)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if !bytes.Equal(expected, got) {
|
if !bytes.Equal(expected, got) {
|
||||||
diff := difflib.UnifiedDiff{
|
diff := difflib.UnifiedDiff{
|
||||||
|
@ -158,33 +159,27 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
text, err := difflib.GetUnifiedDiffString(diff)
|
text, err := difflib.GetUnifiedDiffString(diff)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err, text)
|
||||||
c.Error(text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulSuite) assertWhoami(c *check.C, host string, expectedStatusCode int) {
|
func (s *ConsulSuite) assertWhoami(host string, expectedStatusCode int) {
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
if err != nil {
|
require.NoError(s.T(), err)
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
req.Host = host
|
req.Host = host
|
||||||
|
|
||||||
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
|
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConsulSuite) TestDeleteRootKey(c *check.C) {
|
func (s *ConsulSuite) TestDeleteRootKey() {
|
||||||
// This test case reproduce the issue: https://github.com/traefik/traefik/issues/8092
|
// This test case reproduce the issue: https://github.com/traefik/traefik/issues/8092
|
||||||
s.setupStore(c)
|
|
||||||
s.resetStore(c)
|
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
svcaddr := net.JoinHostPort(s.getComposeServiceIP(c, "whoami"), "80")
|
svcaddr := net.JoinHostPort(s.getComposeServiceIP("whoami"), "80")
|
||||||
|
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
@ -201,32 +196,28 @@ func (s *ConsulSuite) TestDeleteRootKey(c *check.C) {
|
||||||
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
err := s.kvClient.Put(ctx, k, []byte(v), nil)
|
err := s.kvClient.Put(ctx, k, []byte(v), nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
try.BodyContains(`"Router0@consul":`, `"Router1@consul":`, `"simplesvc0@consul":`, `"simplesvc1@consul":`),
|
try.BodyContains(`"Router0@consul":`, `"Router1@consul":`, `"simplesvc0@consul":`, `"simplesvc1@consul":`),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
s.assertWhoami(c, "kv1.localhost", http.StatusOK)
|
s.assertWhoami("kv1.localhost", http.StatusOK)
|
||||||
s.assertWhoami(c, "kv2.localhost", http.StatusOK)
|
s.assertWhoami("kv2.localhost", http.StatusOK)
|
||||||
|
|
||||||
// delete router1
|
// delete router1
|
||||||
err = s.kvClient.DeleteTree(ctx, "traefik/http/routers/Router1")
|
err = s.kvClient.DeleteTree(ctx, "traefik/http/routers/Router1")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
s.assertWhoami(c, "kv1.localhost", http.StatusOK)
|
s.assertWhoami("kv1.localhost", http.StatusOK)
|
||||||
s.assertWhoami(c, "kv2.localhost", http.StatusNotFound)
|
s.assertWhoami("kv2.localhost", http.StatusNotFound)
|
||||||
|
|
||||||
// delete simple services and router0
|
// delete simple services and router0
|
||||||
err = s.kvClient.DeleteTree(ctx, "traefik")
|
err = s.kvClient.DeleteTree(ctx, "traefik")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
s.assertWhoami(c, "kv1.localhost", http.StatusNotFound)
|
s.assertWhoami("kv1.localhost", http.StatusNotFound)
|
||||||
s.assertWhoami(c, "kv2.localhost", http.StatusNotFound)
|
s.assertWhoami("kv2.localhost", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,16 @@ package integration
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Docker tests suite.
|
// Docker tests suite.
|
||||||
|
@ -19,12 +20,21 @@ type DockerComposeSuite struct {
|
||||||
BaseSuite
|
BaseSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerComposeSuite) SetUpSuite(c *check.C) {
|
func TestDockerComposeSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "minimal")
|
suite.Run(t, new(DockerComposeSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
|
func (s *DockerComposeSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
s.createComposeProject("minimal")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerComposeSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerComposeSuite) TestComposeScale() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -32,41 +42,36 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
|
||||||
DockerHost: s.getDockerHost(),
|
DockerHost: s.getDockerHost(),
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
file := s.adaptFile(c, "fixtures/docker/minimal.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/minimal.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
_, err := try.ResponseUntilStatusCode(req, 5*time.Second, http.StatusOK)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var rtconf api.RunTimeRepresentation
|
var rtconf api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&rtconf)
|
err = json.NewDecoder(resp.Body).Decode(&rtconf)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// check that we have only three routers (the one from this test + 2 unrelated internal ones)
|
// check that we have only three routers (the one from this test + 2 unrelated internal ones)
|
||||||
c.Assert(rtconf.Routers, checker.HasLen, 3)
|
assert.Len(s.T(), rtconf.Routers, 3)
|
||||||
|
|
||||||
// check that we have only one service (not counting the internal ones) with n servers
|
// check that we have only one service (not counting the internal ones) with n servers
|
||||||
services := rtconf.Services
|
services := rtconf.Services
|
||||||
c.Assert(services, checker.HasLen, 4)
|
assert.Len(s.T(), services, 4)
|
||||||
for name, service := range services {
|
for name, service := range services {
|
||||||
if strings.HasSuffix(name, "@internal") {
|
if strings.HasSuffix(name, "@internal") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Assert(name, checker.Equals, "whoami1-"+s.composeProject.Name+"@docker")
|
assert.Equal(s.T(), "service-mini@docker", name)
|
||||||
c.Assert(service.LoadBalancer.Servers, checker.HasLen, 2)
|
assert.Len(s.T(), service.LoadBalancer.Servers, 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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,15 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Docker tests suite.
|
// Docker tests suite.
|
||||||
|
@ -18,15 +18,24 @@ type DockerSuite struct {
|
||||||
BaseSuite
|
BaseSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) SetUpTest(c *check.C) {
|
func TestDockerSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "docker")
|
suite.Run(t, new(DockerSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TearDownTest(c *check.C) {
|
func (s *DockerSuite) SetupSuite() {
|
||||||
s.composeDown(c)
|
s.BaseSuite.SetupSuite()
|
||||||
|
s.createComposeProject("docker")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *DockerSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TearDownTest() {
|
||||||
|
s.composeStop("simple", "withtcplabels", "withlabels1", "withlabels2", "withonelabelmissing", "powpow")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestSimpleConfiguration() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -35,24 +44,18 @@ func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c)
|
s.composeUp()
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// 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)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
|
func (s *DockerSuite) TestDefaultDockerContainers() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -61,37 +64,30 @@ func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c, "simple")
|
s.composeUp("simple")
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = fmt.Sprintf("simple-%s.docker.localhost", s.composeProject.Name)
|
req.Host = "simple.docker.localhost"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
|
||||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var version map[string]interface{}
|
var version map[string]interface{}
|
||||||
|
|
||||||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
assert.NoError(s.T(), json.Unmarshal(body, &version))
|
||||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestDockerContainersWithTCPLabels(c *check.C) {
|
func (s *DockerSuite) TestDockerContainersWithTCPLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -100,29 +96,23 @@ func (s *DockerSuite) TestDockerContainersWithTCPLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c, "withtcplabels")
|
s.composeUp("withtcplabels")
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
who, err := guessWho("127.0.0.1:8000", "my.super.host", true)
|
who, err := guessWho("127.0.0.1:8000", "my.super.host", true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
c.Assert(who, checker.Contains, "my.super.host")
|
assert.Contains(s.T(), who, "my.super.host")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
func (s *DockerSuite) TestDockerContainersWithLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -131,44 +121,37 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c, "withlabels1", "withlabels2")
|
s.composeUp("withlabels1", "withlabels2")
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my-super.host"
|
req.Host = "my-super.host"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
_, err = try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
|
||||||
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
|
||||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var version map[string]interface{}
|
var version map[string]interface{}
|
||||||
|
|
||||||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
assert.NoError(s.T(), json.Unmarshal(body, &version))
|
||||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
|
func (s *DockerSuite) TestDockerContainersWithOneMissingLabels() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -177,31 +160,23 @@ func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c, "withonelabelmissing")
|
s.composeUp("withonelabelmissing")
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
|
||||||
// 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.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(req, 3*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
func (s *DockerSuite) TestRestartDockerContainers() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
DefaultRule string
|
DefaultRule string
|
||||||
|
@ -210,46 +185,40 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
||||||
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
}
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
s.composeUp(c, "powpow")
|
s.composeUp("powpow")
|
||||||
|
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
// TODO 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)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var version map[string]interface{}
|
var version map[string]interface{}
|
||||||
|
|
||||||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
assert.NoError(s.T(), json.Unmarshal(body, &version))
|
||||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
s.composeStop(c, "powpow")
|
s.composeStop("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)
|
assert.Error(s.T(), err)
|
||||||
|
|
||||||
s.composeUp(c, "powpow")
|
s.composeUp("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)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ package integration
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorPagesSuite test suites.
|
// ErrorPagesSuite test suites.
|
||||||
|
@ -18,83 +18,78 @@ type ErrorPagesSuite struct {
|
||||||
BackendIP string
|
BackendIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorPagesSuite) SetUpSuite(c *check.C) {
|
func TestErrorPagesSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "error_pages")
|
suite.Run(t, new(ErrorPagesSuite))
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.ErrorPageIP = s.getComposeServiceIP(c, "nginx2")
|
|
||||||
s.BackendIP = s.getComposeServiceIP(c, "nginx1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *ErrorPagesSuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/error_pages/simple.toml", struct {
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("error_pages")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.ErrorPageIP = s.getComposeServiceIP("nginx2")
|
||||||
|
s.BackendIP = s.getComposeServiceIP("nginx1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ErrorPagesSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ErrorPagesSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/error_pages/simple.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{"http://" + s.BackendIP + ":80", s.ErrorPageIP})
|
}{"http://" + s.BackendIP + ":80", s.ErrorPageIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendReq.Host = "test.local"
|
frontendReq.Host = "test.local"
|
||||||
|
|
||||||
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("nginx"))
|
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("nginx"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorPagesSuite) TestErrorPage(c *check.C) {
|
func (s *ErrorPagesSuite) TestErrorPage() {
|
||||||
// error.toml contains a mis-configuration of the backend host
|
// error.toml contains a mis-configuration of the backend host
|
||||||
file := s.adaptFile(c, "fixtures/error_pages/error.toml", struct {
|
file := s.adaptFile("fixtures/error_pages/error.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{s.BackendIP, s.ErrorPageIP})
|
}{s.BackendIP, s.ErrorPageIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendReq.Host = "test.local"
|
frontendReq.Host = "test.local"
|
||||||
|
|
||||||
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred."))
|
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred."))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ErrorPagesSuite) TestErrorPageFlush(c *check.C) {
|
func (s *ErrorPagesSuite) TestErrorPageFlush() {
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Header().Add("Transfer-Encoding", "chunked")
|
rw.Header().Add("Transfer-Encoding", "chunked")
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
_, _ = rw.Write([]byte("KO"))
|
_, _ = rw.Write([]byte("KO"))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/error_pages/simple.toml", struct {
|
file := s.adaptFile("fixtures/error_pages/simple.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{srv.URL, s.ErrorPageIP})
|
}{srv.URL, s.ErrorPageIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendReq.Host = "test.local"
|
frontendReq.Host = "test.local"
|
||||||
|
|
||||||
err = try.Request(frontendReq, 2*time.Second,
|
err = try.Request(frontendReq, 2*time.Second,
|
||||||
try.BodyContains("An error occurred."),
|
try.BodyContains("An error occurred."),
|
||||||
try.HasHeaderValue("Content-Type", "text/html", true),
|
try.HasHeaderValue("Content-Type", "text/html", true),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,17 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/kvtools/etcdv3"
|
"github.com/kvtools/etcdv3"
|
||||||
"github.com/kvtools/valkeyrie"
|
"github.com/kvtools/valkeyrie"
|
||||||
"github.com/kvtools/valkeyrie/store"
|
"github.com/kvtools/valkeyrie/store"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// etcd test suites.
|
// etcd test suites.
|
||||||
|
@ -27,12 +28,18 @@ type EtcdSuite struct {
|
||||||
etcdAddr string
|
etcdAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EtcdSuite) SetUpSuite(c *check.C) {
|
func TestEtcdSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "etcd")
|
suite.Run(t, new(EtcdSuite))
|
||||||
s.composeUp(c)
|
}
|
||||||
|
|
||||||
|
func (s *EtcdSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("etcd")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.etcdAddr = net.JoinHostPort(s.getComposeServiceIP(c, "etcd"), "2379")
|
s.etcdAddr = net.JoinHostPort(s.getComposeServiceIP("etcd"), "2379")
|
||||||
s.kvClient, err = valkeyrie.NewStore(
|
s.kvClient, err = valkeyrie.NewStore(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
etcdv3.StoreName,
|
etcdv3.StoreName,
|
||||||
|
@ -41,18 +48,19 @@ func (s *EtcdSuite) SetUpSuite(c *check.C) {
|
||||||
ConnectionTimeout: 10 * time.Second,
|
ConnectionTimeout: 10 * time.Second,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
require.NoError(s.T(), err)
|
||||||
c.Fatal("Cannot create store etcd")
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for etcd
|
// wait for etcd
|
||||||
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
|
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *EtcdSuite) TearDownSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
|
s.BaseSuite.TearDownSuite()
|
||||||
defer os.Remove(file)
|
}
|
||||||
|
|
||||||
|
func (s *EtcdSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
|
||||||
|
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
@ -101,39 +109,35 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
try.BodyContains(`"striper@etcd":`, `"compressor@etcd":`, `"srvcA@etcd":`, `"srvcB@etcd":`),
|
try.BodyContains(`"striper@etcd":`, `"compressor@etcd":`, `"srvcA@etcd":`, `"srvcB@etcd":`),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var obtained api.RunTimeRepresentation
|
var obtained api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
got, err := json.MarshalIndent(obtained, "", " ")
|
got, err := json.MarshalIndent(obtained, "", " ")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash("testdata/rawdata-etcd.json")
|
expectedJSON := filepath.FromSlash("testdata/rawdata-etcd.json")
|
||||||
|
|
||||||
if *updateExpected {
|
if *updateExpected {
|
||||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := os.ReadFile(expectedJSON)
|
expected, err := os.ReadFile(expectedJSON)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if !bytes.Equal(expected, got) {
|
if !bytes.Equal(expected, got) {
|
||||||
diff := difflib.UnifiedDiff{
|
diff := difflib.UnifiedDiff{
|
||||||
|
@ -145,7 +149,6 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
text, err := difflib.GetUnifiedDiffString(diff)
|
text, err := difflib.GetUnifiedDiffString(diff)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err, text)
|
||||||
c.Error(text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,61 +2,58 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// File tests suite.
|
// File tests suite.
|
||||||
type FileSuite struct{ BaseSuite }
|
type FileSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *FileSuite) SetUpSuite(c *check.C) {
|
func TestFileSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "file")
|
suite.Run(t, new(FileSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *FileSuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/file/simple.toml", struct{}{})
|
s.BaseSuite.SetupSuite()
|
||||||
defer os.Remove(file)
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.createComposeProject("file")
|
||||||
defer display(c)
|
s.composeUp()
|
||||||
err := cmd.Start()
|
}
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
func (s *FileSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/file/simple.toml", struct{}{})
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
// 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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #56 regression test, make sure it does not fail?
|
// #56 regression test, make sure it does not fail?
|
||||||
func (s *FileSuite) TestSimpleConfigurationNoPanic(c *check.C) {
|
func (s *FileSuite) TestSimpleConfigurationNoPanic() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml"))
|
s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml"))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// 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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileSuite) TestDirectoryConfiguration(c *check.C) {
|
func (s *FileSuite) TestDirectoryConfiguration() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/directory.toml"))
|
s.traefikCmd(withConfigFile("fixtures/file/directory.toml"))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Expected a 404 as we did not configure anything at /test
|
// Expected a 404 as we did not configure anything at /test
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/test", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err := try.GetRequest("http://127.0.0.1:8000/test", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Expected a 502 as there is no backend server
|
// Expected a 502 as there is no backend server
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/test2", 1000*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
err = try.GetRequest("http://127.0.0.1:8000/test2", 1000*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,14 @@
|
||||||
noColor = true
|
noColor = true
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.web]
|
[entryPoints.trust]
|
||||||
address = ":8000"
|
address = ":8000"
|
||||||
[entryPoints.web.proxyProtocol]
|
[entryPoints.trust.proxyProtocol]
|
||||||
trustedIPs = ["{{.HaproxyIP}}"]
|
trustedIPs = ["127.0.0.1"]
|
||||||
|
[entryPoints.nottrust]
|
||||||
|
address = ":9000"
|
||||||
|
[entryPoints.nottrust.proxyProtocol]
|
||||||
|
trustedIPs = ["1.2.3.4"]
|
||||||
|
|
||||||
[api]
|
[api]
|
||||||
insecure = true
|
insecure = true
|
|
@ -1,33 +0,0 @@
|
||||||
[global]
|
|
||||||
checkNewVersion = false
|
|
||||||
sendAnonymousUsage = false
|
|
||||||
|
|
||||||
[log]
|
|
||||||
level = "DEBUG"
|
|
||||||
noColor = true
|
|
||||||
|
|
||||||
[entryPoints]
|
|
||||||
[entryPoints.web]
|
|
||||||
address = ":8000"
|
|
||||||
[entryPoints.web.proxyProtocol]
|
|
||||||
trustedIPs = ["1.2.3.4"]
|
|
||||||
|
|
||||||
[api]
|
|
||||||
insecure = true
|
|
||||||
|
|
||||||
[providers.file]
|
|
||||||
filename = "{{ .SelfFilename }}"
|
|
||||||
|
|
||||||
## dynamic configuration ##
|
|
||||||
|
|
||||||
[http.routers]
|
|
||||||
[http.routers.router1]
|
|
||||||
service = "service1"
|
|
||||||
rule = "Path(`/whoami`)"
|
|
||||||
|
|
||||||
[http.services]
|
|
||||||
[http.services.service1]
|
|
||||||
[http.services.service1.loadBalancer]
|
|
||||||
|
|
||||||
[[http.services.service1.loadBalancer.servers]]
|
|
||||||
url = "http://{{.WhoamiIP}}"
|
|
|
@ -5,6 +5,7 @@
|
||||||
[log]
|
[log]
|
||||||
level = "DEBUG"
|
level = "DEBUG"
|
||||||
|
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.tcp]
|
[entryPoints.tcp]
|
||||||
address = ":8093"
|
address = ":8093"
|
||||||
|
@ -24,16 +25,16 @@
|
||||||
rule = "HostSNI(`whoami-a.test`)"
|
rule = "HostSNI(`whoami-a.test`)"
|
||||||
service = "whoami-a"
|
service = "whoami-a"
|
||||||
middlewares = ["blocking-ipallowlist"]
|
middlewares = ["blocking-ipallowlist"]
|
||||||
[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]
|
||||||
entryPoints = ["tcp"]
|
entryPoints = ["tcp"]
|
||||||
rule = "HostSNI(`whoami-b.test`)"
|
rule = "HostSNI(`whoami-b.test`)"
|
||||||
service = "whoami-b"
|
service = "whoami-b"
|
||||||
middlewares = ["allowing-ipallowlist"]
|
middlewares = ["allowing-ipallowlist"]
|
||||||
[tcp.routers.to-whoami-b.tls]
|
[tcp.routers.to-whoami-b.tls]
|
||||||
passthrough = true
|
passthrough = true
|
||||||
|
|
||||||
[tcp.services]
|
[tcp.services]
|
||||||
[tcp.services.whoami-a.loadBalancer]
|
[tcp.services.whoami-a.loadBalancer]
|
||||||
|
@ -44,8 +45,8 @@
|
||||||
[[tcp.services.whoami-b.loadBalancer.servers]]
|
[[tcp.services.whoami-b.loadBalancer.servers]]
|
||||||
address = "{{ .WhoamiB }}"
|
address = "{{ .WhoamiB }}"
|
||||||
|
|
||||||
[tcp.middlewares]
|
[tcp.middlewares]
|
||||||
[tcp.middlewares.allowing-ipallowlist.ipAllowList]
|
[tcp.middlewares.allowing-ipallowlist.ipAllowList]
|
||||||
sourceRange = ["127.0.0.1/32"]
|
sourceRange = ["127.0.0.1/32"]
|
||||||
[tcp.middlewares.blocking-ipallowlist.ipAllowList]
|
[tcp.middlewares.blocking-ipallowlist.ipAllowList]
|
||||||
sourceRange = ["127.127.127.127/32"]
|
sourceRange = ["127.127.127.127/32"]
|
|
@ -8,10 +8,12 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/helloworld"
|
"github.com/traefik/traefik/v3/integration/helloworld"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
@ -29,16 +31,20 @@ const randCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567
|
||||||
// GRPCSuite tests suite.
|
// GRPCSuite tests suite.
|
||||||
type GRPCSuite struct{ BaseSuite }
|
type GRPCSuite struct{ BaseSuite }
|
||||||
|
|
||||||
|
func TestGRPCSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(GRPCSuite))
|
||||||
|
}
|
||||||
|
|
||||||
type myserver struct {
|
type myserver struct {
|
||||||
stopStreamExample chan bool
|
stopStreamExample chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) SetUpSuite(c *check.C) {
|
func (s *GRPCSuite) SetupSuite() {
|
||||||
var err error
|
var err error
|
||||||
LocalhostCert, err = os.ReadFile("./resources/tls/local.cert")
|
LocalhostCert, err = os.ReadFile("./resources/tls/local.cert")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
LocalhostKey, err = os.ReadFile("./resources/tls/local.key")
|
LocalhostKey, err = os.ReadFile("./resources/tls/local.key")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
|
func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
|
||||||
|
@ -137,19 +143,18 @@ func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func
|
||||||
return t, closer, nil
|
return t, closer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPC(c *check.C) {
|
func (s *GRPCSuite) TestGRPC() {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis, &myserver{})
|
err := startGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -159,79 +164,65 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC("World", true)
|
response, err = callHelloClientGRPC("World", true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
assert.Equal(s.T(), "Hello World", response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCh2c(c *check.C) {
|
func (s *GRPCSuite) TestGRPCh2c() {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := starth2cGRPCServer(lis, &myserver{})
|
err := starth2cGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config_h2c.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config_h2c.toml", struct {
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
}{
|
}{
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC("World", false)
|
response, err = callHelloClientGRPC("World", false)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
assert.Equal(s.T(), "Hello World", response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
|
func (s *GRPCSuite) TestGRPCh2cTermination() {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := starth2cGRPCServer(lis, &myserver{})
|
err := starth2cGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config_h2c_termination.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config_h2c_termination.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -241,40 +232,33 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC("World", true)
|
response, err = callHelloClientGRPC("World", true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
assert.Equal(s.T(), "Hello World", response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
func (s *GRPCSuite) TestGRPCInsecure() {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis, &myserver{})
|
err := startGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config_insecure.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config_insecure.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -284,44 +268,37 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC("World", true)
|
response, err = callHelloClientGRPC("World", true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
assert.Equal(s.T(), "Hello World", response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
func (s *GRPCSuite) TestGRPCBuffer() {
|
||||||
stopStreamExample := make(chan bool)
|
stopStreamExample := make(chan bool)
|
||||||
defer func() { stopStreamExample <- true }()
|
defer func() { stopStreamExample <- true }()
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis, &myserver{
|
err := startGRPCServer(lis, &myserver{
|
||||||
stopStreamExample: stopStreamExample,
|
stopStreamExample: stopStreamExample,
|
||||||
})
|
})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -331,27 +308,21 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
var client helloworld.Greeter_StreamExampleClient
|
var client helloworld.Greeter_StreamExampleClient
|
||||||
client, closer, err := callStreamExampleClientGRPC()
|
client, closer, err := callStreamExampleClientGRPC()
|
||||||
defer func() { _ = closer() }()
|
defer func() { _ = closer() }()
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
received := make(chan bool)
|
received := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
tr, err := client.Recv()
|
tr, err := client.Recv()
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(len(tr.GetData()), check.Equals, 512)
|
assert.Len(s.T(), tr.GetData(), 512)
|
||||||
received <- true
|
received <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -363,25 +334,24 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||||
return errors.New("failed to receive stream data")
|
return errors.New("failed to receive stream data")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval() {
|
||||||
stopStreamExample := make(chan bool)
|
stopStreamExample := make(chan bool)
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis, &myserver{
|
err := startGRPCServer(lis, &myserver{
|
||||||
stopStreamExample: stopStreamExample,
|
stopStreamExample: stopStreamExample,
|
||||||
})
|
})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -390,17 +360,11 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
KeyContent: string(LocalhostKey),
|
KeyContent: string(LocalhostKey),
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var client helloworld.Greeter_StreamExampleClient
|
var client helloworld.Greeter_StreamExampleClient
|
||||||
client, closer, err := callStreamExampleClientGRPC()
|
client, closer, err := callStreamExampleClientGRPC()
|
||||||
|
@ -408,13 +372,13 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
_ = closer()
|
_ = closer()
|
||||||
stopStreamExample <- true
|
stopStreamExample <- true
|
||||||
}()
|
}()
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
received := make(chan bool)
|
received := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
tr, err := client.Recv()
|
tr, err := client.Recv()
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(len(tr.GetData()), check.Equals, 512)
|
assert.Len(s.T(), tr.GetData(), 512)
|
||||||
received <- true
|
received <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -426,22 +390,21 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
return errors.New("failed to receive stream data")
|
return errors.New("failed to receive stream data")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
|
func (s *GRPCSuite) TestGRPCWithRetry() {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis, &myserver{})
|
err := startGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/grpc/config_retry.toml", struct {
|
file := s.adaptFile("fixtures/grpc/config_retry.toml", struct {
|
||||||
CertContent string
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -451,23 +414,17 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
|
||||||
GRPCServerPort: port,
|
GRPCServerPort: port,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC("World", true)
|
response, err = callHelloClientGRPC("World", true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
assert.Equal(s.T(), "Hello World", response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,50 +4,45 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Headers tests suite.
|
// Headers tests suite.
|
||||||
type HeadersSuite struct{ BaseSuite }
|
type HeadersSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *HeadersSuite) TestSimpleConfiguration(c *check.C) {
|
func TestHeadersSuite(t *testing.T) {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
|
suite.Run(t, new(HeadersSuite))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Expected a 404 as we did not configure anything
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) {
|
func (s *HeadersSuite) TestSimpleConfiguration() {
|
||||||
file := s.adaptFile(c, "fixtures/headers/remove_reverseproxy_headers.toml", struct{}{})
|
s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
|
||||||
defer os.Remove(file)
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
// Expected a 404 as we did not configure anything
|
||||||
c.Assert(err, checker.IsNil)
|
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
defer s.killCmd(cmd)
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HeadersSuite) TestReverseProxyHeaderRemoved() {
|
||||||
|
file := s.adaptFile("fixtures/headers/remove_reverseproxy_headers.toml", struct{}{})
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
_, found := r.Header["X-Forwarded-Host"]
|
_, found := r.Header["X-Forwarded-Host"]
|
||||||
c.Assert(found, checker.True)
|
assert.True(s.T(), found)
|
||||||
_, found = r.Header["Foo"]
|
_, found = r.Header["Foo"]
|
||||||
c.Assert(found, checker.False)
|
assert.False(s.T(), found)
|
||||||
_, found = r.Header["X-Forwarded-For"]
|
_, found = r.Header["X-Forwarded-For"]
|
||||||
c.Assert(found, checker.False)
|
assert.False(s.T(), found)
|
||||||
})
|
})
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", "127.0.0.1:9000")
|
listener, err := net.Listen("tcp", "127.0.0.1:9000")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
ts := &httptest.Server{
|
ts := &httptest.Server{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
|
@ -57,31 +52,25 @@ func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) {
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "test.localhost"
|
req.Host = "test.localhost"
|
||||||
req.Header = http.Header{
|
req.Header = http.Header{
|
||||||
"Foo": {"bar"},
|
"Foo": {"bar"},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HeadersSuite) TestCorsResponses(c *check.C) {
|
func (s *HeadersSuite) TestCorsResponses() {
|
||||||
file := s.adaptFile(c, "fixtures/headers/cors.toml", struct{}{})
|
file := s.adaptFile("fixtures/headers/cors.toml", struct{}{})
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
backend := startTestServer("9000", http.StatusOK, "")
|
backend := startTestServer("9000", http.StatusOK, "")
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -147,30 +136,24 @@ func (s *HeadersSuite) TestCorsResponses(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
req, err := http.NewRequest(test.method, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(test.method, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = test.reqHost
|
req.Host = test.reqHost
|
||||||
req.Header = test.requestHeaders
|
req.Header = test.requestHeaders
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
|
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
|
func (s *HeadersSuite) TestSecureHeadersResponses() {
|
||||||
file := s.adaptFile(c, "fixtures/headers/secure.toml", struct{}{})
|
file := s.adaptFile("fixtures/headers/secure.toml", struct{}{})
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
backend := startTestServer("9000", http.StatusOK, "")
|
backend := startTestServer("9000", http.StatusOK, "")
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -190,36 +173,30 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = test.reqHost
|
req.Host = test.reqHost
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil)
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = test.internalReqHost
|
req.Host = test.internalReqHost
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) {
|
func (s *HeadersSuite) TestMultipleSecureHeadersResponses() {
|
||||||
file := s.adaptFile(c, "fixtures/headers/secure_multiple.toml", struct{}{})
|
file := s.adaptFile("fixtures/headers/secure_multiple.toml", struct{}{})
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
backend := startTestServer("9000", http.StatusOK, "")
|
backend := startTestServer("9000", http.StatusOK, "")
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -238,10 +215,10 @@ func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = test.reqHost
|
req.Host = test.reqHost
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
|
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HealthCheck test suites.
|
// HealthCheck test suites.
|
||||||
|
@ -23,287 +25,272 @@ type HealthCheckSuite struct {
|
||||||
whoami4IP string
|
whoami4IP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) SetUpSuite(c *check.C) {
|
func TestHealthCheckSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "healthcheck")
|
suite.Run(t, new(HealthCheckSuite))
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.whoami1IP = s.getComposeServiceIP(c, "whoami1")
|
|
||||||
s.whoami2IP = s.getComposeServiceIP(c, "whoami2")
|
|
||||||
s.whoami3IP = s.getComposeServiceIP(c, "whoami3")
|
|
||||||
s.whoami4IP = s.getComposeServiceIP(c, "whoami4")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *HealthCheckSuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/simple.toml", struct {
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("healthcheck")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.whoami1IP = s.getComposeServiceIP("whoami1")
|
||||||
|
s.whoami2IP = s.getComposeServiceIP("whoami2")
|
||||||
|
s.whoami3IP = s.getComposeServiceIP("whoami3")
|
||||||
|
s.whoami4IP = s.getComposeServiceIP("whoami4")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HealthCheckSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HealthCheckSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/healthcheck/simple.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{s.whoami1IP, s.whoami2IP})
|
}{s.whoami1IP, s.whoami2IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendHealthReq.Host = "test.localhost"
|
frontendHealthReq.Host = "test.localhost"
|
||||||
|
|
||||||
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Fix all whoami health to 500
|
// Fix all whoami health to 500
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
|
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
|
||||||
for _, whoami := range whoamiHosts {
|
for _, whoami := range whoamiHosts {
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify no backend service is available due to failing health checks
|
// Verify no backend service is available due to failing health checks
|
||||||
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Change one whoami health to 200
|
// Change one whoami health to 200
|
||||||
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusOKReq1)
|
_, err = client.Do(statusOKReq1)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify frontend health : after
|
// Verify frontend health : after
|
||||||
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendReq.Host = "test.localhost"
|
frontendReq.Host = "test.localhost"
|
||||||
|
|
||||||
// Check if whoami1 responds
|
// Check if whoami1 responds
|
||||||
err = try.Request(frontendReq, 500*time.Millisecond, try.BodyContains(s.whoami1IP))
|
err = try.Request(frontendReq, 500*time.Millisecond, try.BodyContains(s.whoami1IP))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// 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.NotNil)
|
assert.Error(s.T(), err)
|
||||||
|
|
||||||
// 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/")
|
||||||
|
|
||||||
// Expected a 404 as we did not configure anything
|
// Expected a 404 as we did not configure anything
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
assert.Equal(s.T(), http.StatusNotFound, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestMultipleEntrypoints(c *check.C) {
|
func (s *HealthCheckSuite) TestMultipleEntrypoints() {
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/multiple-entrypoints.toml", struct {
|
file := s.adaptFile("fixtures/healthcheck/multiple-entrypoints.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{s.whoami1IP, s.whoami2IP})
|
}{s.whoami1IP, s.whoami2IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Wait for traefik
|
// Wait for traefik
|
||||||
err = try.GetRequest("http://localhost:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Check entrypoint http1
|
// Check entrypoint http1
|
||||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendHealthReq.Host = "test.localhost"
|
frontendHealthReq.Host = "test.localhost"
|
||||||
|
|
||||||
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Check entrypoint http2
|
// Check entrypoint http2
|
||||||
frontendHealthReq, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
frontendHealthReq, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendHealthReq.Host = "test.localhost"
|
frontendHealthReq.Host = "test.localhost"
|
||||||
|
|
||||||
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Set the both whoami health to 500
|
// Set the both whoami health to 500
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
|
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
|
||||||
for _, whoami := range whoamiHosts {
|
for _, whoami := range whoamiHosts {
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify no backend service is available due to failing health checks
|
// Verify no backend service is available due to failing health checks
|
||||||
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// reactivate the whoami2
|
// reactivate the whoami2
|
||||||
statusInternalServerOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami2IP+"/health", bytes.NewBufferString("200"))
|
statusInternalServerOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami2IP+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerOkReq)
|
_, err = client.Do(statusInternalServerOkReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
frontend1Req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
frontend1Req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontend1Req.Host = "test.localhost"
|
frontend1Req.Host = "test.localhost"
|
||||||
|
|
||||||
frontend2Req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/", nil)
|
frontend2Req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontend2Req.Host = "test.localhost"
|
frontend2Req.Host = "test.localhost"
|
||||||
|
|
||||||
// Check if whoami1 never responds
|
// Check if whoami1 never responds
|
||||||
err = try.Request(frontend2Req, 2*time.Second, try.BodyContains(s.whoami1IP))
|
err = try.Request(frontend2Req, 2*time.Second, try.BodyContains(s.whoami1IP))
|
||||||
c.Assert(err, checker.NotNil)
|
assert.Error(s.T(), err)
|
||||||
|
|
||||||
// Check if whoami1 never responds
|
// Check if whoami1 never responds
|
||||||
err = try.Request(frontend1Req, 2*time.Second, try.BodyContains(s.whoami1IP))
|
err = try.Request(frontend1Req, 2*time.Second, try.BodyContains(s.whoami1IP))
|
||||||
c.Assert(err, checker.NotNil)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
|
func (s *HealthCheckSuite) TestPortOverload() {
|
||||||
// Set one whoami health to 200
|
// Set one whoami health to 200
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/port_overload.toml", struct {
|
file := s.adaptFile("fixtures/healthcheck/port_overload.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
}{s.whoami1IP})
|
}{s.whoami1IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err = cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
frontendHealthReq.Host = "test.localhost"
|
frontendHealthReq.Host = "test.localhost"
|
||||||
|
|
||||||
// We test bad gateway because we use an invalid port for the backend
|
// We test bad gateway because we use an invalid port for the backend
|
||||||
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Set one whoami health to 500
|
// Set one whoami health to 500
|
||||||
statusInternalServerErrorReq, err = http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err = http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify no backend service is available due to failing health checks
|
// Verify no backend service is available due to failing health checks
|
||||||
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if all the loadbalancers created will correctly update the server status.
|
// Checks if all the loadbalancers created will correctly update the server status.
|
||||||
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService(c *check.C) {
|
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService() {
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
|
file := s.adaptFile("fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
}{s.whoami1IP})
|
}{s.whoami1IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Set whoami health to 200 to be sure to start with the wanted status
|
// Set whoami health to 200 to be sure to start with the wanted status
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
statusOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
statusOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusOkReq)
|
_, err = client.Do(statusOkReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// check healthcheck on web1 entrypoint
|
// check healthcheck on web1 entrypoint
|
||||||
healthReqWeb1, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
healthReqWeb1, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
healthReqWeb1.Host = "test.localhost"
|
healthReqWeb1.Host = "test.localhost"
|
||||||
err = try.Request(healthReqWeb1, 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(healthReqWeb1, 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// check healthcheck on web2 entrypoint
|
// check healthcheck on web2 entrypoint
|
||||||
healthReqWeb2, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
healthReqWeb2, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
healthReqWeb2.Host = "test.localhost"
|
healthReqWeb2.Host = "test.localhost"
|
||||||
|
|
||||||
err = try.Request(healthReqWeb2, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(healthReqWeb2, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Set whoami health to 500
|
// Set whoami health to 500
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify no backend service is available due to failing health checks
|
// Verify no backend service is available due to failing health checks
|
||||||
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Change one whoami health to 200
|
// Change one whoami health to 200
|
||||||
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusOKReq1)
|
_, err = client.Do(statusOKReq1)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify health check
|
// Verify health check
|
||||||
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
func (s *HealthCheckSuite) TestPropagate() {
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/propagate.toml", struct {
|
file := s.adaptFile("fixtures/healthcheck/propagate.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
Server3 string
|
Server3 string
|
||||||
Server4 string
|
Server4 string
|
||||||
}{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP})
|
}{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
rootReq.Host = "root.localhost"
|
rootReq.Host = "root.localhost"
|
||||||
|
|
||||||
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Bring whoami1 and whoami3 down
|
// Bring whoami1 and whoami3 down
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
|
@ -313,9 +300,9 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
whoamiHosts := []string{s.whoami1IP, s.whoami3IP}
|
whoamiHosts := []string{s.whoami1IP, s.whoami3IP}
|
||||||
for _, whoami := range whoamiHosts {
|
for _, whoami := range whoamiHosts {
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
try.Sleep(time.Second)
|
try.Sleep(time.Second)
|
||||||
|
@ -327,19 +314,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
reachedServers := make(map[string]int)
|
reachedServers := make(map[string]int)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(rootReq)
|
resp, err := client.Do(rootReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if reachedServers[s.whoami4IP] > reachedServers[s.whoami2IP] {
|
if reachedServers[s.whoami4IP] > reachedServers[s.whoami2IP] {
|
||||||
c.Assert(string(body), checker.Contains, want2)
|
assert.Contains(s.T(), string(body), want2)
|
||||||
reachedServers[s.whoami2IP]++
|
reachedServers[s.whoami2IP]++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if reachedServers[s.whoami2IP] > reachedServers[s.whoami4IP] {
|
if reachedServers[s.whoami2IP] > reachedServers[s.whoami4IP] {
|
||||||
c.Assert(string(body), checker.Contains, want4)
|
assert.Contains(s.T(), string(body), want4)
|
||||||
reachedServers[s.whoami4IP]++
|
reachedServers[s.whoami4IP]++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -356,48 +343,48 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 2)
|
assert.Equal(s.T(), 2, reachedServers[s.whoami2IP])
|
||||||
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 2)
|
assert.Equal(s.T(), 2, reachedServers[s.whoami4IP])
|
||||||
|
|
||||||
fooReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
fooReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
fooReq.Host = "foo.localhost"
|
fooReq.Host = "foo.localhost"
|
||||||
|
|
||||||
// Verify load-balancing on foo still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
|
// Verify load-balancing on foo still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
|
||||||
want := `IP: ` + s.whoami2IP
|
want := `IP: ` + s.whoami2IP
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(fooReq)
|
resp, err := client.Do(fooReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
c.Assert(string(body), checker.Contains, want)
|
assert.Contains(s.T(), string(body), want)
|
||||||
}
|
}
|
||||||
|
|
||||||
barReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
barReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
barReq.Host = "bar.localhost"
|
barReq.Host = "bar.localhost"
|
||||||
|
|
||||||
// Verify load-balancing on bar still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
|
// Verify load-balancing on bar still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
|
||||||
want = `IP: ` + s.whoami2IP
|
want = `IP: ` + s.whoami2IP
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(barReq)
|
resp, err := client.Do(barReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
c.Assert(string(body), checker.Contains, want)
|
assert.Contains(s.T(), string(body), want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring whoami2 and whoami4 down
|
// Bring whoami2 and whoami4 down
|
||||||
whoamiHosts = []string{s.whoami2IP, s.whoami4IP}
|
whoamiHosts = []string{s.whoami2IP, s.whoami4IP}
|
||||||
for _, whoami := range whoamiHosts {
|
for _, whoami := range whoamiHosts {
|
||||||
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusInternalServerErrorReq)
|
_, err = client.Do(statusInternalServerErrorReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
try.Sleep(time.Second)
|
try.Sleep(time.Second)
|
||||||
|
@ -405,25 +392,25 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
// Verify that everything is down, and that we get 503s everywhere.
|
// Verify that everything is down, and that we get 503s everywhere.
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
resp, err := client.Do(rootReq)
|
resp, err := client.Do(rootReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
|
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
|
||||||
|
|
||||||
resp, err = client.Do(fooReq)
|
resp, err = client.Do(fooReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
|
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
|
||||||
|
|
||||||
resp, err = client.Do(barReq)
|
resp, err = client.Do(barReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
|
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring everything back up.
|
// Bring everything back up.
|
||||||
whoamiHosts = []string{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP}
|
whoamiHosts = []string{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP}
|
||||||
for _, whoami := range whoamiHosts {
|
for _, whoami := range whoamiHosts {
|
||||||
statusOKReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("200"))
|
statusOKReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("200"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusOKReq)
|
_, err = client.Do(statusOKReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
try.Sleep(time.Second)
|
try.Sleep(time.Second)
|
||||||
|
@ -432,10 +419,10 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
reachedServers = make(map[string]int)
|
reachedServers = make(map[string]int)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(rootReq)
|
resp, err := client.Do(rootReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
||||||
reachedServers[s.whoami1IP]++
|
reachedServers[s.whoami1IP]++
|
||||||
|
@ -458,19 +445,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami1IP])
|
||||||
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
|
||||||
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
|
||||||
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami4IP])
|
||||||
|
|
||||||
// Verify everything is up on foo router.
|
// Verify everything is up on foo router.
|
||||||
reachedServers = make(map[string]int)
|
reachedServers = make(map[string]int)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(fooReq)
|
resp, err := client.Do(fooReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
||||||
reachedServers[s.whoami1IP]++
|
reachedServers[s.whoami1IP]++
|
||||||
|
@ -493,19 +480,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 2)
|
assert.Equal(s.T(), 2, reachedServers[s.whoami1IP])
|
||||||
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
|
||||||
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
|
||||||
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 0)
|
assert.Equal(s.T(), 0, reachedServers[s.whoami4IP])
|
||||||
|
|
||||||
// Verify everything is up on bar router.
|
// Verify everything is up on bar router.
|
||||||
reachedServers = make(map[string]int)
|
reachedServers = make(map[string]int)
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
resp, err := client.Do(barReq)
|
resp, err := client.Do(barReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
|
||||||
reachedServers[s.whoami1IP]++
|
reachedServers[s.whoami1IP]++
|
||||||
|
@ -528,102 +515,87 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 2)
|
assert.Equal(s.T(), 2, reachedServers[s.whoami1IP])
|
||||||
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
|
||||||
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
|
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
|
||||||
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 0)
|
assert.Equal(s.T(), 0, reachedServers[s.whoami4IP])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestPropagateNoHealthCheck(c *check.C) {
|
func (s *HealthCheckSuite) TestPropagateNoHealthCheck() {
|
||||||
file := s.adaptFile(c, "fixtures/healthcheck/propagate_no_healthcheck.toml", struct {
|
file := s.adaptFile("fixtures/healthcheck/propagate_no_healthcheck.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
}{s.whoami1IP})
|
}{s.whoami1IP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`noop.localhost`)"), try.BodyNotContains("Host(`root.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`noop.localhost`)"), try.BodyNotContains("Host(`root.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
rootReq.Host = "root.localhost"
|
rootReq.Host = "root.localhost"
|
||||||
|
|
||||||
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HealthCheckSuite) TestPropagateReload(c *check.C) {
|
func (s *HealthCheckSuite) TestPropagateReload() {
|
||||||
// Setup a WSP service without the healthcheck enabled (wsp-service1)
|
// Setup a WSP service without the healthcheck enabled (wsp-service1)
|
||||||
withoutHealthCheck := s.adaptFile(c, "fixtures/healthcheck/reload_without_healthcheck.toml", struct {
|
withoutHealthCheck := s.adaptFile("fixtures/healthcheck/reload_without_healthcheck.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{s.whoami1IP, s.whoami2IP})
|
}{s.whoami1IP, s.whoami2IP})
|
||||||
defer os.Remove(withoutHealthCheck)
|
withHealthCheck := s.adaptFile("fixtures/healthcheck/reload_with_healthcheck.toml", struct {
|
||||||
withHealthCheck := s.adaptFile(c, "fixtures/healthcheck/reload_with_healthcheck.toml", struct {
|
|
||||||
Server1 string
|
Server1 string
|
||||||
Server2 string
|
Server2 string
|
||||||
}{s.whoami1IP, s.whoami2IP})
|
}{s.whoami1IP, s.whoami2IP})
|
||||||
defer os.Remove(withHealthCheck)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(withoutHealthCheck))
|
s.traefikCmd(withConfigFile(withoutHealthCheck))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Allow one of the underlying services on it to fail all servers HC (whoami2)
|
// Allow one of the underlying services on it to fail all servers HC (whoami2)
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
statusOKReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami2IP+"/health", bytes.NewBufferString("500"))
|
statusOKReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami2IP+"/health", bytes.NewBufferString("500"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = client.Do(statusOKReq)
|
_, err = client.Do(statusOKReq)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
rootReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
rootReq.Host = "root.localhost"
|
rootReq.Host = "root.localhost"
|
||||||
|
|
||||||
// Check the failed service (whoami2) is getting requests, but answer 500
|
// Check the failed service (whoami2) is getting requests, but answer 500
|
||||||
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusServiceUnavailable))
|
err = try.Request(rootReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Enable the healthcheck on the root WSP (wsp-service1) and let Traefik reload the config
|
// Enable the healthcheck on the root WSP (wsp-service1) and let Traefik reload the config
|
||||||
fr1, err := os.OpenFile(withoutHealthCheck, os.O_APPEND|os.O_WRONLY, 0o644)
|
fr1, err := os.OpenFile(withoutHealthCheck, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||||
c.Assert(fr1, checker.NotNil)
|
assert.NotNil(s.T(), fr1)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = fr1.Truncate(0)
|
err = fr1.Truncate(0)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
fr2, err := os.ReadFile(withHealthCheck)
|
fr2, err := os.ReadFile(withHealthCheck)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = fmt.Fprint(fr1, string(fr2))
|
_, err = fmt.Fprint(fr1, string(fr2))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = fr1.Close()
|
err = fr1.Close()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
try.Sleep(1 * time.Second)
|
// Waiting for the reflected change.
|
||||||
|
err = try.Request(rootReq, 5*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Check the failed service (whoami2) is not getting requests
|
// Check the failed service (whoami2) is not getting requests
|
||||||
wantIPs := []string{s.whoami1IP, s.whoami1IP, s.whoami1IP, s.whoami1IP}
|
wantIPs := []string{s.whoami1IP, s.whoami1IP, s.whoami1IP, s.whoami1IP}
|
||||||
for _, ip := range wantIPs {
|
for _, ip := range wantIPs {
|
||||||
want := "IP: " + ip
|
err = try.Request(rootReq, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("IP: "+ip))
|
||||||
resp, err := client.Do(rootReq)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
c.Assert(string(body), checker.Contains, want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,27 +2,33 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HostResolverSuite struct{ BaseSuite }
|
type HostResolverSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *HostResolverSuite) SetUpSuite(c *check.C) {
|
func TestHostResolverSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "hostresolver")
|
suite.Run(t, new(HostResolverSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
|
func (s *HostResolverSuite) SetupSuite() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/simple_hostresolver.toml"))
|
s.BaseSuite.SetupSuite()
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.createComposeProject("hostresolver")
|
||||||
c.Assert(err, checker.IsNil)
|
s.composeUp()
|
||||||
defer s.killCmd(cmd)
|
}
|
||||||
|
|
||||||
|
func (s *HostResolverSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HostResolverSuite) TestSimpleConfig() {
|
||||||
|
s.traefikCmd(withConfigFile("fixtures/simple_hostresolver.toml"))
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -43,10 +49,10 @@ func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = test.host
|
req.Host = test.host
|
||||||
|
|
||||||
err = try.Request(req, 1*time.Second, try.StatusCodeIs(test.status), try.HasBody())
|
err = try.Request(req, 5*time.Second, try.StatusCodeIs(test.status), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,28 +5,27 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPSuite struct{ BaseSuite }
|
type HTTPSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *HTTPSuite) TestSimpleConfiguration(c *check.C) {
|
func TestHTTPSuite(t *testing.T) {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/http/simple.toml"))
|
suite.Run(t, new(HTTPSuite))
|
||||||
defer display(c)
|
}
|
||||||
|
|
||||||
err := cmd.Start()
|
func (s *HTTPSuite) TestSimpleConfiguration() {
|
||||||
c.Assert(err, checker.IsNil)
|
s.traefikCmd(withConfigFile("fixtures/http/simple.toml"))
|
||||||
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Expect a 404 as we configured nothing.
|
// Expect a 404 as we configured nothing.
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusNotFound))
|
err := try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Provide a configuration, fetched by Traefik provider.
|
// Provide a configuration, fetched by Traefik provider.
|
||||||
configuration := &dynamic.Configuration{
|
configuration := &dynamic.Configuration{
|
||||||
|
@ -55,14 +54,14 @@ func (s *HTTPSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
configData, err := json.Marshal(configuration)
|
configData, err := json.Marshal(configuration)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
server := startTestServerWithResponse(configData)
|
server := startTestServerWithResponse(configData)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
// Expect configuration to be applied.
|
// Expect configuration to be applied.
|
||||||
err = try.GetRequest("http://127.0.0.1:9090/api/rawdata", 3*time.Second, try.BodyContains("routerHTTP@http", "serviceHTTP@http", "http://bacon:80"))
|
err = try.GetRequest("http://127.0.0.1:9090/api/rawdata", 3*time.Second, try.BodyContains("routerHTTP@http", "serviceHTTP@http", "http://bacon:80"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTestServerWithResponse(response []byte) (ts *httptest.Server) {
|
func startTestServerWithResponse(response []byte) (ts *httptest.Server) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,208 +7,327 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/cli"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
dockernetwork "github.com/docker/docker/api/types/network"
|
||||||
"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/rs/zerolog"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/traefik/traefik/v3/pkg/logs"
|
"github.com/stretchr/testify/suite"
|
||||||
checker "github.com/vdemeester/shakers"
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
"github.com/testcontainers/testcontainers-go/network"
|
||||||
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var showLog = flag.Bool("tlog", false, "always show Traefik logs")
|
||||||
integration = flag.Bool("integration", false, "run integration tests")
|
|
||||||
showLog = flag.Bool("tlog", false, "always show Traefik logs")
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
type composeConfig struct {
|
||||||
if !*integration {
|
Services map[string]composeService `yaml:"services"`
|
||||||
log.Info().Msg("Integration tests disabled.")
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).
|
type composeService struct {
|
||||||
With().Timestamp().Caller().Logger()
|
Image string `yaml:"image"`
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
Labels map[string]string `yaml:"labels"`
|
||||||
|
Hostname string `yaml:"hostname"`
|
||||||
|
Volumes []string `yaml:"volumes"`
|
||||||
|
CapAdd []string `yaml:"cap_add"`
|
||||||
|
Command []string `yaml:"command"`
|
||||||
|
Environment map[string]string `yaml:"environment"`
|
||||||
|
Privileged bool `yaml:"privileged"`
|
||||||
|
Deploy composeDeploy `yaml:"deploy"`
|
||||||
|
}
|
||||||
|
|
||||||
// Global logrus replacement
|
type composeDeploy struct {
|
||||||
logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
|
Replicas int `yaml:"replicas"`
|
||||||
|
|
||||||
// configure default standard log.
|
|
||||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
|
||||||
stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
|
|
||||||
|
|
||||||
// TODO(mpl): very niche optimization: do not start tailscale if none of the wanted tests actually need it (e.g. KeepAliveSuite does not).
|
|
||||||
var (
|
|
||||||
vpn *tailscaleNotSuite
|
|
||||||
useVPN bool
|
|
||||||
)
|
|
||||||
if os.Getenv("IN_DOCKER") != "true" {
|
|
||||||
if vpn = setupVPN(nil, "tailscale.secret"); vpn != nil {
|
|
||||||
defer vpn.TearDownSuite(nil)
|
|
||||||
useVPN = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check.Suite(&AccessLogSuite{})
|
|
||||||
if !useVPN {
|
|
||||||
check.Suite(&AcmeSuite{})
|
|
||||||
}
|
|
||||||
check.Suite(&ConsulCatalogSuite{})
|
|
||||||
check.Suite(&ConsulSuite{})
|
|
||||||
check.Suite(&DockerComposeSuite{})
|
|
||||||
check.Suite(&DockerSuite{})
|
|
||||||
check.Suite(&ErrorPagesSuite{})
|
|
||||||
check.Suite(&EtcdSuite{})
|
|
||||||
check.Suite(&FileSuite{})
|
|
||||||
check.Suite(&GRPCSuite{})
|
|
||||||
check.Suite(&HeadersSuite{})
|
|
||||||
check.Suite(&HealthCheckSuite{})
|
|
||||||
check.Suite(&HostResolverSuite{})
|
|
||||||
check.Suite(&HTTPSSuite{})
|
|
||||||
check.Suite(&HTTPSuite{})
|
|
||||||
if !useVPN {
|
|
||||||
check.Suite(&K8sSuite{})
|
|
||||||
}
|
|
||||||
check.Suite(&KeepAliveSuite{})
|
|
||||||
check.Suite(&LogRotationSuite{})
|
|
||||||
if !useVPN {
|
|
||||||
check.Suite(&ProxyProtocolSuite{})
|
|
||||||
}
|
|
||||||
check.Suite(&RateLimitSuite{})
|
|
||||||
check.Suite(&RedisSuite{})
|
|
||||||
check.Suite(&RestSuite{})
|
|
||||||
check.Suite(&RetrySuite{})
|
|
||||||
check.Suite(&SimpleSuite{})
|
|
||||||
check.Suite(&TCPSuite{})
|
|
||||||
check.Suite(&TimeoutSuite{})
|
|
||||||
check.Suite(&ThrottlingSuite{})
|
|
||||||
check.Suite(&TLSClientHeadersSuite{})
|
|
||||||
check.Suite(&TracingSuite{})
|
|
||||||
check.Suite(&UDPSuite{})
|
|
||||||
check.Suite(&WebsocketSuite{})
|
|
||||||
check.Suite(&ZookeeperSuite{})
|
|
||||||
|
|
||||||
check.TestingT(t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var traefikBinary = "../dist/traefik"
|
var traefikBinary = "../dist/traefik"
|
||||||
|
|
||||||
type BaseSuite struct {
|
type BaseSuite struct {
|
||||||
composeProject *types.Project
|
suite.Suite
|
||||||
dockerComposeService composeapi.Service
|
containers map[string]testcontainers.Container
|
||||||
dockerClient *client.Client
|
network *testcontainers.DockerNetwork
|
||||||
|
hostIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) TearDownSuite(c *check.C) {
|
func (s *BaseSuite) waitForTraefik(containerName string) {
|
||||||
if s.composeProject != nil && s.dockerComposeService != nil {
|
time.Sleep(1 * time.Second)
|
||||||
s.composeDown(c)
|
|
||||||
|
// Wait for Traefik to turn ready.
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseSuite) displayTraefikLogFile(path string) {
|
||||||
|
if s.T().Failed() {
|
||||||
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||||
|
content, errRead := os.ReadFile(path)
|
||||||
|
// TODO TestName
|
||||||
|
// fmt.Printf("%s: Traefik logs: \n", c.TestName())
|
||||||
|
fmt.Print("Traefik logs: \n")
|
||||||
|
if errRead == nil {
|
||||||
|
fmt.Println(content)
|
||||||
|
} else {
|
||||||
|
fmt.Println(errRead)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fmt.Printf("%s: No Traefik logs.\n", c.TestName())
|
||||||
|
fmt.Print("No Traefik logs.\n")
|
||||||
|
}
|
||||||
|
errRemove := os.Remove(path)
|
||||||
|
if errRemove != nil {
|
||||||
|
fmt.Println(errRemove)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BaseSuite) SetupSuite() {
|
||||||
|
// configure default standard log.
|
||||||
|
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||||
|
// TODO
|
||||||
|
// stdlog.SetOutput(log.Logger)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
// Create docker network
|
||||||
|
// docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||||
|
var opts []network.NetworkCustomizer
|
||||||
|
opts = append(opts, network.WithDriver("bridge"))
|
||||||
|
opts = append(opts, network.WithIPAM(&dockernetwork.IPAM{
|
||||||
|
Driver: "default",
|
||||||
|
Config: []dockernetwork.IPAMConfig{
|
||||||
|
{
|
||||||
|
Subnet: "172.31.42.0/24",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
dockerNetwork, err := network.New(ctx, opts...)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
s.network = dockerNetwork
|
||||||
|
s.hostIP = "172.31.42.1"
|
||||||
|
if isDockerDesktop(ctx, s.T()) {
|
||||||
|
s.hostIP = getDockerDesktopHostIP(ctx, s.T())
|
||||||
|
s.setupVPN("tailscale.secret")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: "alpine",
|
||||||
|
HostConfigModifier: func(config *container.HostConfig) {
|
||||||
|
config.AutoRemove = true
|
||||||
|
},
|
||||||
|
Cmd: []string{"getent", "hosts", "host.docker.internal"},
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
closer, err := con.Logs(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
all, err := io.ReadAll(closer)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ipRegex := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
|
||||||
|
matches := ipRegex.FindAllString(string(all), -1)
|
||||||
|
require.Len(t, matches, 1)
|
||||||
|
|
||||||
|
return matches[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDockerDesktop(ctx context.Context, t *testing.T) bool {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cli, err := testcontainers.NewDockerClientWithOpts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create docker client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := cli.Info(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get docker info: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.OperatingSystem == "Docker Desktop"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseSuite) TearDownSuite() {
|
||||||
|
s.composeDown()
|
||||||
|
|
||||||
|
err := try.Do(5*time.Second, func() error {
|
||||||
|
if s.network != nil {
|
||||||
|
err := s.network.Remove(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
// createComposeProject creates the docker compose project stored as a field in the BaseSuite.
|
// 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.
|
// 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(name string) {
|
||||||
projectName := fmt.Sprintf("traefik-integration-test-%s", name)
|
|
||||||
composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
|
composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
|
||||||
|
|
||||||
var err error
|
file, err := os.ReadFile(composeFile)
|
||||||
s.dockerClient, err = client.NewClientWithOpts()
|
require.NoError(s.T(), err)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
s.dockerComposeService = compose.NewComposeService(s.dockerClient, &configfile.ConfigFile{})
|
var composeConfigData composeConfig
|
||||||
ops, err := cli.NewProjectOptions([]string{composeFile}, cli.WithName(projectName))
|
err = yaml.Unmarshal(file, &composeConfigData)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
s.composeProject, err = cli.ProjectFromOptions(ops)
|
if s.containers == nil {
|
||||||
c.Assert(err, checker.IsNil)
|
s.containers = map[string]testcontainers.Container{}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
for id, containerConfig := range composeConfigData.Services {
|
||||||
|
var mounts []mount.Mount
|
||||||
|
for _, volume := range containerConfig.Volumes {
|
||||||
|
split := strings.Split(volume, ":")
|
||||||
|
if len(split) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(split[0], "./") {
|
||||||
|
path, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("can't determine current directory")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
split[0] = strings.Replace(split[0], "./", path+"/", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
abs, err := filepath.Abs(split[0])
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
mounts = append(mounts, mount.Mount{Source: abs, Target: split[1], Type: mount.TypeBind})
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerConfig.Deploy.Replicas > 0 {
|
||||||
|
for i := 0; i < containerConfig.Deploy.Replicas; i++ {
|
||||||
|
id = fmt.Sprintf("%s-%d", id, i+1)
|
||||||
|
con, err := s.createContainer(ctx, containerConfig, id, mounts)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
s.containers[id] = con
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := s.createContainer(ctx, containerConfig, id, mounts)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
s.containers[id] = con
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseSuite) createContainer(ctx context.Context, containerConfig composeService, id string, mounts []mount.Mount) (testcontainers.Container, error) {
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: containerConfig.Image,
|
||||||
|
Env: containerConfig.Environment,
|
||||||
|
Cmd: containerConfig.Command,
|
||||||
|
Labels: containerConfig.Labels,
|
||||||
|
Name: id,
|
||||||
|
Hostname: containerConfig.Hostname,
|
||||||
|
Privileged: containerConfig.Privileged,
|
||||||
|
Networks: []string{s.network.Name},
|
||||||
|
HostConfigModifier: func(config *container.HostConfig) {
|
||||||
|
if containerConfig.CapAdd != nil {
|
||||||
|
config.CapAdd = containerConfig.CapAdd
|
||||||
|
}
|
||||||
|
if !isDockerDesktop(ctx, s.T()) {
|
||||||
|
config.ExtraHosts = append(config.ExtraHosts, "host.docker.internal:"+s.hostIP)
|
||||||
|
}
|
||||||
|
config.Mounts = mounts
|
||||||
|
},
|
||||||
|
}
|
||||||
|
con, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return con, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// composeUp starts the given services of the current docker compose project, if they are not already started.
|
// composeUp starts the given services of the current docker compose project, if they are not already started.
|
||||||
// Already running services are not affected (i.e. not stopped).
|
// Already running services are not affected (i.e. not stopped).
|
||||||
func (s *BaseSuite) composeUp(c *check.C, services ...string) {
|
func (s *BaseSuite) composeUp(services ...string) {
|
||||||
c.Assert(s.composeProject, check.NotNil)
|
for name, con := range s.containers {
|
||||||
c.Assert(s.dockerComposeService, check.NotNil)
|
if len(services) == 0 || slices.Contains(services, name) {
|
||||||
|
err := con.Start(context.Background())
|
||||||
// We use Create and Restart instead of Up, because the only option that actually works to control which containers
|
require.NoError(s.T(), err)
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// composeExec runs the command in the given args in the given compose service container.
|
|
||||||
// Already running services are not affected (i.e. not stopped).
|
|
||||||
func (s *BaseSuite) composeExec(c *check.C, service string, args ...string) {
|
|
||||||
c.Assert(s.composeProject, check.NotNil)
|
|
||||||
c.Assert(s.dockerComposeService, check.NotNil)
|
|
||||||
|
|
||||||
_, err := s.dockerComposeService.Exec(context.Background(), s.composeProject.Name, composeapi.RunOptions{
|
|
||||||
Service: service,
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
Command: args,
|
|
||||||
Tty: false,
|
|
||||||
Index: 1,
|
|
||||||
})
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// composeStop stops the given services of the current docker compose project and removes the corresponding containers.
|
// 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) {
|
func (s *BaseSuite) composeStop(services ...string) {
|
||||||
c.Assert(s.dockerComposeService, check.NotNil)
|
for name, con := range s.containers {
|
||||||
c.Assert(s.composeProject, check.NotNil)
|
if len(services) == 0 || slices.Contains(services, name) {
|
||||||
|
timeout := 10 * time.Second
|
||||||
err := s.dockerComposeService.Stop(context.Background(), s.composeProject, composeapi.StopOptions{Services: services})
|
err := con.Stop(context.Background(), &timeout)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
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.
|
// composeDown stops all compose project services and removes the corresponding containers.
|
||||||
func (s *BaseSuite) composeDown(c *check.C) {
|
func (s *BaseSuite) composeDown() {
|
||||||
c.Assert(s.dockerComposeService, check.NotNil)
|
for _, c := range s.containers {
|
||||||
c.Assert(s.composeProject, check.NotNil)
|
err := c.Terminate(context.Background())
|
||||||
|
require.NoError(s.T(), err)
|
||||||
err := s.dockerComposeService.Down(context.Background(), s.composeProject.Name, composeapi.DownOptions{})
|
}
|
||||||
c.Assert(err, checker.IsNil)
|
s.containers = map[string]testcontainers.Container{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
|
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
|
||||||
cmd := exec.Command(traefikBinary, args...)
|
cmd := exec.Command(traefikBinary, args...)
|
||||||
|
|
||||||
|
s.T().Cleanup(func() {
|
||||||
|
s.killCmd(cmd)
|
||||||
|
})
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
cmd.Stderr = &out
|
cmd.Stderr = &out
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
return cmd, &out
|
return cmd, &out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
|
func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
|
||||||
|
if cmd.Process == nil {
|
||||||
|
log.Error().Msg("No process to kill")
|
||||||
|
return
|
||||||
|
}
|
||||||
err := cmd.Process.Kill()
|
err := cmd.Process.Kill()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Kill")
|
log.Error().Err(err).Msg("Kill")
|
||||||
|
@ -217,15 +336,18 @@ func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) traefikCmd(args ...string) (*exec.Cmd, func(*check.C)) {
|
func (s *BaseSuite) traefikCmd(args ...string) *exec.Cmd {
|
||||||
cmd, out := s.cmdTraefik(args...)
|
cmd, out := s.cmdTraefik(args...)
|
||||||
return cmd, func(c *check.C) {
|
|
||||||
if c.Failed() || *showLog {
|
s.T().Cleanup(func() {
|
||||||
|
if s.T().Failed() || *showLog {
|
||||||
s.displayLogK3S()
|
s.displayLogK3S()
|
||||||
s.displayLogCompose(c)
|
s.displayLogCompose()
|
||||||
s.displayTraefikLog(c, out)
|
s.displayTraefikLog(out)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) displayLogK3S() {
|
func (s *BaseSuite) displayLogK3S() {
|
||||||
|
@ -242,30 +364,33 @@ func (s *BaseSuite) displayLogK3S() {
|
||||||
log.Print()
|
log.Print()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) displayLogCompose(c *check.C) {
|
func (s *BaseSuite) displayLogCompose() {
|
||||||
if s.dockerComposeService == nil || s.composeProject == nil {
|
for name, ctn := range s.containers {
|
||||||
log.Info().Str("testName", c.TestName()).Msg("No docker compose logs.")
|
readCloser, err := ctn.Logs(context.Background())
|
||||||
return
|
require.NoError(s.T(), err)
|
||||||
|
for {
|
||||||
|
b := make([]byte, 1024)
|
||||||
|
_, err := readCloser.Read(b)
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
trimLogs := bytes.Trim(bytes.TrimSpace(b), string([]byte{0}))
|
||||||
|
if len(trimLogs) > 0 {
|
||||||
|
log.Info().Str("container", name).Msg(string(trimLogs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Str("testName", c.TestName()).Msg("docker compose logs")
|
|
||||||
|
|
||||||
logConsumer := formatter.NewLogConsumer(context.Background(), logs.NoLevel(log.Logger, zerolog.InfoLevel), false, true)
|
|
||||||
|
|
||||||
err := s.dockerComposeService.Logs(context.Background(), s.composeProject.Name, logConsumer, composeapi.LogOptions{})
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
log.Print()
|
|
||||||
log.Print("################################")
|
|
||||||
log.Print()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) displayTraefikLog(c *check.C, output *bytes.Buffer) {
|
func (s *BaseSuite) displayTraefikLog(output *bytes.Buffer) {
|
||||||
if output == nil || output.Len() == 0 {
|
if output == nil || output.Len() == 0 {
|
||||||
log.Info().Str("testName", c.TestName()).Msg("No Traefik logs.")
|
log.Info().Msg("No Traefik logs.")
|
||||||
} else {
|
} else {
|
||||||
log.Info().Str("testName", c.TestName()).
|
for _, line := range strings.Split(output.String(), "\n") {
|
||||||
Str("logs", output.String()).Msg("Traefik logs")
|
log.Info().Msg(line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,67 +404,47 @@ func (s *BaseSuite) getDockerHost() string {
|
||||||
return dockerHost
|
return dockerHost
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) adaptFile(c *check.C, path string, tempObjects interface{}) string {
|
func (s *BaseSuite) adaptFile(path string, tempObjects interface{}) string {
|
||||||
// Load file
|
// Load file
|
||||||
tmpl, err := template.ParseFiles(path)
|
tmpl, err := template.ParseFiles(path)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
folder, prefix := filepath.Split(path)
|
folder, prefix := filepath.Split(path)
|
||||||
tmpFile, err := os.CreateTemp(folder, strings.TrimSuffix(prefix, filepath.Ext(prefix))+"_*"+filepath.Ext(prefix))
|
tmpFile, err := os.CreateTemp(folder, strings.TrimSuffix(prefix, filepath.Ext(prefix))+"_*"+filepath.Ext(prefix))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer tmpFile.Close()
|
defer tmpFile.Close()
|
||||||
|
|
||||||
model := structs.Map(tempObjects)
|
model := structs.Map(tempObjects)
|
||||||
model["SelfFilename"] = tmpFile.Name()
|
model["SelfFilename"] = tmpFile.Name()
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
|
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = tmpFile.Sync()
|
err = tmpFile.Sync()
|
||||||
|
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
s.T().Cleanup(func() {
|
||||||
|
os.Remove(tmpFile.Name())
|
||||||
|
})
|
||||||
return tmpFile.Name()
|
return tmpFile.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) getComposeServiceIP(c *check.C, name string) string {
|
func (s *BaseSuite) getComposeServiceIP(name string) string {
|
||||||
filter := filters.NewArgs(
|
container, ok := s.containers[name]
|
||||||
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ProjectLabel, s.composeProject.Name)),
|
if !ok {
|
||||||
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ServiceLabel, name)),
|
return ""
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
ip, err := container.ContainerIP(context.Background())
|
||||||
// Should never happen.
|
if err != nil {
|
||||||
c.Error("No network found")
|
return ""
|
||||||
return ""
|
}
|
||||||
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func withConfigFile(file string) string {
|
func withConfigFile(file string) string {
|
||||||
return "--configFile=" + file
|
return "--configFile=" + file
|
||||||
}
|
}
|
||||||
|
|
||||||
// tailscaleNotSuite includes a BaseSuite out of convenience, so we can benefit
|
|
||||||
// from composeUp et co., but it is not meant to function as a TestSuite per se.
|
|
||||||
type tailscaleNotSuite struct{ BaseSuite }
|
|
||||||
|
|
||||||
// setupVPN starts Tailscale on the corresponding container, and makes it a subnet
|
// setupVPN starts Tailscale on the corresponding container, and makes it a subnet
|
||||||
// router, for all the other containers (whoamis, etc) subsequently started for the
|
// router, for all the other containers (whoamis, etc) subsequently started for the
|
||||||
// integration tests.
|
// integration tests.
|
||||||
|
@ -355,25 +460,35 @@ type tailscaleNotSuite struct{ BaseSuite }
|
||||||
// "172.0.0.0/8": ["your_tailscale_identity"],
|
// "172.0.0.0/8": ["your_tailscale_identity"],
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
//
|
func (s *BaseSuite) setupVPN(keyFile string) {
|
||||||
// TODO(mpl): we could maybe even move this setup to the Makefile, to start it
|
|
||||||
// and let it run (forever, or until voluntarily stopped).
|
|
||||||
func setupVPN(c *check.C, keyFile string) *tailscaleNotSuite {
|
|
||||||
data, err := os.ReadFile(keyFile)
|
data, err := os.ReadFile(keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
log.Fatal().Err(err).Send()
|
log.Error().Err(err).Send()
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
authKey := strings.TrimSpace(string(data))
|
authKey := strings.TrimSpace(string(data))
|
||||||
// TODO: copy and create versions that don't need a check.C?
|
// // TODO: copy and create versions that don't need a check.C?
|
||||||
vpn := &tailscaleNotSuite{}
|
s.createComposeProject("tailscale")
|
||||||
vpn.createComposeProject(c, "tailscale")
|
s.composeUp()
|
||||||
vpn.composeUp(c)
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
// If we ever change the docker subnet in the Makefile,
|
// If we ever change the docker subnet in the Makefile,
|
||||||
// we need to change this one below correspondingly.
|
// we need to change this one below correspondingly.
|
||||||
vpn.composeExec(c, "tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24")
|
s.composeExec("tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24")
|
||||||
return vpn
|
}
|
||||||
|
|
||||||
|
// composeExec runs the command in the given args in the given compose service container.
|
||||||
|
// Already running services are not affected (i.e. not stopped).
|
||||||
|
func (s *BaseSuite) composeExec(service string, args ...string) string {
|
||||||
|
require.Contains(s.T(), s.containers, service)
|
||||||
|
|
||||||
|
_, reader, err := s.containers[service].Exec(context.Background(), args)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
content, err := io.ReadAll(reader)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
return string(content)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,21 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
|
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
|
||||||
|
@ -26,25 +29,39 @@ var updateExpected = flag.Bool("update_expected", false, "Update expected files
|
||||||
// K8sSuite tests suite.
|
// K8sSuite tests suite.
|
||||||
type K8sSuite struct{ BaseSuite }
|
type K8sSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *K8sSuite) SetUpSuite(c *check.C) {
|
func TestK8sSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "k8s")
|
suite.Run(t, new(K8sSuite))
|
||||||
s.composeUp(c)
|
}
|
||||||
|
|
||||||
|
func (s *K8sSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("k8s")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.Do(60*time.Second, func() error {
|
err = try.Do(60*time.Second, func() error {
|
||||||
_, err := os.Stat(abs)
|
_, err := os.Stat(abs)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
data, err := os.ReadFile(abs)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
content := strings.ReplaceAll(string(data), "https://server:6443", fmt.Sprintf("https://%s", net.JoinHostPort(s.getComposeServiceIP("server"), "6443")))
|
||||||
|
|
||||||
|
err = os.WriteFile(abs, []byte(content), 0o644)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = os.Setenv("KUBECONFIG", abs)
|
err = os.Setenv("KUBECONFIG", abs)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TearDownSuite(c *check.C) {
|
func (s *K8sSuite) TearDownSuite() {
|
||||||
s.composeDown(c)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
|
||||||
generatedFiles := []string{
|
generatedFiles := []string{
|
||||||
"./fixtures/k8s/config.skip/kubeconfig.yaml",
|
"./fixtures/k8s/config.skip/kubeconfig.yaml",
|
||||||
|
@ -62,120 +79,84 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestIngressConfiguration(c *check.C) {
|
func (s *K8sSuite) TestIngressConfiguration() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-ingress.json", "8080")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-ingress.json", "8080")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestIngressLabelSelector(c *check.C) {
|
func (s *K8sSuite) TestIngressLabelSelector() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-ingress-label-selector.json", "8080")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-ingress-label-selector.json", "8080")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
|
func (s *K8sSuite) TestCRDConfiguration() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-crd.json", "8000")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-crd.json", "8000")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestCRDLabelSelector(c *check.C) {
|
func (s *K8sSuite) TestCRDLabelSelector() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-crd-label-selector.json", "8000")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-crd-label-selector.json", "8000")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestGatewayConfiguration(c *check.C) {
|
func (s *K8sSuite) TestGatewayConfiguration() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-gateway.json", "8080")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-gateway.json", "8080")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestIngressclass(c *check.C) {
|
func (s *K8sSuite) TestIngressclass() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-ingressclass.json", "8080")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *K8sSuite) TestDisableIngressclassLookup(c *check.C) {
|
func (s *K8sSuite) TestDisableIngressclassLookup() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass_disabled.toml"))
|
s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass_disabled.toml"))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
s.testConfiguration("testdata/rawdata-ingressclass-disabled.json", "8080")
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
testConfiguration(c, "testdata/rawdata-ingressclass-disabled.json", "8080")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testConfiguration(c *check.C, path, apiPort string) {
|
func (s *K8sSuite) testConfiguration(path, apiPort string) {
|
||||||
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
|
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash(path)
|
expectedJSON := filepath.FromSlash(path)
|
||||||
|
|
||||||
if *updateExpected {
|
if *updateExpected {
|
||||||
fi, err := os.Create(expectedJSON)
|
fi, err := os.Create(expectedJSON)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = fi.Close()
|
err = fi.Close()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = try.GetRequest("http://127.0.0.1:"+apiPort+"/api/rawdata", 1*time.Minute, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf))
|
err = try.GetRequest("http://127.0.0.1:"+apiPort+"/api/rawdata", 1*time.Minute, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf))
|
||||||
|
|
||||||
if !*updateExpected {
|
if !*updateExpected {
|
||||||
if err != nil {
|
require.NoError(s.T(), err)
|
||||||
c.Error(err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logf("In file update mode, got expected error: %v", err)
|
log.Info().Msgf("In file update mode, got expected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var rtRepr api.RunTimeRepresentation
|
var rtRepr api.RunTimeRepresentation
|
||||||
err = json.Unmarshal(buf.Bytes(), &rtRepr)
|
err = json.Unmarshal(buf.Bytes(), &rtRepr)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
|
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = os.WriteFile(expectedJSON, newJSON, 0o644)
|
err = os.WriteFile(expectedJSON, newJSON, 0o644)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Errorf("We do not want a passing test in file update mode")
|
|
||||||
|
s.T().Fatal("We do not want a passing test in file update mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {
|
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {
|
||||||
|
|
|
@ -5,18 +5,23 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeepAliveSuite struct {
|
type KeepAliveSuite struct {
|
||||||
BaseSuite
|
BaseSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKeepAliveSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(KeepAliveSuite))
|
||||||
|
}
|
||||||
|
|
||||||
type KeepAliveConfig struct {
|
type KeepAliveConfig struct {
|
||||||
KeepAliveServer string
|
KeepAliveServer string
|
||||||
IdleConnTimeout string
|
IdleConnTimeout string
|
||||||
|
@ -27,7 +32,7 @@ type connStateChangeEvent struct {
|
||||||
state http.ConnState
|
state http.ConnState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *check.C) {
|
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime() {
|
||||||
idleTimeout := time.Duration(75) * time.Millisecond
|
idleTimeout := time.Duration(75) * time.Millisecond
|
||||||
|
|
||||||
connStateChanges := make(chan connStateChangeEvent)
|
connStateChanges := make(chan connStateChangeEvent)
|
||||||
|
@ -59,18 +64,18 @@ func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *
|
||||||
case <-noMoreRequests:
|
case <-noMoreRequests:
|
||||||
moreRequestsExpected = false
|
moreRequestsExpected = false
|
||||||
case <-maxWaitTimeExceeded:
|
case <-maxWaitTimeExceeded:
|
||||||
c.Logf("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
|
log.Info().Msgf("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
|
||||||
c.Fail()
|
s.T().Fail()
|
||||||
close(completed)
|
close(completed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Check(connCount, checker.Equals, 1)
|
require.Equal(s.T(), 1, connCount)
|
||||||
|
|
||||||
for _, idlePeriod := range idlePeriodLengthMap {
|
for _, idlePeriod := range idlePeriodLengthMap {
|
||||||
// Our method of measuring the actual idle period is not precise, so allow some sub-ms deviation
|
// Our method of measuring the actual idle period is not precise, so allow some sub-ms deviation
|
||||||
c.Check(math.Round(idlePeriod.Seconds()), checker.LessOrEqualThan, idleTimeout.Seconds())
|
require.LessOrEqual(s.T(), math.Round(idlePeriod.Seconds()), idleTimeout.Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
close(completed)
|
close(completed)
|
||||||
|
@ -87,22 +92,16 @@ func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := KeepAliveConfig{KeepAliveServer: server.URL, IdleConnTimeout: idleTimeout.String()}
|
config := KeepAliveConfig{KeepAliveServer: server.URL, IdleConnTimeout: idleTimeout.String()}
|
||||||
file := s.adaptFile(c, "fixtures/timeout/keepalive.toml", config)
|
file := s.adaptFile("fixtures/timeout/keepalive.toml", config)
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file))
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Check(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Wait for Traefik
|
// Wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Duration(1)*time.Second, try.StatusCodeIs(200), try.BodyContains("PathPrefix(`/keepalive`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Duration(1)*time.Second, try.StatusCodeIs(200), try.BodyContains("PathPrefix(`/keepalive`)"))
|
||||||
c.Check(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/keepalive", time.Duration(1)*time.Second, try.StatusCodeIs(200))
|
err = try.GetRequest("http://127.0.0.1:8000/keepalive", time.Duration(1)*time.Second, try.StatusCodeIs(200))
|
||||||
c.Check(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
close(noMoreRequests)
|
close(noMoreRequests)
|
||||||
<-completed
|
<-completed
|
||||||
|
|
|
@ -9,12 +9,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
|
const traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
|
||||||
|
@ -22,13 +24,23 @@ const 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 TestLogRorationSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "access_log")
|
suite.Run(t, new(LogRotationSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LogRotationSuite) TearDownSuite(c *check.C) {
|
func (s *LogRotationSuite) SetupSuite() {
|
||||||
s.composeDown(c)
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
os.Remove(traefikTestAccessLogFile)
|
||||||
|
os.Remove(traefikTestLogFile)
|
||||||
|
os.Remove(traefikTestAccessLogFileRotated)
|
||||||
|
|
||||||
|
s.createComposeProject("access_log")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LogRotationSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
|
||||||
generatedFiles := []string{
|
generatedFiles := []string{
|
||||||
traefikTestLogFile,
|
traefikTestLogFile,
|
||||||
|
@ -43,84 +55,80 @@ func (s *LogRotationSuite) TearDownSuite(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
|
func (s *LogRotationSuite) TestAccessLogRotation() {
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
cmd, _ := s.cmdTraefik(withConfigFile("fixtures/access_log_config.toml"))
|
||||||
defer display(c)
|
defer s.displayTraefikLogFile(traefikTestLogFile)
|
||||||
defer displayTraefikLogFile(c, traefikTestLogFile)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// Verify Traefik started ok
|
// Verify Traefik started ok
|
||||||
verifyEmptyErrorLog(c, "traefik.log")
|
s.verifyEmptyErrorLog("traefik.log")
|
||||||
|
|
||||||
waitForTraefik(c, "server1")
|
s.waitForTraefik("server1")
|
||||||
|
|
||||||
// Make some requests
|
// Make some requests
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
req.Host = "frontend1.docker.local"
|
req.Host = "frontend1.docker.local"
|
||||||
|
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Rename access log
|
// Rename access log
|
||||||
err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFileRotated)
|
err = os.Rename(traefikTestAccessLogFile, traefikTestAccessLogFileRotated)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// in the midst of the requests, issue SIGUSR1 signal to server process
|
// in the midst of the requests, issue SIGUSR1 signal to server process
|
||||||
err = cmd.Process.Signal(syscall.SIGUSR1)
|
err = cmd.Process.Signal(syscall.SIGUSR1)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// continue issuing requests
|
// continue issuing requests
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Verify access.log.rotated output as expected
|
// Verify access.log.rotated output as expected
|
||||||
logAccessLogFile(c, traefikTestAccessLogFileRotated)
|
s.logAccessLogFile(traefikTestAccessLogFileRotated)
|
||||||
lineCount := verifyLogLines(c, traefikTestAccessLogFileRotated, 0, true)
|
lineCount := s.verifyLogLines(traefikTestAccessLogFileRotated, 0, true)
|
||||||
c.Assert(lineCount, checker.GreaterOrEqualThan, 1)
|
assert.GreaterOrEqual(s.T(), lineCount, 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
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
_, err := os.Stat(traefikTestAccessLogFile)
|
_, err := os.Stat(traefikTestAccessLogFile)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil, check.Commentf("access log file was not created in time"))
|
assert.NoError(s.T(), err, "access log file was not created in time")
|
||||||
|
|
||||||
// Verify access.log output as expected
|
// Verify access.log output as expected
|
||||||
logAccessLogFile(c, traefikTestAccessLogFile)
|
s.logAccessLogFile(traefikTestAccessLogFile)
|
||||||
lineCount = verifyLogLines(c, traefikTestAccessLogFile, lineCount, true)
|
lineCount = s.verifyLogLines(traefikTestAccessLogFile, lineCount, true)
|
||||||
c.Assert(lineCount, checker.Equals, 3)
|
assert.Equal(s.T(), 3, lineCount)
|
||||||
|
|
||||||
verifyEmptyErrorLog(c, traefikTestLogFile)
|
s.verifyEmptyErrorLog(traefikTestLogFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logAccessLogFile(c *check.C, fileName string) {
|
func (s *LogRotationSuite) logAccessLogFile(fileName string) {
|
||||||
output, err := os.ReadFile(fileName)
|
output, err := os.ReadFile(fileName)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Logf("Contents of file %s\n%s", fileName, string(output))
|
log.Info().Msgf("Contents of file %s\n%s", fileName, string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyEmptyErrorLog(c *check.C, name string) {
|
func (s *LogRotationSuite) verifyEmptyErrorLog(name string) {
|
||||||
err := try.Do(5*time.Second, func() error {
|
err := try.Do(5*time.Second, func() error {
|
||||||
traefikLog, e2 := os.ReadFile(name)
|
traefikLog, e2 := os.ReadFile(name)
|
||||||
if e2 != nil {
|
if e2 != nil {
|
||||||
return e2
|
return e2
|
||||||
}
|
}
|
||||||
c.Assert(string(traefikLog), checker.HasLen, 0)
|
assert.Empty(s.T(), string(traefikLog))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyLogLines(c *check.C, fileName string, countInit int, accessLog bool) int {
|
func (s *LogRotationSuite) verifyLogLines(fileName string, countInit int, accessLog bool) int {
|
||||||
rotated, err := os.Open(fileName)
|
rotated, err := os.Open(fileName)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
rotatedLog := bufio.NewScanner(rotated)
|
rotatedLog := bufio.NewScanner(rotated)
|
||||||
count := countInit
|
count := countInit
|
||||||
for rotatedLog.Scan() {
|
for rotatedLog.Scan() {
|
||||||
|
@ -128,7 +136,7 @@ func verifyLogLines(c *check.C, fileName string, countInit int, accessLog bool)
|
||||||
if accessLog {
|
if accessLog {
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
if !strings.Contains(line, "/api/rawdata") {
|
if !strings.Contains(line, "/api/rawdata") {
|
||||||
CheckAccessLogFormat(c, line, count)
|
s.CheckAccessLogFormat(line, count)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +1,138 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"bufio"
|
||||||
"os"
|
"net"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/pires/go-proxyproto"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyProtocolSuite struct {
|
type ProxyProtocolSuite struct {
|
||||||
BaseSuite
|
BaseSuite
|
||||||
gatewayIP string
|
whoamiIP string
|
||||||
haproxyIP string
|
|
||||||
whoamiIP string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProxyProtocolSuite) SetUpSuite(c *check.C) {
|
func TestProxyProtocolSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "proxy-protocol")
|
suite.Run(t, new(ProxyProtocolSuite))
|
||||||
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) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("proxy-protocol")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.whoamiIP = s.getComposeServiceIP("whoami")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyProtocolSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyProtocolSuite) TestProxyProtocolTrusted() {
|
||||||
|
file := s.adaptFile("fixtures/proxy-protocol/proxy-protocol.toml", struct {
|
||||||
HaproxyIP string
|
HaproxyIP string
|
||||||
WhoamiIP string
|
WhoamiIP string
|
||||||
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
|
}{WhoamiIP: s.whoamiIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second)
|
||||||
try.StatusCodeIs(http.StatusOK),
|
require.NoError(s.T(), err)
|
||||||
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
|
|
||||||
c.Assert(err, checker.IsNil)
|
content, err := proxyProtoRequest("127.0.0.1:8000", 1)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Contains(s.T(), content, "X-Forwarded-For: 1.2.3.4")
|
||||||
|
|
||||||
|
content, err = proxyProtoRequest("127.0.0.1:8000", 2)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Contains(s.T(), content, "X-Forwarded-For: 1.2.3.4")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) {
|
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted() {
|
||||||
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
|
file := s.adaptFile("fixtures/proxy-protocol/proxy-protocol.toml", struct {
|
||||||
HaproxyIP string
|
HaproxyIP string
|
||||||
WhoamiIP string
|
WhoamiIP string
|
||||||
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
|
}{WhoamiIP: s.whoamiIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
|
err := try.GetRequest("http://127.0.0.1:9000/whoami", 10*time.Second)
|
||||||
try.StatusCodeIs(http.StatusOK),
|
require.NoError(s.T(), err)
|
||||||
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
|
|
||||||
c.Assert(err, checker.IsNil)
|
content, err := proxyProtoRequest("127.0.0.1:9000", 1)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Contains(s.T(), content, "X-Forwarded-For: 127.0.0.1")
|
||||||
|
|
||||||
|
content, err = proxyProtoRequest("127.0.0.1:9000", 2)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Contains(s.T(), content, "X-Forwarded-For: 127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) {
|
func proxyProtoRequest(address string, version byte) (string, error) {
|
||||||
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
|
// Open a TCP connection to the server
|
||||||
HaproxyIP string
|
conn, err := net.Dial("tcp", address)
|
||||||
WhoamiIP string
|
if err != nil {
|
||||||
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
|
return "", err
|
||||||
defer os.Remove(file)
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
// Create a Proxy Protocol header with v1
|
||||||
defer display(c)
|
proxyHeader := &proxyproto.Header{
|
||||||
err := cmd.Start()
|
Version: version,
|
||||||
c.Assert(err, checker.IsNil)
|
Command: proxyproto.PROXY,
|
||||||
defer s.killCmd(cmd)
|
TransportProtocol: proxyproto.TCPv4,
|
||||||
|
DestinationAddr: &net.TCPAddr{
|
||||||
|
IP: net.ParseIP("127.0.0.1"),
|
||||||
|
Port: 8000,
|
||||||
|
},
|
||||||
|
SourceAddr: &net.TCPAddr{
|
||||||
|
IP: net.ParseIP("1.2.3.4"),
|
||||||
|
Port: 62541,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second,
|
// After the connection was created write the proxy headers first
|
||||||
try.StatusCodeIs(http.StatusOK),
|
_, err = proxyHeader.WriteTo(conn)
|
||||||
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
|
if err != nil {
|
||||||
c.Assert(err, checker.IsNil)
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
|
// Create an HTTP request
|
||||||
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
|
request := "GET /whoami HTTP/1.1\r\n" +
|
||||||
HaproxyIP string
|
"Host: 127.0.0.1\r\n" +
|
||||||
WhoamiIP string
|
"Connection: close\r\n" +
|
||||||
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
|
"\r\n"
|
||||||
defer os.Remove(file)
|
|
||||||
|
// Write the HTTP request to the TCP connection
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
writer := bufio.NewWriter(conn)
|
||||||
defer display(c)
|
_, err = writer.WriteString(request)
|
||||||
err := cmd.Start()
|
if err != nil {
|
||||||
c.Assert(err, checker.IsNil)
|
return "", err
|
||||||
defer s.killCmd(cmd)
|
}
|
||||||
|
|
||||||
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
|
// Flush the buffer to ensure the request is sent
|
||||||
try.StatusCodeIs(http.StatusOK),
|
err = writer.Flush()
|
||||||
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
|
if err != nil {
|
||||||
c.Assert(err, checker.IsNil)
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the response from the server
|
||||||
|
var content string
|
||||||
|
scanner := bufio.NewScanner(conn)
|
||||||
|
for scanner.Scan() {
|
||||||
|
content += scanner.Text() + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanner.Err() != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RateLimitSuite struct {
|
type RateLimitSuite struct {
|
||||||
|
@ -15,33 +15,38 @@ type RateLimitSuite struct {
|
||||||
ServerIP string
|
ServerIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RateLimitSuite) SetUpSuite(c *check.C) {
|
func TestRateLimitSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "ratelimit")
|
suite.Run(t, new(RateLimitSuite))
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.ServerIP = s.getComposeServiceIP(c, "whoami1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *RateLimitSuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/ratelimit/simple.toml", struct {
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("ratelimit")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.ServerIP = s.getComposeServiceIP("whoami1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RateLimitSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RateLimitSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/ratelimit/simple.toml", struct {
|
||||||
Server1 string
|
Server1 string
|
||||||
}{s.ServerIP})
|
}{s.ServerIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 1*time.Second, try.BodyContains("ratelimit"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
count := 0
|
count := 0
|
||||||
for {
|
for {
|
||||||
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
count++
|
count++
|
||||||
if count > 100 {
|
if count > 100 {
|
||||||
break
|
break
|
||||||
|
@ -50,6 +55,6 @@ func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
stop := time.Now()
|
stop := time.Now()
|
||||||
elapsed := stop.Sub(start)
|
elapsed := stop.Sub(start)
|
||||||
if elapsed < time.Second*99/100 {
|
if elapsed < time.Second*99/100 {
|
||||||
c.Fatalf("requests throughput was too fast wrt to rate limiting: 100 requests in %v", elapsed)
|
s.T().Fatalf("requests throughput was too fast wrt to rate limiting: 100 requests in %v", elapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
201
integration/redis_sentinel_test.go
Normal file
201
integration/redis_sentinel_test.go
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/kvtools/redis"
|
||||||
|
"github.com/kvtools/valkeyrie"
|
||||||
|
"github.com/kvtools/valkeyrie/store"
|
||||||
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redis test suites.
|
||||||
|
type RedisSentinelSuite struct {
|
||||||
|
BaseSuite
|
||||||
|
kvClient store.Store
|
||||||
|
redisEndpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRedisSentinelSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(RedisSentinelSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSentinelSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.setupSentinelConfiguration([]string{"26379", "26379", "26379"})
|
||||||
|
|
||||||
|
s.createComposeProject("redis_sentinel")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.redisEndpoints = []string{
|
||||||
|
net.JoinHostPort(s.getComposeServiceIP("sentinel1"), "26379"),
|
||||||
|
net.JoinHostPort(s.getComposeServiceIP("sentinel2"), "26379"),
|
||||||
|
net.JoinHostPort(s.getComposeServiceIP("sentinel3"), "26379"),
|
||||||
|
}
|
||||||
|
kv, err := valkeyrie.NewStore(
|
||||||
|
context.Background(),
|
||||||
|
redis.StoreName,
|
||||||
|
s.redisEndpoints,
|
||||||
|
&redis.Config{
|
||||||
|
Sentinel: &redis.Sentinel{
|
||||||
|
MasterName: "mymaster",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(s.T(), err, "Cannot create store redis")
|
||||||
|
s.kvClient = kv
|
||||||
|
|
||||||
|
// wait for redis
|
||||||
|
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSentinelSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
|
||||||
|
for _, filename := range []string{"sentinel1.conf", "sentinel2.conf", "sentinel3.conf"} {
|
||||||
|
_ = os.Remove(filepath.Join(".", "resources", "compose", "config", filename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSentinelSuite) setupSentinelConfiguration(ports []string) {
|
||||||
|
for i, port := range ports {
|
||||||
|
templateValue := struct{ SentinelPort string }{SentinelPort: port}
|
||||||
|
|
||||||
|
// Load file
|
||||||
|
templateFile := "resources/compose/config/sentinel_template.conf"
|
||||||
|
tmpl, err := template.ParseFiles(templateFile)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
folder, prefix := filepath.Split(templateFile)
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
|
||||||
|
tmpFile, err := os.Create(fileName)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
defer tmpFile.Close()
|
||||||
|
|
||||||
|
model := structs.Map(templateValue)
|
||||||
|
model["SelfFilename"] = tmpFile.Name()
|
||||||
|
|
||||||
|
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = tmpFile.Sync()
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSentinelSuite) TestSentinelConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/redis/sentinel.toml", struct{ RedisAddress string }{
|
||||||
|
RedisAddress: strings.Join(s.redisEndpoints, `","`),
|
||||||
|
})
|
||||||
|
|
||||||
|
data := map[string]string{
|
||||||
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
"traefik/http/routers/Router0/middlewares/0": "compressor",
|
||||||
|
"traefik/http/routers/Router0/middlewares/1": "striper",
|
||||||
|
"traefik/http/routers/Router0/service": "simplesvc",
|
||||||
|
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
|
||||||
|
"traefik/http/routers/Router0/priority": "42",
|
||||||
|
"traefik/http/routers/Router0/tls": "true",
|
||||||
|
|
||||||
|
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
|
||||||
|
"traefik/http/routers/Router1/priority": "42",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/0/main": "aaa.localhost",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/0/sans/0": "aaa.aaa.localhost",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/0/sans/1": "bbb.aaa.localhost",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/1/main": "bbb.localhost",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/1/sans/0": "aaa.bbb.localhost",
|
||||||
|
"traefik/http/routers/Router1/tls/domains/1/sans/1": "bbb.bbb.localhost",
|
||||||
|
"traefik/http/routers/Router1/entryPoints/0": "web",
|
||||||
|
"traefik/http/routers/Router1/service": "simplesvc",
|
||||||
|
|
||||||
|
"traefik/http/services/simplesvc/loadBalancer/servers/0/url": "http://10.0.1.1:8888",
|
||||||
|
"traefik/http/services/simplesvc/loadBalancer/servers/1/url": "http://10.0.1.1:8889",
|
||||||
|
|
||||||
|
"traefik/http/services/srvcA/loadBalancer/servers/0/url": "http://10.0.1.2:8888",
|
||||||
|
"traefik/http/services/srvcA/loadBalancer/servers/1/url": "http://10.0.1.2:8889",
|
||||||
|
|
||||||
|
"traefik/http/services/srvcB/loadBalancer/servers/0/url": "http://10.0.1.3:8888",
|
||||||
|
"traefik/http/services/srvcB/loadBalancer/servers/1/url": "http://10.0.1.3:8889",
|
||||||
|
|
||||||
|
"traefik/http/services/mirror/mirroring/service": "simplesvc",
|
||||||
|
"traefik/http/services/mirror/mirroring/mirrors/0/name": "srvcA",
|
||||||
|
"traefik/http/services/mirror/mirroring/mirrors/0/percent": "42",
|
||||||
|
"traefik/http/services/mirror/mirroring/mirrors/1/name": "srvcB",
|
||||||
|
"traefik/http/services/mirror/mirroring/mirrors/1/percent": "42",
|
||||||
|
|
||||||
|
"traefik/http/services/Service03/weighted/services/0/name": "srvcA",
|
||||||
|
"traefik/http/services/Service03/weighted/services/0/weight": "42",
|
||||||
|
"traefik/http/services/Service03/weighted/services/1/name": "srvcB",
|
||||||
|
"traefik/http/services/Service03/weighted/services/1/weight": "42",
|
||||||
|
|
||||||
|
"traefik/http/middlewares/compressor/compress": "true",
|
||||||
|
"traefik/http/middlewares/striper/stripPrefix/prefixes/0": "foo",
|
||||||
|
"traefik/http/middlewares/striper/stripPrefix/prefixes/1": "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range data {
|
||||||
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
// wait for traefik
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
|
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
|
||||||
|
)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
var obtained api.RunTimeRepresentation
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
got, err := json.MarshalIndent(obtained, "", " ")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
|
||||||
|
|
||||||
|
if *updateExpected {
|
||||||
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := os.ReadFile(expectedJSON)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
if !bytes.Equal(expected, got) {
|
||||||
|
diff := difflib.UnifiedDiff{
|
||||||
|
FromFile: "Expected",
|
||||||
|
A: difflib.SplitLines(string(expected)),
|
||||||
|
ToFile: "Got",
|
||||||
|
B: difflib.SplitLines(string(got)),
|
||||||
|
Context: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
text, err := difflib.GetUnifiedDiffString(diff)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
log.Info().Msg(text)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,26 +4,22 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/structs"
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/kvtools/redis"
|
"github.com/kvtools/redis"
|
||||||
"github.com/kvtools/valkeyrie"
|
"github.com/kvtools/valkeyrie"
|
||||||
"github.com/kvtools/valkeyrie/store"
|
"github.com/kvtools/valkeyrie/store"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Redis test suites.
|
// Redis test suites.
|
||||||
|
@ -33,23 +29,18 @@ type RedisSuite struct {
|
||||||
redisEndpoints []string
|
redisEndpoints []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RedisSuite) TearDownSuite(c *check.C) {
|
func TestRedisSuite(t *testing.T) {
|
||||||
s.composeDown(c)
|
suite.Run(t, new(RedisSuite))
|
||||||
|
|
||||||
for _, filename := range []string{"sentinel1.conf", "sentinel2.conf", "sentinel3.conf"} {
|
|
||||||
err := os.Remove(filepath.Join(".", "resources", "compose", "config", filename))
|
|
||||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
|
||||||
c.Fatal("unable to clean configuration file for sentinel: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RedisSuite) setupStore(c *check.C) {
|
func (s *RedisSuite) SetupSuite() {
|
||||||
s.createComposeProject(c, "redis")
|
s.BaseSuite.SetupSuite()
|
||||||
s.composeUp(c)
|
|
||||||
|
s.createComposeProject("redis")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
s.redisEndpoints = []string{}
|
s.redisEndpoints = []string{}
|
||||||
s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP(c, "redis"), "6379"))
|
s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP("redis"), "6379"))
|
||||||
|
|
||||||
kv, err := valkeyrie.NewStore(
|
kv, err := valkeyrie.NewStore(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
|
@ -57,23 +48,23 @@ func (s *RedisSuite) setupStore(c *check.C) {
|
||||||
s.redisEndpoints,
|
s.redisEndpoints,
|
||||||
&redis.Config{},
|
&redis.Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
require.NoError(s.T(), err, "Cannot create store redis")
|
||||||
c.Fatal("Cannot create store redis: ", err)
|
|
||||||
}
|
|
||||||
s.kvClient = kv
|
s.kvClient = kv
|
||||||
|
|
||||||
// wait for redis
|
// wait for redis
|
||||||
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *RedisSuite) TearDownSuite() {
|
||||||
s.setupStore(c)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{
|
func (s *RedisSuite) TestSimpleConfiguration() {
|
||||||
|
file := s.adaptFile("fixtures/redis/simple.toml", struct{ RedisAddress string }{
|
||||||
RedisAddress: strings.Join(s.redisEndpoints, ","),
|
RedisAddress: strings.Join(s.redisEndpoints, ","),
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
@ -122,39 +113,35 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||||
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
|
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var obtained api.RunTimeRepresentation
|
var obtained api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
got, err := json.MarshalIndent(obtained, "", " ")
|
got, err := json.MarshalIndent(obtained, "", " ")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
|
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
|
||||||
|
|
||||||
if *updateExpected {
|
if *updateExpected {
|
||||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := os.ReadFile(expectedJSON)
|
expected, err := os.ReadFile(expectedJSON)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if !bytes.Equal(expected, got) {
|
if !bytes.Equal(expected, got) {
|
||||||
diff := difflib.UnifiedDiff{
|
diff := difflib.UnifiedDiff{
|
||||||
|
@ -166,170 +153,6 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
text, err := difflib.GetUnifiedDiffString(diff)
|
text, err := difflib.GetUnifiedDiffString(diff)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err, text)
|
||||||
c.Error(text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RedisSuite) setupSentinelStore(c *check.C) {
|
|
||||||
s.setupSentinelConfiguration(c, []string{"26379", "36379", "46379"})
|
|
||||||
|
|
||||||
s.createComposeProject(c, "redis_sentinel")
|
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.redisEndpoints = []string{
|
|
||||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel1"), "26379"),
|
|
||||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel2"), "36379"),
|
|
||||||
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel3"), "46379"),
|
|
||||||
}
|
|
||||||
|
|
||||||
kv, err := valkeyrie.NewStore(
|
|
||||||
context.Background(),
|
|
||||||
redis.StoreName,
|
|
||||||
s.redisEndpoints,
|
|
||||||
&redis.Config{
|
|
||||||
Sentinel: &redis.Sentinel{
|
|
||||||
MasterName: "mymaster",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
c.Fatal("Cannot create store redis sentinel")
|
|
||||||
}
|
|
||||||
s.kvClient = kv
|
|
||||||
|
|
||||||
// wait for redis
|
|
||||||
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RedisSuite) setupSentinelConfiguration(c *check.C, ports []string) {
|
|
||||||
for i, port := range ports {
|
|
||||||
templateValue := struct{ SentinelPort string }{SentinelPort: port}
|
|
||||||
|
|
||||||
// Load file
|
|
||||||
templateFile := "resources/compose/config/sentinel_template.conf"
|
|
||||||
tmpl, err := template.ParseFiles(templateFile)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
folder, prefix := filepath.Split(templateFile)
|
|
||||||
|
|
||||||
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
|
|
||||||
tmpFile, err := os.Create(fileName)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer tmpFile.Close()
|
|
||||||
|
|
||||||
model := structs.Map(templateValue)
|
|
||||||
model["SelfFilename"] = tmpFile.Name()
|
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
err = tmpFile.Sync()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RedisSuite) TestSentinelConfiguration(c *check.C) {
|
|
||||||
s.setupSentinelStore(c)
|
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/redis/sentinel.toml", struct{ RedisAddress string }{
|
|
||||||
RedisAddress: strings.Join(s.redisEndpoints, `","`),
|
|
||||||
})
|
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
data := map[string]string{
|
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
|
||||||
"traefik/http/routers/Router0/middlewares/0": "compressor",
|
|
||||||
"traefik/http/routers/Router0/middlewares/1": "striper",
|
|
||||||
"traefik/http/routers/Router0/service": "simplesvc",
|
|
||||||
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
|
|
||||||
"traefik/http/routers/Router0/priority": "42",
|
|
||||||
"traefik/http/routers/Router0/tls": "true",
|
|
||||||
|
|
||||||
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
|
|
||||||
"traefik/http/routers/Router1/priority": "42",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/0/main": "aaa.localhost",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/0/sans/0": "aaa.aaa.localhost",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/0/sans/1": "bbb.aaa.localhost",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/1/main": "bbb.localhost",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/1/sans/0": "aaa.bbb.localhost",
|
|
||||||
"traefik/http/routers/Router1/tls/domains/1/sans/1": "bbb.bbb.localhost",
|
|
||||||
"traefik/http/routers/Router1/entryPoints/0": "web",
|
|
||||||
"traefik/http/routers/Router1/service": "simplesvc",
|
|
||||||
|
|
||||||
"traefik/http/services/simplesvc/loadBalancer/servers/0/url": "http://10.0.1.1:8888",
|
|
||||||
"traefik/http/services/simplesvc/loadBalancer/servers/1/url": "http://10.0.1.1:8889",
|
|
||||||
|
|
||||||
"traefik/http/services/srvcA/loadBalancer/servers/0/url": "http://10.0.1.2:8888",
|
|
||||||
"traefik/http/services/srvcA/loadBalancer/servers/1/url": "http://10.0.1.2:8889",
|
|
||||||
|
|
||||||
"traefik/http/services/srvcB/loadBalancer/servers/0/url": "http://10.0.1.3:8888",
|
|
||||||
"traefik/http/services/srvcB/loadBalancer/servers/1/url": "http://10.0.1.3:8889",
|
|
||||||
|
|
||||||
"traefik/http/services/mirror/mirroring/service": "simplesvc",
|
|
||||||
"traefik/http/services/mirror/mirroring/mirrors/0/name": "srvcA",
|
|
||||||
"traefik/http/services/mirror/mirroring/mirrors/0/percent": "42",
|
|
||||||
"traefik/http/services/mirror/mirroring/mirrors/1/name": "srvcB",
|
|
||||||
"traefik/http/services/mirror/mirroring/mirrors/1/percent": "42",
|
|
||||||
|
|
||||||
"traefik/http/services/Service03/weighted/services/0/name": "srvcA",
|
|
||||||
"traefik/http/services/Service03/weighted/services/0/weight": "42",
|
|
||||||
"traefik/http/services/Service03/weighted/services/1/name": "srvcB",
|
|
||||||
"traefik/http/services/Service03/weighted/services/1/weight": "42",
|
|
||||||
|
|
||||||
"traefik/http/middlewares/compressor/compress": "true",
|
|
||||||
"traefik/http/middlewares/striper/stripPrefix/prefixes/0": "foo",
|
|
||||||
"traefik/http/middlewares/striper/stripPrefix/prefixes/1": "bar",
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range data {
|
|
||||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
|
||||||
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
|
|
||||||
)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
var obtained api.RunTimeRepresentation
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
got, err := json.MarshalIndent(obtained, "", " ")
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
|
|
||||||
|
|
||||||
if *updateExpected {
|
|
||||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected, err := os.ReadFile(expectedJSON)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, got) {
|
|
||||||
diff := difflib.UnifiedDiff{
|
|
||||||
FromFile: "Expected",
|
|
||||||
A: difflib.SplitLines(string(expected)),
|
|
||||||
ToFile: "Got",
|
|
||||||
B: difflib.SplitLines(string(got)),
|
|
||||||
Context: 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
text, err := difflib.GetUnifiedDiffString(diff)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
c.Error(text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ services:
|
||||||
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:
|
||||||
|
@ -94,8 +94,3 @@ services:
|
||||||
traefik.http.routers.rt-preflightCORS.middlewares: preflightCORS
|
traefik.http.routers.rt-preflightCORS.middlewares: preflightCORS
|
||||||
traefik.http.middlewares.preflightCORS.headers.accessControlAllowMethods: OPTIONS, GET
|
traefik.http.middlewares.preflightCORS.headers.accessControlAllowMethods: OPTIONS, GET
|
||||||
traefik.http.services.preflightCORS.loadbalancer.server.port: 80
|
traefik.http.services.preflightCORS.loadbalancer.server.port: 80
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -34,8 +34,3 @@ services:
|
||||||
traefik.http.routers.rt4.middlewares: wl4
|
traefik.http.routers.rt4.middlewares: wl4
|
||||||
traefik.http.middlewares.wl4.ipallowlist.sourceRange: 8.8.8.8
|
traefik.http.middlewares.wl4.ipallowlist.sourceRange: 8.8.8.8
|
||||||
traefik.http.middlewares.wl4.ipallowlist.ipStrategy.excludedIPs: 10.0.0.1,10.0.0.2
|
traefik.http.middlewares.wl4.ipallowlist.ipStrategy.excludedIPs: 10.0.0.1,10.0.0.2
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -11,8 +11,3 @@ services:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
labels:
|
labels:
|
||||||
traefik.enable: false
|
traefik.enable: false
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -4,8 +4,3 @@ services:
|
||||||
image: consul:1.6
|
image: consul:1.6
|
||||||
whoami:
|
whoami:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,11 +2,24 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
consul:
|
consul:
|
||||||
image: consul:1.6.2
|
image: consul:1.6.2
|
||||||
command: agent -server -bootstrap -ui -client 0.0.0.0 -hcl 'connect { enabled = true }'
|
command:
|
||||||
|
- agent
|
||||||
|
- -server
|
||||||
|
- -bootstrap
|
||||||
|
- -ui
|
||||||
|
- -client
|
||||||
|
- 0.0.0.0
|
||||||
|
- -hcl
|
||||||
|
- 'connect { enabled = true }'
|
||||||
|
|
||||||
consul-agent:
|
consul-agent:
|
||||||
image: consul:1.6.2
|
image: consul:1.6.2
|
||||||
command: agent -retry-join consul -client 0.0.0.0
|
command:
|
||||||
|
- agent
|
||||||
|
- -retry-join
|
||||||
|
- consul
|
||||||
|
- -client
|
||||||
|
- 0.0.0.0
|
||||||
|
|
||||||
whoami1:
|
whoami1:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
@ -30,8 +43,3 @@ services:
|
||||||
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
|
|
||||||
|
|
|
@ -36,8 +36,3 @@ services:
|
||||||
labels:
|
labels:
|
||||||
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
|
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
|
||||||
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
|
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
nginx1:
|
nginx1:
|
||||||
image: nginx:1.13.8-alpine
|
image: nginx:1.25.3-alpine3.18
|
||||||
|
|
||||||
nginx2:
|
nginx2:
|
||||||
image: nginx:1.13.8-alpine
|
image: nginx:1.25.3-alpine3.18
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
etcd:
|
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
|
||||||
networks:
|
- --listen-client-urls
|
||||||
default:
|
- http://0.0.0.0:2379
|
||||||
name: traefik-test-network
|
- --advertise-client-urls
|
||||||
external: true
|
- http://0.0.0.0:2380
|
||||||
|
|
|
@ -14,8 +14,3 @@ services:
|
||||||
|
|
||||||
whoami5:
|
whoami5:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -11,8 +11,3 @@ services:
|
||||||
|
|
||||||
whoami4:
|
whoami4:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -6,8 +6,3 @@ services:
|
||||||
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
|
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
image: rancher/k3s:v1.23.17-k3s1
|
image: rancher/k3s:v1.20.15-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 --bind-address=server --tls-san=server
|
privileged: true
|
||||||
|
command:
|
||||||
|
- server
|
||||||
|
- --disable-agent
|
||||||
|
- --disable=coredns
|
||||||
|
- --disable=servicelb
|
||||||
|
- --disable=traefik
|
||||||
|
- --disable=local-storage
|
||||||
|
- --disable=metrics-server
|
||||||
|
- --log=/output/k3s.log
|
||||||
|
- --bind-address=server
|
||||||
|
- --tls-san=server
|
||||||
|
- --tls-san=172.31.42.3
|
||||||
|
- --tls-san=172.31.42.4
|
||||||
environment:
|
environment:
|
||||||
K3S_CLUSTER_SECRET: somethingtotallyrandom
|
K3S_CLUSTER_SECRET: somethingtotallyrandom
|
||||||
|
K3S_TOKEN: somethingtotallyrandom
|
||||||
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
|
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
|
||||||
K3S_KUBECONFIG_MODE: 666
|
K3S_KUBECONFIG_MODE: 666
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -12,13 +26,9 @@ services:
|
||||||
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
|
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
|
||||||
|
|
||||||
node:
|
node:
|
||||||
image: rancher/k3s:v1.23.17-k3s1
|
image: rancher/k3s:v1.20.15-k3s1
|
||||||
privileged: true
|
privileged: true
|
||||||
environment:
|
environment:
|
||||||
|
K3S_TOKEN: somethingtotallyrandom
|
||||||
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
|
|
||||||
|
|
|
@ -3,12 +3,9 @@ services:
|
||||||
whoami1:
|
whoami1:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
labels:
|
labels:
|
||||||
traefik.http.Routers.RouterMini.Rule: PathPrefix(`/whoami`)
|
traefik.http.routers.router-mini.Rule: PathPrefix(`/whoami`)
|
||||||
|
traefik.http.routers.router-mini.service: service-mini
|
||||||
|
traefik.http.services.service-mini.loadbalancer.server.port: 80
|
||||||
traefik.enable: true
|
traefik.enable: true
|
||||||
deploy:
|
deploy:
|
||||||
replicas: 2
|
replicas: 2
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,14 +2,12 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
pebble:
|
pebble:
|
||||||
image: letsencrypt/pebble:v2.3.1
|
image: letsencrypt/pebble:v2.3.1
|
||||||
command: pebble --dnsserver traefik:5053
|
command:
|
||||||
|
- pebble
|
||||||
|
- --dnsserver
|
||||||
|
- host.docker.internal:5053
|
||||||
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
|
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
haproxy:
|
|
||||||
image: haproxy:2.2
|
|
||||||
volumes:
|
|
||||||
- ./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
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami1:
|
whoami1:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
image: redis:5.0
|
image: redis:5.0
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -3,59 +3,51 @@ services:
|
||||||
master:
|
master:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-master
|
container_name: redis-master
|
||||||
command: redis-server --port 6380
|
command:
|
||||||
ports:
|
- redis-server
|
||||||
- 6380:6380
|
- --port
|
||||||
healthcheck:
|
- 6380
|
||||||
test: redis-cli -p 6380 ping
|
|
||||||
node1:
|
node1:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-node-1
|
container_name: redis-node-1
|
||||||
ports:
|
command:
|
||||||
- 6381:6381
|
- redis-server
|
||||||
command: redis-server --port 6381 --slaveof redis-master 6380
|
- --port
|
||||||
healthcheck:
|
- 6381
|
||||||
test: redis-cli -p 6381 ping
|
- --slaveof
|
||||||
|
- redis-master
|
||||||
|
- 6380
|
||||||
node2:
|
node2:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-node-2
|
container_name: redis-node-2
|
||||||
ports:
|
command:
|
||||||
- 6382:6382
|
- redis-server
|
||||||
command: redis-server --port 6382 --slaveof redis-master 6380
|
- --port
|
||||||
healthcheck:
|
- 6382
|
||||||
test: redis-cli -p 6382 ping
|
- --slaveof
|
||||||
|
- redis-master
|
||||||
|
- 6380
|
||||||
sentinel1:
|
sentinel1:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-sentinel-1
|
container_name: redis-sentinel-1
|
||||||
ports:
|
command:
|
||||||
- 26379:26379
|
- redis-sentinel
|
||||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel1.conf
|
- /usr/local/etc/redis/conf/sentinel1.conf
|
||||||
healthcheck:
|
|
||||||
test: redis-cli -p 26379 ping
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||||
sentinel2:
|
sentinel2:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-sentinel-2
|
container_name: redis-sentinel-2
|
||||||
ports:
|
command:
|
||||||
- 36379:26379
|
- redis-sentinel
|
||||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel2.conf
|
- /usr/local/etc/redis/conf/sentinel2.conf
|
||||||
healthcheck:
|
|
||||||
test: redis-cli -p 36379 ping
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||||
sentinel3:
|
sentinel3:
|
||||||
image: redis
|
image: redis
|
||||||
container_name: redis-sentinel-3
|
container_name: redis-sentinel-3
|
||||||
ports:
|
command:
|
||||||
- 46379:26379
|
- redis-sentinel
|
||||||
command: redis-sentinel /usr/local/etc/redis/conf/sentinel3.conf
|
- /usr/local/etc/redis/conf/sentinel3.conf
|
||||||
healthcheck:
|
|
||||||
test: redis-cli -p 46379 ping
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./resources/compose/config:/usr/local/etc/redis/conf
|
- ./resources/compose/config:/usr/local/etc/redis/conf
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami:
|
whoami:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami1:
|
whoami1:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami:
|
whoami:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -5,8 +5,3 @@ services:
|
||||||
|
|
||||||
whoami2:
|
whoami2:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -9,9 +9,5 @@ services:
|
||||||
cap_add: # Required for tailscale to work
|
cap_add: # Required for tailscale to work
|
||||||
- net_admin
|
- net_admin
|
||||||
- sys_module
|
- sys_module
|
||||||
command: tailscaled
|
command:
|
||||||
|
- tailscaled
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,38 +2,58 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami-a:
|
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
whoami-ab:
|
whoami-ab:
|
||||||
image: traefik/whoamitcp
|
image: traefik/whoamitcp
|
||||||
command: -name whoami-ab -certFile /certs/whoami-b.crt -keyFile /certs/whoami-b.key
|
command:
|
||||||
|
- -name
|
||||||
|
- whoami-ab
|
||||||
|
- -certFile
|
||||||
|
- /certs/whoami-b.crt
|
||||||
|
- -keyFile
|
||||||
|
- /certs/whoami-b.key
|
||||||
volumes:
|
volumes:
|
||||||
- ./fixtures/tcp:/certs
|
- ./fixtures/tcp:/certs
|
||||||
|
|
||||||
whoami-no-cert:
|
whoami-no-cert:
|
||||||
image: traefik/whoamitcp
|
image: traefik/whoamitcp
|
||||||
command: -name whoami-no-cert
|
command:
|
||||||
|
- -name
|
||||||
|
- whoami-no-cert
|
||||||
|
|
||||||
whoami-no-tls:
|
whoami-no-tls:
|
||||||
image: traefik/whoamitcp
|
image: traefik/whoamitcp
|
||||||
command: -name whoami-no-tls
|
command:
|
||||||
|
- -name
|
||||||
|
- whoami-no-tls
|
||||||
|
|
||||||
whoami:
|
whoami:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
whoami-banner:
|
whoami-banner:
|
||||||
image: traefik/whoamitcp
|
image: traefik/whoamitcp
|
||||||
command: -name whoami-banner --banner
|
command:
|
||||||
|
- -name
|
||||||
networks:
|
- whoami-banner
|
||||||
default:
|
- --banner
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -5,8 +5,3 @@ services:
|
||||||
environment:
|
environment:
|
||||||
PROTO: http
|
PROTO: http
|
||||||
PORT: 9000
|
PORT: 9000
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -7,8 +7,3 @@ services:
|
||||||
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
|
|
||||||
|
|
|
@ -12,8 +12,3 @@ services:
|
||||||
- ./fixtures/tracing/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
|
- ./fixtures/tracing/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
|
||||||
whoami:
|
whoami:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -2,20 +2,21 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
whoami-a:
|
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
|
|
||||||
|
|
|
@ -2,8 +2,3 @@ version: "3.8"
|
||||||
services:
|
services:
|
||||||
zookeeper:
|
zookeeper:
|
||||||
image: zookeeper:3.5
|
image: zookeeper:3.5
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: traefik-test-network
|
|
||||||
external: true
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
global
|
|
||||||
maxconn 4096
|
|
||||||
|
|
||||||
defaults
|
|
||||||
log global
|
|
||||||
mode http
|
|
||||||
retries 3
|
|
||||||
option redispatch
|
|
||||||
maxconn 2000
|
|
||||||
timeout connect 5000
|
|
||||||
timeout client 50000
|
|
||||||
timeout server 50000
|
|
||||||
|
|
||||||
frontend TestServerTest
|
|
||||||
bind 0.0.0.0:80
|
|
||||||
mode tcp
|
|
||||||
default_backend TestServerNodes
|
|
||||||
|
|
||||||
frontend TestServerTestV2
|
|
||||||
bind 0.0.0.0:81
|
|
||||||
mode tcp
|
|
||||||
default_backend TestServerNodesV2
|
|
||||||
|
|
||||||
backend TestServerNodes
|
|
||||||
mode tcp
|
|
||||||
server TestServer01 traefik:8000 send-proxy
|
|
||||||
|
|
||||||
backend TestServerNodesV2
|
|
||||||
mode tcp
|
|
||||||
server TestServer01 traefik:8000 send-proxy-v2
|
|
|
@ -5,14 +5,15 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RestSuite struct {
|
type RestSuite struct {
|
||||||
|
@ -20,28 +21,33 @@ type RestSuite struct {
|
||||||
whoamiAddr string
|
whoamiAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RestSuite) SetUpSuite(c *check.C) {
|
func TestRestSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "rest")
|
suite.Run(t, new(RestSuite))
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
|
func (s *RestSuite) SetupSuite() {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/rest/simple.toml"))
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
defer display(c)
|
s.createComposeProject("rest")
|
||||||
err := cmd.Start()
|
s.composeUp()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP("whoami1"), "80")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RestSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RestSuite) TestSimpleConfigurationInsecure() {
|
||||||
|
s.traefikCmd(withConfigFile("fixtures/rest/simple.toml"))
|
||||||
|
|
||||||
// wait for Traefik
|
// wait for Traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// 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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -105,47 +111,41 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
data, err := json.Marshal(test.config)
|
data, err := json.Marshal(test.config)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
|
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.DefaultClient.Do(request)
|
response, err := http.DefaultClient.Do(request)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 3*time.Second, try.BodyContains(test.ruleMatch))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 3*time.Second, try.BodyContains(test.ruleMatch))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *RestSuite) TestSimpleConfiguration() {
|
||||||
file := s.adaptFile(c, "fixtures/rest/simple_secure.toml", struct{}{})
|
file := s.adaptFile("fixtures/rest/simple_secure.toml", struct{}{})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// 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/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2000*time.Millisecond, try.BodyContains("PathPrefix(`/secure`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2000*time.Millisecond, try.BodyContains("PathPrefix(`/secure`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", strings.NewReader("{}"))
|
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", strings.NewReader("{}"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.DefaultClient.Do(request)
|
response, err := http.DefaultClient.Do(request)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusNotFound)
|
assert.Equal(s.T(), http.StatusNotFound, response.StatusCode)
|
||||||
|
|
||||||
testCase := []struct {
|
testCase := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -209,19 +209,19 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
for _, test := range testCase {
|
for _, test := range testCase {
|
||||||
data, err := json.Marshal(test.config)
|
data, err := json.Marshal(test.config)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/secure/api/providers/rest", bytes.NewReader(data))
|
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/secure/api/providers/rest", bytes.NewReader(data))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.DefaultClient.Do(request)
|
response, err := http.DefaultClient.Do(request)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, 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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@ package integration
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RetrySuite struct {
|
type RetrySuite struct {
|
||||||
|
@ -17,96 +18,86 @@ type RetrySuite struct {
|
||||||
whoamiIP string
|
whoamiIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetrySuite) SetUpSuite(c *check.C) {
|
func TestRetrySuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "retry")
|
suite.Run(t, new(RetrySuite))
|
||||||
s.composeUp(c)
|
|
||||||
|
|
||||||
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetrySuite) TestRetry(c *check.C) {
|
func (s *RetrySuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
s.BaseSuite.SetupSuite()
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.createComposeProject("retry")
|
||||||
defer display(c)
|
s.composeUp()
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
s.whoamiIP = s.getComposeServiceIP("whoami")
|
||||||
c.Assert(err, checker.IsNil)
|
}
|
||||||
|
|
||||||
|
func (s *RetrySuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RetrySuite) TestRetry() {
|
||||||
|
file := s.adaptFile("fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.Get("http://127.0.0.1:8000/")
|
response, err := http.Get("http://127.0.0.1:8000/")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// The test only verifies that the retry middleware makes sure that the working service is eventually reached.
|
// The test only verifies that the retry middleware makes sure that the working service is eventually reached.
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetrySuite) TestRetryBackoff(c *check.C) {
|
func (s *RetrySuite) TestRetryBackoff() {
|
||||||
file := s.adaptFile(c, "fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
file := s.adaptFile("fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.Get("http://127.0.0.1:8000/")
|
response, err := http.Get("http://127.0.0.1:8000/")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// The test only verifies that the retry middleware allows finally to reach the working service.
|
// The test only verifies that the retry middleware allows finally to reach the working service.
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
|
func (s *RetrySuite) TestRetryWebsocket() {
|
||||||
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
file := s.adaptFile("fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// The test only verifies that the retry middleware makes sure that the working service is eventually reached.
|
// The test only verifies that the retry middleware makes sure that the working service is eventually reached.
|
||||||
_, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
_, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
|
||||||
|
|
||||||
// The test verifies a second time that the working service is eventually reached.
|
// The test verifies a second time that the working service is eventually reached.
|
||||||
_, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
_, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetrySuite) TestRetryWithStripPrefix(c *check.C) {
|
func (s *RetrySuite) TestRetryWithStripPrefix() {
|
||||||
file := s.adaptFile(c, "fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
file := s.adaptFile("fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
response, err := http.Get("http://127.0.0.1:8000/test")
|
response, err := http.Get("http://127.0.0.1:8000/test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
c.Assert(string(body), checker.Contains, "GET / HTTP/1.1")
|
assert.Contains(s.T(), string(body), "GET / HTTP/1.1")
|
||||||
c.Assert(string(body), checker.Contains, "X-Forwarded-Prefix: /test")
|
assert.Contains(s.T(), string(body), "X-Forwarded-Prefix: /test")
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,63 +5,69 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TCPSuite struct{ BaseSuite }
|
type TCPSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *TCPSuite) SetUpSuite(c *check.C) {
|
func TestTCPSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "tcp")
|
suite.Run(t, new(TCPSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestMixed(c *check.C) {
|
func (s *TCPSuite) SetupSuite() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/mixed.toml", struct {
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("tcp")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TCPSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TCPSuite) TestMixed() {
|
||||||
|
file := s.adaptFile("fixtures/tcp/mixed.toml", struct {
|
||||||
Whoami string
|
Whoami string
|
||||||
WhoamiA string
|
WhoamiA string
|
||||||
WhoamiB string
|
WhoamiB string
|
||||||
WhoamiNoCert string
|
WhoamiNoCert string
|
||||||
}{
|
}{
|
||||||
Whoami: "http://" + s.getComposeServiceIP(c, "whoami") + ":80",
|
Whoami: "http://" + s.getComposeServiceIP("whoami") + ":80",
|
||||||
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
|
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
|
||||||
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
|
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
|
||||||
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080",
|
WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Traefik passes through, termination handled by whoami-a
|
// Traefik passes through, termination handled by whoami-a
|
||||||
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-a")
|
assert.Contains(s.T(), out, "whoami-a")
|
||||||
|
|
||||||
// Traefik passes through, termination handled by whoami-b
|
// Traefik passes through, termination handled by whoami-b
|
||||||
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-b")
|
assert.Contains(s.T(), out, "whoami-b")
|
||||||
|
|
||||||
// Termination handled by traefik
|
// Termination handled by traefik
|
||||||
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
|
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
assert.Contains(s.T(), out, "whoami-no-cert")
|
||||||
|
|
||||||
tr1 := &http.Transport{
|
tr1 := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
|
@ -69,174 +75,143 @@ func (s *TCPSuite) TestMixed(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/whoami/", nil)
|
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/whoami/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusOK))
|
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/not-found/", nil)
|
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/not-found/", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusNotFound))
|
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8093/test", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8093/test", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.GetRequest("http://127.0.0.1:8093/not-found", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.GetRequest("http://127.0.0.1:8093/not-found", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestTLSOptions(c *check.C) {
|
func (s *TCPSuite) TestTLSOptions() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/multi-tls-options.toml", struct {
|
file := s.adaptFile("fixtures/tcp/multi-tls-options.toml", struct {
|
||||||
WhoamiNoCert string
|
WhoamiNoCert string
|
||||||
}{
|
}{
|
||||||
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080",
|
WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
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-c.test`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Check that we can use a client tls version <= 1.2 with hostSNI 'whoami-c.test'
|
// Check that we can use a client tls version <= 1.2 with hostSNI 'whoami-c.test'
|
||||||
out, err := guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-c.test", true, tls.VersionTLS12)
|
out, err := guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-c.test", true, tls.VersionTLS12)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
assert.Contains(s.T(), out, "whoami-no-cert")
|
||||||
|
|
||||||
// Check that we can use a client tls version <= 1.3 with hostSNI 'whoami-d.test'
|
// Check that we can use a client tls version <= 1.3 with hostSNI 'whoami-d.test'
|
||||||
out, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS13)
|
out, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS13)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
assert.Contains(s.T(), out, "whoami-no-cert")
|
||||||
|
|
||||||
// Check that we cannot use a client tls version <= 1.2 with hostSNI 'whoami-d.test'
|
// Check that we cannot use a client tls version <= 1.2 with hostSNI 'whoami-d.test'
|
||||||
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
|
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
|
||||||
c.Assert(err, checker.NotNil)
|
assert.ErrorContains(s.T(), err, "protocol version not supported")
|
||||||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
|
||||||
|
|
||||||
// Check that we can't reach a route with an invalid mTLS configuration.
|
// Check that we can't reach a route with an invalid mTLS configuration.
|
||||||
conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
|
conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
|
||||||
ServerName: "whoami-i.test",
|
ServerName: "whoami-i.test",
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
})
|
})
|
||||||
c.Assert(conn, checker.IsNil)
|
assert.Nil(s.T(), conn)
|
||||||
c.Assert(err, checker.NotNil)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
|
func (s *TCPSuite) TestNonTLSFallback() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/non-tls-fallback.toml", struct {
|
file := s.adaptFile("fixtures/tcp/non-tls-fallback.toml", struct {
|
||||||
WhoamiA string
|
WhoamiA string
|
||||||
WhoamiB string
|
WhoamiB string
|
||||||
WhoamiNoCert string
|
WhoamiNoCert string
|
||||||
WhoamiNoTLS string
|
WhoamiNoTLS string
|
||||||
}{
|
}{
|
||||||
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
|
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
|
||||||
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
|
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
|
||||||
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080",
|
WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
|
||||||
WhoamiNoTLS: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080",
|
WhoamiNoTLS: s.getComposeServiceIP("whoami-no-tls") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Traefik passes through, termination handled by whoami-a
|
// Traefik passes through, termination handled by whoami-a
|
||||||
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-a")
|
assert.Contains(s.T(), out, "whoami-a")
|
||||||
|
|
||||||
// Traefik passes through, termination handled by whoami-b
|
// Traefik passes through, termination handled by whoami-b
|
||||||
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-b")
|
assert.Contains(s.T(), out, "whoami-b")
|
||||||
|
|
||||||
// Termination handled by traefik
|
// Termination handled by traefik
|
||||||
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
|
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-cert")
|
assert.Contains(s.T(), out, "whoami-no-cert")
|
||||||
|
|
||||||
out, err = guessWho("127.0.0.1:8093", "", false)
|
out, err = guessWho("127.0.0.1:8093", "", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-tls")
|
assert.Contains(s.T(), out, "whoami-no-tls")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestNonTlsTcp(c *check.C) {
|
func (s *TCPSuite) TestNonTlsTcp() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/non-tls.toml", struct {
|
file := s.adaptFile("fixtures/tcp/non-tls.toml", struct {
|
||||||
WhoamiNoTLS string
|
WhoamiNoTLS string
|
||||||
}{
|
}{
|
||||||
WhoamiNoTLS: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080",
|
WhoamiNoTLS: s.getComposeServiceIP("whoami-no-tls") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Traefik will forward every requests on the given port to whoami-no-tls
|
// Traefik will forward every requests on the given port to whoami-no-tls
|
||||||
out, err := guessWho("127.0.0.1:8093", "", false)
|
out, err := guessWho("127.0.0.1:8093", "", false)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-no-tls")
|
assert.Contains(s.T(), out, "whoami-no-tls")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestCatchAllNoTLS(c *check.C) {
|
func (s *TCPSuite) TestCatchAllNoTLS() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls.toml", struct {
|
file := s.adaptFile("fixtures/tcp/catch-all-no-tls.toml", struct {
|
||||||
WhoamiBannerAddress string
|
WhoamiBannerAddress string
|
||||||
}{
|
}{
|
||||||
WhoamiBannerAddress: s.getComposeServiceIP(c, "whoami-banner") + ":8080",
|
WhoamiBannerAddress: s.getComposeServiceIP("whoami-banner") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Traefik will forward every requests on the given port to whoami-no-tls
|
// Traefik will forward every requests on the given port to whoami-no-tls
|
||||||
out, err := welcome("127.0.0.1:8093")
|
out, err := welcome("127.0.0.1:8093")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "Welcome")
|
assert.Contains(s.T(), out, "Welcome")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) {
|
func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls-with-https.toml", struct {
|
file := s.adaptFile("fixtures/tcp/catch-all-no-tls-with-https.toml", struct {
|
||||||
WhoamiNoTLSAddress string
|
WhoamiNoTLSAddress string
|
||||||
WhoamiURL string
|
WhoamiURL string
|
||||||
}{
|
}{
|
||||||
WhoamiNoTLSAddress: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080",
|
WhoamiNoTLSAddress: s.getComposeServiceIP("whoami-no-tls") + ":8080",
|
||||||
WhoamiURL: "http://" + s.getComposeServiceIP(c, "whoami") + ":80",
|
WhoamiURL: "http://" + s.getComposeServiceIP("whoami") + ":80",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "https://127.0.0.1:8093/test", nil)
|
req := httptest.NewRequest(http.MethodGet, "https://127.0.0.1:8093/test", nil)
|
||||||
req.RequestURI = ""
|
req.RequestURI = ""
|
||||||
|
@ -246,64 +221,52 @@ func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) {
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
}, try.StatusCodeIs(http.StatusOK))
|
}, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestMiddlewareAllowList(c *check.C) {
|
func (s *TCPSuite) TestMiddlewareAllowList() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/ipallowlist.toml", struct {
|
file := s.adaptFile("fixtures/tcp/ip-allowlist.toml", struct {
|
||||||
WhoamiA string
|
WhoamiA string
|
||||||
WhoamiB string
|
WhoamiB string
|
||||||
}{
|
}{
|
||||||
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
|
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
|
||||||
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
|
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
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)
|
require.NoError(s.T(), err)
|
||||||
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-a.test`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
// Traefik not passes through, ipAllowList closes connection
|
// Traefik not passes through, ipAllowList closes connection
|
||||||
_, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
_, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
|
||||||
c.Assert(err, checker.ErrorMatches, "EOF")
|
assert.ErrorIs(s.T(), err, io.EOF)
|
||||||
|
|
||||||
// Traefik passes through, termination handled by whoami-b
|
// Traefik passes through, termination handled by whoami-b
|
||||||
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(out, checker.Contains, "whoami-b")
|
assert.Contains(s.T(), out, "whoami-b")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPSuite) TestWRR(c *check.C) {
|
func (s *TCPSuite) TestWRR() {
|
||||||
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct {
|
file := s.adaptFile("fixtures/tcp/wrr.toml", struct {
|
||||||
WhoamiB string
|
WhoamiB string
|
||||||
WhoamiAB string
|
WhoamiAB string
|
||||||
}{
|
}{
|
||||||
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
|
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
|
||||||
WhoamiAB: s.getComposeServiceIP(c, "whoami-ab") + ":8080",
|
WhoamiAB: s.getComposeServiceIP("whoami-ab") + ":8080",
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
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)
|
require.NoError(s.T(), err)
|
||||||
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{}
|
call := map[string]int{}
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
// Traefik passes through, termination handled by whoami-b or whoami-bb
|
// Traefik passes through, termination handled by whoami-b or whoami-bb
|
||||||
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
switch {
|
switch {
|
||||||
case strings.Contains(out, "whoami-b"):
|
case strings.Contains(out, "whoami-b"):
|
||||||
call["whoami-b"]++
|
call["whoami-b"]++
|
||||||
|
@ -315,7 +278,7 @@ func (s *TCPSuite) TestWRR(c *check.C) {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-b": 3, "whoami-ab": 1})
|
assert.EqualValues(s.T(), call, map[string]int{"whoami-b": 3, "whoami-ab": 1})
|
||||||
}
|
}
|
||||||
|
|
||||||
func welcome(addr string) (string, error) {
|
func welcome(addr string) (string, error) {
|
||||||
|
|
|
@ -4,47 +4,53 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TimeoutSuite struct{ BaseSuite }
|
type TimeoutSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *TimeoutSuite) SetUpSuite(c *check.C) {
|
func TestTimeoutSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "timeout")
|
suite.Run(t, new(TimeoutSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
|
func (s *TimeoutSuite) SetupSuite() {
|
||||||
timeoutEndpointIP := s.getComposeServiceIP(c, "timeoutEndpoint")
|
s.BaseSuite.SetupSuite()
|
||||||
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct{ TimeoutEndpoint string }{timeoutEndpointIP})
|
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.createComposeProject("timeout")
|
||||||
defer display(c)
|
s.composeUp()
|
||||||
err := cmd.Start()
|
}
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Path(`/dialTimeout`)"))
|
func (s *TimeoutSuite) TearDownSuite() {
|
||||||
c.Assert(err, checker.IsNil)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TimeoutSuite) TestForwardingTimeouts() {
|
||||||
|
timeoutEndpointIP := s.getComposeServiceIP("timeoutEndpoint")
|
||||||
|
file := s.adaptFile("fixtures/timeout/forwarding_timeouts.toml", struct{ TimeoutEndpoint string }{timeoutEndpointIP})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Path(`/dialTimeout`)"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// This simulates a DialTimeout when connecting to the backend server.
|
// This simulates a DialTimeout when connecting to the backend server.
|
||||||
response, err := http.Get("http://127.0.0.1:8000/dialTimeout")
|
response, err := http.Get("http://127.0.0.1:8000/dialTimeout")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
|
assert.Equal(s.T(), http.StatusGatewayTimeout, response.StatusCode)
|
||||||
|
|
||||||
// Check that timeout service is available
|
// Check that timeout service is available
|
||||||
statusURL := fmt.Sprintf("http://%s/statusTest?status=200",
|
statusURL := fmt.Sprintf("http://%s/statusTest?status=200",
|
||||||
net.JoinHostPort(timeoutEndpointIP, "9000"))
|
net.JoinHostPort(timeoutEndpointIP, "9000"))
|
||||||
c.Assert(try.GetRequest(statusURL, 60*time.Second, try.StatusCodeIs(http.StatusOK)), checker.IsNil)
|
assert.NoError(s.T(), try.GetRequest(statusURL, 60*time.Second, try.StatusCodeIs(http.StatusOK)))
|
||||||
|
|
||||||
// This simulates a ResponseHeaderTimeout.
|
// This simulates a ResponseHeaderTimeout.
|
||||||
response, err = http.Get("http://127.0.0.1:8000/responseHeaderTimeout?sleep=1000")
|
response, err = http.Get("http://127.0.0.1:8000/responseHeaderTimeout?sleep=1000")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
|
assert.Equal(s.T(), http.StatusGatewayTimeout, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -19,20 +21,30 @@ const (
|
||||||
|
|
||||||
type TLSClientHeadersSuite struct{ BaseSuite }
|
type TLSClientHeadersSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) {
|
func TestTLSClientHeadersSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "tlsclientheaders")
|
suite.Run(t, new(TLSClientHeadersSuite))
|
||||||
s.composeUp(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
func (s *TLSClientHeadersSuite) SetupSuite() {
|
||||||
rootCertContent, err := os.ReadFile(rootCertPath)
|
s.BaseSuite.SetupSuite()
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
serverCertContent, err := os.ReadFile(certPemPath)
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
ServerKeyContent, err := os.ReadFile(certKeyPath)
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/tlsclientheaders/simple.toml", struct {
|
s.createComposeProject("tlsclientheaders")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TLSClientHeadersSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TLSClientHeadersSuite) TestTLSClientHeaders() {
|
||||||
|
rootCertContent, err := os.ReadFile(rootCertPath)
|
||||||
|
assert.NoError(s.T(), err)
|
||||||
|
serverCertContent, err := os.ReadFile(certPemPath)
|
||||||
|
assert.NoError(s.T(), err)
|
||||||
|
ServerKeyContent, err := os.ReadFile(certKeyPath)
|
||||||
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
|
file := s.adaptFile("fixtures/tlsclientheaders/simple.toml", struct {
|
||||||
RootCertContent string
|
RootCertContent string
|
||||||
ServerCertContent string
|
ServerCertContent string
|
||||||
ServerKeyContent string
|
ServerKeyContent string
|
||||||
|
@ -41,22 +53,17 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
||||||
ServerCertContent: string(serverCertContent),
|
ServerCertContent: string(serverCertContent),
|
||||||
ServerKeyContent: string(ServerKeyContent),
|
ServerKeyContent: string(ServerKeyContent),
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
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", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)"))
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil)
|
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
|
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
|
@ -66,5 +73,5 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA+bBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x/lhKLlv4amSDHDx8Cmz/6blOUM8XOfMRW1xx++AgChWN9dx/kf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL/aaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V+XyVkIPmzfPrRV+4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW+B80gMhpLdxXD+KO0/Wgc5xpB/wLSirNtRQyxAa3+EEcIwJv/wdh8EyjlDLSpFm/8ghntrKhkOfIOPDFE41M5HNfx/Fuh5btKEenOL/XdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k+3QydO1lqB5JJmcrs59BEveTqaJ3eeh/0I4OOab6OkTTZ0JNjJp1573oxO+fce/bfGud8xHY5gSN9huU7U6RsgvO7Dhmal/sDNl8XC8oU90hVDVXZdA7ewh4jjaoIv"))
|
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA+bBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x/lhKLlv4amSDHDx8Cmz/6blOUM8XOfMRW1xx++AgChWN9dx/kf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL/aaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V+XyVkIPmzfPrRV+4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW+B80gMhpLdxXD+KO0/Wgc5xpB/wLSirNtRQyxAa3+EEcIwJv/wdh8EyjlDLSpFm/8ghntrKhkOfIOPDFE41M5HNfx/Fuh5btKEenOL/XdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k+3QydO1lqB5JJmcrs59BEveTqaJ3eeh/0I4OOab6OkTTZ0JNjJp1573oxO+fce/bfGud8xHY5gSN9huU7U6RsgvO7Dhmal/sDNl8XC8oU90hVDVXZdA7ewh4jjaoIv"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TracingSuite struct {
|
type TracingSuite struct {
|
||||||
|
@ -23,6 +25,10 @@ type TracingSuite struct {
|
||||||
otelCollectorIP string
|
otelCollectorIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTracingSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(TracingSuite))
|
||||||
|
}
|
||||||
|
|
||||||
type TracingTemplate struct {
|
type TracingTemplate struct {
|
||||||
WhoamiIP string
|
WhoamiIP string
|
||||||
WhoamiPort int
|
WhoamiPort int
|
||||||
|
@ -31,59 +37,60 @@ type TracingTemplate struct {
|
||||||
IsHTTP bool
|
IsHTTP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) SetUpSuite(c *check.C) {
|
func (s *TracingSuite) SetupSuite() {
|
||||||
s.createComposeProject(c, "tracing")
|
s.BaseSuite.SetupSuite()
|
||||||
s.composeUp(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TracingSuite) SetUpTest(c *check.C) {
|
s.createComposeProject("tracing")
|
||||||
s.composeUp(c, "tempo", "otel-collector", "whoami")
|
s.composeUp()
|
||||||
|
|
||||||
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
|
s.whoamiIP = s.getComposeServiceIP("whoami")
|
||||||
s.whoamiPort = 80
|
s.whoamiPort = 80
|
||||||
|
|
||||||
// Wait for whoami to turn ready.
|
// Wait for whoami to turn ready.
|
||||||
err := try.GetRequest("http://"+s.whoamiIP+":80", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
err := try.GetRequest("http://"+s.whoamiIP+":80", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
s.tempoIP = s.getComposeServiceIP(c, "tempo")
|
s.otelCollectorIP = s.getComposeServiceIP("otel-collector")
|
||||||
|
|
||||||
// Wait for tempo to turn ready.
|
|
||||||
err = try.GetRequest("http://"+s.tempoIP+":3200/ready", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
s.otelCollectorIP = s.getComposeServiceIP(c, "otel-collector")
|
|
||||||
|
|
||||||
// Wait for otel collector to turn ready.
|
// Wait for otel collector to turn ready.
|
||||||
err = try.GetRequest("http://"+s.otelCollectorIP+":13133/", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://"+s.otelCollectorIP+":13133/", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TearDownTest(c *check.C) {
|
func (s *TracingSuite) TearDownSuite() {
|
||||||
s.composeStop(c, "tempo")
|
s.BaseSuite.TearDownSuite()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TestOpentelemetryBasic_HTTP(c *check.C) {
|
func (s *TracingSuite) SetupTest() {
|
||||||
file := s.adaptFile(c, "fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
s.composeUp("tempo")
|
||||||
|
|
||||||
|
s.tempoIP = s.getComposeServiceIP("tempo")
|
||||||
|
|
||||||
|
// Wait for tempo to turn ready.
|
||||||
|
err := try.GetRequest("http://"+s.tempoIP+":3200/ready", 30*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TracingSuite) TearDownTest() {
|
||||||
|
s.composeStop("tempo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TracingSuite) TestOpentelemetryBasic_HTTP() {
|
||||||
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
WhoamiIP: s.whoamiIP,
|
WhoamiIP: s.whoamiIP,
|
||||||
WhoamiPort: s.whoamiPort,
|
WhoamiPort: s.whoamiPort,
|
||||||
IP: s.otelCollectorIP,
|
IP: s.otelCollectorIP,
|
||||||
IsHTTP: true,
|
IsHTTP: true,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/basic", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/basic", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contains := []map[string]string{
|
contains := []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -113,11 +120,11 @@ func (s *TracingSuite) TestOpentelemetryBasic_HTTP(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTraceContent(c, s.tempoIP, contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TestOpentelemetryBasic_gRPC(c *check.C) {
|
func (s *TracingSuite) TestOpentelemetryBasic_gRPC() {
|
||||||
file := s.adaptFile(c, "fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
WhoamiIP: s.whoamiIP,
|
WhoamiIP: s.whoamiIP,
|
||||||
WhoamiPort: s.whoamiPort,
|
WhoamiPort: s.whoamiPort,
|
||||||
IP: s.otelCollectorIP,
|
IP: s.otelCollectorIP,
|
||||||
|
@ -125,18 +132,14 @@ func (s *TracingSuite) TestOpentelemetryBasic_gRPC(c *check.C) {
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/basic", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/basic", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contains := []map[string]string{
|
contains := []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -166,51 +169,47 @@ func (s *TracingSuite) TestOpentelemetryBasic_gRPC(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTraceContent(c, s.tempoIP, contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TestOpentelemetryRateLimit(c *check.C) {
|
func (s *TracingSuite) TestOpentelemetryRateLimit() {
|
||||||
file := s.adaptFile(c, "fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
WhoamiIP: s.whoamiIP,
|
WhoamiIP: s.whoamiIP,
|
||||||
WhoamiPort: s.whoamiPort,
|
WhoamiPort: s.whoamiPort,
|
||||||
IP: s.otelCollectorIP,
|
IP: s.otelCollectorIP,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// sleep for 4 seconds to be certain the configured time period has elapsed
|
// sleep for 4 seconds to be certain the configured time period has elapsed
|
||||||
// then test another request and verify a 200 status code
|
// then test another request and verify a 200 status code
|
||||||
time.Sleep(4 * time.Second)
|
time.Sleep(4 * time.Second)
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// continue requests at 3 second intervals to test the other rate limit time period
|
// continue requests at 3 second intervals to test the other rate limit time period
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contains := []map[string]string{
|
contains := []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -289,29 +288,25 @@ func (s *TracingSuite) TestOpentelemetryRateLimit(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTraceContent(c, s.tempoIP, contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TestOpentelemetryRetry(c *check.C) {
|
func (s *TracingSuite) TestOpentelemetryRetry() {
|
||||||
file := s.adaptFile(c, "fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
WhoamiIP: s.whoamiIP,
|
WhoamiIP: s.whoamiIP,
|
||||||
WhoamiPort: 81,
|
WhoamiPort: 81,
|
||||||
IP: s.otelCollectorIP,
|
IP: s.otelCollectorIP,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contains := []map[string]string{
|
contains := []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -374,29 +369,25 @@ func (s *TracingSuite) TestOpentelemetryRetry(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTraceContent(c, s.tempoIP, contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) TestOpentelemetryAuth(c *check.C) {
|
func (s *TracingSuite) TestOpentelemetryAuth() {
|
||||||
file := s.adaptFile(c, "fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
WhoamiIP: s.whoamiIP,
|
WhoamiIP: s.whoamiIP,
|
||||||
WhoamiPort: s.whoamiPort,
|
WhoamiPort: s.whoamiPort,
|
||||||
IP: s.otelCollectorIP,
|
IP: s.otelCollectorIP,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
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)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contains := []map[string]string{
|
contains := []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -420,12 +411,14 @@ func (s *TracingSuite) TestOpentelemetryAuth(c *check.C) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTraceContent(c, s.tempoIP, contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkTraceContent(c *check.C, tempoIP string, expectedJSON []map[string]string) {
|
func (s *TracingSuite) checkTraceContent(expectedJSON []map[string]string) {
|
||||||
baseURL, err := url.Parse("http://" + tempoIP + ":3200/api/search")
|
s.T().Helper()
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
baseURL, err := url.Parse("http://" + s.tempoIP + ":3200/api/search")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
|
@ -434,22 +427,20 @@ func checkTraceContent(c *check.C, tempoIP string, expectedJSON []map[string]str
|
||||||
// Wait for traces to be available.
|
// Wait for traces to be available.
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
resp, err := try.Response(req, 5*time.Second)
|
resp, err := try.Response(req, 5*time.Second)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
out := &TraceResponse{}
|
out := &TraceResponse{}
|
||||||
content, err := io.ReadAll(resp.Body)
|
content, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = json.Unmarshal(content, &out)
|
err = json.Unmarshal(content, &out)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if len(out.Traces) == 0 {
|
s.NotEmptyf(len(out.Traces), "expected at least one trace")
|
||||||
c.Fatalf("expected at least one trace, got %d (%s)", len(out.Traces), string(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
var contents []string
|
var contents []string
|
||||||
for _, t := range out.Traces {
|
for _, t := range out.Traces {
|
||||||
baseURL, err := url.Parse("http://" + tempoIP + ":3200/api/traces/" + t.TraceID)
|
baseURL, err := url.Parse("http://" + s.tempoIP + ":3200/api/traces/" + t.TraceID)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
|
@ -457,20 +448,20 @@ func checkTraceContent(c *check.C, tempoIP string, expectedJSON []map[string]str
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := try.Response(req, 5*time.Second)
|
resp, err := try.Response(req, 5*time.Second)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
content, err := io.ReadAll(resp.Body)
|
content, err := io.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
contents = append(contents, string(content))
|
contents = append(contents, string(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, expected := range expectedJSON {
|
for _, expected := range expectedJSON {
|
||||||
containsAll(c, expected, contents)
|
containsAll(expected, contents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsAll(c *check.C, expectedJSON map[string]string, contents []string) {
|
func containsAll(expectedJSON map[string]string, contents []string) {
|
||||||
for k, v := range expectedJSON {
|
for k, v := range expectedJSON {
|
||||||
found := false
|
found := false
|
||||||
for _, content := range contents {
|
for _, content := range contents {
|
||||||
|
@ -481,8 +472,8 @@ func containsAll(c *check.C, expectedJSON map[string]string, contents []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
c.Log("[" + strings.Join(contents, ",") + "]")
|
log.Info().Msgf("[" + strings.Join(contents, ",") + "]")
|
||||||
c.Errorf("missing element: \nKey: %q\nValue: %q ", k, v)
|
log.Error().Msgf("missing element: \nKey: %q\nValue: %q ", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,32 @@ package integration
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UDPSuite struct{ BaseSuite }
|
type UDPSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *UDPSuite) SetUpSuite(c *check.C) {
|
func TestUDPSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "udp")
|
suite.Run(t, new(UDPSuite))
|
||||||
s.composeUp(c)
|
}
|
||||||
|
|
||||||
|
func (s *UDPSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("udp")
|
||||||
|
s.composeUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UDPSuite) TearDownSuite() {
|
||||||
|
s.BaseSuite.TearDownSuite()
|
||||||
}
|
}
|
||||||
|
|
||||||
func guessWhoUDP(addr string) (string, error) {
|
func guessWhoUDP(addr string) (string, error) {
|
||||||
|
@ -46,39 +58,33 @@ func guessWhoUDP(addr string) (string, error) {
|
||||||
return string(out[:n]), nil
|
return string(out[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPSuite) TestWRR(c *check.C) {
|
func (s *UDPSuite) TestWRR() {
|
||||||
file := s.adaptFile(c, "fixtures/udp/wrr.toml", struct {
|
file := s.adaptFile("fixtures/udp/wrr.toml", struct {
|
||||||
WhoamiAIP string
|
WhoamiAIP string
|
||||||
WhoamiBIP string
|
WhoamiBIP string
|
||||||
WhoamiCIP string
|
WhoamiCIP string
|
||||||
WhoamiDIP string
|
WhoamiDIP string
|
||||||
}{
|
}{
|
||||||
WhoamiAIP: s.getComposeServiceIP(c, "whoami-a"),
|
WhoamiAIP: s.getComposeServiceIP("whoami-a"),
|
||||||
WhoamiBIP: s.getComposeServiceIP(c, "whoami-b"),
|
WhoamiBIP: s.getComposeServiceIP("whoami-b"),
|
||||||
WhoamiCIP: s.getComposeServiceIP(c, "whoami-c"),
|
WhoamiCIP: s.getComposeServiceIP("whoami-c"),
|
||||||
WhoamiDIP: s.getComposeServiceIP(c, "whoami-d"),
|
WhoamiDIP: s.getComposeServiceIP("whoami-d"),
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("whoami-a"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("whoami-a"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8093/who", 5*time.Second, try.StatusCodeIs(http.StatusOK))
|
err = try.GetRequest("http://127.0.0.1:8093/who", 5*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
stop := make(chan struct{})
|
stop := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
call := map[string]int{}
|
call := map[string]int{}
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
out, err := guessWhoUDP("127.0.0.1:8093")
|
out, err := guessWhoUDP("127.0.0.1:8093")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
switch {
|
switch {
|
||||||
case strings.Contains(out, "whoami-a"):
|
case strings.Contains(out, "whoami-a"):
|
||||||
call["whoami-a"]++
|
call["whoami-a"]++
|
||||||
|
@ -90,13 +96,13 @@ func (s *UDPSuite) TestWRR(c *check.C) {
|
||||||
call["unknown"]++
|
call["unknown"]++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 3, "whoami-b": 2, "whoami-c": 3})
|
assert.EqualValues(s.T(), call, map[string]int{"whoami-a": 3, "whoami-b": 2, "whoami-c": 3})
|
||||||
close(stop)
|
close(stop)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
case <-time.Tick(5 * time.Second):
|
case <-time.Tick(5 * time.Second):
|
||||||
c.Error("Timeout")
|
log.Info().Msg("Timeout")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,25 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
gorillawebsocket "github.com/gorilla/websocket"
|
gorillawebsocket "github.com/gorilla/websocket"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebsocketSuite tests suite.
|
// WebsocketSuite tests suite.
|
||||||
type WebsocketSuite struct{ BaseSuite }
|
type WebsocketSuite struct{ BaseSuite }
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestBase(c *check.C) {
|
func TestWebsocketSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(WebsocketSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WebsocketSuite) TestBase() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -41,36 +47,30 @@ func (s *WebsocketSuite) TestBase(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
|
func (s *WebsocketSuite) TestWrongOrigin() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -91,35 +91,28 @@ func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800")
|
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
_, err = websocket.NewClient(config, conn)
|
_, err = websocket.NewClient(config, conn)
|
||||||
c.Assert(err, checker.NotNil)
|
assert.ErrorContains(s.T(), err, "bad status")
|
||||||
c.Assert(err, checker.ErrorMatches, "bad status")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestOrigin(c *check.C) {
|
func (s *WebsocketSuite) TestOrigin() {
|
||||||
// use default options
|
// use default options
|
||||||
upgrader := gorillawebsocket.Upgrader{}
|
upgrader := gorillawebsocket.Upgrader{}
|
||||||
|
|
||||||
|
@ -141,44 +134,38 @@ func (s *WebsocketSuite) TestOrigin(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
client, err := websocket.NewClient(config, conn)
|
client, err := websocket.NewClient(config, conn)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
n, err := client.Write([]byte("OK"))
|
n, err := client.Write([]byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
|
|
||||||
msg := make([]byte, 2)
|
msg := make([]byte, 2)
|
||||||
n, err = client.Read(msg)
|
n, err = client.Read(msg)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) {
|
func (s *WebsocketSuite) TestWrongOriginIgnoredByServer() {
|
||||||
upgrader := gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
|
upgrader := gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
}}
|
}}
|
||||||
|
@ -201,44 +188,38 @@ func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80")
|
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80")
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
client, err := websocket.NewClient(config, conn)
|
client, err := websocket.NewClient(config, conn)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
n, err := client.Write([]byte("OK"))
|
n, err := client.Write([]byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
|
|
||||||
msg := make([]byte, 2)
|
msg := make([]byte, 2)
|
||||||
n, err = client.Read(msg)
|
n, err = client.Read(msg)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
|
func (s *WebsocketSuite) TestSSLTermination() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -258,44 +239,38 @@ func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Add client self-signed cert
|
// Add client self-signed cert
|
||||||
roots := x509.NewCertPool()
|
roots := x509.NewCertPool()
|
||||||
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
roots.AppendCertsFromPEM(certContent)
|
roots.AppendCertsFromPEM(certContent)
|
||||||
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
|
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
|
||||||
RootCAs: roots,
|
RootCAs: roots,
|
||||||
}
|
}
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/ws", nil)
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/ws", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
|
func (s *WebsocketSuite) TestBasicAuth() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -306,8 +281,8 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
user, password, _ := r.BasicAuth()
|
user, password, _ := r.BasicAuth()
|
||||||
c.Assert(user, check.Equals, "traefiker")
|
assert.Equal(s.T(), "traefiker", user)
|
||||||
c.Assert(password, check.Equals, "secret")
|
assert.Equal(s.T(), "secret", password)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
mt, message, err := conn.ReadMessage()
|
mt, message, err := conn.ReadMessage()
|
||||||
|
@ -320,78 +295,66 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
||||||
auth := "traefiker:secret"
|
auth := "traefiker:secret"
|
||||||
config.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
|
config.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
|
||||||
|
|
||||||
c.Assert(err, check.IsNil)
|
assert.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
client, err := websocket.NewClient(config, conn)
|
client, err := websocket.NewClient(config, conn)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
n, err := client.Write([]byte("OK"))
|
n, err := client.Write([]byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
|
|
||||||
msg := make([]byte, 2)
|
msg := make([]byte, 2)
|
||||||
n, err = client.Read(msg)
|
n, err = client.Read(msg)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(n, checker.Equals, 2)
|
assert.Equal(s.T(), 2, n)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestSpecificResponseFromBackend(c *check.C) {
|
func (s *WebsocketSuite) TestSpecificResponseFromBackend() {
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
}))
|
}))
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
||||||
c.Assert(err, checker.NotNil)
|
assert.Error(s.T(), err)
|
||||||
c.Assert(resp.StatusCode, check.Equals, http.StatusUnauthorized)
|
assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
|
func (s *WebsocketSuite) TestURLWithURLEncodedChar() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
c.Assert(r.URL.EscapedPath(), check.Equals, "/ws/http%3A%2F%2Ftest")
|
assert.Equal(s.T(), "/ws/http%3A%2F%2Ftest", r.URL.EscapedPath())
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -409,36 +372,30 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil)
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
|
func (s *WebsocketSuite) TestSSLhttp2() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -463,48 +420,42 @@ func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
|
||||||
ts.TLS.NextProtos = append(ts.TLS.NextProtos, `h2`, `http/1.1`)
|
ts.TLS.NextProtos = append(ts.TLS.NextProtos, `h2`, `http/1.1`)
|
||||||
ts.StartTLS()
|
ts.StartTLS()
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: ts.URL,
|
WebsocketServer: ts.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
// Add client self-signed cert
|
// Add client self-signed cert
|
||||||
roots := x509.NewCertPool()
|
roots := x509.NewCertPool()
|
||||||
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
roots.AppendCertsFromPEM(certContent)
|
roots.AppendCertsFromPEM(certContent)
|
||||||
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
|
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
|
||||||
RootCAs: roots,
|
RootCAs: roots,
|
||||||
}
|
}
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/echo", nil)
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/echo", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketSuite) TestHeaderAreForwarded(c *check.C) {
|
func (s *WebsocketSuite) TestHeaderAreForwarded() {
|
||||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
c.Assert(r.Header.Get("X-Token"), check.Equals, "my-token")
|
assert.Equal(s.T(), "my-token", r.Header.Get("X-Token"))
|
||||||
c, err := upgrader.Upgrade(w, r, nil)
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -522,33 +473,27 @@ func (s *WebsocketSuite) TestHeaderAreForwarded(c *check.C) {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
|
file := s.adaptFile("fixtures/websocket/config.toml", struct {
|
||||||
WebsocketServer string
|
WebsocketServer string
|
||||||
}{
|
}{
|
||||||
WebsocketServer: srv.URL,
|
WebsocketServer: srv.URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer os.Remove(file)
|
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
|
|
||||||
defer display(c)
|
|
||||||
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
headers := http.Header{}
|
headers := http.Header{}
|
||||||
headers.Add("X-Token", "my-token")
|
headers.Add("X-Token", "my-token")
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", headers)
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", headers)
|
||||||
|
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
_, msg, err := conn.ReadMessage()
|
_, msg, err := conn.ReadMessage()
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
assert.Equal(s.T(), "OK", string(msg))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,18 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-check/check"
|
|
||||||
"github.com/kvtools/valkeyrie"
|
"github.com/kvtools/valkeyrie"
|
||||||
"github.com/kvtools/valkeyrie/store"
|
"github.com/kvtools/valkeyrie/store"
|
||||||
"github.com/kvtools/zookeeper"
|
"github.com/kvtools/zookeeper"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/traefik/traefik/v3/integration/try"
|
"github.com/traefik/traefik/v3/integration/try"
|
||||||
"github.com/traefik/traefik/v3/pkg/api"
|
"github.com/traefik/traefik/v3/pkg/api"
|
||||||
checker "github.com/vdemeester/shakers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Zk test suites.
|
// Zk test suites.
|
||||||
|
@ -27,11 +29,17 @@ type ZookeeperSuite struct {
|
||||||
zookeeperAddr string
|
zookeeperAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ZookeeperSuite) setupStore(c *check.C) {
|
func TestZookeeperSuite(t *testing.T) {
|
||||||
s.createComposeProject(c, "zookeeper")
|
suite.Run(t, new(ZookeeperSuite))
|
||||||
s.composeUp(c)
|
}
|
||||||
|
|
||||||
s.zookeeperAddr = net.JoinHostPort(s.getComposeServiceIP(c, "zookeeper"), "2181")
|
func (s *ZookeeperSuite) SetupSuite() {
|
||||||
|
s.BaseSuite.SetupSuite()
|
||||||
|
|
||||||
|
s.createComposeProject("zookeeper")
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.zookeeperAddr = net.JoinHostPort(s.getComposeServiceIP("zookeeper"), "2181")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.kvClient, err = valkeyrie.NewStore(
|
s.kvClient, err = valkeyrie.NewStore(
|
||||||
|
@ -42,20 +50,19 @@ func (s *ZookeeperSuite) setupStore(c *check.C) {
|
||||||
ConnectionTimeout: 10 * time.Second,
|
ConnectionTimeout: 10 * time.Second,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
require.NoError(s.T(), err, "Cannot create store zookeeper")
|
||||||
c.Fatal("Cannot create store zookeeper")
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for zk
|
// wait for zk
|
||||||
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
|
err = try.Do(60*time.Second, try.KVExists(s.kvClient, "test"))
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
|
func (s *ZookeeperSuite) TearDownSuite() {
|
||||||
s.setupStore(c)
|
s.BaseSuite.TearDownSuite()
|
||||||
|
}
|
||||||
|
|
||||||
file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
|
func (s *ZookeeperSuite) TestSimpleConfiguration() {
|
||||||
defer os.Remove(file)
|
file := s.adaptFile("fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
|
||||||
|
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"traefik/http/routers/Router0/entryPoints/0": "web",
|
"traefik/http/routers/Router0/entryPoints/0": "web",
|
||||||
|
@ -104,39 +111,35 @@ func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
|
||||||
err := cmd.Start()
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
defer s.killCmd(cmd)
|
|
||||||
|
|
||||||
// wait for traefik
|
// wait for traefik
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second,
|
||||||
try.BodyContains(`"striper@zookeeper":`, `"compressor@zookeeper":`, `"srvcA@zookeeper":`, `"srvcB@zookeeper":`),
|
try.BodyContains(`"striper@zookeeper":`, `"compressor@zookeeper":`, `"srvcA@zookeeper":`, `"srvcB@zookeeper":`),
|
||||||
)
|
)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
var obtained api.RunTimeRepresentation
|
var obtained api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
err = json.NewDecoder(resp.Body).Decode(&obtained)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
got, err := json.MarshalIndent(obtained, "", " ")
|
got, err := json.MarshalIndent(obtained, "", " ")
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
expectedJSON := filepath.FromSlash("testdata/rawdata-zk.json")
|
expectedJSON := filepath.FromSlash("testdata/rawdata-zk.json")
|
||||||
|
|
||||||
if *updateExpected {
|
if *updateExpected {
|
||||||
err = os.WriteFile(expectedJSON, got, 0o666)
|
err = os.WriteFile(expectedJSON, got, 0o666)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := os.ReadFile(expectedJSON)
|
expected, err := os.ReadFile(expectedJSON)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
if !bytes.Equal(expected, got) {
|
if !bytes.Equal(expected, got) {
|
||||||
diff := difflib.UnifiedDiff{
|
diff := difflib.UnifiedDiff{
|
||||||
|
@ -148,7 +151,7 @@ func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
text, err := difflib.GetUnifiedDiffString(diff)
|
text, err := difflib.GetUnifiedDiffString(diff)
|
||||||
c.Assert(err, checker.IsNil)
|
require.NoError(s.T(), err)
|
||||||
c.Error(text)
|
log.Info().Msg(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ set -e -o pipefail
|
||||||
|
|
||||||
source /go/src/k8s.io/code-generator/kube_codegen.sh
|
source /go/src/k8s.io/code-generator/kube_codegen.sh
|
||||||
|
|
||||||
git config --global --add safe.directory /go/src/${PROJECT_MODULE}
|
git config --global --add safe.directory "/go/src/${PROJECT_MODULE}"
|
||||||
|
|
||||||
rm -rf "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}"
|
rm -rf "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}"
|
||||||
mkdir -p "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}/"
|
mkdir -p "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}/"
|
||||||
|
|
|
@ -3,18 +3,9 @@ set -e
|
||||||
|
|
||||||
export DEST=.
|
export DEST=.
|
||||||
|
|
||||||
TESTFLAGS+=("-test.timeout=20m" -check.v)
|
|
||||||
|
|
||||||
if [ -n "${VERBOSE}" ]; then
|
|
||||||
TESTFLAGS+=(-v)
|
|
||||||
elif [ -n "${VERBOSE_INTEGRATION}" ]; then
|
|
||||||
TESTFLAGS+=(-v)
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd integration
|
|
||||||
echo "Testing against..."
|
echo "Testing against..."
|
||||||
docker version
|
docker version
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
# shellcheck disable=SC2048
|
# shellcheck disable=SC2048
|
||||||
CGO_ENABLED=0 go test -integration ${TESTFLAGS[*]}
|
go test ./integration -test.timeout=20m -failfast -v ${TESTFLAGS[*]}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
|
|
Loading…
Reference in a new issue