Improve integration tests

Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
This commit is contained in:
Michael 2024-01-09 17:00:07 +01:00 committed by GitHub
parent cd8d5b8f10
commit e522446909
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 3482 additions and 4609 deletions

View file

@ -8,7 +8,6 @@ on:
env:
GO_VERSION: '1.21'
CGO_ENABLED: 0
IN_DOCKER: ""
jobs:
@ -17,7 +16,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -39,38 +38,22 @@ jobs:
os: [ ubuntu-20.04, macos-latest, windows-latest ]
needs:
- build-webui
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v3
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
path: |
~/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-
go-version: ${{ env.GO_VERSION }}
- name: Artifact webui
uses: actions/download-artifact@v2
with:
name: webui.tar.gz
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
- name: Untar webui
run: tar xvf webui.tar.gz

View file

@ -13,7 +13,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

View file

@ -28,7 +28,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View file

@ -19,7 +19,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

View file

@ -17,7 +17,7 @@ jobs:
# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

74
.github/workflows/test-integration.yaml vendored Normal file
View 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}}"

View file

@ -7,37 +7,22 @@ on:
env:
GO_VERSION: '1.21'
IN_DOCKER: ""
jobs:
test-unit:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v3
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-test-unit-go-
go-version: ${{ env.GO_VERSION }}
- name: Avoid generating webui
run: touch webui/static/index.html

View file

@ -8,38 +8,23 @@ on:
env:
GO_VERSION: '1.21'
GOLANGCI_LINT_VERSION: v1.55.2
MISSSPELL_VERSION: v0.4.0
IN_DOCKER: ""
MISSSPELL_VERSION: v0.4.1
jobs:
validate:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v3
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-go-
go-version: ${{ env.GO_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}
@ -56,30 +41,16 @@ jobs:
validate-generate:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v3
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-generate-go-
go-version: ${{ env.GO_VERSION }}
- name: go generate
run: |

View file

@ -146,7 +146,6 @@ linters-settings:
gomoddirectives:
replace-allow-list:
- github.com/abbot/go-http-auth
- github.com/go-check/check
- github.com/gorilla/mux
- github.com/mailgun/minheap
- github.com/mailgun/multibuf
@ -162,7 +161,6 @@ linters-settings:
- expected-actual
- float-compare
- len
- suite-dont-use-pkg
- suite-extra-assert-call
- suite-thelper

View file

@ -31,24 +31,6 @@ global_job_config:
- cache restore traefik-$(checksum go.sum)
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
dependencies: []
run:
@ -65,8 +47,6 @@ blocks:
value: 2.32.1
- name: CODENAME
value: "mimolette"
- name: IN_DOCKER
value: ""
prologue:
commands:
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}

103
Makefile
View file

@ -6,34 +6,6 @@ VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
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
default: binary
@ -42,20 +14,6 @@ default: binary
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
.PHONY: build-webui-image
build-webui-image:
@ -79,8 +37,8 @@ generate-webui: webui/static/index.html
## Build the binary
.PHONY: binary
binary: generate-webui build-dev-image
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
binary: generate-webui
./script/make.sh generate binary
## Build the linux binary locally
.PHONY: binary-debug
@ -89,35 +47,29 @@ binary-debug: generate-webui
## Build the binary for the standard platforms (linux, darwin, windows)
.PHONY: crossbinary-default
crossbinary-default: generate-webui build-dev-image
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
crossbinary-default: generate-webui
./script/make.sh generate crossbinary-default
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
.PHONY: crossbinary-default-parallel
crossbinary-default-parallel:
$(MAKE) generate-webui
$(MAKE) build-dev-image crossbinary-default
$(MAKE) crossbinary-default
## Run the unit and integration tests
.PHONY: test
test: build-dev-image
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit binary test-integration
test:
./script/make.sh generate test-unit binary test-integration
## Run the unit tests
.PHONY: test-unit
test-unit: build-dev-image
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
test-unit:
./script/make.sh generate test-unit
## Run the integration tests
.PHONY: test-integration
test-integration: build-dev-image
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate binary test-integration
test-integration:
./script/make.sh generate binary test-integration
## Pull all images for integration tests
.PHONY: pull-images
@ -128,16 +80,22 @@ pull-images:
| uniq \
| xargs -P 6 -n 1 docker pull
EXECUTABLES = misspell shellcheck
## Validate code and docs
.PHONY: validate-files
validate-files: build-dev-image
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
validate-files:
$(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
## Validate code, docs, and vendor
.PHONY: validate
validate: build-dev-image
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
validate:
$(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
## Clean up static directory and build a Docker Traefik image
@ -155,11 +113,6 @@ build-image-dirty: binary
build-image-debug: binary-debug
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
.PHONY: docs
docs:
@ -187,23 +140,23 @@ generate-genconf:
## Create packages for the release
.PHONY: release-packages
release-packages: generate-webui build-dev-image
release-packages: generate-webui
rm -rf dist
@- $(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)); \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) go clean -cache; \
goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \
go clean -cache; \
)
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) rm dist/**/*_checksums.txt
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
rm dist/**/*_checksums.txt
tar cfz dist/traefik-${VERSION}.src.tar.gz \
--exclude-vcs \
--exclude .idea \
--exclude .travis \
--exclude .semaphoreci \
--exclude .github \
--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
.PHONY: fmt

View file

@ -13,67 +13,13 @@ Let's see how.
## 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.
For changes to its dependencies, the `dep` dependency management tool is required.
### Method 1: Using `Docker` and `Makefile`
Run make with the `binary` target.
```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`
You need:
- [Docker](https://github.com/docker/docker "Link to website of Docker")
- `make`
- [Go](https://go.dev/ "Link to website of Go")
- [misspell](https://github.com/golangci/misspell)
- [shellcheck](https://github.com/koalaman/shellcheck)
- [Tailscale](https://tailscale.com/) if you are using Docker Desktop
!!! tip "Source Directory"
@ -106,41 +52,33 @@ Requirements:
## ... 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.
```bash
# Generate UI static files
make clean-webui generate-webui
$ make binary
./script/make.sh generate binary
---> Making bundle: generate (in .)
# required to merge non-code components into the final binary,
# such as the web dashboard/UI
go generate
---> Making bundle: binary (in .)
$ ls dist/
traefik*
```
```bash
# Standard go build
go build ./cmd/traefik
```
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
You will find the Traefik executable (`traefik`) in the `./dist` directory.
## Testing
### Method 1: `Docker` and `make`
Run unit tests using the `test-unit` target.
Run integration tests using the `test-integration` target.
Run all tests (unit and integration) using the `test` target.
```bash
$ make test-unit
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
# […]
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
./script/make.sh generate test-unit
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: test-unit (in .)
+ 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):
??? 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
# 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
TESTFLAGS="-check.f MyTestSuite.MyTest" 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
TESTFLAGS="-test.run TestAccessLogSuite -testify.m ^TestAccessLog$" 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 ./...`.

91
go.mod
View file

@ -13,14 +13,12 @@ require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/docker/cli v24.0.7+incompatible
github.com/docker/compose/v2 v2.19.0
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
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/golang/protobuf v1.5.3
github.com/google/go-github/v28 v28.1.1
@ -58,14 +56,13 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
github.com/testcontainers/testcontainers-go v0.27.0
github.com/traefik/paerser v0.2.0
github.com/traefik/yaegi v0.15.1
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2
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/predicate v1.2.0
go.elastic.co/apm v1.13.1
@ -90,9 +87,8 @@ require (
require (
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/AlecAivazis/survey/v2 v2.3.7 // 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/azidentity v1.3.0 // indirect
@ -121,6 +117,7 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
@ -144,29 +141,20 @@ require (
github.com/aws/smithy-go v1.14.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/buger/goterm v1.0.4 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/civo/civogo v0.3.11 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
github.com/compose-spec/compose-go v1.15.0 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.7.2 // indirect
github.com/containerd/continuity v0.4.1 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/containerd/containerd v1.7.11 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/deepmap/oapi-codegen v1.9.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493 // indirect
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
github.com/docker/buildx v0.11.2 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.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.5.0 // indirect
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@ -178,42 +166,33 @@ require (
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/exoscale/egoscale v0.100.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsevents v0.1.1 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.3.0 // 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.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-zookeeper/zk v1.0.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // 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.3.1 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/gophercloud/gophercloud v1.0.0 // indirect
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // 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/hashicorp/cronexpr v1.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -225,8 +204,6 @@ require (
github.com/huandu/xstrings v1.4.0 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jcchavezs/porto v0.1.0 // indirect
@ -236,8 +213,8 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // 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/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect
@ -246,28 +223,20 @@ require (
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
github.com/looplab/fsm v0.1.0 // 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/multibuf v0.1.2 // indirect
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/moby/buildkit v0.12.3 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/symlink v0.2.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@ -286,23 +255,22 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opencontainers/runc v1.1.7 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
github.com/outcaste-io/ristretto v0.2.3 // indirect
github.com/ovh/go-ovh v1.4.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // 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/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/redis/go-redis/v9 v9.2.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/sacloud/api-client-go v0.2.8 // indirect
github.com/sacloud/go-http v0.1.6 // indirect
github.com/sacloud/iaas-api-go v1.11.1 // indirect
@ -311,53 +279,36 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
github.com/shibumi/go-pathspec v1.3.0 // 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/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // 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/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // 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/dnspod v1.0.490 // indirect
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/transip/gotransip/v6 v6.20.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b // 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-sdk v0.0.0-20220805164847-cf028e604997 // indirect
github.com/zmap/zlint/v3 v3.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.elastic.co/apm/module/apmhttp v1.13.1 // indirect
go.elastic.co/fastjson v1.1.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.6 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect
go.etcd.io/etcd/client/v3 v3.5.6 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 // indirect
go.opentelemetry.io/otel v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0 // indirect
go.opentelemetry.io/otel/metric v1.20.0 // indirect
go.opentelemetry.io/otel/sdk v1.20.0 // indirect
go.opentelemetry.io/otel/trace v1.20.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/mock v0.3.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
@ -368,8 +319,7 @@ require (
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.128.0 // indirect
@ -394,7 +344,6 @@ require (
// Containous forks
replace (
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
)

332
go.sum
View file

@ -40,15 +40,13 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 h1:+vTEFqeoeur6XSq06bs+roX3YiT49gUniJK7Zky7Xjg=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE=
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
@ -131,20 +129,15 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek=
github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM=
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A=
github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g=
@ -163,8 +156,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@ -178,8 +169,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
@ -227,27 +216,15 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/bugsnag/bugsnag-go v1.0.5/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/bugsnag-go v1.5.0 h1:tP8hiPv1pGGW3LA6LKy5lW6WG+y9J2xWUdPd3WC452k=
github.com/bugsnag/bugsnag-go v1.5.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@ -268,9 +245,6 @@ github.com/civo/civogo v0.3.11 h1:mON/fyrV946Sbk6paRtOSGsN+asCgCmHCgArf5xmGxM=
github.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8=
github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0=
github.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -281,38 +255,15 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go v1.15.0 h1:rv3TTgbS3U4Y8sRTngrcxDmpbz+fq26wTqHculSCi6s=
github.com/compose-spec/compose-go v1.15.0/go.mod h1:3yngGBGfls6FHGQsg4B1z6gz8ej9SOvmAJtxCwgbcnc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/containerd v1.7.2 h1:UF2gdONnxO8I6byZXDi5sXWiWvlW3D/sci7dTQimEJo=
github.com/containerd/containerd v1.7.2/go.mod h1:afcz74+K10M/+cjGHIVQrCt3RAQhUSCAjJ9iMYhhkuI=
github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU=
github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/nydus-snapshotter v0.8.2 h1:7SOrMU2YmLzfbsr5J7liMZJlNi5WT6vtIOxLGv+iz7E=
github.com/containerd/nydus-snapshotter v0.8.2/go.mod h1:UJILTN5LVBRY+dt8BGJbp72Xy729hUZsOugObEI3/O8=
github.com/containerd/stargz-snapshotter v0.14.3 h1:OTUVZoPSPs8mGgmQUE1dqw3WX/3nrsmsurW7UPLWl1U=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs=
github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak=
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw=
github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd h1:0n+lFLh5zU0l6KSk3KpnDwfbPGAR44aRLgTbCnhRBHU=
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd/go.mod h1:BbQgeDS5i0tNvypwEoF1oNjOJw8knRAE1DnVvjDstcQ=
github.com/containous/check v0.0.0-20170915194414-ca0bf163426a h1:8esAQaPKjfntQR1bag/mAOvWJd5HqSX5nsa+0KT63zo=
github.com/containous/check v0.0.0-20170915194414-ca0bf163426a/go.mod h1:eQOqZ7GoFsLxI7jFKLs7+Nv2Rm1x4FyK8d2NV+yGjwQ=
github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e h1:D+uTEzDZc1Fhmd0Pq06c+O9+KkAyExw0eVmu/NOqaHU=
github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e/go.mod h1:s8kLgBQolDbsJOPVIGCEEv9zGAKUUf/685Gi0Qqg8z8=
github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595 h1:aPspFRO6b94To3gl4yTDOEtpjFwXI7V2W+z0JcNljQ4=
@ -336,18 +287,16 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -357,7 +306,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/deepmap/oapi-codegen v1.9.1 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIxr1vtI=
github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw=
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
@ -366,38 +314,20 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493 h1:fm5DpBD+A7o0+x9Nf+o9/4/qPGbfxLpr9qIPVuV8vQc=
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493/go.mod h1:+fqBJ4vPYo4Uu1ZE4d+bUtTLRXfdSL3NvCZIZ9GHv58=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dnsimple/dnsimple-go v1.2.0 h1:ddTGyLVKly5HKb5L65AkLqFqwZlWo3WnR0BlFZlIddM=
github.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U=
github.com/docker/buildx v0.11.2 h1:R3p9F0gnI4FwvQ0p40UwdX1T4ugap4UWxY3TFHoP4Ws=
github.com/docker/buildx v0.11.2/go.mod h1:CWAABt10iIuGpleypA3103mplDfcGu0A2AvT03xfpTc=
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/compose/v2 v2.19.0 h1:88LLRdyeVdkuIlc9wJ6SPUs2x5IoF4O9DtCgPS/d9lA=
github.com/docker/compose/v2 v2.19.0/go.mod h1:08yDbnLQKUqBpL862sUo4RBSKXt0n0XLtIbaUSMW1O8=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e h1:rMOGp6HPeMHbdLrZkX2nD+94uqDunc27tXVuS+ey4mQ=
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
@ -405,7 +335,6 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.4.0 h1:3OK9bWpPk5q6pbFAaYSEwD9CLUSHG8bnZuqX2yMt3B0=
@ -427,8 +356,6 @@ github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6
github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
@ -443,9 +370,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@ -461,8 +385,6 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
@ -470,15 +392,11 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsevents v0.1.1 h1:/125uxJvvoSDDBPen6yUZbil8J9ydKZnnl3TWWmvnkw=
github.com/fsnotify/fsevents v0.1.1/go.mod h1:+d+hS27T6k5J8CRaPLKFgwKYcpS7GwW3Ule9+SC2ZRc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI=
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
@ -512,12 +430,11 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
@ -543,9 +460,6 @@ github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSG
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
@ -558,14 +472,9 @@ github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVr
github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@ -578,11 +487,8 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -598,7 +504,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -627,9 +532,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ=
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -646,6 +548,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
@ -678,14 +581,12 @@ github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0Z
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg=
github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -711,18 +612,12 @@ github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf/go.mod h1:z
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
@ -778,8 +673,6 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
@ -791,8 +684,6 @@ github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f/go.mod h1:b/Ao
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
@ -807,11 +698,7 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb-client-go/v2 v2.7.0 h1:QgP5mlBE9sGnzplpnf96pr+p7uqlIlL4W2GAP3n+XZg=
github.com/influxdata/influxdb-client-go/v2 v2.7.0/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA=
@ -840,20 +727,11 @@ github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -875,16 +753,11 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@ -936,7 +809,6 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linode/linodego v1.17.2 h1:b32dj4662PGG5P9qVa6nBezccWdqgukndlMIuPGq1CQ=
@ -951,13 +823,14 @@ github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHS
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/looplab/fsm v0.1.0 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20=
github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailgun/multibuf v0.1.2 h1:QE9kE27lK6LFZB4aYNVtUPlWVHVCT0zpgUr2uoq/+jk=
github.com/mailgun/multibuf v0.1.2/go.mod h1:E+sUhIy69qgT6EM57kCPdUTlHnjTuxQBO/yf6af9Hes=
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 h1:Kg/NPZLLC3aAFr1YToMs98dbCdhootQ1hZIvZU28hAQ=
@ -996,11 +869,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@ -1008,17 +876,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -1036,7 +899,6 @@ github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -1045,22 +907,11 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.12.3 h1:cFaPVnyC0PwAP5xHHfzdU5v9rgQrCi6HnGSg3WuFKp4=
github.com/moby/buildkit v0.12.3/go.mod h1:adB4y0SxxX8trnrY+oEulb48ODLqPO6pKMF0ppGcCoI=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc=
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
@ -1073,7 +924,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@ -1120,7 +970,6 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
@ -1134,7 +983,6 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
@ -1145,15 +993,10 @@ github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+q
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk=
github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/opencontainers/runtime-spec v1.1.0-rc.2 h1:ucBtEms2tamYYW/SvGpvq9yUN0NEVL6oyLEwDcTSrk8=
github.com/opencontainers/runtime-spec v1.1.0-rc.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
@ -1183,10 +1026,6 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
@ -1213,10 +1052,11 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@ -1229,7 +1069,6 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -1238,7 +1077,6 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@ -1250,7 +1088,6 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@ -1279,14 +1116,12 @@ github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6Qml
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -1311,22 +1146,21 @@ github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAj
github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 h1:ka9QPuQg2u4LGipiZGsgkg3rJCo4iIUCy75FddM0GRQ=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 h1:ZTzdx88+AcnjqUfJwnz89UBrMSBQ1NEysg9u5d+dU9c=
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -1346,41 +1180,27 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/tools-golang v0.5.1 h1:fJg3SVOGG+eIva9ZUBm/hvyA7PIPVFjRxUKe6fdAgwE=
github.com/spdx/tools-golang v0.5.1/go.mod h1:/DRDQuBfB37HctM29YtrX1v+bXiVmT2OpQDalRmX9aU=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@ -1408,26 +1228,20 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g=
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ=
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0=
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
github.com/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk=
github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb h1:uUe8rNyVXM8moActoBol6Xf6xX2GMr7SosR2EywMvGg=
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb/go.mod h1:SxX/oNQ/ag6Vaoli547ipFK9J7BZn5JqJG0JE8lf8bA=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVNLMPXgVfsRT40CSFKXCxuU8LoHySjs=
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
github.com/traefik/paerser v0.2.0 h1:zqCLGSXoNlcBd+mzqSCLjon/I6phqIjeJL2xFB2ysgQ=
github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc=
github.com/traefik/yaegi v0.15.1 h1:YA5SbaL6HZA0Exh9T/oArRHqGN2HQ+zgmCY7dkoTXu4=
@ -1456,10 +1270,6 @@ github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKn
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c h1:Qt/YKpE8uAKNF4x2mwBZxmVo2WtgUL1WFDeXr1nlfpA=
@ -1468,15 +1278,8 @@ github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt
github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f h1:cG+ehPRJSlqljSufLf1KXeXpUd1dLNjnzA18mZcB/O0=
@ -1489,13 +1292,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
github.com/zmap/zcrypto v0.0.0-20210123152837-9cf5beac6d91/go.mod h1:R/deQh6+tSWlgI9tb4jNmXxn8nSCabl5ZQsBX9//I/E=
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c h1:ufDm/IlBYZYLuiqvQuhpTKwrcAS2OlXEzWbDvTVGbSQ=
github.com/zmap/zcrypto v0.0.0-20220605182715-4dfcec6e9a8c/go.mod h1:egdRkzUylATvPkWMpebZbXhv0FMEMJGX/ur0D3Csk2s=
github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.elastic.co/apm v1.13.1 h1:ICIcUcQOImg/bve9mQVyLCvm1cSUZ1afdwK6ACnxczU=
go.elastic.co/apm v1.13.1/go.mod h1:dylGv2HKR0tiCV+wliJz1KHtDyuD8SPe69oV7VyK6WY=
go.elastic.co/apm/module/apmhttp v1.13.1 h1:g2id6+AY8NRSA6nzwPDSU1AmBiHyZeh/lJRBlXq2yfQ=
@ -1529,29 +1327,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.0 h1:WUMhXWqLmFlznidWF4B9iML8VMdZy4TzJVYzdYTCuaM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.0/go.mod h1:H1XIOXyXFff1aZa7nQeFHGYMB+gHH1TtZSti37uHX6o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0/go.mod h1:HyABWq60Uy1kjJSa2BVOxUVao8Cdick5AWSKPutqy6U=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0 h1:CsBiKCiQPdSjS+MlRiqeTI9JDDpSuk0Hb6QTRfwer8k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0/go.mod h1:CMJYNAfooOwSZSAmAeMUV1M+TXld3BiK++z9fqIm2xk=
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -1588,7 +1364,6 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -1597,14 +1372,11 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
@ -1776,6 +1548,7 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1813,8 +1586,8 @@ golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1825,7 +1598,6 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1837,11 +1609,9 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1853,8 +1623,10 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -2029,7 +1801,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
@ -2059,7 +1830,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@ -2108,10 +1878,7 @@ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1 h1:AUe/ZF7xm6vYnigPe+TY54DmfWYJxhMRaw/TfvrbzvE=
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1/go.mod h1:KDLJ3CWVOSuVVwu+0ZR5KZo2rP6c7YyBV3v387dIpUU=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -2122,7 +1889,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@ -2137,8 +1903,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
gopkg.in/ns1/ns1-go.v2 v2.7.6 h1:mCPl7q0jbIGACXvGBljAuuApmKZo3rRi4tlRIEbMvjA=
gopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -2160,8 +1924,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -11,13 +11,15 @@ import (
"os"
"strconv"
"strings"
"testing"
"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/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
checker "github.com/vdemeester/shakers"
)
const (
@ -28,6 +30,10 @@ const (
// AccessLogSuite tests suite.
type AccessLogSuite struct{ BaseSuite }
func TestAccessLogSuite(t *testing.T) {
suite.Run(t, new(AccessLogSuite))
}
type accessLogValue struct {
formatOnly bool
code string
@ -36,67 +42,67 @@ type accessLogValue struct {
serviceURL string
}
func (s *AccessLogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log")
s.composeUp(c)
func (s *AccessLogSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("access_log")
s.composeUp()
}
func (s *AccessLogSuite) TearDownTest(c *check.C) {
displayTraefikLogFile(c, traefikTestLogFile)
func (s *AccessLogSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *AccessLogSuite) TearDownTest() {
s.displayTraefikLogFile(traefikTestLogFile)
_ = os.Remove(traefikTestAccessLogFile)
}
func (s *AccessLogSuite) TestAccessLog(c *check.C) {
func (s *AccessLogSuite) TestAccessLog() {
ensureWorkingDirectoryIsClean()
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer func() {
traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
log.WithoutContext().Info(string(traefikLog))
}()
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.waitForTraefik("server1")
waitForTraefik(c, "server1")
checkStatsForLogFile(c)
s.checkStatsForLogFile()
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Make some requests
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
req.Host = "frontend2.docker.local"
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())
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// 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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
func (s *AccessLogSuite) TestAccessLogAuthFrontend() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -124,48 +130,43 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "authFrontend")
s.waitForTraefik("authFrontend")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test auth entrypoint
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"
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", "")
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")
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -193,27 +194,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "digestAuthMiddleware")
s.waitForTraefik("digestAuthMiddleware")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test auth entrypoint
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"
resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
digest := digestParts(resp)
digest["uri"] = "/"
@ -225,22 +221,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
req.Header.Set("Content-Type", "application/json")
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"
req.Header.Set("Authorization", getDigestAuthorization(digest))
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
// Thanks to mvndaai for digest authentication
@ -291,7 +287,7 @@ func getDigestAuthorization(digestParts map[string]string) string {
return authorization
}
func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
func (s *AccessLogSuite) TestAccessLogFrontendRedirect() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -308,38 +304,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "frontendRedirect")
s.waitForTraefik("frontendRedirect")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test frontend redirect
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 = ""
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect() {
ensureWorkingDirectoryIsClean()
type logLine struct {
@ -365,30 +356,25 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "frontendRedirect")
s.waitForTraefik("frontendRedirect")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test frontend redirect
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 = ""
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)
c.Assert(len(lines), checker.GreaterOrEqualThan, len(expected))
lines := s.extractLines()
assert.GreaterOrEqual(s.T(), len(lines), len(expected))
for i, line := range lines {
if line == "" {
@ -396,15 +382,15 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
}
var logline logLine
err := json.Unmarshal([]byte(line), &logline)
c.Assert(err, checker.IsNil)
c.Assert(logline.DownstreamStatus, checker.Equals, expected[i].DownstreamStatus)
c.Assert(logline.OriginStatus, checker.Equals, expected[i].OriginStatus)
c.Assert(logline.RouterName, checker.Equals, expected[i].RouterName)
c.Assert(logline.ServiceName, checker.Equals, expected[i].ServiceName)
require.NoError(s.T(), err)
assert.Equal(s.T(), expected[i].DownstreamStatus, logline.DownstreamStatus)
assert.Equal(s.T(), expected[i].OriginStatus, logline.OriginStatus)
assert.Equal(s.T(), expected[i].RouterName, logline.RouterName)
assert.Equal(s.T(), expected[i].ServiceName, logline.ServiceName)
}
}
func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
func (s *AccessLogSuite) TestAccessLogRateLimit() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -424,42 +410,37 @@ func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "rateLimit")
s.waitForTraefik("rateLimit")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test rate limit
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"
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())
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
func (s *AccessLogSuite) TestAccessLogBackendNotFound() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -473,38 +454,33 @@ func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.waitForTraefik("server1")
waitForTraefik(c, "server1")
checkStatsForLogFile(c)
s.checkStatsForLogFile()
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test rate limit
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"
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
func (s *AccessLogSuite) TestAccessLogFrontendWhitelist() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -518,38 +494,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "frontendWhitelist")
s.waitForTraefik("frontendWhitelist")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test rate limit
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.whitelist.docker.local"
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -563,39 +534,34 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "authFrontend")
s.waitForTraefik("authFrontend")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test auth entrypoint
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.SetBasicAuth("test", "test")
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) {
func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
@ -609,79 +575,74 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) {
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.checkStatsForLogFile()
checkStatsForLogFile(c)
waitForTraefik(c, "preflightCORS")
s.waitForTraefik("preflightCORS")
// Verify Traefik started OK
checkTraefikStarted(c)
s.checkTraefikStarted()
// Test preflight response
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.Header.Set("Origin", "whatever")
req.Header.Set("Access-Control-Request-Method", "GET")
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
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
checkNoOtherTraefikProblems(c)
s.checkNoOtherTraefikProblems()
}
func checkNoOtherTraefikProblems(c *check.C) {
func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog))
}
}
func checkAccessLogOutput(c *check.C) int {
lines := extractLines(c)
func (s *AccessLogSuite) checkAccessLogOutput() int {
lines := s.extractLines()
count := 0
for i, line := range lines {
if len(line) > 0 {
count++
CheckAccessLogFormat(c, line, i)
s.CheckAccessLogFormat(line, i)
}
}
return count
}
func checkAccessLogExactValuesOutput(c *check.C, values []accessLogValue) int {
lines := extractLines(c)
func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
lines := s.extractLines()
count := 0
for i, line := range lines {
fmt.Println(line)
if len(line) > 0 {
count++
if values[i].formatOnly {
CheckAccessLogFormat(c, line, i)
s.CheckAccessLogFormat(line, i)
} else {
checkAccessLogExactValues(c, line, i, values[i])
s.checkAccessLogExactValues(line, i, values[i])
}
}
}
return count
}
func extractLines(c *check.C) []string {
func (s *AccessLogSuite) extractLines() []string {
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
lines := strings.Split(string(accessLog), "\n")
@ -694,14 +655,14 @@ func extractLines(c *check.C) []string {
return clean
}
func checkStatsForLogFile(c *check.C) {
func (s *AccessLogSuite) checkStatsForLogFile() {
err := try.Do(1*time.Second, func() error {
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
return fmt.Errorf("could not get stats for log file: %w", errStat)
}
return nil
})
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func ensureWorkingDirectoryIsClean() {
@ -709,69 +670,38 @@ func ensureWorkingDirectoryIsClean() {
os.Remove(traefikTestLogFile)
}
func checkTraefikStarted(c *check.C) []byte {
func (s *AccessLogSuite) checkTraefikStarted() []byte {
traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(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)
c.Assert(err, checker.IsNil)
c.Assert(results, checker.HasLen, 14)
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`)
require.NoError(s.T(), err)
assert.Len(s.T(), results, 14)
assert.Regexp(s.T(), `^(-|\d{3})$`, results[accesslog.OriginStatus])
count, _ := strconv.Atoi(results[accesslog.RequestCount])
c.Assert(count, checker.GreaterOrEqualThan, i+1)
c.Assert(results[accesslog.RouterName], checker.Matches, `"(rt-.+@docker|api@internal)"`)
c.Assert(results[accesslog.ServiceURL], checker.HasPrefix, `"http://`)
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
assert.GreaterOrEqual(s.T(), count, i+1)
assert.Regexp(s.T(), `"(rt-.+@docker|api@internal)"`, results[accesslog.RouterName])
assert.True(s.T(), strings.HasPrefix(results[accesslog.ServiceURL], `"http://`))
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)
c.Assert(err, checker.IsNil)
c.Assert(results, checker.HasLen, 14)
require.NoError(s.T(), err)
assert.Len(s.T(), results, 14)
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])
c.Assert(count, checker.GreaterOrEqualThan, i+1)
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?`+v.routerName+`.*(@docker)?$`)
c.Assert(results[accesslog.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`)
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
}
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)
}
}
assert.GreaterOrEqual(s.T(), count, i+1)
assert.Regexp(s.T(), `^"?`+v.routerName+`.*(@docker)?$`, results[accesslog.RouterName])
assert.Regexp(s.T(), `^"?`+v.serviceURL+`.*$`, results[accesslog.ServiceURL])
assert.Regexp(s.T(), `^\d+ms$`, results[accesslog.Duration])
}

View file

@ -8,16 +8,19 @@ import (
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-check/check"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/static"
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/provider/acme"
"github.com/traefik/traefik/v2/pkg/testhelpers"
"github.com/traefik/traefik/v2/pkg/types"
checker "github.com/vdemeester/shakers"
)
// ACME test suites.
@ -27,6 +30,10 @@ type AcmeSuite struct {
fakeDNSServer *dns.Server
}
func TestAcmeSuite(t *testing.T) {
suite.Run(t, new(AcmeSuite))
}
type subCases struct {
host string
expectedCommonName string
@ -87,17 +94,18 @@ func setupPebbleRootCA() (*http.Transport, error) {
}, nil
}
func (s *AcmeSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "pebble")
s.composeUp(c)
func (s *AcmeSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.fakeDNSServer = startFakeDNSServer(s.getContainerIP(c, "traefik"))
s.pebbleIP = s.getComposeServiceIP(c, "pebble")
s.createComposeProject("pebble")
s.composeUp()
// Retrieving the Docker host ip.
s.fakeDNSServer = startFakeDNSServer(s.hostIP)
s.pebbleIP = s.getComposeServiceIP("pebble")
pebbleTransport, err := setupPebbleRootCA()
if err != nil {
c.Fatal(err)
}
require.NoError(s.T(), err)
// wait for pebble
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)
})
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 {
err := s.fakeDNSServer.Shutdown()
if err != nil {
c.Log(err)
log.WithoutContext().Info(err.Error())
}
}
s.composeDown(c)
s.composeDown()
}
func (s *AcmeSuite) TestHTTP01Domains(c *check.C) {
func (s *AcmeSuite) TestHTTP01Domains() {
testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_store_domains.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_base.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_base.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_base.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_tcp.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_base.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
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{
traefikConfFilePath: "fixtures/acme/acme_domains.toml",
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.
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
file := s.adaptFile(c, "fixtures/acme/acme_base.toml", templateModel{
func (s *AcmeSuite) TestNoValidLetsEncryptServer() {
file := s.adaptFile("fixtures/acme/acme_base.toml", templateModel{
Acme: map[string]static.CertificateResolver{
"default": {ACME: &acme.Configuration{
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Expected traefik works
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
}
// 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 {
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)
defer os.Remove(file)
file := s.adaptFile(testCase.traefikConfFilePath, testCase.template)
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
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)
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")
return errGet
})
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
for _, sub := range testCase.subCases {
client = &http.Client{
@ -503,7 +505,7 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
var resp *http.Response
// 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)
if err != nil {
return err
@ -517,10 +519,10 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
return nil
})
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
// Check Domain into response certificate
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, sub.expectedCommonName)
c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, sub.expectedAlgorithm)
assert.Equal(s.T(), sub.expectedCommonName, resp.TLS.PeerCertificates[0].Subject.CommonName)
assert.Equal(s.T(), sub.expectedAlgorithm, resp.TLS.PeerCertificates[0].PublicKeyAlgorithm)
}
}

View file

@ -8,36 +8,38 @@ import (
"net/http"
"regexp"
"strconv"
"testing"
"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/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
checker "github.com/vdemeester/shakers"
)
type ThrottlingSuite struct{ BaseSuite }
func (s *ThrottlingSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "rest")
s.composeUp(c)
func TestThrottlingSuite(t *testing.T) {
suite.Run(t, new(ThrottlingSuite))
}
func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml"))
func (s *ThrottlingSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("rest")
s.composeUp()
}
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *ThrottlingSuite) TestThrottleConfReload() {
s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml"))
// wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
require.NoError(s.T(), err)
// 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)
require.NoError(s.T(), err)
config := &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
@ -47,7 +49,7 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
LoadBalancer: &dynamic.ServersLoadBalancer{
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++ {
config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
time.Sleep(200 * time.Millisecond)
}
reloadsRegexp := regexp.MustCompile(`traefik_config_reloads_total (\d*)\n`)
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()
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
fields := reloadsRegexp.FindStringSubmatch(string(body))
c.Assert(len(fields), checker.Equals, 2)
assert.Len(s.T(), fields, 2)
reloads, err := strconv.Atoi(fields[1])
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
// (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).
c.Assert(reloads, checker.LessOrEqualThan, 6)
assert.LessOrEqual(s.T(), reloads, 6)
}

View file

@ -4,13 +4,14 @@ import (
"fmt"
"net"
"net/http"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/hashicorp/consul/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type ConsulCatalogSuite struct {
@ -20,26 +21,36 @@ type ConsulCatalogSuite struct {
consulURL string
}
func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "consul_catalog")
s.composeUp(c)
func TestConsulCatalogSuite(t *testing.T) {
suite.Run(t, new(ConsulCatalogSuite))
}
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
s.consulClient, err = api.NewClient(&api.Config{
Address: s.consulURL,
})
c.Check(err, check.IsNil)
require.NoError(s.T(), err)
// Wait for consul to elect itself leader
err = s.waitToElectConsulLeader()
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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 {
@ -83,36 +94,36 @@ func (s *ConsulCatalogSuite) deregisterService(id string, onAgent bool) error {
return client.Agent().ServiceDeregister(id)
}
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *check.C) {
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings() {
reg1 := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
reg2 := &api.AgentServiceRegistration{
ID: "whoami2",
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami2"),
Address: s.getComposeServiceIP("whoami2"),
}
err = s.registerService(reg2, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
reg3 := &api.AgentServiceRegistration{
ID: "whoami3",
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami3"),
Address: s.getComposeServiceIP("whoami3"),
}
err = s.registerService(reg3, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
@ -120,23 +131,18 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
err = try.Request(req, 2*time.Second,
try.StatusCodeIs(200),
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,
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"`, reg3.Address),
))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("whoami1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("whoami2", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("whoami3", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
containerIP := s.getComposeServiceIP(c, "whoami1")
func (s *ConsulCatalogSuite) TestByLabels() {
containerIP := s.getComposeServiceIP("whoami1")
reg := &api.AgentServiceRegistration{
ID: "whoami1",
@ -171,7 +177,7 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
Address: containerIP,
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
@ -179,23 +185,18 @@ func (s *ConsulCatalogSuite) TestByLabels(c *check.C) {
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
func (s *ConsulCatalogSuite) TestSimpleConfiguration() {
tempObjects := struct {
ConsulAddress string
DefaultRule string
@ -204,37 +205,32 @@ func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
reg := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch() {
tempObjects := struct {
ConsulAddress string
DefaultRule string
@ -243,39 +239,34 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple_watch.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple_watch.toml", tempObjects)
reg := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
Tags: []string{"traefik.enable=true"},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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{
CheckID: "some-ok-check",
TCP: whoamiIP + ":80",
@ -285,10 +276,10 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
}
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"))
c.Assert(err, checker.IsNil)
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1"))
require.NoError(s.T(), err)
reg.Check = &api.AgentServiceCheck{
CheckID: "some-failing-check",
@ -299,16 +290,16 @@ func (s *ConsulCatalogSuite) TestSimpleConfigurationWithWatch(c *check.C) {
}
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))
c.Assert(err, checker.IsNil)
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
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 {
ConsulAddress string
DefaultRule string
@ -317,8 +308,7 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
reg := &api.AgentServiceRegistration{
ID: "whoami1",
@ -328,25 +318,21 @@ func (s *ConsulCatalogSuite) TestRegisterServiceWithoutIP(c *check.C) {
Address: "",
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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\""))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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 {
ConsulAddress string
DefaultRule string
@ -355,37 +341,32 @@ func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
reg := &api.AgentServiceRegistration{
ID: "whoami1",
Name: "whoami",
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels() {
tempObjects := struct {
ConsulAddress string
DefaultRule string
@ -394,8 +375,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
// Start a container with some tags
reg := &api.AgentServiceRegistration{
@ -407,32 +387,28 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) {
"traefik.tcp.Services.Super.Loadbalancer.server.port=8080",
},
Port: 8080,
Address: s.getComposeServiceIP(c, "whoamitcp"),
Address: s.getComposeServiceIP("whoamitcp"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
func (s *ConsulCatalogSuite) TestConsulServiceWithLabels() {
tempObjects := struct {
ConsulAddress string
DefaultRule string
@ -441,8 +417,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
// Start a container with some tags
reg1 := &api.AgentServiceRegistration{
@ -452,11 +427,11 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
"traefik.http.Routers.Super.Rule=Host(`my.super.host`)",
},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start another container by replacing a '.' by a '-'
reg2 := &api.AgentServiceRegistration{
@ -466,40 +441,36 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) {
"traefik.http.Routers.SuperHost.Rule=Host(`my-super.host`)",
},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami2"),
Address: s.getComposeServiceIP("whoami2"),
}
err = s.registerService(reg2, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
req.Host = "my.super.host"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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 {
ConsulAddress string
DefaultRule string
@ -508,8 +479,7 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/default_not_exposed.toml", tempObjects)
// Start a container with some tags
tags := []string{
@ -523,50 +493,46 @@ func (s *ConsulCatalogSuite) TestSameServiceIDOnDifferentConsulAgent(c *check.C)
Name: "whoami",
Tags: tags,
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
reg2 := &api.AgentServiceRegistration{
ID: "whoami",
Name: "whoami",
Tags: tags,
Port: 80,
Address: s.getComposeServiceIP(c, "whoami2"),
Address: s.getComposeServiceIP("whoami2"),
}
err = s.registerService(reg2, true)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200),
try.BodyContainsOr(s.getComposeServiceIP(c, "whoami1"), s.getComposeServiceIP(c, "whoami2")))
c.Assert(err, checker.IsNil)
try.BodyContainsOr(s.getComposeServiceIP("whoami1"), s.getComposeServiceIP("whoami2")))
require.NoError(s.T(), err)
err = s.deregisterService("whoami", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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 {
ConsulAddress string
DefaultRule string
@ -575,8 +541,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)",
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
// Start a container with some tags
reg := &api.AgentServiceRegistration{
@ -586,32 +551,28 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) {
"traefik.random.value=my.super.host",
},
Port: 80,
Address: s.getComposeServiceIP(c, "whoami1"),
Address: s.getComposeServiceIP("whoami1"),
}
err := s.registerService(reg, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
// 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
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) {
whoamiIP := s.getComposeServiceIP(c, "whoami1")
func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck() {
whoamiIP := s.getComposeServiceIP("whoami1")
tags := []string{
"traefik.enable=true",
"traefik.http.routers.router1.rule=Path(`/whoami`)",
@ -635,7 +596,7 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
}
err := s.registerService(reg1, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
@ -643,22 +604,17 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/simple.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
whoami2IP := s.getComposeServiceIP(c, "whoami2")
whoami2IP := s.getComposeServiceIP("whoami2")
reg2 := &api.AgentServiceRegistration{
ID: "whoami2",
Name: "whoami",
@ -675,26 +631,26 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) {
}
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
req.Host = "whoami"
// 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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
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{
ID: "uuid-api1",
Name: "uuid-api",
@ -712,9 +668,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
Address: connectIP,
}
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{
ID: "whoami1",
Name: "whoami",
@ -727,40 +683,35 @@ func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) {
Address: whoamiIP,
}
err = s.registerService(regWhoami, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/connect.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("uuid-api1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
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{
ID: "uuid-api1",
Name: "uuid-api",
@ -777,9 +728,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
Address: connectIP,
}
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{
ID: "whoami1",
Name: "whoami1",
@ -792,9 +743,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
Address: whoamiIP,
}
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{
ID: "whoami2",
Name: "whoami2",
@ -808,45 +759,40 @@ func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) {
Address: whoami2IP,
}
err = s.registerService(regWhoami2, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect_by_default.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/connect_by_default.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("uuid-api1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("whoami1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
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{
ID: "uuid-api1",
Name: "uuid-api",
@ -864,9 +810,9 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
Address: connectIP,
}
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{
ID: "whoami1",
Name: "whoami",
@ -879,30 +825,25 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) {
Address: whoamiIP,
}
err = s.registerService(regWhoami, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tempObjects := struct {
ConsulAddress string
}{
ConsulAddress: s.consulURL,
}
file := s.adaptFile(c, "fixtures/consul_catalog/connect_not_aware.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/consul_catalog/connect_not_aware.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("uuid-api1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = s.deregisterService("whoami1", false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -10,16 +10,17 @@ import (
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-check/check"
"github.com/kvtools/consul"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
checker "github.com/vdemeester/shakers"
)
// Consul test suites.
@ -29,18 +30,16 @@ type ConsulSuite struct {
consulURL string
}
func (s *ConsulSuite) resetStore(c *check.C) {
err := s.kvClient.DeleteTree(context.Background(), "traefik")
if err != nil && !errors.Is(err, store.ErrKeyNotFound) {
c.Fatal(err)
}
func TestConsulSuite(t *testing.T) {
suite.Run(t, new(ConsulSuite))
}
func (s *ConsulSuite) setupStore(c *check.C) {
s.createComposeProject(c, "consul")
s.composeUp(c)
func (s *ConsulSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
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)
kv, err := valkeyrie.NewStore(
@ -51,21 +50,27 @@ func (s *ConsulSuite) setupStore(c *check.C) {
ConnectionTimeout: 10 * time.Second,
},
)
if err != nil {
c.Fatal("Cannot create store consul")
}
require.NoError(s.T(), err, "Cannot create store consul")
s.kvClient = kv
// wait for consul
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) {
s.setupStore(c)
func (s *ConsulSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
defer os.Remove(file)
func (s *ConsulSuite) TearDownTest() {
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{
"traefik/http/routers/Router0/entryPoints/0": "web",
@ -115,39 +120,35 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
for k, v := range data {
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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":`),
)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
err = json.NewDecoder(resp.Body).Decode(&obtained)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
got, err := json.MarshalIndent(obtained, "", " ")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash("testdata/rawdata-consul.json")
if *updateExpected {
err = os.WriteFile(expectedJSON, got, 0o666)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
expected, err := os.ReadFile(expectedJSON)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if !bytes.Equal(expected, got) {
diff := difflib.UnifiedDiff{
@ -159,33 +160,27 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
}
text, err := difflib.GetUnifiedDiffString(diff)
c.Assert(err, checker.IsNil)
c.Error(text)
require.NoError(s.T(), err, 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)
if err != nil {
c.Fatal(err)
}
require.NoError(s.T(), err)
req.Host = host
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
require.NoError(s.T(), err)
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
s.setupStore(c)
s.resetStore(c)
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
defer os.Remove(file)
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
ctx := context.Background()
svcaddr := net.JoinHostPort(s.getComposeServiceIP(c, "whoami"), "80")
svcaddr := net.JoinHostPort(s.getComposeServiceIP("whoami"), "80")
data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web",
@ -202,32 +197,28 @@ func (s *ConsulSuite) TestDeleteRootKey(c *check.C) {
for k, v := range data {
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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":`),
)
c.Assert(err, checker.IsNil)
s.assertWhoami(c, "kv1.localhost", http.StatusOK)
s.assertWhoami(c, "kv2.localhost", http.StatusOK)
require.NoError(s.T(), err)
s.assertWhoami("kv1.localhost", http.StatusOK)
s.assertWhoami("kv2.localhost", http.StatusOK)
// delete router1
err = s.kvClient.DeleteTree(ctx, "traefik/http/routers/Router1")
c.Assert(err, checker.IsNil)
s.assertWhoami(c, "kv1.localhost", http.StatusOK)
s.assertWhoami(c, "kv2.localhost", http.StatusNotFound)
require.NoError(s.T(), err)
s.assertWhoami("kv1.localhost", http.StatusOK)
s.assertWhoami("kv2.localhost", http.StatusNotFound)
// delete simple services and router0
err = s.kvClient.DeleteTree(ctx, "traefik")
c.Assert(err, checker.IsNil)
s.assertWhoami(c, "kv1.localhost", http.StatusNotFound)
s.assertWhoami(c, "kv2.localhost", http.StatusNotFound)
require.NoError(s.T(), err)
s.assertWhoami("kv1.localhost", http.StatusNotFound)
s.assertWhoami("kv2.localhost", http.StatusNotFound)
}

View file

@ -3,15 +3,16 @@ package integration
import (
"encoding/json"
"net/http"
"os"
"strings"
"testing"
"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/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
"github.com/traefik/traefik/v2/pkg/testhelpers"
checker "github.com/vdemeester/shakers"
)
// Docker tests suite.
@ -19,12 +20,21 @@ type DockerComposeSuite struct {
BaseSuite
}
func (s *DockerComposeSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "minimal")
s.composeUp(c)
func TestDockerComposeSuite(t *testing.T) {
suite.Run(t, new(DockerComposeSuite))
}
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 {
DockerHost string
DefaultRule string
@ -32,45 +42,36 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
DockerHost: s.getDockerHost(),
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/minimal.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/minimal.toml", tempObjects)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
req.Host = "my.super.host"
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
c.Assert(err, checker.IsNil)
_, err := try.ResponseUntilStatusCode(req, 5*time.Second, http.StatusOK)
require.NoError(s.T(), err)
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()
var rtconf api.RunTimeRepresentation
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)
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
services := rtconf.Services
c.Assert(services, checker.HasLen, 4)
assert.Len(s.T(), services, 4)
for name, service := range services {
if strings.HasSuffix(name, "@internal") {
continue
}
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
c.Assert(name, checker.Equals, "whoami1-"+composeProject.Name+"@docker")
c.Assert(service.LoadBalancer.Servers, checker.HasLen, 2)
assert.Equal(s.T(), "service-mini@docker", name)
assert.Len(s.T(), service.LoadBalancer.Servers, 2)
// We could break here, but we don't just to keep us honest.
}
}

View file

@ -2,15 +2,15 @@ package integration
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// Docker tests suite.
@ -18,15 +18,24 @@ type DockerSuite struct {
BaseSuite
}
func (s *DockerSuite) SetUpTest(c *check.C) {
s.createComposeProject(c, "docker")
func TestDockerSuite(t *testing.T) {
suite.Run(t, new(DockerSuite))
}
func (s *DockerSuite) TearDownTest(c *check.C) {
s.composeDown(c)
func (s *DockerSuite) SetupSuite() {
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 {
DockerHost string
DefaultRule string
@ -35,24 +44,18 @@ func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c)
s.composeUp()
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
}
func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
func (s *DockerSuite) TestDefaultDockerContainers() {
tempObjects := struct {
DockerHost string
DefaultRule string
@ -61,41 +64,30 @@ func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c, "simple")
s.composeUp("simple")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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 = "simple.docker.localhost"
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
req.Host = fmt.Sprintf("simple-%s.docker.localhost", composeProject.Name)
// 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)
c.Assert(err, checker.IsNil)
resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
var version map[string]interface{}
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
assert.NoError(s.T(), json.Unmarshal(body, &version))
assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
}
func (s *DockerSuite) TestDockerContainersWithTCPLabels(c *check.C) {
func (s *DockerSuite) TestDockerContainersWithTCPLabels() {
tempObjects := struct {
DockerHost string
DefaultRule string
@ -104,29 +96,23 @@ func (s *DockerSuite) TestDockerContainersWithTCPLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c, "withtcplabels")
s.composeUp("withtcplabels")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)"))
require.NoError(s.T(), err)
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 {
DockerHost string
DefaultRule string
@ -135,44 +121,37 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c, "withlabels1", "withlabels2")
s.composeUp("withlabels1", "withlabels2")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
// TODO Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
c.Assert(err, checker.IsNil)
_, err = try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
require.NoError(s.T(), err)
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"
// 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)
c.Assert(err, checker.IsNil)
resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
var version map[string]interface{}
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
assert.NoError(s.T(), json.Unmarshal(body, &version))
assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
}
func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
func (s *DockerSuite) TestDockerContainersWithOneMissingLabels() {
tempObjects := struct {
DockerHost string
DefaultRule string
@ -181,31 +160,23 @@ func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c, "withonelabelmissing")
s.composeUp("withonelabelmissing")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
// 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
err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err = try.Request(req, 3*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
}
func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
func (s *DockerSuite) TestRestartDockerContainers() {
tempObjects := struct {
DockerHost string
DefaultRule string
@ -214,46 +185,40 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile(c, "fixtures/docker/simple.toml", tempObjects)
defer os.Remove(file)
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp(c, "powpow")
s.composeUp("powpow")
// Start traefik
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
// 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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
var version map[string]interface{}
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
assert.NoError(s.T(), json.Unmarshal(body, &version))
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
s.composeStop(c, "powpow")
s.composeStop("powpow")
time.Sleep(5 * time.Second)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("powpow"))
c.Assert(err, checker.NotNil)
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -3,12 +3,12 @@ package integration
import (
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// ErrorPagesSuite test suites.
@ -18,83 +18,78 @@ type ErrorPagesSuite struct {
BackendIP string
}
func (s *ErrorPagesSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "error_pages")
s.composeUp(c)
s.ErrorPageIP = s.getComposeServiceIP(c, "nginx2")
s.BackendIP = s.getComposeServiceIP(c, "nginx1")
func TestErrorPagesSuite(t *testing.T) {
suite.Run(t, new(ErrorPagesSuite))
}
func (s *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/error_pages/simple.toml", struct {
func (s *ErrorPagesSuite) SetupSuite() {
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
Server2 string
}{"http://" + s.BackendIP + ":80", s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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
file := s.adaptFile(c, "fixtures/error_pages/error.toml", struct {
file := s.adaptFile("fixtures/error_pages/error.toml", struct {
Server1 string
Server2 string
}{s.BackendIP, s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
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) {
rw.Header().Add("Transfer-Encoding", "chunked")
rw.WriteHeader(http.StatusInternalServerError)
_, _ = 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
Server2 string
}{srv.URL, s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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"
err = try.Request(frontendReq, 2*time.Second,
try.BodyContains("An error occurred."),
try.HasHeaderValue("Content-Type", "text/html", true),
)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -8,16 +8,17 @@ import (
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-check/check"
"github.com/kvtools/etcdv3"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
checker "github.com/vdemeester/shakers"
)
// etcd test suites.
@ -27,12 +28,18 @@ type EtcdSuite struct {
etcdAddr string
}
func (s *EtcdSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "etcd")
s.composeUp(c)
func TestEtcdSuite(t *testing.T) {
suite.Run(t, new(EtcdSuite))
}
func (s *EtcdSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("etcd")
s.composeUp()
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(
context.Background(),
etcdv3.StoreName,
@ -41,18 +48,19 @@ func (s *EtcdSuite) SetUpSuite(c *check.C) {
ConnectionTimeout: 10 * time.Second,
},
)
if err != nil {
c.Fatal("Cannot create store etcd")
}
require.NoError(s.T(), err)
// wait for etcd
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) {
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
defer os.Remove(file)
func (s *EtcdSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *EtcdSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/etcd/simple.toml", struct{ EtcdAddress string }{s.etcdAddr})
data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web",
@ -102,39 +110,35 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
for k, v := range data {
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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":`),
)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
err = json.NewDecoder(resp.Body).Decode(&obtained)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
got, err := json.MarshalIndent(obtained, "", " ")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash("testdata/rawdata-etcd.json")
if *updateExpected {
err = os.WriteFile(expectedJSON, got, 0o666)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
expected, err := os.ReadFile(expectedJSON)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if !bytes.Equal(expected, got) {
diff := difflib.UnifiedDiff{
@ -146,7 +150,6 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
}
text, err := difflib.GetUnifiedDiffString(diff)
c.Assert(err, checker.IsNil)
c.Error(text)
require.NoError(s.T(), err, text)
}
}

View file

@ -2,61 +2,58 @@ package integration
import (
"net/http"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// File tests suite.
type FileSuite struct{ BaseSuite }
func (s *FileSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "file")
s.composeUp(c)
func TestFileSuite(t *testing.T) {
suite.Run(t, new(FileSuite))
}
func (s *FileSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/file/simple.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *FileSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("file")
s.composeUp()
}
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
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
}
// #56 regression test, make sure it does not fail?
func (s *FileSuite) TestSimpleConfigurationNoPanic(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *FileSuite) TestSimpleConfigurationNoPanic() {
s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml"))
// 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)
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
}
func (s *FileSuite) TestDirectoryConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/directory.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *FileSuite) TestDirectoryConfiguration() {
s.traefikCmd(withConfigFile("fixtures/file/directory.toml"))
// 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))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/test", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
// 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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -6,10 +6,14 @@
level = "DEBUG"
[entryPoints]
[entryPoints.web]
[entryPoints.trust]
address = ":8000"
[entryPoints.web.proxyProtocol]
trustedIPs = ["{{.HaproxyIP}}"]
[entryPoints.trust.proxyProtocol]
trustedIPs = ["127.0.0.1"]
[entryPoints.nottrust]
address = ":9000"
[entryPoints.nottrust.proxyProtocol]
trustedIPs = ["1.2.3.4"]
[api]
insecure = true

View file

@ -1,32 +0,0 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
[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}}"

View file

@ -8,9 +8,11 @@ import (
"math/rand"
"net"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/helloworld"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
@ -29,16 +31,20 @@ const randCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567
// GRPCSuite tests suite.
type GRPCSuite struct{ BaseSuite }
func TestGRPCSuite(t *testing.T) {
suite.Run(t, new(GRPCSuite))
}
type myserver struct {
stopStreamExample chan bool
}
func (s *GRPCSuite) SetUpSuite(c *check.C) {
func (s *GRPCSuite) SetupSuite() {
var err error
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")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}
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
}
func (s *GRPCSuite) TestGRPC(c *check.C) {
func (s *GRPCSuite) TestGRPC() {
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := startGRPCServer(lis, &myserver{})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -159,79 +164,65 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
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
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
err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true)
return err
})
c.Assert(err, check.IsNil)
c.Assert(response, check.Equals, "Hello World")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "Hello World", response)
}
func (s *GRPCSuite) TestGRPCh2c(c *check.C) {
func (s *GRPCSuite) TestGRPCh2c() {
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := starth2cGRPCServer(lis, &myserver{})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config_h2c.toml", struct {
file := s.adaptFile("fixtures/grpc/config_h2c.toml", struct {
GRPCServerPort string
}{
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
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
err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", false)
return err
})
c.Assert(err, check.IsNil)
c.Assert(response, check.Equals, "Hello World")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "Hello World", response)
}
func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
func (s *GRPCSuite) TestGRPCh2cTermination() {
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := starth2cGRPCServer(lis, &myserver{})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config_h2c_termination.toml", struct {
file := s.adaptFile("fixtures/grpc/config_h2c_termination.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -241,40 +232,33 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
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
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
err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true)
return err
})
c.Assert(err, check.IsNil)
c.Assert(response, check.Equals, "Hello World")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "Hello World", response)
}
func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
func (s *GRPCSuite) TestGRPCInsecure() {
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := startGRPCServer(lis, &myserver{})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config_insecure.toml", struct {
file := s.adaptFile("fixtures/grpc/config_insecure.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -284,44 +268,37 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
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
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
err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true)
return err
})
c.Assert(err, check.IsNil)
c.Assert(response, check.Equals, "Hello World")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "Hello World", response)
}
func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
func (s *GRPCSuite) TestGRPCBuffer() {
stopStreamExample := make(chan bool)
defer func() { stopStreamExample <- true }()
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := startGRPCServer(lis, &myserver{
stopStreamExample: stopStreamExample,
})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -331,27 +308,21 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
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
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
client, closer, err := callStreamExampleClientGRPC()
defer func() { _ = closer() }()
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
received := make(chan bool)
go func() {
tr, err := client.Recv()
c.Assert(err, check.IsNil)
c.Assert(len(tr.GetData()), check.Equals, 512)
assert.NoError(s.T(), err)
assert.Len(s.T(), tr.GetData(), 512)
received <- true
}()
@ -363,25 +334,24 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
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)
lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := startGRPCServer(lis, &myserver{
stopStreamExample: stopStreamExample,
})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -390,17 +360,11 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
KeyContent: string(LocalhostKey),
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
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
client, closer, err := callStreamExampleClientGRPC()
@ -408,13 +372,13 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
_ = closer()
stopStreamExample <- true
}()
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
received := make(chan bool)
go func() {
tr, err := client.Recv()
c.Assert(err, check.IsNil)
c.Assert(len(tr.GetData()), check.Equals, 512)
assert.NoError(s.T(), err)
assert.Len(s.T(), tr.GetData(), 512)
received <- true
}()
@ -426,22 +390,21 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
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")
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
go func() {
err := startGRPCServer(lis, &myserver{})
c.Log(err)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
}()
file := s.adaptFile(c, "fixtures/grpc/config_retry.toml", struct {
file := s.adaptFile("fixtures/grpc/config_retry.toml", struct {
CertContent string
KeyContent string
GRPCServerPort string
@ -451,23 +414,17 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
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
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
err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true)
return err
})
c.Assert(err, check.IsNil)
c.Assert(response, check.Equals, "Hello World")
assert.NoError(s.T(), err)
assert.Equal(s.T(), "Hello World", response)
}

View file

@ -4,50 +4,45 @@ import (
"net"
"net/http"
"net/http/httptest"
"os"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// Headers tests suite.
type HeadersSuite struct{ BaseSuite }
func (s *HeadersSuite) TestSimpleConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/headers/basic.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
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
func TestHeadersSuite(t *testing.T) {
suite.Run(t, new(HeadersSuite))
}
func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) {
file := s.adaptFile(c, "fixtures/headers/remove_reverseproxy_headers.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
func (s *HeadersSuite) TestSimpleConfiguration() {
s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
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))
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) {
_, found := r.Header["X-Forwarded-Host"]
c.Assert(found, checker.True)
assert.True(s.T(), found)
_, found = r.Header["Foo"]
c.Assert(found, checker.False)
assert.False(s.T(), found)
_, 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")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
ts := &httptest.Server{
Listener: listener,
@ -57,31 +52,25 @@ func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) {
defer ts.Close()
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.Header = http.Header{
"Foo": {"bar"},
}
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) {
file := s.adaptFile(c, "fixtures/headers/cors.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *HeadersSuite) TestCorsResponses() {
file := s.adaptFile("fixtures/headers/cors.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
testCase := []struct {
desc string
@ -147,30 +136,24 @@ func (s *HeadersSuite) TestCorsResponses(c *check.C) {
for _, test := range testCase {
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.Header = test.requestHeaders
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) {
file := s.adaptFile(c, "fixtures/headers/secure.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *HeadersSuite) TestSecureHeadersResponses() {
file := s.adaptFile("fixtures/headers/secure.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
testCase := []struct {
desc string
@ -190,36 +173,30 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
for _, test := range testCase {
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
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
req.Host = test.internalReqHost
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) {
file := s.adaptFile(c, "fixtures/headers/secure_multiple.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *HeadersSuite) TestMultipleSecureHeadersResponses() {
file := s.adaptFile("fixtures/headers/secure_multiple.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
testCase := []struct {
desc string
@ -238,10 +215,10 @@ func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) {
for _, test := range testCase {
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
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
}

View file

@ -7,11 +7,13 @@ import (
"net/http"
"os"
"strings"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// HealthCheck test suites.
@ -23,287 +25,272 @@ type HealthCheckSuite struct {
whoami4IP string
}
func (s *HealthCheckSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "healthcheck")
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 TestHealthCheckSuite(t *testing.T) {
suite.Run(t, new(HealthCheckSuite))
}
func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/simple.toml", struct {
func (s *HealthCheckSuite) SetupSuite() {
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
Server2 string
}{s.whoami1IP, s.whoami2IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
require.NoError(s.T(), err)
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"
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
client := &http.Client{}
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
for _, whoami := range whoamiHosts {
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
// Verify no backend service is available due to failing health checks
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
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Verify frontend health : after
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
frontendReq.Host = "test.localhost"
// Check if whoami1 responds
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.
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
resp, err := http.Get("http://127.0.0.1:8000/")
// Expected a 404 as we did not configure anything
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusNotFound, resp.StatusCode)
}
func (s *HealthCheckSuite) TestMultipleEntrypoints(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/multiple-entrypoints.toml", struct {
func (s *HealthCheckSuite) TestMultipleEntrypoints() {
file := s.adaptFile("fixtures/healthcheck/multiple-entrypoints.toml", struct {
Server1 string
Server2 string
}{s.whoami1IP, s.whoami2IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Wait for traefik
err = try.GetRequest("http://localhost:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
require.NoError(s.T(), err)
// Check entrypoint http1
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"
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
// Check entrypoint http2
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"
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
// Set the both whoami health to 500
client := &http.Client{}
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
for _, whoami := range whoamiHosts {
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
// Verify no backend service is available due to failing health checks
err = try.Request(frontendHealthReq, 5*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// reactivate the whoami2
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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"
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"
// Check if whoami1 never responds
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
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
client := &http.Client{}
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)
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
}{s.whoami1IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
frontendHealthReq.Host = "test.localhost"
// We test bad gateway because we use an invalid port for the backend
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
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Verify no backend service is available due to failing health checks
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.
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService() {
file := s.adaptFile("fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
Server1 string
}{s.whoami1IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
require.NoError(s.T(), err)
// Set whoami health to 200 to be sure to start with the wanted status
client := &http.Client{}
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// check healthcheck on web1 entrypoint
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"
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
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"
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
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Verify no backend service is available due to failing health checks
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Change one whoami health to 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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Verify health check
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *HealthCheckSuite) TestPropagate(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/propagate.toml", struct {
func (s *HealthCheckSuite) TestPropagate() {
file := s.adaptFile("fixtures/healthcheck/propagate.toml", struct {
Server1 string
Server2 string
Server3 string
Server4 string
}{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
require.NoError(s.T(), err)
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"
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
client := http.Client{
@ -313,9 +300,9 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
whoamiHosts := []string{s.whoami1IP, s.whoami3IP}
for _, whoami := range whoamiHosts {
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
try.Sleep(time.Second)
@ -327,19 +314,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
reachedServers := make(map[string]int)
for i := 0; i < 4; i++ {
resp, err := client.Do(rootReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if reachedServers[s.whoami4IP] > reachedServers[s.whoami2IP] {
c.Assert(string(body), checker.Contains, want2)
assert.Contains(s.T(), string(body), want2)
reachedServers[s.whoami2IP]++
continue
}
if reachedServers[s.whoami2IP] > reachedServers[s.whoami4IP] {
c.Assert(string(body), checker.Contains, want4)
assert.Contains(s.T(), string(body), want4)
reachedServers[s.whoami4IP]++
continue
}
@ -356,48 +343,48 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
}
}
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 2)
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 2)
assert.Equal(s.T(), 2, reachedServers[s.whoami2IP])
assert.Equal(s.T(), 2, reachedServers[s.whoami4IP])
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"
// Verify load-balancing on foo still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
want := `IP: ` + s.whoami2IP
for i := 0; i < 4; i++ {
resp, err := client.Do(fooReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
barReq.Host = "bar.localhost"
// Verify load-balancing on bar still works, and that we're getting wsp2, wsp2, wsp2, wsp2, etc.
want = `IP: ` + s.whoami2IP
for i := 0; i < 4; i++ {
resp, err := client.Do(barReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
whoamiHosts = []string{s.whoami2IP, s.whoami4IP}
for _, whoami := range whoamiHosts {
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
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.
for i := 0; i < 2; i++ {
resp, err := client.Do(rootReq)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
resp, err = client.Do(fooReq)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
resp, err = client.Do(barReq)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusServiceUnavailable)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusServiceUnavailable, resp.StatusCode)
}
// Bring everything back up.
whoamiHosts = []string{s.whoami1IP, s.whoami2IP, s.whoami3IP, s.whoami4IP}
for _, whoami := range whoamiHosts {
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
try.Sleep(time.Second)
@ -432,10 +419,10 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
reachedServers = make(map[string]int)
for i := 0; i < 4; i++ {
resp, err := client.Do(rootReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
reachedServers[s.whoami1IP]++
@ -458,19 +445,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
}
}
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 1)
assert.Equal(s.T(), 1, reachedServers[s.whoami1IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami4IP])
// Verify everything is up on foo router.
reachedServers = make(map[string]int)
for i := 0; i < 4; i++ {
resp, err := client.Do(fooReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
reachedServers[s.whoami1IP]++
@ -493,19 +480,19 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
}
}
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 2)
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 0)
assert.Equal(s.T(), 2, reachedServers[s.whoami1IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
assert.Equal(s.T(), 0, reachedServers[s.whoami4IP])
// Verify everything is up on bar router.
reachedServers = make(map[string]int)
for i := 0; i < 4; i++ {
resp, err := client.Do(barReq)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if strings.Contains(string(body), `IP: `+s.whoami1IP) {
reachedServers[s.whoami1IP]++
@ -528,102 +515,87 @@ func (s *HealthCheckSuite) TestPropagate(c *check.C) {
}
}
c.Assert(reachedServers[s.whoami1IP], checker.Equals, 2)
c.Assert(reachedServers[s.whoami2IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami3IP], checker.Equals, 1)
c.Assert(reachedServers[s.whoami4IP], checker.Equals, 0)
assert.Equal(s.T(), 2, reachedServers[s.whoami1IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami2IP])
assert.Equal(s.T(), 1, reachedServers[s.whoami3IP])
assert.Equal(s.T(), 0, reachedServers[s.whoami4IP])
}
func (s *HealthCheckSuite) TestPropagateNoHealthCheck(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/propagate_no_healthcheck.toml", struct {
func (s *HealthCheckSuite) TestPropagateNoHealthCheck() {
file := s.adaptFile("fixtures/healthcheck/propagate_no_healthcheck.toml", struct {
Server1 string
}{s.whoami1IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`noop.localhost`)"), try.BodyNotContains("Host(`root.localhost`)"))
require.NoError(s.T(), err)
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"
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)
withoutHealthCheck := s.adaptFile(c, "fixtures/healthcheck/reload_without_healthcheck.toml", struct {
withoutHealthCheck := s.adaptFile("fixtures/healthcheck/reload_without_healthcheck.toml", struct {
Server1 string
Server2 string
}{s.whoami1IP, s.whoami2IP})
defer os.Remove(withoutHealthCheck)
withHealthCheck := s.adaptFile(c, "fixtures/healthcheck/reload_with_healthcheck.toml", struct {
withHealthCheck := s.adaptFile("fixtures/healthcheck/reload_with_healthcheck.toml", struct {
Server1 string
Server2 string
}{s.whoami1IP, s.whoami2IP})
defer os.Remove(withHealthCheck)
cmd, display := s.traefikCmd(withConfigFile(withoutHealthCheck))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(withoutHealthCheck))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`root.localhost`)"))
require.NoError(s.T(), err)
// Allow one of the underlying services on it to fail all servers HC (whoami2)
client := http.Client{
Timeout: 10 * time.Second,
}
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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"
// Check the failed service (whoami2) is getting requests, but answer 500
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
fr1, err := os.OpenFile(withoutHealthCheck, os.O_APPEND|os.O_WRONLY, 0o644)
c.Assert(fr1, checker.NotNil)
c.Assert(err, checker.IsNil)
assert.NotNil(s.T(), fr1)
require.NoError(s.T(), err)
err = fr1.Truncate(0)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
fr2, err := os.ReadFile(withHealthCheck)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, err = fmt.Fprint(fr1, string(fr2))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
wantIPs := []string{s.whoami1IP, s.whoami1IP, s.whoami1IP, s.whoami1IP}
for _, ip := range wantIPs {
want := "IP: " + ip
resp, err := client.Do(rootReq)
c.Assert(err, checker.IsNil)
body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
c.Assert(string(body), checker.Contains, want)
err = try.Request(rootReq, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("IP: "+ip))
require.NoError(s.T(), err)
}
}

View file

@ -2,27 +2,33 @@ package integration
import (
"net/http"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type HostResolverSuite struct{ BaseSuite }
func (s *HostResolverSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "hostresolver")
s.composeUp(c)
func TestHostResolverSuite(t *testing.T) {
suite.Run(t, new(HostResolverSuite))
}
func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/simple_hostresolver.toml"))
defer display(c)
func (s *HostResolverSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.createComposeProject("hostresolver")
s.composeUp()
}
func (s *HostResolverSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *HostResolverSuite) TestSimpleConfig() {
s.traefikCmd(withConfigFile("fixtures/simple_hostresolver.toml"))
testCase := []struct {
desc string
@ -43,10 +49,10 @@ func (s *HostResolverSuite) TestSimpleConfig(c *check.C) {
for _, test := range testCase {
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
err = try.Request(req, 1*time.Second, try.StatusCodeIs(test.status), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.Request(req, 5*time.Second, try.StatusCodeIs(test.status), try.HasBody())
require.NoError(s.T(), err)
}
}

View file

@ -5,28 +5,27 @@ import (
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
checker "github.com/vdemeester/shakers"
)
type HTTPSuite struct{ BaseSuite }
func (s *HTTPSuite) TestSimpleConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/http/simple.toml"))
defer display(c)
func TestHTTPSuite(t *testing.T) {
suite.Run(t, new(HTTPSuite))
}
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
func (s *HTTPSuite) TestSimpleConfiguration() {
s.traefikCmd(withConfigFile("fixtures/http/simple.toml"))
// Expect a 404 as we configured nothing.
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
// Provide a configuration, fetched by Traefik provider.
configuration := &dynamic.Configuration{
@ -55,14 +54,14 @@ func (s *HTTPSuite) TestSimpleConfiguration(c *check.C) {
}
configData, err := json.Marshal(configuration)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
server := startTestServerWithResponse(configData)
defer server.Close()
// 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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func startTestServerWithResponse(response []byte) (ts *httptest.Server) {

File diff suppressed because it is too large Load diff

View file

@ -9,210 +9,325 @@ import (
"fmt"
"io"
"io/fs"
stdlog "log"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"slices"
"strings"
"testing"
"text/template"
"time"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/context/docker"
"github.com/docker/cli/cli/context/store"
manifeststore "github.com/docker/cli/cli/manifest/store"
registryclient "github.com/docker/cli/cli/registry/client"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
cmdcompose "github.com/docker/compose/v2/cmd/compose"
"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/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
dockernetwork "github.com/docker/docker/api/types/network"
"github.com/fatih/structs"
"github.com/go-check/check"
notaryclient "github.com/theupdateframework/notary/client"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/network"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
"gopkg.in/yaml.v3"
)
var (
integration = flag.Bool("integration", false, "run integration tests")
showLog = flag.Bool("tlog", false, "always show Traefik logs")
)
var showLog = flag.Bool("tlog", false, "always show Traefik logs")
func Test(t *testing.T) {
if !*integration {
log.WithoutContext().Info("Integration tests disabled.")
return
}
type composeConfig struct {
Services map[string]composeService `yaml:"services"`
}
// 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
}
}
type composeService struct {
Image string `yaml:"image"`
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"`
}
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{})
check.Suite(&MarathonSuite{})
check.Suite(&MarathonSuite15{})
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)
type composeDeploy struct {
Replicas int `yaml:"replicas"`
}
var traefikBinary = "../dist/traefik"
type BaseSuite struct {
composeProjectOptions *cmdcompose.ProjectOptions
dockerComposeService composeapi.Service
dockerClient *client.Client
suite.Suite
containers map[string]testcontainers.Container
network *testcontainers.DockerNetwork
hostIP string
}
func (s *BaseSuite) TearDownSuite(c *check.C) {
if s.composeProjectOptions != nil && s.dockerComposeService != nil {
s.composeDown(c)
func (s *BaseSuite) waitForTraefik(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)
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.
// This method should be called before starting and/or stopping compose services.
func (s *BaseSuite) createComposeProject(c *check.C, name string) {
projectName := fmt.Sprintf("traefik-integration-test-%s", name)
func (s *BaseSuite) createComposeProject(name string) {
composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
var err error
s.dockerClient, err = client.NewClientWithOpts()
c.Assert(err, checker.IsNil)
file, err := os.ReadFile(composeFile)
require.NoError(s.T(), err)
fakeCLI := &FakeDockerCLI{client: s.dockerClient}
s.dockerComposeService = compose.NewComposeService(fakeCLI)
var composeConfigData composeConfig
err = yaml.Unmarshal(file, &composeConfigData)
require.NoError(s.T(), err)
s.composeProjectOptions = &cmdcompose.ProjectOptions{
ProjectDir: ".",
ProjectName: projectName,
ConfigPaths: []string{composeFile},
if s.containers == nil {
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.WithoutContext().Errorf("can't determine current directory: %v", err)
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.
// Already running services are not affected (i.e. not stopped).
func (s *BaseSuite) composeUp(c *check.C, services ...string) {
c.Assert(s.composeProjectOptions, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
// We use Create and Restart instead of Up, because the only option that actually works to control which containers
// are started is within the RestartOptions.
err = s.dockerComposeService.Create(context.Background(), composeProject, composeapi.CreateOptions{})
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Restart(context.Background(), composeProject.Name, 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.composeProjectOptions, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
_, err = s.dockerComposeService.Exec(context.Background(), composeProject.Name, composeapi.RunOptions{
Service: service,
Command: args,
Tty: false,
Index: 1,
})
c.Assert(err, checker.IsNil)
func (s *BaseSuite) composeUp(services ...string) {
for name, con := range s.containers {
if len(services) == 0 || slices.Contains(services, name) {
err := con.Start(context.Background())
require.NoError(s.T(), err)
}
}
}
// composeStop stops the given services of the current docker compose project and removes the corresponding containers.
func (s *BaseSuite) composeStop(c *check.C, services ...string) {
c.Assert(s.composeProjectOptions, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Stop(context.Background(), composeProject.Name, composeapi.StopOptions{Services: services})
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Remove(context.Background(), composeProject.Name, composeapi.RemoveOptions{})
c.Assert(err, checker.IsNil)
func (s *BaseSuite) composeStop(services ...string) {
for name, con := range s.containers {
if len(services) == 0 || slices.Contains(services, name) {
timeout := 10 * time.Second
err := con.Stop(context.Background(), &timeout)
require.NoError(s.T(), err)
}
}
}
// composeDown stops all compose project services and removes the corresponding containers.
func (s *BaseSuite) composeDown(c *check.C) {
c.Assert(s.composeProjectOptions, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Down(context.Background(), composeProject.Name, composeapi.DownOptions{})
c.Assert(err, checker.IsNil)
func (s *BaseSuite) composeDown() {
for _, c := range s.containers {
err := c.Terminate(context.Background())
require.NoError(s.T(), err)
}
s.containers = map[string]testcontainers.Container{}
}
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
cmd := exec.Command(traefikBinary, args...)
s.T().Cleanup(func() {
s.killCmd(cmd)
})
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err := cmd.Start()
require.NoError(s.T(), err)
return cmd, &out
}
func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
if cmd.Process == nil {
log.WithoutContext().Error("No process to kill")
return
}
err := cmd.Process.Kill()
if err != nil {
log.WithoutContext().Errorf("Kill: %v", err)
@ -221,15 +336,18 @@ func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
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...)
return cmd, func(c *check.C) {
if c.Failed() || *showLog {
s.T().Cleanup(func() {
if s.T().Failed() || *showLog {
s.displayLogK3S()
s.displayLogCompose(c)
s.displayTraefikLog(c, out)
s.displayLogCompose()
s.displayTraefikLog(out)
}
}
})
return cmd
}
func (s *BaseSuite) displayLogK3S() {
@ -246,34 +364,33 @@ func (s *BaseSuite) displayLogK3S() {
log.WithoutContext().Println()
}
func (s *BaseSuite) displayLogCompose(c *check.C) {
if s.dockerComposeService == nil || s.composeProjectOptions == nil {
log.WithoutContext().Infof("%s: No docker compose logs.", c.TestName())
return
func (s *BaseSuite) displayLogCompose() {
for name, ctn := range s.containers {
readCloser, err := ctn.Logs(context.Background())
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.WithoutContext().WithField("container", name).Info(string(trimLogs))
}
}
}
log.WithoutContext().Infof("%s: docker compose logs: ", c.TestName())
logWriter := log.WithoutContext().WriterLevel(log.GetLevel())
logConsumer := formatter.NewLogConsumer(context.Background(), logWriter, logWriter, false, true, true)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
err = s.dockerComposeService.Logs(context.Background(), composeProject.Name, logConsumer, composeapi.LogOptions{})
c.Assert(err, checker.IsNil)
log.WithoutContext().Println()
log.WithoutContext().Println("################################")
log.WithoutContext().Println()
}
func (s *BaseSuite) displayTraefikLog(c *check.C, output *bytes.Buffer) {
func (s *BaseSuite) displayTraefikLog(output *bytes.Buffer) {
if output == nil || output.Len() == 0 {
log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName())
log.WithoutContext().Info("No Traefik logs.")
} else {
log.WithoutContext().Infof("%s: Traefik logs: ", c.TestName())
log.WithoutContext().Infof(output.String())
for _, line := range strings.Split(output.String(), "\n") {
log.WithoutContext().Info(line)
}
}
}
@ -287,73 +404,47 @@ func (s *BaseSuite) getDockerHost() string {
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
tmpl, err := template.ParseFiles(path)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
folder, prefix := filepath.Split(path)
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()
model := structs.Map(tempObjects)
model["SelfFilename"] = tmpFile.Name()
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = tmpFile.Sync()
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
s.T().Cleanup(func() {
os.Remove(tmpFile.Name())
})
return tmpFile.Name()
}
func (s *BaseSuite) getComposeServiceIP(c *check.C, name string) string {
c.Assert(s.composeProjectOptions, check.NotNil)
c.Assert(s.dockerComposeService, check.NotNil)
composeProject, err := s.composeProjectOptions.ToProject(nil)
c.Assert(err, checker.IsNil)
filter := filters.NewArgs(
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ProjectLabel, composeProject.Name)),
filters.Arg("label", fmt.Sprintf("%s=%s", composeapi.ServiceLabel, name)),
)
containers, err := s.dockerClient.ContainerList(context.Background(), dockertypes.ContainerListOptions{Filters: filter})
c.Assert(err, checker.IsNil)
c.Assert(containers, checker.HasLen, 1)
networkNames := composeProject.NetworkNames()
c.Assert(networkNames, checker.HasLen, 1)
network := 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
func (s *BaseSuite) getComposeServiceIP(name string) string {
container, ok := s.containers[name]
if !ok {
return ""
}
// Should never happen.
c.Error("No network found")
return ""
ip, err := container.ContainerIP(context.Background())
if err != nil {
return ""
}
return ip
}
func withConfigFile(file string) string {
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
// router, for all the other containers (whoamis, etc) subsequently started for the
// integration tests.
@ -369,101 +460,35 @@ type tailscaleNotSuite struct{ BaseSuite }
// "172.0.0.0/8": ["your_tailscale_identity"],
// },
// },
//
// 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 {
func (s *BaseSuite) setupVPN(keyFile string) {
data, err := os.ReadFile(keyFile)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
log.Fatal(err)
log.WithoutContext().Error(err)
}
return nil
return
}
authKey := strings.TrimSpace(string(data))
// TODO: copy and create versions that don't need a check.C?
vpn := &tailscaleNotSuite{}
vpn.createComposeProject(c, "tailscale")
vpn.composeUp(c)
// // TODO: copy and create versions that don't need a check.C?
s.createComposeProject("tailscale")
s.composeUp()
time.Sleep(5 * time.Second)
// If we ever change the docker subnet in the Makefile,
// we need to change this one below correspondingly.
vpn.composeExec(c, "tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24")
return vpn
s.composeExec("tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24")
}
type FakeDockerCLI struct {
client client.APIClient
}
// 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)
func (f FakeDockerCLI) Client() client.APIClient {
return f.client
}
_, reader, err := s.containers[service].Exec(context.Background(), args)
require.NoError(s.T(), err)
func (f FakeDockerCLI) In() *streams.In {
return streams.NewIn(os.Stdin)
}
content, err := io.ReadAll(reader)
require.NoError(s.T(), err)
func (f FakeDockerCLI) Out() *streams.Out {
return streams.NewOut(os.Stdout)
}
func (f FakeDockerCLI) Err() io.Writer {
return streams.NewOut(os.Stderr)
}
func (f FakeDockerCLI) SetIn(in *streams.In) {
panic("implement me if you need me")
}
func (f FakeDockerCLI) Apply(ops ...command.DockerCliOption) error {
panic("implement me if you need me")
}
func (f FakeDockerCLI) ConfigFile() *configfile.ConfigFile {
return &configfile.ConfigFile{}
}
func (f FakeDockerCLI) ServerInfo() command.ServerInfo {
panic("implement me if you need me")
}
func (f FakeDockerCLI) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) {
panic("implement me if you need me")
}
func (f FakeDockerCLI) DefaultVersion() string {
panic("implement me if you need me")
}
func (f FakeDockerCLI) CurrentVersion() string {
panic("implement me if you need me")
}
func (f FakeDockerCLI) ManifestStore() manifeststore.Store {
panic("implement me if you need me")
}
func (f FakeDockerCLI) RegistryClient(b bool) registryclient.RegistryClient {
panic("implement me if you need me")
}
func (f FakeDockerCLI) ContentTrustEnabled() bool {
panic("implement me if you need me")
}
func (f FakeDockerCLI) BuildKitEnabled() (bool, error) {
panic("implement me if you need me")
}
func (f FakeDockerCLI) ContextStore() store.Store {
panic("implement me if you need me")
}
func (f FakeDockerCLI) CurrentContext() string {
panic("implement me if you need me")
}
func (f FakeDockerCLI) DockerEndpoint() docker.Endpoint {
panic("implement me if you need me")
return string(content)
}

View file

@ -7,18 +7,21 @@ import (
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/go-check/check"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
)
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.
type K8sSuite struct{ BaseSuite }
func (s *K8sSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "k8s")
s.composeUp(c)
func TestK8sSuite(t *testing.T) {
suite.Run(t, new(K8sSuite))
}
func (s *K8sSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("k8s")
s.composeUp()
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 := os.Stat(abs)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *K8sSuite) TearDownSuite(c *check.C) {
s.composeDown(c)
func (s *K8sSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
generatedFiles := []string{
"./fixtures/k8s/config.skip/kubeconfig.yaml",
@ -62,109 +79,78 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
}
}
func (s *K8sSuite) TestIngressConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
defer display(c)
func (s *K8sSuite) TestIngressConfiguration() {
s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingress.json", "8080")
s.testConfiguration("testdata/rawdata-ingress.json", "8080")
}
func (s *K8sSuite) TestIngressLabelSelector(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
defer display(c)
func (s *K8sSuite) TestIngressLabelSelector() {
s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingress-label-selector.json", "8080")
s.testConfiguration("testdata/rawdata-ingress-label-selector.json", "8080")
}
func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
defer display(c)
func (s *K8sSuite) TestCRDConfiguration() {
s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-crd.json", "8000")
s.testConfiguration("testdata/rawdata-crd.json", "8000")
}
func (s *K8sSuite) TestCRDLabelSelector(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
defer display(c)
func (s *K8sSuite) TestCRDLabelSelector() {
s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-crd-label-selector.json", "8000")
s.testConfiguration("testdata/rawdata-crd-label-selector.json", "8000")
}
func (s *K8sSuite) TestGatewayConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
defer display(c)
func (s *K8sSuite) TestGatewayConfiguration() {
s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-gateway.json", "8080")
s.testConfiguration("testdata/rawdata-gateway.json", "8080")
}
func (s *K8sSuite) TestIngressclass(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
defer display(c)
func (s *K8sSuite) TestIngressclass() {
s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
s.testConfiguration("testdata/rawdata-ingressclass.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"`))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash(path)
if *updateExpected {
fi, err := os.Create(expectedJSON)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = fi.Close()
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
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))
if !*updateExpected {
if err != nil {
c.Error(err)
}
require.NoError(s.T(), err)
return
}
if err != nil {
c.Logf("In file update mode, got expected error: %v", err)
log.WithoutContext().Infof("In file update mode, got expected error: %v", err)
}
var rtRepr api.RunTimeRepresentation
err = json.Unmarshal(buf.Bytes(), &rtRepr)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = os.WriteFile(expectedJSON, newJSON, 0o644)
c.Assert(err, checker.IsNil)
c.Errorf("We do not want a passing test in file update mode")
require.NoError(s.T(), err)
s.T().Fatal("We do not want a passing test in file update mode")
}
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {

View file

@ -5,18 +5,23 @@ import (
"net"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
"github.com/traefik/traefik/v2/pkg/log"
)
type KeepAliveSuite struct {
BaseSuite
}
func TestKeepAliveSuite(t *testing.T) {
suite.Run(t, new(KeepAliveSuite))
}
type KeepAliveConfig struct {
KeepAliveServer string
IdleConnTimeout string
@ -27,7 +32,7 @@ type connStateChangeEvent struct {
state http.ConnState
}
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *check.C) {
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime() {
idleTimeout := time.Duration(75) * time.Millisecond
connStateChanges := make(chan connStateChangeEvent)
@ -59,18 +64,18 @@ func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *
case <-noMoreRequests:
moreRequestsExpected = false
case <-maxWaitTimeExceeded:
c.Logf("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
c.Fail()
log.WithoutContext().Infof("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
s.T().Fail()
close(completed)
return
}
}
c.Check(connCount, checker.Equals, 1)
require.Equal(s.T(), 1, connCount)
for _, idlePeriod := range idlePeriodLengthMap {
// 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)
@ -87,22 +92,16 @@ func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *
defer server.Close()
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)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Check(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Duration(1)*time.Second, try.StatusCodeIs(200), try.BodyContains("PathPrefix(`/keepalive`)"))
c.Check(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Duration(1)*time.Second, try.StatusCodeIs(200), try.BodyContains("PathPrefix(`/keepalive`)"))
require.NoError(s.T(), err)
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)
<-completed

View file

@ -9,12 +9,14 @@ import (
"os"
"strings"
"syscall"
"testing"
"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/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
)
const (
@ -25,13 +27,23 @@ const (
// Log rotation integration test suite.
type LogRotationSuite struct{ BaseSuite }
func (s *LogRotationSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log")
s.composeUp(c)
func TestLogRorationSuite(t *testing.T) {
suite.Run(t, new(LogRotationSuite))
}
func (s *LogRotationSuite) TearDownSuite(c *check.C) {
s.composeDown(c)
func (s *LogRotationSuite) SetupSuite() {
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{
traefikTestLogFile,
@ -47,126 +59,116 @@ func (s *LogRotationSuite) TearDownSuite(c *check.C) {
}
}
func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
func (s *LogRotationSuite) TestAccessLogRotation() {
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
defer displayTraefikLogFile(c, traefikTestLogFile)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
cmd, _ := s.cmdTraefik(withConfigFile("fixtures/access_log_config.toml"))
defer s.displayTraefikLogFile(traefikTestLogFile)
// Verify Traefik started ok
verifyEmptyErrorLog(c, "traefik.log")
s.verifyEmptyErrorLog("traefik.log")
waitForTraefik(c, "server1")
s.waitForTraefik("server1")
// Make some requests
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"
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
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
err = cmd.Process.Signal(syscall.SIGUSR1)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// continue issuing requests
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())
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Verify access.log.rotated output as expected
logAccessLogFile(c, traefikTestAccessLogFileRotated)
lineCount := verifyLogLines(c, traefikTestAccessLogFileRotated, 0, true)
c.Assert(lineCount, checker.GreaterOrEqualThan, 1)
s.logAccessLogFile(traefikTestAccessLogFileRotated)
lineCount := s.verifyLogLines(traefikTestAccessLogFileRotated, 0, true)
assert.GreaterOrEqual(s.T(), lineCount, 1)
// 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 := os.Stat(traefikTestAccessLogFile)
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
logAccessLogFile(c, traefikTestAccessLogFile)
lineCount = verifyLogLines(c, traefikTestAccessLogFile, lineCount, true)
c.Assert(lineCount, checker.Equals, 3)
s.logAccessLogFile(traefikTestAccessLogFile)
lineCount = s.verifyLogLines(traefikTestAccessLogFile, lineCount, true)
assert.Equal(s.T(), 3, lineCount)
verifyEmptyErrorLog(c, traefikTestLogFile)
s.verifyEmptyErrorLog(traefikTestLogFile)
}
func (s *LogRotationSuite) TestTraefikLogRotation(c *check.C) {
func (s *LogRotationSuite) TestTraefikLogRotation() {
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/traefik_log_config.toml"))
defer display(c)
defer displayTraefikLogFile(c, traefikTestLogFile)
cmd := s.traefikCmd(withConfigFile("fixtures/traefik_log_config.toml"))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
waitForTraefik(c, "server1")
s.waitForTraefik("server1")
// Rename traefik log
err = os.Rename(traefikTestLogFile, traefikTestLogFileRotated)
c.Assert(err, checker.IsNil)
err := os.Rename(traefikTestLogFile, traefikTestLogFileRotated)
require.NoError(s.T(), err)
// issue SIGUSR1 signal to server process
err = cmd.Process.Signal(syscall.SIGUSR1)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = cmd.Process.Signal(syscall.SIGTERM)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Allow time for switch to be processed
err = try.Do(3*time.Second, func() error {
_, err = os.Stat(traefikTestLogFile)
return err
})
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// we have at least 6 lines in traefik.log.rotated
lineCount := verifyLogLines(c, traefikTestLogFileRotated, 0, false)
lineCount := s.verifyLogLines(traefikTestLogFileRotated, 0, false)
// GreaterOrEqualThan used to ensure test doesn't break
// If more log entries are output on startup
c.Assert(lineCount, checker.GreaterOrEqualThan, 5)
assert.GreaterOrEqual(s.T(), lineCount, 5)
// Verify traefik.log output as expected
lineCount = verifyLogLines(c, traefikTestLogFile, lineCount, false)
c.Assert(lineCount, checker.GreaterOrEqualThan, 7)
lineCount = s.verifyLogLines(traefikTestLogFile, lineCount, false)
assert.GreaterOrEqual(s.T(), lineCount, 7)
}
func logAccessLogFile(c *check.C, fileName string) {
func (s *LogRotationSuite) logAccessLogFile(fileName string) {
output, err := os.ReadFile(fileName)
c.Assert(err, checker.IsNil)
c.Logf("Contents of file %s\n%s", fileName, string(output))
require.NoError(s.T(), err)
log.WithoutContext().Infof("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 {
traefikLog, e2 := os.ReadFile(name)
if e2 != nil {
return e2
}
c.Assert(string(traefikLog), checker.HasLen, 0)
assert.Empty(s.T(), string(traefikLog))
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
rotatedLog := bufio.NewScanner(rotated)
count := countInit
for rotatedLog.Scan() {
@ -174,7 +176,7 @@ func verifyLogLines(c *check.C, fileName string, countInit int, accessLog bool)
if accessLog {
if len(line) > 0 {
if !strings.Contains(line, "/api/rawdata") {
CheckAccessLogFormat(c, line, count)
s.CheckAccessLogFormat(line, count)
count++
}
}

View file

@ -2,13 +2,13 @@ package integration
import (
"net/http"
"os"
"testing"
"time"
"github.com/gambol99/go-marathon"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
// Marathon test suites.
@ -17,42 +17,46 @@ type MarathonSuite15 struct {
marathonURL string
}
func (s *MarathonSuite15) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon15")
s.composeUp(c)
func TestMarathonSuite15(t *testing.T) {
suite.Run(t, new(MarathonSuite))
}
s.marathonURL = "http://" + s.getComposeServiceIP(c, containerNameMarathon) + ":8080"
func (s *MarathonSuite15) SetUpSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("marathon15")
s.composeUp()
s.marathonURL = "http://" + s.getComposeServiceIP(containerNameMarathon) + ":8080"
// Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the
// start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {
c.Skip("doesn't work")
func (s *MarathonSuite15) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *MarathonSuite15) TestConfigurationUpdate() {
s.T().Skip("doesn't work")
// Start Traefik.
file := s.adaptFile(c, "fixtures/marathon/simple.toml", struct {
file := s.adaptFile("fixtures/marathon/simple.toml", struct {
MarathonURL string
}{s.marathonURL})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Wait for Traefik to turn ready.
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
// Prepare Marathon client.
config := marathon.NewDefaultConfig()
config.URL = s.marathonURL
client, err := marathon.NewClient(config)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Create test application to be deployed.
app := marathon.NewDockerApplication().
@ -68,11 +72,11 @@ func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {
*app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork())
// Deploy the test application.
deployApplication(c, client, app)
s.deployApplication(client, app)
// Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/service", 30*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Create test application with services to be deployed.
app = marathon.NewDockerApplication().
@ -88,9 +92,9 @@ func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {
*app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork())
// Deploy the test application.
deployApplication(c, client, app)
s.deployApplication(client, app)
// Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/app", 30*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -2,13 +2,13 @@ package integration
import (
"net/http"
"os"
"testing"
"time"
"github.com/gambol99/go-marathon"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
const containerNameMarathon = "marathon"
@ -19,50 +19,55 @@ type MarathonSuite struct {
marathonURL string
}
func (s *MarathonSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "marathon")
s.composeUp(c)
func TestMarathonSuite(t *testing.T) {
suite.Run(t, new(MarathonSuite))
}
s.marathonURL = "http://" + s.getComposeServiceIP(c, containerNameMarathon) + ":8080"
func (s *MarathonSuite) SetUpSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("marathon")
s.composeUp()
s.marathonURL = "http://" + s.getComposeServiceIP(containerNameMarathon) + ":8080"
// Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the
// start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func deployApplication(c *check.C, client marathon.Marathon, application *marathon.Application) {
func (s *MarathonSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *BaseSuite) deployApplication(client marathon.Marathon, application *marathon.Application) {
deploy, err := client.UpdateApplication(application, false)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Wait for deployment to complete.
c.Assert(client.WaitOnDeployment(deploy.DeploymentID, 1*time.Minute), checker.IsNil)
err = client.WaitOnDeployment(deploy.DeploymentID, 1*time.Minute)
require.NoError(s.T(), err)
}
func (s *MarathonSuite) TestConfigurationUpdate(c *check.C) {
c.Skip("doesn't work")
func (s *MarathonSuite) TestConfigurationUpdate() {
s.T().Skip("doesn't work")
// Start Traefik.
file := s.adaptFile(c, "fixtures/marathon/simple.toml", struct {
file := s.adaptFile("fixtures/marathon/simple.toml", struct {
MarathonURL string
}{s.marathonURL})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// Wait for Traefik to turn ready.
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
// Prepare Marathon client.
config := marathon.NewDefaultConfig()
config.URL = s.marathonURL
client, err := marathon.NewClient(config)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Create test application to be deployed.
app := marathon.NewDockerApplication().
@ -75,11 +80,11 @@ func (s *MarathonSuite) TestConfigurationUpdate(c *check.C) {
Container("traefik/whoami")
// Deploy the test application.
deployApplication(c, client, app)
s.deployApplication(client, app)
// Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/service", 30*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// Create test application with services to be deployed.
app = marathon.NewDockerApplication().
@ -92,9 +97,9 @@ func (s *MarathonSuite) TestConfigurationUpdate(c *check.C) {
Container("traefik/whoami")
// Deploy the test application.
deployApplication(c, client, app)
s.deployApplication(client, app)
// Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/app", 30*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -1,103 +1,138 @@
package integration
import (
"net/http"
"os"
"bufio"
"net"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type ProxyProtocolSuite struct {
BaseSuite
gatewayIP string
haproxyIP string
whoamiIP string
whoamiIP string
}
func (s *ProxyProtocolSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "proxy-protocol")
s.composeUp(c)
s.gatewayIP = s.getContainerIP(c, "traefik")
s.haproxyIP = s.getComposeServiceIP(c, "haproxy")
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
func TestProxyProtocolSuite(t *testing.T) {
suite.Run(t, new(ProxyProtocolSuite))
}
func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) {
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
func (s *ProxyProtocolSuite) SetupSuite() {
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
WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
}{WhoamiIP: s.whoamiIP})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second)
require.NoError(s.T(), err)
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) {
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct {
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted() {
file := s.adaptFile("fixtures/proxy-protocol/proxy-protocol.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
}{WhoamiIP: s.whoamiIP})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:9000/whoami", 10*time.Second)
require.NoError(s.T(), err)
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) {
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
func proxyProtoRequest(address string, version byte) (string, error) {
// Open a TCP connection to the server
conn, err := net.Dial("tcp", address)
if err != nil {
return "", err
}
defer conn.Close()
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Create a Proxy Protocol header with v1
proxyHeader := &proxyproto.Header{
Version: version,
Command: proxyproto.PROXY,
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,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil)
}
func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct {
HaproxyIP string
WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second,
try.StatusCodeIs(http.StatusOK),
try.BodyContains("X-Forwarded-For: "+s.haproxyIP))
c.Assert(err, checker.IsNil)
// After the connection was created write the proxy headers first
_, err = proxyHeader.WriteTo(conn)
if err != nil {
return "", err
}
// Create an HTTP request
request := "GET /whoami HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Connection: close\r\n" +
"\r\n"
// Write the HTTP request to the TCP connection
writer := bufio.NewWriter(conn)
_, err = writer.WriteString(request)
if err != nil {
return "", err
}
// Flush the buffer to ensure the request is sent
err = writer.Flush()
if err != nil {
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
}

View file

@ -2,12 +2,12 @@ package integration
import (
"net/http"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type RateLimitSuite struct {
@ -15,33 +15,38 @@ type RateLimitSuite struct {
ServerIP string
}
func (s *RateLimitSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "ratelimit")
s.composeUp(c)
s.ServerIP = s.getComposeServiceIP(c, "whoami1")
func TestRateLimitSuite(t *testing.T) {
suite.Run(t, new(RateLimitSuite))
}
func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/ratelimit/simple.toml", struct {
func (s *RateLimitSuite) SetupSuite() {
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
}{s.ServerIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit"))
require.NoError(s.T(), err)
start := time.Now()
count := 0
for {
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++
if count > 100 {
break
@ -50,6 +55,6 @@ func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
stop := time.Now()
elapsed := stop.Sub(start)
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)
}
}

View 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/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
"github.com/traefik/traefik/v2/pkg/log"
)
// 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.WithoutContext().Info(text)
}
}

View file

@ -4,26 +4,22 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/fs"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"text/template"
"testing"
"time"
"github.com/fatih/structs"
"github.com/go-check/check"
"github.com/kvtools/redis"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
checker "github.com/vdemeester/shakers"
)
// Redis test suites.
@ -33,23 +29,18 @@ type RedisSuite struct {
redisEndpoints []string
}
func (s *RedisSuite) TearDownSuite(c *check.C) {
s.composeDown(c)
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 TestRedisSuite(t *testing.T) {
suite.Run(t, new(RedisSuite))
}
func (s *RedisSuite) setupStore(c *check.C) {
s.createComposeProject(c, "redis")
s.composeUp(c)
func (s *RedisSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("redis")
s.composeUp()
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(
context.Background(),
@ -57,23 +48,23 @@ func (s *RedisSuite) setupStore(c *check.C) {
s.redisEndpoints,
&redis.Config{},
)
if err != nil {
c.Fatal("Cannot create store redis: ", err)
}
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
s.setupStore(c)
func (s *RedisSuite) TearDownSuite() {
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, ","),
})
defer os.Remove(file)
data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web",
@ -123,39 +114,35 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
for k, v := range data {
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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":`),
)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
err = json.NewDecoder(resp.Body).Decode(&obtained)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
got, err := json.MarshalIndent(obtained, "", " ")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
if *updateExpected {
err = os.WriteFile(expectedJSON, got, 0o666)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
expected, err := os.ReadFile(expectedJSON)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if !bytes.Equal(expected, got) {
diff := difflib.UnifiedDiff{
@ -167,171 +154,6 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
}
text, err := difflib.GetUnifiedDiffString(diff)
c.Assert(err, checker.IsNil)
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",
"traefik/http/middlewares/striper/stripPrefix/forceSlash": "true",
}
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)
require.NoError(s.T(), err, text)
}
}

View file

@ -40,7 +40,7 @@ services:
traefik.http.routers.rt-authFrontend.entryPoints: httpFrontendAuth
traefik.http.routers.rt-authFrontend.rule: Host(`frontend.auth.docker.local`)
traefik.http.routers.rt-authFrontend.middlewares: basicauth
traefik.http.middlewares.basicauth.basicauth.users: test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/
traefik.http.middlewares.basicauth.basicauth.users: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
traefik.http.services.service3.loadbalancer.server.port: 80
digestAuthMiddleware:
@ -94,8 +94,3 @@ services:
traefik.http.routers.rt-preflightCORS.middlewares: preflightCORS
traefik.http.middlewares.preflightCORS.headers.accessControlAllowMethods: OPTIONS, GET
traefik.http.services.preflightCORS.loadbalancer.server.port: 80
networks:
default:
name: traefik-test-network
external: true

View file

@ -34,8 +34,3 @@ services:
traefik.http.routers.rt4.middlewares: wl4
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
networks:
default:
name: traefik-test-network
external: true

View file

@ -11,8 +11,3 @@ services:
image: traefik/whoami
labels:
traefik.enable: false
networks:
default:
name: traefik-test-network
external: true

View file

@ -4,8 +4,3 @@ services:
image: consul:1.6
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -2,11 +2,24 @@ version: "3.8"
services:
consul:
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:
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:
image: traefik/whoami
@ -30,8 +43,3 @@ services:
PORT: 443
BIND: 0.0.0.0
CONSUL_HTTP_ADDR: http://consul:8500
networks:
default:
name: traefik-test-network
external: true

View file

@ -36,8 +36,3 @@ services:
labels:
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

@ -14,8 +14,3 @@ services:
whoami5:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -11,8 +11,3 @@ services:
whoami4:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -6,8 +6,3 @@ services:
traefik.enable: true
traefik.http.services.service1.loadbalancer.server.port: 80
traefik.http.routers.router1.rule: Host(`github.com`)
networks:
default:
name: traefik-test-network
external: true

View file

@ -2,9 +2,23 @@ version: "3.8"
services:
server:
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:
K3S_CLUSTER_SECRET: somethingtotallyrandom
K3S_TOKEN: somethingtotallyrandom
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
K3S_KUBECONFIG_MODE: 666
volumes:
@ -15,10 +29,6 @@ services:
image: rancher/k3s:v1.20.15-k3s1
privileged: true
environment:
K3S_TOKEN: somethingtotallyrandom
K3S_URL: https://server:6443
K3S_CLUSTER_SECRET: somethingtotallyrandom
networks:
default:
name: traefik-test-network
external: true

View file

@ -3,12 +3,9 @@ services:
whoami1:
image: traefik/whoami
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
deploy:
replicas: 2
networks:
default:
name: traefik-test-network
external: true

View file

@ -2,14 +2,12 @@ version: "3.8"
services:
pebble:
image: letsencrypt/pebble:v2.3.1
command: pebble --dnsserver traefik:5053
command:
- pebble
- --dnsserver
- host.docker.internal:5053
environment:
# https://github.com/letsencrypt/pebble#testing-at-full-speed
PEBBLE_VA_NOSLEEP: 1
# https://github.com/letsencrypt/pebble#invalid-anti-replay-nonce-errors
PEBBLE_WFE_NONCEREJECT: 0
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

@ -2,8 +2,3 @@ version: "3.8"
services:
redis:
image: redis:5.0
networks:
default:
name: traefik-test-network
external: true

View file

@ -3,59 +3,51 @@ services:
master:
image: redis
container_name: redis-master
command: redis-server --port 6380
ports:
- 6380:6380
healthcheck:
test: redis-cli -p 6380 ping
command:
- redis-server
- --port
- 6380
node1:
image: redis
container_name: redis-node-1
ports:
- 6381:6381
command: redis-server --port 6381 --slaveof redis-master 6380
healthcheck:
test: redis-cli -p 6381 ping
command:
- redis-server
- --port
- 6381
- --slaveof
- redis-master
- 6380
node2:
image: redis
container_name: redis-node-2
ports:
- 6382:6382
command: redis-server --port 6382 --slaveof redis-master 6380
healthcheck:
test: redis-cli -p 6382 ping
command:
- redis-server
- --port
- 6382
- --slaveof
- redis-master
- 6380
sentinel1:
image: redis
container_name: redis-sentinel-1
ports:
- 26379:26379
command: redis-sentinel /usr/local/etc/redis/conf/sentinel1.conf
healthcheck:
test: redis-cli -p 26379 ping
command:
- redis-sentinel
- /usr/local/etc/redis/conf/sentinel1.conf
volumes:
- ./resources/compose/config:/usr/local/etc/redis/conf
sentinel2:
image: redis
container_name: redis-sentinel-2
ports:
- 36379:26379
command: redis-sentinel /usr/local/etc/redis/conf/sentinel2.conf
healthcheck:
test: redis-cli -p 36379 ping
command:
- redis-sentinel
- /usr/local/etc/redis/conf/sentinel2.conf
volumes:
- ./resources/compose/config:/usr/local/etc/redis/conf
sentinel3:
image: redis
container_name: redis-sentinel-3
ports:
- 46379:26379
command: redis-sentinel /usr/local/etc/redis/conf/sentinel3.conf
healthcheck:
test: redis-cli -p 46379 ping
command:
- redis-sentinel
- /usr/local/etc/redis/conf/sentinel3.conf
volumes:
- ./resources/compose/config:/usr/local/etc/redis/conf
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

View file

@ -5,8 +5,3 @@ services:
whoami2:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

@ -9,9 +9,5 @@ services:
cap_add: # Required for tailscale to work
- net_admin
- sys_module
command: tailscaled
networks:
default:
name: traefik-test-network
external: true
command:
- tailscaled

View file

@ -2,38 +2,58 @@ version: "3.8"
services:
whoami-a:
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:
- ./fixtures/tcp:/certs
whoami-b:
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:
- ./fixtures/tcp:/certs
whoami-ab:
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:
- ./fixtures/tcp:/certs
whoami-no-cert:
image: traefik/whoamitcp
command: -name whoami-no-cert
command:
- -name
- whoami-no-cert
whoami-no-tls:
image: traefik/whoamitcp
command: -name whoami-no-tls
command:
- -name
- whoami-no-tls
whoami:
image: traefik/whoami
whoami-banner:
image: traefik/whoamitcp
command: -name whoami-banner --banner
networks:
default:
name: traefik-test-network
external: true
command:
- -name
- whoami-banner
- --banner

View file

@ -5,8 +5,3 @@ services:
environment:
PROTO: http
PORT: 9000
networks:
default:
name: traefik-test-network
external: true

View file

@ -7,8 +7,3 @@ services:
traefik.http.routers.route1.middlewares: passtls
traefik.http.routers.route1.tls: true
traefik.http.middlewares.passtls.passtlsclientcert.pem: true
networks:
default:
name: traefik-test-network
external: true

View file

@ -13,8 +13,3 @@ services:
whoami:
image: traefik/whoami
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

@ -2,8 +2,3 @@ version: "3.8"
services:
zookeeper:
image: zookeeper:3.5
networks:
default:
name: traefik-test-network
external: true

View file

@ -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

View file

@ -5,14 +5,15 @@ import (
"encoding/json"
"net"
"net/http"
"os"
"strings"
"testing"
"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/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
checker "github.com/vdemeester/shakers"
)
type RestSuite struct {
@ -20,28 +21,33 @@ type RestSuite struct {
whoamiAddr string
}
func (s *RestSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "rest")
s.composeUp(c)
s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
func TestRestSuite(t *testing.T) {
suite.Run(t, new(RestSuite))
}
func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/rest/simple.toml"))
func (s *RestSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.createComposeProject("rest")
s.composeUp()
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
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
require.NoError(s.T(), err)
// 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)
require.NoError(s.T(), err)
testCase := []struct {
desc string
@ -105,47 +111,41 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
for _, test := range testCase {
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
require.NoError(s.T(), err)
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
}
func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
file := s.adaptFile(c, "fixtures/rest/simple_secure.toml", struct{}{})
defer os.Remove(file)
func (s *RestSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/rest/simple_secure.toml", struct{}{})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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)
err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
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("{}"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusNotFound)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusNotFound, response.StatusCode)
testCase := []struct {
desc string
@ -209,19 +209,19 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
for _, test := range testCase {
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
require.NoError(s.T(), err)
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
}

View file

@ -3,13 +3,14 @@ package integration
import (
"io"
"net/http"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type RetrySuite struct {
@ -17,96 +18,86 @@ type RetrySuite struct {
whoamiIP string
}
func (s *RetrySuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "retry")
s.composeUp(c)
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
func TestRetrySuite(t *testing.T) {
suite.Run(t, new(RetrySuite))
}
func (s *RetrySuite) TestRetry(c *check.C) {
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
func (s *RetrySuite) SetupSuite() {
s.BaseSuite.SetupSuite()
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.createComposeProject("retry")
s.composeUp()
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil)
s.whoamiIP = s.getComposeServiceIP("whoami")
}
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/")
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.
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
}
func (s *RetrySuite) TestRetryBackoff(c *check.C) {
file := s.adaptFile(c, "fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
func (s *RetrySuite) TestRetryBackoff() {
file := s.adaptFile("fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil)
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/")
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.
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
}
func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
func (s *RetrySuite) TestRetryWebsocket() {
file := s.adaptFile("fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
require.NoError(s.T(), err)
// 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)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
// 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)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
}
func (s *RetrySuite) TestRetryWithStripPrefix(c *check.C) {
file := s.adaptFile(c, "fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
func (s *RetrySuite) TestRetryWithStripPrefix() {
file := s.adaptFile("fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP})
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil)
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/test")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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")
c.Assert(string(body), checker.Contains, "X-Forwarded-Prefix: /test")
assert.Contains(s.T(), string(body), "GET / HTTP/1.1")
assert.Contains(s.T(), string(body), "X-Forwarded-Prefix: /test")
}

File diff suppressed because it is too large Load diff

View file

@ -5,63 +5,69 @@ import (
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type TCPSuite struct{ BaseSuite }
func (s *TCPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tcp")
s.composeUp(c)
func TestTCPSuite(t *testing.T) {
suite.Run(t, new(TCPSuite))
}
func (s *TCPSuite) TestMixed(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/mixed.toml", struct {
func (s *TCPSuite) SetupSuite() {
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
WhoamiA string
WhoamiB string
WhoamiNoCert string
}{
Whoami: "http://" + s.getComposeServiceIP(c, "whoami") + ":80",
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080",
Whoami: "http://" + s.getComposeServiceIP("whoami") + ":80",
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
require.NoError(s.T(), err)
// Traefik passes through, termination handled by whoami-a
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-a")
// Traefik passes through, termination handled by whoami-b
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-b")
// Termination handled by traefik
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-cert")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-cert")
tr1 := &http.Transport{
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *TCPSuite) TestTLSOptions(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/multi-tls-options.toml", struct {
func (s *TCPSuite) TestTLSOptions() {
file := s.adaptFile("fixtures/tcp/multi-tls-options.toml", struct {
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))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
require.NoError(s.T(), err)
// 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)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-cert")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-cert")
// 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)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-cert")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-cert")
// 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)
c.Assert(err, checker.NotNil)
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
assert.ErrorContains(s.T(), err, "protocol version not supported")
// 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{
ServerName: "whoami-i.test",
InsecureSkipVerify: true,
})
c.Assert(conn, checker.IsNil)
c.Assert(err, checker.NotNil)
assert.Nil(s.T(), conn)
assert.Error(s.T(), err)
}
func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/non-tls-fallback.toml", struct {
func (s *TCPSuite) TestNonTLSFallback() {
file := s.adaptFile("fixtures/tcp/non-tls-fallback.toml", struct {
WhoamiA string
WhoamiB string
WhoamiNoCert string
WhoamiNoTLS string
}{
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080",
WhoamiNoTLS: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080",
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
WhoamiNoTLS: s.getComposeServiceIP("whoami-no-tls") + ":8080",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
require.NoError(s.T(), err)
// Traefik passes through, termination handled by whoami-a
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-a")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-a")
// Traefik passes through, termination handled by whoami-b
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-b")
// Termination handled by traefik
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-cert")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-cert")
out, err = guessWho("127.0.0.1:8093", "", false)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-tls")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-tls")
}
func (s *TCPSuite) TestNonTlsTcp(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/non-tls.toml", struct {
func (s *TCPSuite) TestNonTlsTcp() {
file := s.adaptFile("fixtures/tcp/non-tls.toml", struct {
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))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
require.NoError(s.T(), err)
// Traefik will forward every requests on the given port to whoami-no-tls
out, err := guessWho("127.0.0.1:8093", "", false)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-no-tls")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-no-tls")
}
func (s *TCPSuite) TestCatchAllNoTLS(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls.toml", struct {
func (s *TCPSuite) TestCatchAllNoTLS() {
file := s.adaptFile("fixtures/tcp/catch-all-no-tls.toml", struct {
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))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
require.NoError(s.T(), err)
// Traefik will forward every requests on the given port to whoami-no-tls
out, err := welcome("127.0.0.1:8093")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "Welcome")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "Welcome")
}
func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls-with-https.toml", struct {
func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS() {
file := s.adaptFile("fixtures/tcp/catch-all-no-tls-with-https.toml", struct {
WhoamiNoTLSAddress string
WhoamiURL string
}{
WhoamiNoTLSAddress: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080",
WhoamiURL: "http://" + s.getComposeServiceIP(c, "whoami") + ":80",
WhoamiNoTLSAddress: s.getComposeServiceIP("whoami-no-tls") + ":8080",
WhoamiURL: "http://" + s.getComposeServiceIP("whoami") + ":80",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
require.NoError(s.T(), err)
req := httptest.NewRequest(http.MethodGet, "https://127.0.0.1:8093/test", nil)
req.RequestURI = ""
@ -246,64 +221,52 @@ func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) {
InsecureSkipVerify: true,
},
}, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
func (s *TCPSuite) TestMiddlewareAllowList(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/ip-allowlist.toml", struct {
func (s *TCPSuite) TestMiddlewareAllowList() {
file := s.adaptFile("fixtures/tcp/ip-allowlist.toml", struct {
WhoamiA string
WhoamiB string
}{
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
require.NoError(s.T(), err)
// Traefik not passes through, ipWhitelist closes connection
_, 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
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "whoami-b")
require.NoError(s.T(), err)
assert.Contains(s.T(), out, "whoami-b")
}
func (s *TCPSuite) TestWRR(c *check.C) {
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct {
func (s *TCPSuite) TestWRR() {
file := s.adaptFile("fixtures/tcp/wrr.toml", struct {
WhoamiB string
WhoamiAB string
}{
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080",
WhoamiAB: s.getComposeServiceIP(c, "whoami-ab") + ":8080",
WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiAB: s.getComposeServiceIP("whoami-ab") + ":8080",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-b.test`)"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-b.test`)"))
require.NoError(s.T(), err)
call := map[string]int{}
for i := 0; i < 4; i++ {
// Traefik passes through, termination handled by whoami-b or whoami-bb
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
switch {
case strings.Contains(out, "whoami-b"):
call["whoami-b"]++
@ -315,7 +278,7 @@ func (s *TCPSuite) TestWRR(c *check.C) {
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) {

View file

@ -4,47 +4,53 @@ import (
"fmt"
"net"
"net/http"
"os"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type TimeoutSuite struct{ BaseSuite }
func (s *TimeoutSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "timeout")
s.composeUp(c)
func TestTimeoutSuite(t *testing.T) {
suite.Run(t, new(TimeoutSuite))
}
func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
timeoutEndpointIP := s.getComposeServiceIP(c, "timeoutEndpoint")
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct{ TimeoutEndpoint string }{timeoutEndpointIP})
defer os.Remove(file)
func (s *TimeoutSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.createComposeProject("timeout")
s.composeUp()
}
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Path(`/dialTimeout`)"))
c.Assert(err, checker.IsNil)
func (s *TimeoutSuite) TearDownSuite() {
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.
response, err := http.Get("http://127.0.0.1:8000/dialTimeout")
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusGatewayTimeout, response.StatusCode)
// Check that timeout service is available
statusURL := fmt.Sprintf("http://%s/statusTest?status=200",
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.
response, err = http.Get("http://127.0.0.1:8000/responseHeaderTimeout?sleep=1000")
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusGatewayTimeout, response.StatusCode)
}

View file

@ -4,11 +4,13 @@ import (
"crypto/tls"
"net/http"
"os"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
const (
@ -19,20 +21,30 @@ const (
type TLSClientHeadersSuite struct{ BaseSuite }
func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tlsclientheaders")
s.composeUp(c)
func TestTLSClientHeadersSuite(t *testing.T) {
suite.Run(t, new(TLSClientHeadersSuite))
}
func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
rootCertContent, err := os.ReadFile(rootCertPath)
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)
func (s *TLSClientHeadersSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
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
ServerCertContent string
ServerKeyContent string
@ -41,22 +53,17 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
ServerCertContent: string(serverCertContent),
ServerKeyContent: string(ServerKeyContent),
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
tr := &http.Transport{
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}

View file

@ -2,19 +2,24 @@ package integration
import (
"net/http"
"os"
"testing"
"time"
"github.com/go-check/check"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
)
type TracingSuite struct {
BaseSuite
whoamiIP string
whoamiPort int
tracerIP string
whoamiIP string
whoamiPort int
tracerZipkinIP string
tracerJaegerIP string
}
func TestTracingSuite(t *testing.T) {
suite.Run(t, new(TracingSuite))
}
type TracingTemplate struct {
@ -24,302 +29,264 @@ type TracingTemplate struct {
TraceContextHeaderName string
}
func (s *TracingSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "tracing")
s.composeUp(c)
func (s *TracingSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
s.createComposeProject("tracing")
s.composeUp()
s.whoamiIP = s.getComposeServiceIP("whoami")
s.whoamiPort = 80
}
func (s *TracingSuite) startZipkin(c *check.C) {
s.composeUp(c, "zipkin")
s.tracerIP = s.getComposeServiceIP(c, "zipkin")
func (s *TracingSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *TracingSuite) startZipkin() {
s.composeUp("zipkin")
s.tracerZipkinIP = s.getComposeServiceIP("zipkin")
// Wait for Zipkin to turn ready.
err := try.GetRequest("http://"+s.tracerIP+":9411/api/v2/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://"+s.tracerZipkinIP+":9411/api/v2/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
s.startZipkin(c)
// defer s.composeStop(c, "zipkin")
func (s *TracingSuite) TestZipkinRateLimit() {
s.startZipkin()
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerZipkinIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// sleep for 4 seconds to be certain the configured time period has elapsed
// then test another request and verify a 200 status code
time.Sleep(4 * time.Second)
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
time.Sleep(3 * time.Second)
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)
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)
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)
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestZipkinRetry(c *check.C) {
s.startZipkin(c)
defer s.composeStop(c, "zipkin")
func (s *TracingSuite) TestZipkinRetry() {
s.startZipkin()
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: 81,
IP: s.tracerIP,
IP: s.tracerZipkinIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestZipkinAuth(c *check.C) {
s.startZipkin(c)
defer s.composeStop(c, "zipkin")
func (s *TracingSuite) TestZipkinAuth() {
s.startZipkin()
file := s.adaptFile(c, "fixtures/tracing/simple-zipkin.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-zipkin.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerZipkinIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerZipkinIP+":9411/api/v2/spans?serviceName=tracing", 20*time.Second, try.BodyContains("entrypoint web", "basic-auth@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) startJaeger(c *check.C) {
s.composeUp(c, "jaeger", "whoami")
s.tracerIP = s.getComposeServiceIP(c, "jaeger")
func (s *TracingSuite) startJaeger() {
s.composeUp("jaeger", "whoami")
s.tracerJaegerIP = s.getComposeServiceIP("jaeger")
// Wait for Jaeger to turn ready.
err := try.GetRequest("http://"+s.tracerIP+":16686/api/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/services", 20*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestJaegerRateLimit(c *check.C) {
s.startJaeger(c)
defer s.composeStop(c, "jaeger")
func (s *TracingSuite) TestJaegerRateLimit() {
s.startJaeger()
// defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerJaegerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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))
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
// sleep for 4 seconds to be certain the configured time period has elapsed
// then test another request and verify a 200 status code
time.Sleep(4 * time.Second)
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
time.Sleep(3 * time.Second)
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)
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))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service1/router1@file", "ratelimit-1@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestJaegerRetry(c *check.C) {
s.startJaeger(c)
defer s.composeStop(c, "jaeger")
func (s *TracingSuite) TestJaegerRetry() {
s.startJaeger()
// defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: 81,
IP: s.tracerIP,
IP: s.tracerJaegerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("forward service2/router2@file", "retry@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestJaegerAuth(c *check.C) {
s.startJaeger(c)
defer s.composeStop(c, "jaeger")
func (s *TracingSuite) TestJaegerAuth() {
s.startJaeger()
// defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerJaegerIP,
TraceContextHeaderName: "uber-trace-id",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestJaegerCustomHeader(c *check.C) {
s.startJaeger(c)
defer s.composeStop(c, "jaeger")
func (s *TracingSuite) TestJaegerCustomHeader() {
s.startJaeger()
// defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-jaeger.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerJaegerIP,
TraceContextHeaderName: "powpow",
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
require.NoError(s.T(), err)
}
func (s *TracingSuite) TestJaegerAuthCollector(c *check.C) {
s.startJaeger(c)
defer s.composeStop(c, "jaeger")
func (s *TracingSuite) TestJaegerAuthCollector() {
s.startJaeger()
// defer s.composeStop(c, "jaeger")
file := s.adaptFile(c, "fixtures/tracing/simple-jaeger-collector.toml", TracingTemplate{
file := s.adaptFile("fixtures/tracing/simple-jaeger-collector.toml", TracingTemplate{
WhoamiIP: s.whoamiIP,
WhoamiPort: s.whoamiPort,
IP: s.tracerIP,
IP: s.tracerJaegerIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
require.NoError(s.T(), err)
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)
err = try.GetRequest("http://"+s.tracerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://"+s.tracerJaegerIP+":16686/api/traces?service=tracing", 20*time.Second, try.BodyContains("EntryPoint web", "basic-auth@file"))
require.NoError(s.T(), err)
}

View file

@ -3,20 +3,32 @@ package integration
import (
"net"
"net/http"
"os"
"strings"
"testing"
"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/v2/integration/try"
checker "github.com/vdemeester/shakers"
"github.com/traefik/traefik/v2/pkg/log"
)
type UDPSuite struct{ BaseSuite }
func (s *UDPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "udp")
s.composeUp(c)
func TestUDPSuite(t *testing.T) {
suite.Run(t, new(UDPSuite))
}
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) {
@ -46,39 +58,33 @@ func guessWhoUDP(addr string) (string, error) {
return string(out[:n]), nil
}
func (s *UDPSuite) TestWRR(c *check.C) {
file := s.adaptFile(c, "fixtures/udp/wrr.toml", struct {
func (s *UDPSuite) TestWRR() {
file := s.adaptFile("fixtures/udp/wrr.toml", struct {
WhoamiAIP string
WhoamiBIP string
WhoamiCIP string
WhoamiDIP string
}{
WhoamiAIP: s.getComposeServiceIP(c, "whoami-a"),
WhoamiBIP: s.getComposeServiceIP(c, "whoami-b"),
WhoamiCIP: s.getComposeServiceIP(c, "whoami-c"),
WhoamiDIP: s.getComposeServiceIP(c, "whoami-d"),
WhoamiAIP: s.getComposeServiceIP("whoami-a"),
WhoamiBIP: s.getComposeServiceIP("whoami-b"),
WhoamiCIP: s.getComposeServiceIP("whoami-c"),
WhoamiDIP: s.getComposeServiceIP("whoami-d"),
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
s.traefikCmd(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("whoami-a"))
c.Assert(err, checker.IsNil)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("whoami-a"))
require.NoError(s.T(), err)
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{})
go func() {
call := map[string]int{}
for i := 0; i < 8; i++ {
out, err := guessWhoUDP("127.0.0.1:8093")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
switch {
case strings.Contains(out, "whoami-a"):
call["whoami-a"]++
@ -90,13 +96,13 @@ func (s *UDPSuite) TestWRR(c *check.C) {
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)
}()
select {
case <-stop:
case <-time.Tick(5 * time.Second):
c.Error("Timeout")
log.WithoutContext().Info("Timeout")
}
}

View file

@ -8,19 +8,25 @@ import (
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/go-check/check"
gorillawebsocket "github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
"golang.org/x/net/websocket"
)
// WebsocketSuite tests suite.
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
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), "OK", string(msg))
}
func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
func (s *WebsocketSuite) TestWrongOrigin() {
upgrader := gorillawebsocket.Upgrader{} // use default options
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, err = websocket.NewClient(config, conn)
c.Assert(err, checker.NotNil)
c.Assert(err, checker.ErrorMatches, "bad status")
assert.ErrorContains(s.T(), err, "bad status")
}
func (s *WebsocketSuite) TestOrigin(c *check.C) {
func (s *WebsocketSuite) TestOrigin() {
// use default options
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
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)
c.Assert(err, check.IsNil)
assert.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
msg := make([]byte, 2)
n, err = client.Read(msg)
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
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 {
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
msg := make([]byte, 2)
n, err = client.Read(msg)
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
assert.Equal(s.T(), "OK", string(msg))
}
func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
func (s *WebsocketSuite) TestSSLTermination() {
upgrader := gorillawebsocket.Upgrader{} // use default options
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
// Add client self-signed cert
roots := x509.NewCertPool()
certContent, err := os.ReadFile("./resources/tls/local.cert")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
roots.AppendCertsFromPEM(certContent)
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
RootCAs: roots,
}
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), "OK", string(msg))
}
func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
func (s *WebsocketSuite) TestBasicAuth() {
upgrader := gorillawebsocket.Upgrader{} // use default options
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()
user, password, _ := r.BasicAuth()
c.Assert(user, check.Equals, "traefiker")
c.Assert(password, check.Equals, "secret")
assert.Equal(s.T(), "traefiker", user)
assert.Equal(s.T(), "secret", password)
for {
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
auth := "traefiker:secret"
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)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
msg := make([]byte, 2)
n, err = client.Read(msg)
c.Assert(err, checker.IsNil)
c.Assert(n, checker.Equals, 2)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), 2, n)
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) {
w.WriteHeader(http.StatusUnauthorized)
}))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct {
file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string
}{
WebsocketServer: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
c.Assert(err, checker.NotNil)
c.Assert(resp.StatusCode, check.Equals, http.StatusUnauthorized)
assert.Error(s.T(), err)
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
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)
if err != nil {
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), "OK", string(msg))
}
func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
func (s *WebsocketSuite) TestSSLhttp2() {
upgrader := gorillawebsocket.Upgrader{} // use default options
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.StartTLS()
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct {
file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
WebsocketServer string
}{
WebsocketServer: ts.URL,
})
defer os.Remove(file)
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)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
// Add client self-signed cert
roots := x509.NewCertPool()
certContent, err := os.ReadFile("./resources/tls/local.cert")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
roots.AppendCertsFromPEM(certContent)
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
RootCAs: roots,
}
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), "OK", string(msg))
}
func (s *WebsocketSuite) TestHeaderAreForwarded(c *check.C) {
func (s *WebsocketSuite) TestHeaderAreForwarded() {
upgrader := gorillawebsocket.Upgrader{} // use default options
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)
if err != nil {
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: srv.URL,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
// wait for traefik
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)
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
require.NoError(s.T(), err)
headers := http.Header{}
headers.Add("X-Token", "my-token")
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"))
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil)
c.Assert(string(msg), checker.Equals, "OK")
require.NoError(s.T(), err)
assert.Equal(s.T(), "OK", string(msg))
}

View file

@ -8,16 +8,18 @@ import (
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-check/check"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/kvtools/zookeeper"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api"
checker "github.com/vdemeester/shakers"
"github.com/traefik/traefik/v2/pkg/log"
)
// Zk test suites.
@ -27,11 +29,17 @@ type ZookeeperSuite struct {
zookeeperAddr string
}
func (s *ZookeeperSuite) setupStore(c *check.C) {
s.createComposeProject(c, "zookeeper")
s.composeUp(c)
func TestZookeeperSuite(t *testing.T) {
suite.Run(t, new(ZookeeperSuite))
}
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
s.kvClient, err = valkeyrie.NewStore(
@ -42,20 +50,19 @@ func (s *ZookeeperSuite) setupStore(c *check.C) {
ConnectionTimeout: 10 * time.Second,
},
)
if err != nil {
c.Fatal("Cannot create store zookeeper")
}
require.NoError(s.T(), err, "Cannot create store zookeeper")
// wait for zk
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) {
s.setupStore(c)
func (s *ZookeeperSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
file := s.adaptFile(c, "fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
defer os.Remove(file)
func (s *ZookeeperSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/zookeeper/simple.toml", struct{ ZkAddress string }{s.zookeeperAddr})
data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web",
@ -105,39 +112,35 @@ func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
for k, v := range data {
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))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// 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":`),
)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
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
err = json.NewDecoder(resp.Body).Decode(&obtained)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
got, err := json.MarshalIndent(obtained, "", " ")
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash("testdata/rawdata-zk.json")
if *updateExpected {
err = os.WriteFile(expectedJSON, got, 0o666)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
}
expected, err := os.ReadFile(expectedJSON)
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
if !bytes.Equal(expected, got) {
diff := difflib.UnifiedDiff{
@ -149,7 +152,7 @@ func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) {
}
text, err := difflib.GetUnifiedDiffString(diff)
c.Assert(err, checker.IsNil)
c.Error(text)
require.NoError(s.T(), err)
log.WithoutContext().Info(text)
}
}

View file

@ -5,7 +5,7 @@ set -e -o pipefail
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}"
mkdir -p "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}/"

View file

@ -3,18 +3,9 @@ set -e
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..."
docker version
# shellcheck disable=SC2086
# shellcheck disable=SC2048
CGO_ENABLED=0 go test -integration ${TESTFLAGS[*]}
go test ./integration -test.timeout=20m -failfast -v ${TESTFLAGS[*]}

View file

@ -1,3 +1,3 @@
#!/usr/bin/env bash
golangci-lint run
golangci-lint run