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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -8,38 +8,23 @@ on:
env: env:
GO_VERSION: '1.21' GO_VERSION: '1.21'
GOLANGCI_LINT_VERSION: v1.55.2 GOLANGCI_LINT_VERSION: v1.55.2
MISSSPELL_VERSION: v0.4.0 MISSSPELL_VERSION: v0.4.1
IN_DOCKER: ""
jobs: jobs:
validate: validate:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps: steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0 fetch-depth: 0
- name: Cache Go modules - name: Set up Go ${{ env.GO_VERSION }}
uses: actions/cache@v3 uses: actions/setup-go@v5
with: with:
path: | go-version: ${{ env.GO_VERSION }}
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-go-
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }} - name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION} run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
@ -56,30 +41,16 @@ jobs:
validate-generate: validate-generate:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps: steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0 fetch-depth: 0
- name: Cache Go modules - name: Set up Go ${{ env.GO_VERSION }}
uses: actions/cache@v3 uses: actions/setup-go@v5
with: with:
path: | go-version: ${{ env.GO_VERSION }}
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-generate-go-
- name: go generate - name: go generate
run: | run: |

View file

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

View file

@ -31,24 +31,6 @@ global_job_config:
- cache restore traefik-$(checksum go.sum) - cache restore traefik-$(checksum go.sum)
blocks: blocks:
- name: Test Integration
dependencies: []
run:
when: "branch =~ '.*' OR pull_request =~'.*'"
task:
jobs:
- name: Test Integration
commands:
- make pull-images
- touch webui/static/index.html # Avoid generating webui
- IN_DOCKER="" make binary
- make test-integration
- df -h
epilogue:
always:
commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
- name: Release - name: Release
dependencies: [] dependencies: []
run: run:
@ -65,8 +47,6 @@ blocks:
value: 2.32.1 value: 2.32.1
- name: CODENAME - name: CODENAME
value: "mimolette" value: "mimolette"
- name: IN_DOCKER
value: ""
prologue: prologue:
commands: commands:
- export VERSION=${SEMAPHORE_GIT_TAG_NAME} - 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)) VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)) GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)",-v "/var/run/docker.sock:/var/run/docker.sock")
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
# only used when running in docker
TRAEFIK_ENVS := \
-e OS_ARCH_ARG \
-e OS_PLATFORM_ARG \
-e TESTFLAGS \
-e VERBOSE \
-e VERSION \
-e CODENAME \
-e TESTDIRS \
-e CI \
-e IN_DOCKER=true # Indicator for integration tests that we are running inside a container.
TRAEFIK_MOUNT := -v "$(CURDIR)/dist:/go/src/github.com/traefik/traefik/dist"
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_NON_INTERACTIVE ?= false
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_TEST := docker run --add-host=host.docker.internal:127.0.0.1 --rm --name=traefik --network traefik-test-network -v $(PWD):$(PWD) -w $(PWD) $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
IN_DOCKER ?= true
.PHONY: default .PHONY: default
default: binary default: binary
@ -42,20 +14,6 @@ default: binary
dist: dist:
mkdir -p dist mkdir -p dist
## Build Dev Docker image
.PHONY: build-dev-image
build-dev-image: dist
ifneq ("$(IN_DOCKER)", "")
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
endif
## Build Dev Docker image without cache
.PHONY: build-dev-image-no-cache
build-dev-image-no-cache: dist
ifneq ("$(IN_DOCKER)", "")
docker build $(DOCKER_BUILD_ARGS) --no-cache -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
endif
## Build WebUI Docker image ## Build WebUI Docker image
.PHONY: build-webui-image .PHONY: build-webui-image
build-webui-image: build-webui-image:
@ -79,8 +37,8 @@ generate-webui: webui/static/index.html
## Build the binary ## Build the binary
.PHONY: binary .PHONY: binary
binary: generate-webui build-dev-image binary: generate-webui
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary ./script/make.sh generate binary
## Build the linux binary locally ## Build the linux binary locally
.PHONY: binary-debug .PHONY: binary-debug
@ -89,35 +47,29 @@ binary-debug: generate-webui
## Build the binary for the standard platforms (linux, darwin, windows) ## Build the binary for the standard platforms (linux, darwin, windows)
.PHONY: crossbinary-default .PHONY: crossbinary-default
crossbinary-default: generate-webui build-dev-image crossbinary-default: generate-webui
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default ./script/make.sh generate crossbinary-default
## Build the binary for the standard platforms (linux, darwin, windows) in parallel ## Build the binary for the standard platforms (linux, darwin, windows) in parallel
.PHONY: crossbinary-default-parallel .PHONY: crossbinary-default-parallel
crossbinary-default-parallel: crossbinary-default-parallel:
$(MAKE) generate-webui $(MAKE) generate-webui
$(MAKE) build-dev-image crossbinary-default $(MAKE) crossbinary-default
## Run the unit and integration tests ## Run the unit and integration tests
.PHONY: test .PHONY: test
test: build-dev-image test:
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24 ./script/make.sh generate test-unit binary test-integration
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit binary test-integration
## Run the unit tests ## Run the unit tests
.PHONY: test-unit .PHONY: test-unit
test-unit: build-dev-image test-unit:
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24 ./script/make.sh generate test-unit
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
## Run the integration tests ## Run the integration tests
.PHONY: test-integration .PHONY: test-integration
test-integration: build-dev-image test-integration:
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24 ./script/make.sh generate binary test-integration
trap 'docker network rm traefik-test-network' EXIT; \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate binary test-integration
## Pull all images for integration tests ## Pull all images for integration tests
.PHONY: pull-images .PHONY: pull-images
@ -128,16 +80,22 @@ pull-images:
| uniq \ | uniq \
| xargs -P 6 -n 1 docker pull | xargs -P 6 -n 1 docker pull
EXECUTABLES = misspell shellcheck
## Validate code and docs ## Validate code and docs
.PHONY: validate-files .PHONY: validate-files
validate-files: build-dev-image validate-files:
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
./script/make.sh generate validate-lint validate-misspell
bash $(CURDIR)/script/validate-shell-script.sh bash $(CURDIR)/script/validate-shell-script.sh
## Validate code, docs, and vendor ## Validate code, docs, and vendor
.PHONY: validate .PHONY: validate
validate: build-dev-image validate:
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor $(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
./script/make.sh generate validate-lint validate-misspell validate-vendor
bash $(CURDIR)/script/validate-shell-script.sh bash $(CURDIR)/script/validate-shell-script.sh
## Clean up static directory and build a Docker Traefik image ## Clean up static directory and build a Docker Traefik image
@ -155,11 +113,6 @@ build-image-dirty: binary
build-image-debug: binary-debug build-image-debug: binary-debug
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile . docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
## Start a shell inside the build env
.PHONY: shell
shell: build-dev-image
$(DOCKER_RUN_TRAEFIK) /bin/bash
## Build documentation site ## Build documentation site
.PHONY: docs .PHONY: docs
docs: docs:
@ -187,23 +140,23 @@ generate-genconf:
## Create packages for the release ## Create packages for the release
.PHONY: release-packages .PHONY: release-packages
release-packages: generate-webui build-dev-image release-packages: generate-webui
rm -rf dist rm -rf dist
@- $(foreach os, linux darwin windows freebsd openbsd, \ @- $(foreach os, linux darwin windows freebsd openbsd, \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \ goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) go clean -cache; \ go clean -cache; \
) )
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) rm dist/**/*_checksums.txt rm dist/**/*_checksums.txt
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \ tar cfz dist/traefik-${VERSION}.src.tar.gz \
--exclude-vcs \ --exclude-vcs \
--exclude .idea \ --exclude .idea \
--exclude .travis \ --exclude .travis \
--exclude .semaphoreci \ --exclude .semaphoreci \
--exclude .github \ --exclude .github \
--exclude dist . --exclude dist .
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) chown -R $(shell id -u):$(shell id -g) dist/ chown -R $(shell id -u):$(shell id -g) dist/
## Format the Code ## Format the Code
.PHONY: fmt .PHONY: fmt

View file

@ -13,67 +13,13 @@ Let's see how.
## Building ## Building
You need either [Docker](https://github.com/docker/docker "Link to website of Docker") and `make` (Method 1), or [Go](https://go.dev/ "Link to website of Go") (Method 2) in order to build Traefik. You need:
For changes to its dependencies, the `dep` dependency management tool is required. - [Docker](https://github.com/docker/docker "Link to website of Docker")
- `make`
### Method 1: Using `Docker` and `Makefile` - [Go](https://go.dev/ "Link to website of Go")
- [misspell](https://github.com/golangci/misspell)
Run make with the `binary` target. - [shellcheck](https://github.com/koalaman/shellcheck)
- [Tailscale](https://tailscale.com/) if you are using Docker Desktop
```bash
make binary
```
This will create binaries for the Linux platform in the `dist` folder.
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
```bash
$ make binary
docker build -t traefik-webui -f webui/Dockerfile webui
Sending build context to Docker daemon 2.686MB
Step 1/11 : FROM node:8.15.0
---> 1f6c34f7921c
[...]
Successfully built ce4ff439c06a
Successfully tagged traefik-webui:latest
[...]
docker build -t "traefik-dev:4475--feature-documentation" -f build.Dockerfile .
Sending build context to Docker daemon 279MB
Step 1/10 : FROM golang:1.16-alpine
---> f4bfb3d22bda
[...]
Successfully built 5c3c1a911277
Successfully tagged traefik-dev:4475--feature-documentation
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
---> Making bundle: generate (in .)
removed 'autogen/genstatic/gen.go'
---> Making bundle: binary (in .)
$ ls dist/
traefik*
```
The following targets can be executed outside Docker by setting the variable `IN_DOCKER` to an empty string (although be aware that some of the tests might fail in that context):
- `test-unit`
- `test-integration`
- `validate`
- `binary` (the webUI is still generated by using Docker)
ex:
```bash
IN_DOCKER= make test-unit
```
### Method 2: Using `go`
Requirements:
- `go` v1.16+
- environment variable `GO111MODULE=on`
!!! tip "Source Directory" !!! tip "Source Directory"
@ -106,41 +52,33 @@ Requirements:
## ... and the list goes on ## ... and the list goes on
``` ```
#### Build Traefik ### Build Traefik
Once you've set up your go environment and cloned the source repository, you can build Traefik. Once you've set up your go environment and cloned the source repository, you can build Traefik.
```bash ```bash
# Generate UI static files $ make binary
make clean-webui generate-webui ./script/make.sh generate binary
---> Making bundle: generate (in .)
# required to merge non-code components into the final binary, ---> Making bundle: binary (in .)
# such as the web dashboard/UI
go generate $ ls dist/
traefik*
``` ```
```bash You will find the Traefik executable (`traefik`) in the `./dist` directory.
# Standard go build
go build ./cmd/traefik
```
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
## Testing ## Testing
### Method 1: `Docker` and `make`
Run unit tests using the `test-unit` target. Run unit tests using the `test-unit` target.
Run integration tests using the `test-integration` target. Run integration tests using the `test-integration` target.
Run all tests (unit and integration) using the `test` target. Run all tests (unit and integration) using the `test` target.
```bash ```bash
$ make test-unit $ make test-unit
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile . ./script/make.sh generate test-unit
# […]
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
---> Making bundle: generate (in .) ---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: test-unit (in .) ---> Making bundle: test-unit (in .)
+ go test -cover -coverprofile=cover.out . + go test -cover -coverprofile=cover.out .
@ -151,28 +89,30 @@ Test success
For development purposes, you can specify which tests to run by using (only works the `test-integration` target): For development purposes, you can specify which tests to run by using (only works the `test-integration` target):
??? note "Configuring Tailscale for Docker Desktop user"
Create `tailscale.secret` file in `integration` directory.
This file need to contains a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
(an ephemeral, but reusable, one is recommended).
Add this section to your tailscale ACLs to auto-approve the routes for the
containers in the docker subnet:
```json
"autoApprovers": {
// Allow myself to automatically
// advertize routes for docker networks
"routes": {
"172.31.42.0/24": ["your_tailscale_identity"],
},
},
```
```bash ```bash
# Run every tests in the MyTest suite # Run every tests in the MyTest suite
TESTFLAGS="-check.f MyTestSuite" make test-integration TESTFLAGS="-test.run TestAccessLogSuite" make test-integration
# Run the test "MyTest" in the MyTest suite # Run the test "MyTest" in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration TESTFLAGS="-test.run TestAccessLogSuite -testify.m ^TestAccessLog$" make test-integration
# Run every tests starting with "My", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
# Run every tests ending with "Test", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
``` ```
Check [gocheck](https://labix.org/gocheck "Link to website of gocheck") for more information.
### Method 2: `go`
Unit tests can be run from the cloned directory using `$ go test ./...` which should return `ok`, similar to:
```test
ok _/home/user/go/src/github/traefik/traefik 0.004s
```
Integration tests must be run from the `integration/` directory and require the `-integration` switch: `$ cd integration && go test -integration ./...`.

91
go.mod
View file

@ -13,14 +13,12 @@ require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/docker/cli v24.0.7+incompatible 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/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/fatih/structs v1.1.0 github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
github.com/go-acme/lego/v4 v4.14.0 github.com/go-acme/lego/v4 v4.14.0
github.com/go-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
github.com/golang/protobuf v1.5.3 github.com/golang/protobuf v1.5.3
github.com/google/go-github/v28 v28.1.1 github.com/google/go-github/v28 v28.1.1
@ -58,14 +56,13 @@ require (
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
github.com/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/paerser v0.2.0
github.com/traefik/yaegi v0.15.1 github.com/traefik/yaegi v0.15.1
github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.2 github.com/unrolled/render v1.0.2
github.com/unrolled/secure v1.0.9 github.com/unrolled/secure v1.0.9
github.com/vdemeester/shakers v0.1.0
github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c
github.com/vulcand/predicate v1.2.0 github.com/vulcand/predicate v1.2.0
go.elastic.co/apm v1.13.1 go.elastic.co/apm v1.13.1
@ -90,9 +87,8 @@ require (
require ( require (
cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
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/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 v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
@ -121,6 +117,7 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // 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/aws/smithy-go v1.14.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/buger/goterm v1.0.4 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/civo/civogo v0.3.11 // indirect github.com/civo/civogo v0.3.11 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect github.com/cloudflare/cloudflare-go v0.70.0 // indirect
github.com/compose-spec/compose-go v1.15.0 // indirect github.com/containerd/containerd v1.7.11 // indirect
github.com/containerd/console v1.0.3 // indirect github.com/containerd/log v0.1.0 // 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/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpu/goacmedns v0.1.1 // indirect
github.com/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/deepmap/oapi-codegen v1.9.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493 // indirect
github.com/dnsimple/dnsimple-go v1.2.0 // 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/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/docker/go-units v0.5.0 // indirect
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e // indirect github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e // indirect
github.com/dustin/go-humanize v1.0.1 // 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/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/exoscale/egoscale v0.100.1 // indirect github.com/exoscale/egoscale v0.100.1 // indirect
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/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/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect github.com/go-errors/errors v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-resty/resty/v2 v2.7.0 // 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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-zookeeper/zk v1.0.3 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
github.com/google/s2a-go v0.1.5 // indirect github.com/google/s2a-go v0.1.5 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.4.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/gophercloud/gophercloud v1.0.0 // indirect
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/hashicorp/cronexpr v1.1.1 // indirect github.com/hashicorp/cronexpr v1.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -225,8 +204,6 @@ require (
github.com/huandu/xstrings v1.4.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/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/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jcchavezs/porto v0.1.0 // indirect github.com/jcchavezs/porto v0.1.0 // indirect
@ -236,8 +213,8 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // 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/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // 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-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.3 // indirect github.com/liquidweb/liquidweb-go v1.6.3 // indirect
github.com/looplab/fsm v0.1.0 // 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/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
github.com/mailgun/multibuf v0.1.2 // indirect github.com/mailgun/multibuf v0.1.2 // indirect
github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-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/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/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // 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/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/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/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
@ -286,23 +255,22 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opencontainers/runc v1.1.7 // indirect github.com/opencontainers/runc v1.1.7 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // 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/oracle/oci-go-sdk v24.3.0+incompatible // indirect
github.com/outcaste-io/ristretto v0.2.3 // indirect github.com/outcaste-io/ristretto v0.2.3 // indirect
github.com/ovh/go-ovh v1.4.1 // 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/philhofer/fwd v1.1.2 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pquerna/otp v1.4.0 // indirect github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/redis/go-redis/v9 v9.2.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/api-client-go v0.2.8 // indirect
github.com/sacloud/go-http v0.1.6 // indirect github.com/sacloud/go-http v0.1.6 // indirect
github.com/sacloud/iaas-api-go v1.11.1 // indirect github.com/sacloud/iaas-api-go v1.11.1 // indirect
@ -311,53 +279,36 @@ require (
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect github.com/segmentio/fasthash v1.0.3 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect github.com/shirou/gopsutil/v3 v3.23.11 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/softlayer/softlayer-go v1.1.2 // indirect github.com/softlayer/softlayer-go v1.1.2 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.1 // indirect github.com/stretchr/objx v0.5.1 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect
github.com/tinylib/msgp v1.1.8 // indirect github.com/tinylib/msgp v1.1.8 // indirect
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
github.com/transip/gotransip/v6 v6.20.0 // indirect github.com/transip/gotransip/v6 v6.20.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/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-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
github.com/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/apm/module/apmhttp v1.13.1 // indirect
go.elastic.co/fastjson v1.1.0 // indirect go.elastic.co/fastjson v1.1.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.6 // 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/pkg/v3 v3.5.6 // indirect
go.etcd.io/etcd/client/v3 v3.5.6 // indirect go.etcd.io/etcd/client/v3 v3.5.6 // indirect
go.opencensus.io v0.24.0 // 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/atomic v1.11.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/mock v0.3.0 // indirect go.uber.org/mock v0.3.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/ratelimit v0.2.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/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/term v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.128.0 // indirect google.golang.org/api v0.128.0 // indirect
@ -394,7 +344,6 @@ require (
// Containous forks // Containous forks
replace ( replace (
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e
github.com/go-check/check => github.com/containous/check v0.0.0-20170915194414-ca0bf163426a
github.com/gorilla/mux => github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f github.com/gorilla/mux => github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f
github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595 github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595
) )

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.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.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 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= 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-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
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/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= 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/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 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 v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= 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.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 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 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.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= 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 v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= 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/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 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= 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/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/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.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A= github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A=
github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g= 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/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 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= 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 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= 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= 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.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 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-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/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/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= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/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.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 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/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 h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= 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 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 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 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 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/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/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= 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/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/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/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 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM= 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= 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-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-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-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/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 h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 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/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE=
github.com/compose-spec/compose-go v1.15.0 h1:rv3TTgbS3U4Y8sRTngrcxDmpbz+fq26wTqHculSCi6s= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/compose-spec/compose-go v1.15.0/go.mod h1:3yngGBGfls6FHGQsg4B1z6gz8ej9SOvmAJtxCwgbcnc= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
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/containous/alice v0.0.0-20181107144136-d83ebdd94cbd h1:0n+lFLh5zU0l6KSk3KpnDwfbPGAR44aRLgTbCnhRBHU= 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/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 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/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= 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/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 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= 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-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.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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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.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 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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/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.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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 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.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 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIxr1vtI=
github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw= 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/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 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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/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 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 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 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 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 h1:ddTGyLVKly5HKb5L65AkLqFqwZlWo3WnR0BlFZlIddM=
github.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U= 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 h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 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 h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 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 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/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 h1:rMOGp6HPeMHbdLrZkX2nD+94uqDunc27tXVuS+ey4mQ=
github.com/donovanhide/eventsource v0.0.0-20170630084216-b8f31a59085e/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw= 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.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 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/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/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.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.4.0 h1:3OK9bWpPk5q6pbFAaYSEwD9CLUSHG8bnZuqX2yMt3B0= 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 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY=
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= 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-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 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 v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 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.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/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 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 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.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.11.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/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 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/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.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+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/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 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= 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.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.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.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 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/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 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI=
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s= 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= 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.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 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.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 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-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.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.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 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 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= 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.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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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= 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/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/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/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 h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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.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.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.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 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.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 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 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/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 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-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-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/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.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.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 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/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.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/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.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 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/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 h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 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.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.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.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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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= 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/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 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg=
github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= 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.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.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.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.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= 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= 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/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.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.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/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.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.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 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 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= 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.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.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM= 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 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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.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/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.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 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.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= 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/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.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= 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.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= 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/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.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 h1:QgP5mlBE9sGnzplpnf96pr+p7uqlIlL4W2GAP3n+XZg=
github.com/influxdata/influxdb-client-go/v2 v2.7.0/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU= 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= 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 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 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/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.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 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 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 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 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 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= 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= 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.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/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/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.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/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 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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= 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/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/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 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-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/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linode/linodego v1.17.2 h1:b32dj4662PGG5P9qVa6nBezccWdqgukndlMIuPGq1CQ= 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/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 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20=
github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= 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/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.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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 h1:QE9kE27lK6LFZB4aYNVtUPlWVHVCT0zpgUr2uoq/+jk=
github.com/mailgun/multibuf v0.1.2/go.mod h1:E+sUhIy69qgT6EM57kCPdUTlHnjTuxQBO/yf6af9Hes= 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= 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.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.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.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/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.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 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/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 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= 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.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.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.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.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 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 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M= 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= 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/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 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 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.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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= 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.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 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/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 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 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.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 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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/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 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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-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 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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.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.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.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.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.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 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.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.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.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.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.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 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/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 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= 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/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 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 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= 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.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.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.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/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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= 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/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.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 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/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 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= 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.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-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 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.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 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 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-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-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.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 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 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.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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.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.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= 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-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-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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/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 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA=
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk= 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 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/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.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.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.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 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= 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/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 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 h1:ka9QPuQg2u4LGipiZGsgkg3rJCo4iIUCy75FddM0GRQ= github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= 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 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 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/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 h1:ZTzdx88+AcnjqUfJwnz89UBrMSBQ1NEysg9u5d+dU9c=
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg= 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.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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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.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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 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 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 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 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.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 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.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0/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.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.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= 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.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.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/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 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.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1/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.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/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-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/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 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g=
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= 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.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 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/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 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ= 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/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk=
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw= github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U=
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/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= 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/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-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/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 h1:zqCLGSXoNlcBd+mzqSCLjon/I6phqIjeJL2xFB2ysgQ=
github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc= github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc=
github.com/traefik/yaegi v0.15.1 h1:YA5SbaL6HZA0Exh9T/oArRHqGN2HQ+zgmCY7dkoTXu4= 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/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.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 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 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= 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= 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/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 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= 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-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/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/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/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= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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/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/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
go.elastic.co/apm v1.13.1 h1:ICIcUcQOImg/bve9mQVyLCvm1cSUZ1afdwK6ACnxczU= 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 v1.13.1/go.mod h1:dylGv2HKR0tiCV+wliJz1KHtDyuD8SPe69oV7VyK6WY=
go.elastic.co/apm/module/apmhttp v1.13.1 h1:g2id6+AY8NRSA6nzwPDSU1AmBiHyZeh/lJRBlXq2yfQ= 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.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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 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.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.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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-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-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-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-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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-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-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-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-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-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-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-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-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-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-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/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-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-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-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-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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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-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-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-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-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-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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/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-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-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-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-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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/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-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-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-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-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-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-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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/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-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-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-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-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-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/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/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 h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 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.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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 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= 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 h1:AUe/ZF7xm6vYnigPe+TY54DmfWYJxhMRaw/TfvrbzvE=
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1/go.mod h1:KDLJ3CWVOSuVVwu+0ZR5KZo2rP6c7YyBV3v387dIpUU= 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/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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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/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 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 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 h1:mCPl7q0jbIGACXvGBljAuuApmKZo3rRi4tlRIEbMvjA=
gopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= 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/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/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 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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= 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.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= 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-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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/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" "os"
"strconv" "strconv"
"strings" "strings"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog" "github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
checker "github.com/vdemeester/shakers"
) )
const ( const (
@ -28,6 +30,10 @@ const (
// AccessLogSuite tests suite. // AccessLogSuite tests suite.
type AccessLogSuite struct{ BaseSuite } type AccessLogSuite struct{ BaseSuite }
func TestAccessLogSuite(t *testing.T) {
suite.Run(t, new(AccessLogSuite))
}
type accessLogValue struct { type accessLogValue struct {
formatOnly bool formatOnly bool
code string code string
@ -36,67 +42,67 @@ type accessLogValue struct {
serviceURL string serviceURL string
} }
func (s *AccessLogSuite) SetUpSuite(c *check.C) { func (s *AccessLogSuite) SetupSuite() {
s.createComposeProject(c, "access_log") s.BaseSuite.SetupSuite()
s.composeUp(c) s.createComposeProject("access_log")
s.composeUp()
} }
func (s *AccessLogSuite) TearDownTest(c *check.C) { func (s *AccessLogSuite) TearDownSuite() {
displayTraefikLogFile(c, traefikTestLogFile) s.BaseSuite.TearDownSuite()
}
func (s *AccessLogSuite) TearDownTest() {
s.displayTraefikLogFile(traefikTestLogFile)
_ = os.Remove(traefikTestAccessLogFile) _ = os.Remove(traefikTestAccessLogFile)
} }
func (s *AccessLogSuite) TestAccessLog(c *check.C) { func (s *AccessLogSuite) TestAccessLog() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
defer func() { defer func() {
traefikLog, err := os.ReadFile(traefikTestLogFile) traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
log.WithoutContext().Info(string(traefikLog)) log.WithoutContext().Info(string(traefikLog))
}() }()
err := cmd.Start() s.waitForTraefik("server1")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
waitForTraefik(c, "server1") s.checkStatsForLogFile()
checkStatsForLogFile(c)
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Make some requests // Make some requests
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "frontend1.docker.local" req.Host = "frontend1.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "frontend2.docker.local" req.Host = "frontend2.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogOutput(c) count := s.checkAccessLogOutput()
c.Assert(count, checker.GreaterOrEqualThan, 3) assert.Equal(s.T(), 3, count)
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) { func (s *AccessLogSuite) TestAccessLogAuthFrontend() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -124,48 +130,43 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("authFrontend")
waitForTraefik(c, "authFrontend")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test auth entrypoint // Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "frontend.auth.docker.local" req.Host = "frontend.auth.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.SetBasicAuth("test", "") req.SetBasicAuth("test", "")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.SetBasicAuth("test", "test") req.SetBasicAuth("test", "test")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) { func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -193,27 +194,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("digestAuthMiddleware")
waitForTraefik(c, "digestAuthMiddleware")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test auth entrypoint // Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8008/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8008/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "entrypoint.digest.auth.docker.local" req.Host = "entrypoint.digest.auth.docker.local"
resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized) resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
digest := digestParts(resp) digest := digestParts(resp)
digest["uri"] = "/" digest["uri"] = "/"
@ -225,22 +221,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
digest["password"] = "test" digest["password"] = "test"
req.Header.Set("Authorization", getDigestAuthorization(digest)) req.Header.Set("Authorization", getDigestAuthorization(digest))
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
// Thanks to mvndaai for digest authentication // Thanks to mvndaai for digest authentication
@ -291,7 +287,7 @@ func getDigestAuthorization(digestParts map[string]string) string {
return authorization return authorization
} }
func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) { func (s *AccessLogSuite) TestAccessLogFrontendRedirect() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -308,38 +304,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("frontendRedirect")
waitForTraefik(c, "frontendRedirect")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test frontend redirect // Test frontend redirect
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "" req.Host = ""
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) { func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
type logLine struct { type logLine struct {
@ -365,30 +356,25 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_json_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("frontendRedirect")
waitForTraefik(c, "frontendRedirect")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test frontend redirect // Test frontend redirect
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "" req.Host = ""
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
lines := extractLines(c) lines := s.extractLines()
c.Assert(len(lines), checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), len(lines), len(expected))
for i, line := range lines { for i, line := range lines {
if line == "" { if line == "" {
@ -396,15 +382,15 @@ func (s *AccessLogSuite) TestAccessLogJSONFrontendRedirect(c *check.C) {
} }
var logline logLine var logline logLine
err := json.Unmarshal([]byte(line), &logline) err := json.Unmarshal([]byte(line), &logline)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(logline.DownstreamStatus, checker.Equals, expected[i].DownstreamStatus) assert.Equal(s.T(), expected[i].DownstreamStatus, logline.DownstreamStatus)
c.Assert(logline.OriginStatus, checker.Equals, expected[i].OriginStatus) assert.Equal(s.T(), expected[i].OriginStatus, logline.OriginStatus)
c.Assert(logline.RouterName, checker.Equals, expected[i].RouterName) assert.Equal(s.T(), expected[i].RouterName, logline.RouterName)
c.Assert(logline.ServiceName, checker.Equals, expected[i].ServiceName) assert.Equal(s.T(), expected[i].ServiceName, logline.ServiceName)
} }
} }
func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) { func (s *AccessLogSuite) TestAccessLogRateLimit() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -424,42 +410,37 @@ func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("rateLimit")
waitForTraefik(c, "rateLimit")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test rate limit // Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8007/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8007/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "ratelimit.docker.local" req.Host = "ratelimit.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) { func (s *AccessLogSuite) TestAccessLogBackendNotFound() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -473,38 +454,33 @@ func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.waitForTraefik("server1")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
waitForTraefik(c, "server1") s.checkStatsForLogFile()
checkStatsForLogFile(c)
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test rate limit // Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "backendnotfound.docker.local" req.Host = "backendnotfound.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) { func (s *AccessLogSuite) TestAccessLogFrontendWhitelist() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -518,38 +494,33 @@ func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("frontendWhitelist")
waitForTraefik(c, "frontendWhitelist")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test rate limit // Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "frontend.whitelist.docker.local" req.Host = "frontend.whitelist.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) { func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -563,39 +534,34 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("authFrontend")
waitForTraefik(c, "authFrontend")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test auth entrypoint // Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "frontend.auth.docker.local" req.Host = "frontend.auth.docker.local"
req.SetBasicAuth("test", "test") req.SetBasicAuth("test", "test")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) { func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
ensureWorkingDirectoryIsClean() ensureWorkingDirectoryIsClean()
expected := []accessLogValue{ expected := []accessLogValue{
@ -609,79 +575,74 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware(c *check.C) {
} }
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start() s.checkStatsForLogFile()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
checkStatsForLogFile(c) s.waitForTraefik("preflightCORS")
waitForTraefik(c, "preflightCORS")
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c) s.checkTraefikStarted()
// Test preflight response // Test preflight response
req, err := http.NewRequest(http.MethodOptions, "http://127.0.0.1:8009/", nil) req, err := http.NewRequest(http.MethodOptions, "http://127.0.0.1:8009/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "preflight.docker.local" req.Host = "preflight.docker.local"
req.Header.Set("Origin", "whatever") req.Header.Set("Origin", "whatever")
req.Header.Set("Access-Control-Request-Method", "GET") req.Header.Set("Access-Control-Request-Method", "GET")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Verify access.log output as expected // Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected) count := s.checkAccessLogExactValuesOutput(expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected)) assert.GreaterOrEqual(s.T(), count, len(expected))
// Verify no other Traefik problems // Verify no other Traefik problems
checkNoOtherTraefikProblems(c) s.checkNoOtherTraefikProblems()
} }
func checkNoOtherTraefikProblems(c *check.C) { func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
traefikLog, err := os.ReadFile(traefikTestLogFile) traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) fmt.Printf("%s\n", string(traefikLog))
} }
} }
func checkAccessLogOutput(c *check.C) int { func (s *AccessLogSuite) checkAccessLogOutput() int {
lines := extractLines(c) lines := s.extractLines()
count := 0 count := 0
for i, line := range lines { for i, line := range lines {
if len(line) > 0 { if len(line) > 0 {
count++ count++
CheckAccessLogFormat(c, line, i) s.CheckAccessLogFormat(line, i)
} }
} }
return count return count
} }
func checkAccessLogExactValuesOutput(c *check.C, values []accessLogValue) int { func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
lines := extractLines(c) lines := s.extractLines()
count := 0 count := 0
for i, line := range lines { for i, line := range lines {
fmt.Println(line) fmt.Println(line)
if len(line) > 0 { if len(line) > 0 {
count++ count++
if values[i].formatOnly { if values[i].formatOnly {
CheckAccessLogFormat(c, line, i) s.CheckAccessLogFormat(line, i)
} else { } else {
checkAccessLogExactValues(c, line, i, values[i]) s.checkAccessLogExactValues(line, i, values[i])
} }
} }
} }
return count return count
} }
func extractLines(c *check.C) []string { func (s *AccessLogSuite) extractLines() []string {
accessLog, err := os.ReadFile(traefikTestAccessLogFile) accessLog, err := os.ReadFile(traefikTestAccessLogFile)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
lines := strings.Split(string(accessLog), "\n") lines := strings.Split(string(accessLog), "\n")
@ -694,14 +655,14 @@ func extractLines(c *check.C) []string {
return clean return clean
} }
func checkStatsForLogFile(c *check.C) { func (s *AccessLogSuite) checkStatsForLogFile() {
err := try.Do(1*time.Second, func() error { err := try.Do(1*time.Second, func() error {
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil { if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
return fmt.Errorf("could not get stats for log file: %w", errStat) return fmt.Errorf("could not get stats for log file: %w", errStat)
} }
return nil return nil
}) })
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func ensureWorkingDirectoryIsClean() { func ensureWorkingDirectoryIsClean() {
@ -709,69 +670,38 @@ func ensureWorkingDirectoryIsClean() {
os.Remove(traefikTestLogFile) os.Remove(traefikTestLogFile)
} }
func checkTraefikStarted(c *check.C) []byte { func (s *AccessLogSuite) checkTraefikStarted() []byte {
traefikLog, err := os.ReadFile(traefikTestLogFile) traefikLog, err := os.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) fmt.Printf("%s\n", string(traefikLog))
} }
return traefikLog return traefikLog
} }
func CheckAccessLogFormat(c *check.C, line string, i int) { func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
results, err := accesslog.ParseAccessLog(line) results, err := accesslog.ParseAccessLog(line)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(results, checker.HasLen, 14) assert.Len(s.T(), results, 14)
c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`) assert.Regexp(s.T(), `^(-|\d{3})$`, results[accesslog.OriginStatus])
count, _ := strconv.Atoi(results[accesslog.RequestCount]) count, _ := strconv.Atoi(results[accesslog.RequestCount])
c.Assert(count, checker.GreaterOrEqualThan, i+1) assert.GreaterOrEqual(s.T(), count, i+1)
c.Assert(results[accesslog.RouterName], checker.Matches, `"(rt-.+@docker|api@internal)"`) assert.Regexp(s.T(), `"(rt-.+@docker|api@internal)"`, results[accesslog.RouterName])
c.Assert(results[accesslog.ServiceURL], checker.HasPrefix, `"http://`) assert.True(s.T(), strings.HasPrefix(results[accesslog.ServiceURL], `"http://`))
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`) assert.Regexp(s.T(), `^\d+ms$`, results[accesslog.Duration])
} }
func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue) { func (s *AccessLogSuite) checkAccessLogExactValues(line string, i int, v accessLogValue) {
results, err := accesslog.ParseAccessLog(line) results, err := accesslog.ParseAccessLog(line)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(results, checker.HasLen, 14) assert.Len(s.T(), results, 14)
if len(v.user) > 0 { if len(v.user) > 0 {
c.Assert(results[accesslog.ClientUsername], checker.Equals, v.user) assert.Equal(s.T(), v.user, results[accesslog.ClientUsername])
} }
c.Assert(results[accesslog.OriginStatus], checker.Equals, v.code) assert.Equal(s.T(), v.code, results[accesslog.OriginStatus])
count, _ := strconv.Atoi(results[accesslog.RequestCount]) count, _ := strconv.Atoi(results[accesslog.RequestCount])
c.Assert(count, checker.GreaterOrEqualThan, i+1) assert.GreaterOrEqual(s.T(), count, i+1)
c.Assert(results[accesslog.RouterName], checker.Matches, `^"?`+v.routerName+`.*(@docker)?$`) assert.Regexp(s.T(), `^"?`+v.routerName+`.*(@docker)?$`, results[accesslog.RouterName])
c.Assert(results[accesslog.ServiceURL], checker.Matches, `^"?`+v.serviceURL+`.*$`) assert.Regexp(s.T(), `^"?`+v.serviceURL+`.*$`, results[accesslog.ServiceURL])
c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`) assert.Regexp(s.T(), `^\d+ms$`, results[accesslog.Duration])
}
func waitForTraefik(c *check.C, containerName string) {
time.Sleep(1 * time.Second)
// Wait for Traefik to turn ready.
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
c.Assert(err, checker.IsNil)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
c.Assert(err, checker.IsNil)
}
func displayTraefikLogFile(c *check.C, path string) {
if c.Failed() {
if _, err := os.Stat(path); !os.IsNotExist(err) {
content, errRead := os.ReadFile(path)
fmt.Printf("%s: Traefik logs: \n", c.TestName())
if errRead == nil {
fmt.Println(content)
} else {
fmt.Println(errRead)
}
} else {
fmt.Printf("%s: No Traefik logs.\n", c.TestName())
}
errRemove := os.Remove(path)
if errRemove != nil {
fmt.Println(errRemove)
}
}
} }

View file

@ -8,16 +8,19 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"testing"
"time" "time"
"github.com/go-check/check"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/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/integration/try"
"github.com/traefik/traefik/v2/pkg/config/static" "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/provider/acme"
"github.com/traefik/traefik/v2/pkg/testhelpers" "github.com/traefik/traefik/v2/pkg/testhelpers"
"github.com/traefik/traefik/v2/pkg/types" "github.com/traefik/traefik/v2/pkg/types"
checker "github.com/vdemeester/shakers"
) )
// ACME test suites. // ACME test suites.
@ -27,6 +30,10 @@ type AcmeSuite struct {
fakeDNSServer *dns.Server fakeDNSServer *dns.Server
} }
func TestAcmeSuite(t *testing.T) {
suite.Run(t, new(AcmeSuite))
}
type subCases struct { type subCases struct {
host string host string
expectedCommonName string expectedCommonName string
@ -87,17 +94,18 @@ func setupPebbleRootCA() (*http.Transport, error) {
}, nil }, nil
} }
func (s *AcmeSuite) SetUpSuite(c *check.C) { func (s *AcmeSuite) SetupSuite() {
s.createComposeProject(c, "pebble") s.BaseSuite.SetupSuite()
s.composeUp(c)
s.fakeDNSServer = startFakeDNSServer(s.getContainerIP(c, "traefik")) s.createComposeProject("pebble")
s.pebbleIP = s.getComposeServiceIP(c, "pebble") s.composeUp()
// Retrieving the Docker host ip.
s.fakeDNSServer = startFakeDNSServer(s.hostIP)
s.pebbleIP = s.getComposeServiceIP("pebble")
pebbleTransport, err := setupPebbleRootCA() pebbleTransport, err := setupPebbleRootCA()
if err != nil { require.NoError(s.T(), err)
c.Fatal(err)
}
// wait for pebble // wait for pebble
req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil) req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil)
@ -113,21 +121,24 @@ func (s *AcmeSuite) SetUpSuite(c *check.C) {
} }
return try.StatusCodeIs(http.StatusOK)(resp) return try.StatusCodeIs(http.StatusOK)(resp)
}) })
c.Assert(err, checker.IsNil)
require.NoError(s.T(), err)
} }
func (s *AcmeSuite) TearDownSuite(c *check.C) { func (s *AcmeSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
if s.fakeDNSServer != nil { if s.fakeDNSServer != nil {
err := s.fakeDNSServer.Shutdown() err := s.fakeDNSServer.Shutdown()
if err != nil { if err != nil {
c.Log(err) log.WithoutContext().Info(err.Error())
} }
} }
s.composeDown(c) s.composeDown()
} }
func (s *AcmeSuite) TestHTTP01Domains(c *check.C) { func (s *AcmeSuite) TestHTTP01Domains() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_domains.toml", traefikConfFilePath: "fixtures/acme/acme_domains.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -147,10 +158,10 @@ func (s *AcmeSuite) TestHTTP01Domains(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01StoreDomains(c *check.C) { func (s *AcmeSuite) TestHTTP01StoreDomains() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_store_domains.toml", traefikConfFilePath: "fixtures/acme/acme_store_domains.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -170,10 +181,10 @@ func (s *AcmeSuite) TestHTTP01StoreDomains(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01DomainsInSAN(c *check.C) { func (s *AcmeSuite) TestHTTP01DomainsInSAN() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_domains.toml", traefikConfFilePath: "fixtures/acme/acme_domains.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -194,10 +205,10 @@ func (s *AcmeSuite) TestHTTP01DomainsInSAN(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRule() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_base.toml", traefikConfFilePath: "fixtures/acme/acme_base.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -214,10 +225,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestMultipleResolver(c *check.C) { func (s *AcmeSuite) TestMultipleResolver() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml", traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml",
subCases: []subCases{ subCases: []subCases{
@ -245,10 +256,10 @@ func (s *AcmeSuite) TestMultipleResolver(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_base.toml", traefikConfFilePath: "fixtures/acme/acme_base.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -266,10 +277,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_base.toml", traefikConfFilePath: "fixtures/acme/acme_base.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -287,10 +298,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_tls.toml", traefikConfFilePath: "fixtures/acme/acme_tls.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -307,10 +318,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard(c
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -327,10 +338,10 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP(c *check.C) { func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_tcp.toml", traefikConfFilePath: "fixtures/acme/acme_tcp.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -347,10 +358,10 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) { func (s *AcmeSuite) TestTLSALPN01OnHostRule() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_base.toml", traefikConfFilePath: "fixtures/acme/acme_base.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -367,10 +378,10 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestTLSALPN01Domains(c *check.C) { func (s *AcmeSuite) TestTLSALPN01Domains() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_domains.toml", traefikConfFilePath: "fixtures/acme/acme_domains.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -390,10 +401,10 @@ func (s *AcmeSuite) TestTLSALPN01Domains(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
func (s *AcmeSuite) TestTLSALPN01DomainsInSAN(c *check.C) { func (s *AcmeSuite) TestTLSALPN01DomainsInSAN() {
testCase := acmeTestCase{ testCase := acmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_domains.toml", traefikConfFilePath: "fixtures/acme/acme_domains.toml",
subCases: []subCases{{ subCases: []subCases{{
@ -414,12 +425,12 @@ func (s *AcmeSuite) TestTLSALPN01DomainsInSAN(c *check.C) {
}, },
} }
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(testCase)
} }
// Test Let's encrypt down. // Test Let's encrypt down.
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) { func (s *AcmeSuite) TestNoValidLetsEncryptServer() {
file := s.adaptFile(c, "fixtures/acme/acme_base.toml", templateModel{ file := s.adaptFile("fixtures/acme/acme_base.toml", templateModel{
Acme: map[string]static.CertificateResolver{ Acme: map[string]static.CertificateResolver{
"default": {ACME: &acme.Configuration{ "default": {ACME: &acme.Configuration{
CAServer: "http://wrongurl:4001/directory", CAServer: "http://wrongurl:4001/directory",
@ -427,21 +438,16 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
}}, }},
}, },
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Expected traefik works // Expected traefik works
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
// Doing an HTTPS request and test the response certificate. // Doing an HTTPS request and test the response certificate.
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) { func (s *AcmeSuite) retrieveAcmeCertificate(testCase acmeTestCase) {
if len(testCase.template.PortHTTP) == 0 { if len(testCase.template.PortHTTP) == 0 {
testCase.template.PortHTTP = ":5002" testCase.template.PortHTTP = ":5002"
} }
@ -456,14 +462,10 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
} }
} }
file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.template) file := s.adaptFile(testCase.traefikConfFilePath, testCase.template)
defer os.Remove(file)
s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// A real file is needed to have the right mode on acme.json file // A real file is needed to have the right mode on acme.json file
defer os.Remove("/tmp/acme.json") defer os.Remove("/tmp/acme.json")
@ -477,11 +479,11 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
} }
// wait for traefik (generating acme account take some seconds) // wait for traefik (generating acme account take some seconds)
err = try.Do(60*time.Second, func() error { err := try.Do(60*time.Second, func() error {
_, errGet := client.Get("https://127.0.0.1:5001") _, errGet := client.Get("https://127.0.0.1:5001")
return errGet return errGet
}) })
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
for _, sub := range testCase.subCases { for _, sub := range testCase.subCases {
client = &http.Client{ client = &http.Client{
@ -503,7 +505,7 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
var resp *http.Response var resp *http.Response
// Retry to send a Request which uses the LE generated certificate // Retry to send a Request which uses the LE generated certificate
err = try.Do(60*time.Second, func() error { err := try.Do(60*time.Second, func() error {
resp, err = client.Do(req) resp, err = client.Do(req)
if err != nil { if err != nil {
return err return err
@ -517,10 +519,10 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
return nil return nil
}) })
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
// Check Domain into response certificate // Check Domain into response certificate
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, sub.expectedCommonName) assert.Equal(s.T(), sub.expectedCommonName, resp.TLS.PeerCertificates[0].Subject.CommonName)
c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, sub.expectedAlgorithm) assert.Equal(s.T(), sub.expectedAlgorithm, resp.TLS.PeerCertificates[0].PublicKeyAlgorithm)
} }
} }

View file

@ -8,36 +8,38 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"strconv" "strconv"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/dynamic"
checker "github.com/vdemeester/shakers"
) )
type ThrottlingSuite struct{ BaseSuite } type ThrottlingSuite struct{ BaseSuite }
func (s *ThrottlingSuite) SetUpSuite(c *check.C) { func TestThrottlingSuite(t *testing.T) {
s.createComposeProject(c, "rest") suite.Run(t, new(ThrottlingSuite))
s.composeUp(c)
} }
func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) { func (s *ThrottlingSuite) SetupSuite() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml")) s.BaseSuite.SetupSuite()
s.createComposeProject("rest")
s.composeUp()
}
defer display(c) func (s *ThrottlingSuite) TestThrottleConfReload() {
err := cmd.Start() s.traefikCmd(withConfigFile("fixtures/throttling/simple.toml"))
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Expected a 404 as we did not configure anything. // Expected a 404 as we did not configure anything.
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
config := &dynamic.Configuration{ config := &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{ HTTP: &dynamic.HTTPConfiguration{
@ -47,7 +49,7 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
LoadBalancer: &dynamic.ServersLoadBalancer{ LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{ Servers: []dynamic.Server{
{ {
URL: "http://" + s.getComposeServiceIP(c, "whoami1") + ":80", URL: "http://" + s.getComposeServiceIP("whoami1") + ":80",
}, },
}, },
}, },
@ -68,28 +70,28 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
for i := 0; i < confChanges; i++ { for i := 0; i < confChanges; i++ {
config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router config.HTTP.Routers[fmt.Sprintf("routerHTTP%d", i)] = router
data, err := json.Marshal(config) data, err := json.Marshal(config)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data)) request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request) response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, response.StatusCode)
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
} }
reloadsRegexp := regexp.MustCompile(`traefik_config_reloads_total (\d*)\n`) reloadsRegexp := regexp.MustCompile(`traefik_config_reloads_total (\d*)\n`)
resp, err := http.Get("http://127.0.0.1:8080/metrics") resp, err := http.Get("http://127.0.0.1:8080/metrics")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
fields := reloadsRegexp.FindStringSubmatch(string(body)) fields := reloadsRegexp.FindStringSubmatch(string(body))
c.Assert(len(fields), checker.Equals, 2) assert.Len(s.T(), fields, 2)
reloads, err := strconv.Atoi(fields[1]) reloads, err := strconv.Atoi(fields[1])
if err != nil { if err != nil {
@ -101,5 +103,5 @@ func (s *ThrottlingSuite) TestThrottleConfReload(c *check.C) {
// Therefore the throttling (set at 400ms for this test) should only let // Therefore the throttling (set at 400ms for this test) should only let
// (2s / 400 ms =) 5 config reloads happen in theory. // (2s / 400 ms =) 5 config reloads happen in theory.
// In addition, we have to take into account the extra config reload from the internal provider (5 + 1). // In addition, we have to take into account the extra config reload from the internal provider (5 + 1).
c.Assert(reloads, checker.LessOrEqualThan, 6) assert.LessOrEqual(s.T(), reloads, 6)
} }

View file

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

View file

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

View file

@ -3,15 +3,16 @@ package integration
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"os"
"strings" "strings"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api" "github.com/traefik/traefik/v2/pkg/api"
"github.com/traefik/traefik/v2/pkg/testhelpers" "github.com/traefik/traefik/v2/pkg/testhelpers"
checker "github.com/vdemeester/shakers"
) )
// Docker tests suite. // Docker tests suite.
@ -19,12 +20,21 @@ type DockerComposeSuite struct {
BaseSuite BaseSuite
} }
func (s *DockerComposeSuite) SetUpSuite(c *check.C) { func TestDockerComposeSuite(t *testing.T) {
s.createComposeProject(c, "minimal") suite.Run(t, new(DockerComposeSuite))
s.composeUp(c)
} }
func (s *DockerComposeSuite) TestComposeScale(c *check.C) { func (s *DockerComposeSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("minimal")
s.composeUp()
}
func (s *DockerComposeSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *DockerComposeSuite) TestComposeScale() {
tempObjects := struct { tempObjects := struct {
DockerHost string DockerHost string
DefaultRule string DefaultRule string
@ -32,45 +42,36 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
DockerHost: s.getDockerHost(), DockerHost: s.getDockerHost(),
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)", DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
} }
file := s.adaptFile(c, "fixtures/docker/minimal.toml", tempObjects) file := s.adaptFile("fixtures/docker/minimal.toml", tempObjects)
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
req.Host = "my.super.host" req.Host = "my.super.host"
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK) _, err := try.ResponseUntilStatusCode(req, 5*time.Second, http.StatusOK)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata") resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer resp.Body.Close() defer resp.Body.Close()
var rtconf api.RunTimeRepresentation var rtconf api.RunTimeRepresentation
err = json.NewDecoder(resp.Body).Decode(&rtconf) err = json.NewDecoder(resp.Body).Decode(&rtconf)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// check that we have only three routers (the one from this test + 2 unrelated internal ones) // check that we have only three routers (the one from this test + 2 unrelated internal ones)
c.Assert(rtconf.Routers, checker.HasLen, 3) assert.Len(s.T(), rtconf.Routers, 3)
// check that we have only one service (not counting the internal ones) with n servers // check that we have only one service (not counting the internal ones) with n servers
services := rtconf.Services services := rtconf.Services
c.Assert(services, checker.HasLen, 4) assert.Len(s.T(), services, 4)
for name, service := range services { for name, service := range services {
if strings.HasSuffix(name, "@internal") { if strings.HasSuffix(name, "@internal") {
continue continue
} }
assert.Equal(s.T(), "service-mini@docker", name)
composeProject, err := s.composeProjectOptions.ToProject(nil) assert.Len(s.T(), service.LoadBalancer.Servers, 2)
c.Assert(err, checker.IsNil)
c.Assert(name, checker.Equals, "whoami1-"+composeProject.Name+"@docker")
c.Assert(service.LoadBalancer.Servers, checker.HasLen, 2)
// We could break here, but we don't just to keep us honest. // We could break here, but we don't just to keep us honest.
} }
} }

View file

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

View file

@ -3,12 +3,12 @@ package integration
import ( import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
// ErrorPagesSuite test suites. // ErrorPagesSuite test suites.
@ -18,83 +18,78 @@ type ErrorPagesSuite struct {
BackendIP string BackendIP string
} }
func (s *ErrorPagesSuite) SetUpSuite(c *check.C) { func TestErrorPagesSuite(t *testing.T) {
s.createComposeProject(c, "error_pages") suite.Run(t, new(ErrorPagesSuite))
s.composeUp(c)
s.ErrorPageIP = s.getComposeServiceIP(c, "nginx2")
s.BackendIP = s.getComposeServiceIP(c, "nginx1")
} }
func (s *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) { func (s *ErrorPagesSuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/error_pages/simple.toml", struct { s.BaseSuite.SetupSuite()
s.createComposeProject("error_pages")
s.composeUp()
s.ErrorPageIP = s.getComposeServiceIP("nginx2")
s.BackendIP = s.getComposeServiceIP("nginx1")
}
func (s *ErrorPagesSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *ErrorPagesSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/error_pages/simple.toml", struct {
Server1 string Server1 string
Server2 string Server2 string
}{"http://" + s.BackendIP + ":80", s.ErrorPageIP}) }{"http://" + s.BackendIP + ":80", s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil) frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
frontendReq.Host = "test.local" frontendReq.Host = "test.local"
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("nginx")) err = try.Request(frontendReq, 2*time.Second, try.BodyContains("nginx"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *ErrorPagesSuite) TestErrorPage(c *check.C) { func (s *ErrorPagesSuite) TestErrorPage() {
// error.toml contains a mis-configuration of the backend host // error.toml contains a mis-configuration of the backend host
file := s.adaptFile(c, "fixtures/error_pages/error.toml", struct { file := s.adaptFile("fixtures/error_pages/error.toml", struct {
Server1 string Server1 string
Server2 string Server2 string
}{s.BackendIP, s.ErrorPageIP}) }{s.BackendIP, s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil) frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
frontendReq.Host = "test.local" frontendReq.Host = "test.local"
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred.")) err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred."))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *ErrorPagesSuite) TestErrorPageFlush(c *check.C) { func (s *ErrorPagesSuite) TestErrorPageFlush() {
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Add("Transfer-Encoding", "chunked") rw.Header().Add("Transfer-Encoding", "chunked")
rw.WriteHeader(http.StatusInternalServerError) rw.WriteHeader(http.StatusInternalServerError)
_, _ = rw.Write([]byte("KO")) _, _ = rw.Write([]byte("KO"))
})) }))
file := s.adaptFile(c, "fixtures/error_pages/simple.toml", struct { file := s.adaptFile("fixtures/error_pages/simple.toml", struct {
Server1 string Server1 string
Server2 string Server2 string
}{srv.URL, s.ErrorPageIP}) }{srv.URL, s.ErrorPageIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil) frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
frontendReq.Host = "test.local" frontendReq.Host = "test.local"
err = try.Request(frontendReq, 2*time.Second, err = try.Request(frontendReq, 2*time.Second,
try.BodyContains("An error occurred."), try.BodyContains("An error occurred."),
try.HasHeaderValue("Content-Type", "text/html", true), try.HasHeaderValue("Content-Type", "text/html", true),
) )
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }

View file

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

View file

@ -2,61 +2,58 @@ package integration
import ( import (
"net/http" "net/http"
"os" "testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
// File tests suite. // File tests suite.
type FileSuite struct{ BaseSuite } type FileSuite struct{ BaseSuite }
func (s *FileSuite) SetUpSuite(c *check.C) { func TestFileSuite(t *testing.T) {
s.createComposeProject(c, "file") suite.Run(t, new(FileSuite))
s.composeUp(c)
} }
func (s *FileSuite) TestSimpleConfiguration(c *check.C) { func (s *FileSuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/file/simple.toml", struct{}{}) s.BaseSuite.SetupSuite()
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.createComposeProject("file")
defer display(c) s.composeUp()
err := cmd.Start() }
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) func (s *FileSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *FileSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/file/simple.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
// Expected a 404 as we did not configure anything // Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
// #56 regression test, make sure it does not fail? // #56 regression test, make sure it does not fail?
func (s *FileSuite) TestSimpleConfigurationNoPanic(c *check.C) { func (s *FileSuite) TestSimpleConfigurationNoPanic() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml")) s.traefikCmd(withConfigFile("fixtures/file/56-simple-panic.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Expected a 404 as we did not configure anything // Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *FileSuite) TestDirectoryConfiguration(c *check.C) { func (s *FileSuite) TestDirectoryConfiguration() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/file/directory.toml")) s.traefikCmd(withConfigFile("fixtures/file/directory.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Expected a 404 as we did not configure anything at /test // Expected a 404 as we did not configure anything at /test
err = try.GetRequest("http://127.0.0.1:8000/test", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/test", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Expected a 502 as there is no backend server // Expected a 502 as there is no backend server
err = try.GetRequest("http://127.0.0.1:8000/test2", 1000*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway)) err = try.GetRequest("http://127.0.0.1:8000/test2", 1000*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }

View file

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

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" "math/rand"
"net" "net"
"os" "os"
"testing"
"time" "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/helloworld"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
@ -29,16 +31,20 @@ const randCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567
// GRPCSuite tests suite. // GRPCSuite tests suite.
type GRPCSuite struct{ BaseSuite } type GRPCSuite struct{ BaseSuite }
func TestGRPCSuite(t *testing.T) {
suite.Run(t, new(GRPCSuite))
}
type myserver struct { type myserver struct {
stopStreamExample chan bool stopStreamExample chan bool
} }
func (s *GRPCSuite) SetUpSuite(c *check.C) { func (s *GRPCSuite) SetupSuite() {
var err error var err error
LocalhostCert, err = os.ReadFile("./resources/tls/local.cert") LocalhostCert, err = os.ReadFile("./resources/tls/local.cert")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
LocalhostKey, err = os.ReadFile("./resources/tls/local.key") LocalhostKey, err = os.ReadFile("./resources/tls/local.key")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
} }
func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) { func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
@ -137,19 +143,18 @@ func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func
return t, closer, nil return t, closer, nil
} }
func (s *GRPCSuite) TestGRPC(c *check.C) { func (s *GRPCSuite) TestGRPC() {
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := startGRPCServer(lis, &myserver{}) err := startGRPCServer(lis, &myserver{})
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct { file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -159,79 +164,65 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var response string var response string
err = try.Do(1*time.Second, func() error { err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true) response, err = callHelloClientGRPC("World", true)
return err return err
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(response, check.Equals, "Hello World") assert.Equal(s.T(), "Hello World", response)
} }
func (s *GRPCSuite) TestGRPCh2c(c *check.C) { func (s *GRPCSuite) TestGRPCh2c() {
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := starth2cGRPCServer(lis, &myserver{}) err := starth2cGRPCServer(lis, &myserver{})
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config_h2c.toml", struct { file := s.adaptFile("fixtures/grpc/config_h2c.toml", struct {
GRPCServerPort string GRPCServerPort string
}{ }{
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var response string var response string
err = try.Do(1*time.Second, func() error { err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", false) response, err = callHelloClientGRPC("World", false)
return err return err
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(response, check.Equals, "Hello World") assert.Equal(s.T(), "Hello World", response)
} }
func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) { func (s *GRPCSuite) TestGRPCh2cTermination() {
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := starth2cGRPCServer(lis, &myserver{}) err := starth2cGRPCServer(lis, &myserver{})
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config_h2c_termination.toml", struct { file := s.adaptFile("fixtures/grpc/config_h2c_termination.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -241,40 +232,33 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var response string var response string
err = try.Do(1*time.Second, func() error { err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true) response, err = callHelloClientGRPC("World", true)
return err return err
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(response, check.Equals, "Hello World") assert.Equal(s.T(), "Hello World", response)
} }
func (s *GRPCSuite) TestGRPCInsecure(c *check.C) { func (s *GRPCSuite) TestGRPCInsecure() {
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := startGRPCServer(lis, &myserver{}) err := startGRPCServer(lis, &myserver{})
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config_insecure.toml", struct { file := s.adaptFile("fixtures/grpc/config_insecure.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -284,44 +268,37 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var response string var response string
err = try.Do(1*time.Second, func() error { err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true) response, err = callHelloClientGRPC("World", true)
return err return err
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(response, check.Equals, "Hello World") assert.Equal(s.T(), "Hello World", response)
} }
func (s *GRPCSuite) TestGRPCBuffer(c *check.C) { func (s *GRPCSuite) TestGRPCBuffer() {
stopStreamExample := make(chan bool) stopStreamExample := make(chan bool)
defer func() { stopStreamExample <- true }() defer func() { stopStreamExample <- true }()
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := startGRPCServer(lis, &myserver{ err := startGRPCServer(lis, &myserver{
stopStreamExample: stopStreamExample, stopStreamExample: stopStreamExample,
}) })
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct { file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -331,27 +308,21 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var client helloworld.Greeter_StreamExampleClient var client helloworld.Greeter_StreamExampleClient
client, closer, err := callStreamExampleClientGRPC() client, closer, err := callStreamExampleClientGRPC()
defer func() { _ = closer() }() defer func() { _ = closer() }()
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
received := make(chan bool) received := make(chan bool)
go func() { go func() {
tr, err := client.Recv() tr, err := client.Recv()
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(len(tr.GetData()), check.Equals, 512) assert.Len(s.T(), tr.GetData(), 512)
received <- true received <- true
}() }()
@ -363,25 +334,24 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
return errors.New("failed to receive stream data") return errors.New("failed to receive stream data")
} }
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
} }
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) { func (s *GRPCSuite) TestGRPCBufferWithFlushInterval() {
stopStreamExample := make(chan bool) stopStreamExample := make(chan bool)
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := startGRPCServer(lis, &myserver{ err := startGRPCServer(lis, &myserver{
stopStreamExample: stopStreamExample, stopStreamExample: stopStreamExample,
}) })
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct { file := s.adaptFile("fixtures/grpc/config.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -390,17 +360,11 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
KeyContent: string(LocalhostKey), KeyContent: string(LocalhostKey),
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
s.traefikCmd(withConfigFile(file))
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var client helloworld.Greeter_StreamExampleClient var client helloworld.Greeter_StreamExampleClient
client, closer, err := callStreamExampleClientGRPC() client, closer, err := callStreamExampleClientGRPC()
@ -408,13 +372,13 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
_ = closer() _ = closer()
stopStreamExample <- true stopStreamExample <- true
}() }()
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
received := make(chan bool) received := make(chan bool)
go func() { go func() {
tr, err := client.Recv() tr, err := client.Recv()
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(len(tr.GetData()), check.Equals, 512) assert.Len(s.T(), tr.GetData(), 512)
received <- true received <- true
}() }()
@ -426,22 +390,21 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
return errors.New("failed to receive stream data") return errors.New("failed to receive stream data")
} }
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
} }
func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) { func (s *GRPCSuite) TestGRPCWithRetry() {
lis, err := net.Listen("tcp", ":0") lis, err := net.Listen("tcp", ":0")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
_, port, err := net.SplitHostPort(lis.Addr().String()) _, port, err := net.SplitHostPort(lis.Addr().String())
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
go func() { go func() {
err := startGRPCServer(lis, &myserver{}) err := startGRPCServer(lis, &myserver{})
c.Log(err) assert.NoError(s.T(), err)
c.Assert(err, check.IsNil)
}() }()
file := s.adaptFile(c, "fixtures/grpc/config_retry.toml", struct { file := s.adaptFile("fixtures/grpc/config_retry.toml", struct {
CertContent string CertContent string
KeyContent string KeyContent string
GRPCServerPort string GRPCServerPort string
@ -451,23 +414,17 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
GRPCServerPort: port, GRPCServerPort: port,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
var response string var response string
err = try.Do(1*time.Second, func() error { err = try.Do(1*time.Second, func() error {
response, err = callHelloClientGRPC("World", true) response, err = callHelloClientGRPC("World", true)
return err return err
}) })
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
c.Assert(response, check.Equals, "Hello World") assert.Equal(s.T(), "Hello World", response)
} }

View file

@ -4,50 +4,45 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
// Headers tests suite. // Headers tests suite.
type HeadersSuite struct{ BaseSuite } type HeadersSuite struct{ BaseSuite }
func (s *HeadersSuite) TestSimpleConfiguration(c *check.C) { func TestHeadersSuite(t *testing.T) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/headers/basic.toml")) suite.Run(t, new(HeadersSuite))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Expected a 404 as we did not configure anything
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
} }
func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) { func (s *HeadersSuite) TestSimpleConfiguration() {
file := s.adaptFile(c, "fixtures/headers/remove_reverseproxy_headers.toml", struct{}{}) s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() // Expected a 404 as we did not configure anything
c.Assert(err, checker.IsNil) err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
defer s.killCmd(cmd) require.NoError(s.T(), err)
}
func (s *HeadersSuite) TestReverseProxyHeaderRemoved() {
file := s.adaptFile("fixtures/headers/remove_reverseproxy_headers.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, found := r.Header["X-Forwarded-Host"] _, found := r.Header["X-Forwarded-Host"]
c.Assert(found, checker.True) assert.True(s.T(), found)
_, found = r.Header["Foo"] _, found = r.Header["Foo"]
c.Assert(found, checker.False) assert.False(s.T(), found)
_, found = r.Header["X-Forwarded-For"] _, found = r.Header["X-Forwarded-For"]
c.Assert(found, checker.False) assert.False(s.T(), found)
}) })
listener, err := net.Listen("tcp", "127.0.0.1:9000") listener, err := net.Listen("tcp", "127.0.0.1:9000")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
ts := &httptest.Server{ ts := &httptest.Server{
Listener: listener, Listener: listener,
@ -57,31 +52,25 @@ func (s *HeadersSuite) TestReverseProxyHeaderRemoved(c *check.C) {
defer ts.Close() defer ts.Close()
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = "test.localhost" req.Host = "test.localhost"
req.Header = http.Header{ req.Header = http.Header{
"Foo": {"bar"}, "Foo": {"bar"},
} }
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *HeadersSuite) TestCorsResponses(c *check.C) { func (s *HeadersSuite) TestCorsResponses() {
file := s.adaptFile(c, "fixtures/headers/cors.toml", struct{}{}) file := s.adaptFile("fixtures/headers/cors.toml", struct{}{})
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
backend := startTestServer("9000", http.StatusOK, "") backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close() defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
testCase := []struct { testCase := []struct {
desc string desc string
@ -147,30 +136,24 @@ func (s *HeadersSuite) TestCorsResponses(c *check.C) {
for _, test := range testCase { for _, test := range testCase {
req, err := http.NewRequest(test.method, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(test.method, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = test.reqHost req.Host = test.reqHost
req.Header = test.requestHeaders req.Header = test.requestHeaders
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected)) err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
} }
func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) { func (s *HeadersSuite) TestSecureHeadersResponses() {
file := s.adaptFile(c, "fixtures/headers/secure.toml", struct{}{}) file := s.adaptFile("fixtures/headers/secure.toml", struct{}{})
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
backend := startTestServer("9000", http.StatusOK, "") backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close() defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
testCase := []struct { testCase := []struct {
desc string desc string
@ -190,36 +173,30 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
for _, test := range testCase { for _, test := range testCase {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = test.reqHost req.Host = test.reqHost
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected)) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil) req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = test.internalReqHost req.Host = test.internalReqHost
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected)) err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
} }
func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) { func (s *HeadersSuite) TestMultipleSecureHeadersResponses() {
file := s.adaptFile(c, "fixtures/headers/secure_multiple.toml", struct{}{}) file := s.adaptFile("fixtures/headers/secure_multiple.toml", struct{}{})
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
backend := startTestServer("9000", http.StatusOK, "") backend := startTestServer("9000", http.StatusOK, "")
defer backend.Close() defer backend.Close()
err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
testCase := []struct { testCase := []struct {
desc string desc string
@ -238,10 +215,10 @@ func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) {
for _, test := range testCase { for _, test := range testCase {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req.Host = test.reqHost req.Host = test.reqHost
err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected)) err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
} }

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -9,210 +9,325 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
stdlog "log"
"net/http"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"slices"
"strings" "strings"
"testing" "testing"
"text/template" "text/template"
"time" "time"
"github.com/docker/cli/cli/command" "github.com/docker/docker/api/types/container"
"github.com/docker/cli/cli/config/configfile" "github.com/docker/docker/api/types/mount"
"github.com/docker/cli/cli/context/docker" dockernetwork "github.com/docker/docker/api/types/network"
"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/fatih/structs" "github.com/fatih/structs"
"github.com/go-check/check" "github.com/stretchr/testify/require"
notaryclient "github.com/theupdateframework/notary/client" "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" "github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers" "gopkg.in/yaml.v3"
) )
var ( var showLog = flag.Bool("tlog", false, "always show Traefik logs")
integration = flag.Bool("integration", false, "run integration tests")
showLog = flag.Bool("tlog", false, "always show Traefik logs")
)
func Test(t *testing.T) { type composeConfig struct {
if !*integration { Services map[string]composeService `yaml:"services"`
log.WithoutContext().Info("Integration tests disabled.")
return
} }
// TODO(mpl): very niche optimization: do not start tailscale if none of the type composeService struct {
// wanted tests actually need it (e.g. KeepAliveSuite does not). Image string `yaml:"image"`
var ( Labels map[string]string `yaml:"labels"`
vpn *tailscaleNotSuite Hostname string `yaml:"hostname"`
useVPN bool Volumes []string `yaml:"volumes"`
) CapAdd []string `yaml:"cap_add"`
if os.Getenv("IN_DOCKER") != "true" { Command []string `yaml:"command"`
if vpn = setupVPN(nil, "tailscale.secret"); vpn != nil { Environment map[string]string `yaml:"environment"`
defer vpn.TearDownSuite(nil) Privileged bool `yaml:"privileged"`
useVPN = true Deploy composeDeploy `yaml:"deploy"`
}
} }
check.Suite(&AccessLogSuite{}) type composeDeploy struct {
if !useVPN { Replicas int `yaml:"replicas"`
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)
} }
var traefikBinary = "../dist/traefik" var traefikBinary = "../dist/traefik"
type BaseSuite struct { type BaseSuite struct {
composeProjectOptions *cmdcompose.ProjectOptions suite.Suite
dockerComposeService composeapi.Service containers map[string]testcontainers.Container
dockerClient *client.Client network *testcontainers.DockerNetwork
hostIP string
} }
func (s *BaseSuite) TearDownSuite(c *check.C) { func (s *BaseSuite) waitForTraefik(containerName string) {
if s.composeProjectOptions != nil && s.dockerComposeService != nil { time.Sleep(1 * time.Second)
s.composeDown(c)
// Wait for Traefik to turn ready.
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
require.NoError(s.T(), err)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
require.NoError(s.T(), err)
} }
func (s *BaseSuite) displayTraefikLogFile(path string) {
if s.T().Failed() {
if _, err := os.Stat(path); !os.IsNotExist(err) {
content, errRead := os.ReadFile(path)
// TODO TestName
// fmt.Printf("%s: Traefik logs: \n", c.TestName())
fmt.Print("Traefik logs: \n")
if errRead == nil {
fmt.Println(content)
} else {
fmt.Println(errRead)
}
} else {
// fmt.Printf("%s: No Traefik logs.\n", c.TestName())
fmt.Print("No Traefik logs.\n")
}
errRemove := os.Remove(path)
if errRemove != nil {
fmt.Println(errRemove)
}
}
}
func (s *BaseSuite) SetupSuite() {
// configure default standard log.
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
// TODO
// stdlog.SetOutput(log.Logger)
ctx := context.Background()
// Create docker network
// docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
var opts []network.NetworkCustomizer
opts = append(opts, network.WithDriver("bridge"))
opts = append(opts, network.WithIPAM(&dockernetwork.IPAM{
Driver: "default",
Config: []dockernetwork.IPAMConfig{
{
Subnet: "172.31.42.0/24",
},
},
}))
dockerNetwork, err := network.New(ctx, opts...)
require.NoError(s.T(), err)
s.network = dockerNetwork
s.hostIP = "172.31.42.1"
if isDockerDesktop(ctx, s.T()) {
s.hostIP = getDockerDesktopHostIP(ctx, s.T())
s.setupVPN("tailscale.secret")
}
}
func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string {
t.Helper()
req := testcontainers.ContainerRequest{
Image: "alpine",
HostConfigModifier: func(config *container.HostConfig) {
config.AutoRemove = true
},
Cmd: []string{"getent", "hosts", "host.docker.internal"},
}
con, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
require.NoError(t, err)
closer, err := con.Logs(ctx)
require.NoError(t, err)
all, err := io.ReadAll(closer)
require.NoError(t, err)
ipRegex := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
matches := ipRegex.FindAllString(string(all), -1)
require.Len(t, matches, 1)
return matches[0]
}
func isDockerDesktop(ctx context.Context, t *testing.T) bool {
t.Helper()
cli, err := testcontainers.NewDockerClientWithOpts(ctx)
if err != nil {
t.Fatalf("failed to create docker client: %s", err)
}
info, err := cli.Info(ctx)
if err != nil {
t.Fatalf("failed to get docker info: %s", err)
}
return info.OperatingSystem == "Docker Desktop"
}
func (s *BaseSuite) TearDownSuite() {
s.composeDown()
err := try.Do(5*time.Second, func() error {
if s.network != nil {
err := s.network.Remove(context.Background())
if err != nil {
return err
}
}
return nil
})
require.NoError(s.T(), err)
} }
// createComposeProject creates the docker compose project stored as a field in the BaseSuite. // createComposeProject creates the docker compose project stored as a field in the BaseSuite.
// This method should be called before starting and/or stopping compose services. // This method should be called before starting and/or stopping compose services.
func (s *BaseSuite) createComposeProject(c *check.C, name string) { func (s *BaseSuite) createComposeProject(name string) {
projectName := fmt.Sprintf("traefik-integration-test-%s", name)
composeFile := fmt.Sprintf("resources/compose/%s.yml", name) composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
var err error file, err := os.ReadFile(composeFile)
s.dockerClient, err = client.NewClientWithOpts() require.NoError(s.T(), err)
c.Assert(err, checker.IsNil)
fakeCLI := &FakeDockerCLI{client: s.dockerClient} var composeConfigData composeConfig
s.dockerComposeService = compose.NewComposeService(fakeCLI) err = yaml.Unmarshal(file, &composeConfigData)
require.NoError(s.T(), err)
s.composeProjectOptions = &cmdcompose.ProjectOptions{ if s.containers == nil {
ProjectDir: ".", s.containers = map[string]testcontainers.Container{}
ProjectName: projectName,
ConfigPaths: []string{composeFile},
} }
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. // composeUp starts the given services of the current docker compose project, if they are not already started.
// Already running services are not affected (i.e. not stopped). // Already running services are not affected (i.e. not stopped).
func (s *BaseSuite) composeUp(c *check.C, services ...string) { func (s *BaseSuite) composeUp(services ...string) {
c.Assert(s.composeProjectOptions, check.NotNil) for name, con := range s.containers {
c.Assert(s.dockerComposeService, check.NotNil) if len(services) == 0 || slices.Contains(services, name) {
err := con.Start(context.Background())
composeProject, err := s.composeProjectOptions.ToProject(nil) require.NoError(s.T(), err)
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)
} }
// composeStop stops the given services of the current docker compose project and removes the corresponding containers. // composeStop stops the given services of the current docker compose project and removes the corresponding containers.
func (s *BaseSuite) composeStop(c *check.C, services ...string) { func (s *BaseSuite) composeStop(services ...string) {
c.Assert(s.composeProjectOptions, check.NotNil) for name, con := range s.containers {
c.Assert(s.dockerComposeService, check.NotNil) if len(services) == 0 || slices.Contains(services, name) {
timeout := 10 * time.Second
composeProject, err := s.composeProjectOptions.ToProject(nil) err := con.Stop(context.Background(), &timeout)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
}
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)
} }
// composeDown stops all compose project services and removes the corresponding containers. // composeDown stops all compose project services and removes the corresponding containers.
func (s *BaseSuite) composeDown(c *check.C) { func (s *BaseSuite) composeDown() {
c.Assert(s.composeProjectOptions, check.NotNil) for _, c := range s.containers {
c.Assert(s.dockerComposeService, check.NotNil) err := c.Terminate(context.Background())
require.NoError(s.T(), err)
composeProject, err := s.composeProjectOptions.ToProject(nil) }
c.Assert(err, checker.IsNil) s.containers = map[string]testcontainers.Container{}
err = s.dockerComposeService.Down(context.Background(), composeProject.Name, composeapi.DownOptions{})
c.Assert(err, checker.IsNil)
} }
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) { func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
cmd := exec.Command(traefikBinary, args...) cmd := exec.Command(traefikBinary, args...)
s.T().Cleanup(func() {
s.killCmd(cmd)
})
var out bytes.Buffer var out bytes.Buffer
cmd.Stdout = &out cmd.Stdout = &out
cmd.Stderr = &out cmd.Stderr = &out
err := cmd.Start()
require.NoError(s.T(), err)
return cmd, &out return cmd, &out
} }
func (s *BaseSuite) killCmd(cmd *exec.Cmd) { func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
if cmd.Process == nil {
log.WithoutContext().Error("No process to kill")
return
}
err := cmd.Process.Kill() err := cmd.Process.Kill()
if err != nil { if err != nil {
log.WithoutContext().Errorf("Kill: %v", err) log.WithoutContext().Errorf("Kill: %v", err)
@ -221,15 +336,18 @@ func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
func (s *BaseSuite) traefikCmd(args ...string) (*exec.Cmd, func(*check.C)) { func (s *BaseSuite) traefikCmd(args ...string) *exec.Cmd {
cmd, out := s.cmdTraefik(args...) cmd, out := s.cmdTraefik(args...)
return cmd, func(c *check.C) {
if c.Failed() || *showLog { s.T().Cleanup(func() {
if s.T().Failed() || *showLog {
s.displayLogK3S() s.displayLogK3S()
s.displayLogCompose(c) s.displayLogCompose()
s.displayTraefikLog(c, out) s.displayTraefikLog(out)
}
} }
})
return cmd
} }
func (s *BaseSuite) displayLogK3S() { func (s *BaseSuite) displayLogK3S() {
@ -246,34 +364,33 @@ func (s *BaseSuite) displayLogK3S() {
log.WithoutContext().Println() log.WithoutContext().Println()
} }
func (s *BaseSuite) displayLogCompose(c *check.C) { func (s *BaseSuite) displayLogCompose() {
if s.dockerComposeService == nil || s.composeProjectOptions == nil { for name, ctn := range s.containers {
log.WithoutContext().Infof("%s: No docker compose logs.", c.TestName()) readCloser, err := ctn.Logs(context.Background())
return require.NoError(s.T(), err)
for {
b := make([]byte, 1024)
_, err := readCloser.Read(b)
if errors.Is(err, io.EOF) {
break
}
require.NoError(s.T(), err)
trimLogs := bytes.Trim(bytes.TrimSpace(b), string([]byte{0}))
if len(trimLogs) > 0 {
log.WithoutContext().WithField("container", name).Info(string(trimLogs))
}
}
}
} }
log.WithoutContext().Infof("%s: docker compose logs: ", c.TestName()) func (s *BaseSuite) displayTraefikLog(output *bytes.Buffer) {
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) {
if output == nil || output.Len() == 0 { if output == nil || output.Len() == 0 {
log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName()) log.WithoutContext().Info("No Traefik logs.")
} else { } else {
log.WithoutContext().Infof("%s: Traefik logs: ", c.TestName()) for _, line := range strings.Split(output.String(), "\n") {
log.WithoutContext().Infof(output.String()) log.WithoutContext().Info(line)
}
} }
} }
@ -287,73 +404,47 @@ func (s *BaseSuite) getDockerHost() string {
return dockerHost return dockerHost
} }
func (s *BaseSuite) adaptFile(c *check.C, path string, tempObjects interface{}) string { func (s *BaseSuite) adaptFile(path string, tempObjects interface{}) string {
// Load file // Load file
tmpl, err := template.ParseFiles(path) tmpl, err := template.ParseFiles(path)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
folder, prefix := filepath.Split(path) folder, prefix := filepath.Split(path)
tmpFile, err := os.CreateTemp(folder, strings.TrimSuffix(prefix, filepath.Ext(prefix))+"_*"+filepath.Ext(prefix)) tmpFile, err := os.CreateTemp(folder, strings.TrimSuffix(prefix, filepath.Ext(prefix))+"_*"+filepath.Ext(prefix))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer tmpFile.Close() defer tmpFile.Close()
model := structs.Map(tempObjects) model := structs.Map(tempObjects)
model["SelfFilename"] = tmpFile.Name() model["SelfFilename"] = tmpFile.Name()
err = tmpl.ExecuteTemplate(tmpFile, prefix, model) err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = tmpFile.Sync() err = tmpFile.Sync()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
s.T().Cleanup(func() {
os.Remove(tmpFile.Name())
})
return tmpFile.Name() return tmpFile.Name()
} }
func (s *BaseSuite) getComposeServiceIP(c *check.C, name string) string { func (s *BaseSuite) getComposeServiceIP(name string) string {
c.Assert(s.composeProjectOptions, check.NotNil) container, ok := s.containers[name]
c.Assert(s.dockerComposeService, check.NotNil) if !ok {
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
}
// Should never happen.
c.Error("No network found")
return "" return ""
} }
ip, err := container.ContainerIP(context.Background())
if err != nil {
return ""
}
return ip
}
func withConfigFile(file string) string { func withConfigFile(file string) string {
return "--configFile=" + file return "--configFile=" + file
} }
// tailscaleNotSuite includes a BaseSuite out of convenience, so we can benefit
// from composeUp et co., but it is not meant to function as a TestSuite per se.
type tailscaleNotSuite struct{ BaseSuite }
// setupVPN starts Tailscale on the corresponding container, and makes it a subnet // setupVPN starts Tailscale on the corresponding container, and makes it a subnet
// router, for all the other containers (whoamis, etc) subsequently started for the // router, for all the other containers (whoamis, etc) subsequently started for the
// integration tests. // integration tests.
@ -369,101 +460,35 @@ type tailscaleNotSuite struct{ BaseSuite }
// "172.0.0.0/8": ["your_tailscale_identity"], // "172.0.0.0/8": ["your_tailscale_identity"],
// }, // },
// }, // },
// func (s *BaseSuite) setupVPN(keyFile string) {
// TODO(mpl): we could maybe even move this setup to the Makefile, to start it
// and let it run (forever, or until voluntarily stopped).
func setupVPN(c *check.C, keyFile string) *tailscaleNotSuite {
data, err := os.ReadFile(keyFile) data, err := os.ReadFile(keyFile)
if err != nil { if err != nil {
if !errors.Is(err, fs.ErrNotExist) { if !errors.Is(err, fs.ErrNotExist) {
log.Fatal(err) log.WithoutContext().Error(err)
} }
return nil
return
} }
authKey := strings.TrimSpace(string(data)) authKey := strings.TrimSpace(string(data))
// TODO: copy and create versions that don't need a check.C? // // TODO: copy and create versions that don't need a check.C?
vpn := &tailscaleNotSuite{} s.createComposeProject("tailscale")
vpn.createComposeProject(c, "tailscale") s.composeUp()
vpn.composeUp(c)
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
// If we ever change the docker subnet in the Makefile, // If we ever change the docker subnet in the Makefile,
// we need to change this one below correspondingly. // we need to change this one below correspondingly.
vpn.composeExec(c, "tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24") s.composeExec("tailscaled", "tailscale", "up", "--authkey="+authKey, "--advertise-routes=172.31.42.0/24")
return vpn
} }
type FakeDockerCLI struct { // composeExec runs the command in the given args in the given compose service container.
client client.APIClient // 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 { _, reader, err := s.containers[service].Exec(context.Background(), args)
return f.client require.NoError(s.T(), err)
}
func (f FakeDockerCLI) In() *streams.In { content, err := io.ReadAll(reader)
return streams.NewIn(os.Stdin) require.NoError(s.T(), err)
}
func (f FakeDockerCLI) Out() *streams.Out { return string(content)
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")
} }

View file

@ -7,18 +7,21 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"testing"
"time" "time"
"github.com/go-check/check"
"github.com/pmezard/go-difflib/difflib" "github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api" "github.com/traefik/traefik/v2/pkg/api"
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
checker "github.com/vdemeester/shakers"
) )
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata") var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
@ -26,25 +29,39 @@ var updateExpected = flag.Bool("update_expected", false, "Update expected files
// K8sSuite tests suite. // K8sSuite tests suite.
type K8sSuite struct{ BaseSuite } type K8sSuite struct{ BaseSuite }
func (s *K8sSuite) SetUpSuite(c *check.C) { func TestK8sSuite(t *testing.T) {
s.createComposeProject(c, "k8s") suite.Run(t, new(K8sSuite))
s.composeUp(c) }
func (s *K8sSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("k8s")
s.composeUp()
abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml") abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.Do(60*time.Second, func() error { err = try.Do(60*time.Second, func() error {
_, err := os.Stat(abs) _, err := os.Stat(abs)
return err return err
}) })
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
data, err := os.ReadFile(abs)
require.NoError(s.T(), err)
content := strings.ReplaceAll(string(data), "https://server:6443", fmt.Sprintf("https://%s", net.JoinHostPort(s.getComposeServiceIP("server"), "6443")))
err = os.WriteFile(abs, []byte(content), 0o644)
require.NoError(s.T(), err)
err = os.Setenv("KUBECONFIG", abs) err = os.Setenv("KUBECONFIG", abs)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *K8sSuite) TearDownSuite(c *check.C) { func (s *K8sSuite) TearDownSuite() {
s.composeDown(c) s.BaseSuite.TearDownSuite()
generatedFiles := []string{ generatedFiles := []string{
"./fixtures/k8s/config.skip/kubeconfig.yaml", "./fixtures/k8s/config.skip/kubeconfig.yaml",
@ -62,109 +79,78 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
} }
} }
func (s *K8sSuite) TestIngressConfiguration(c *check.C) { func (s *K8sSuite) TestIngressConfiguration() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-ingress.json", "8080")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingress.json", "8080")
} }
func (s *K8sSuite) TestIngressLabelSelector(c *check.C) { func (s *K8sSuite) TestIngressLabelSelector() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_ingress_label_selector.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-ingress-label-selector.json", "8080")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingress-label-selector.json", "8080")
} }
func (s *K8sSuite) TestCRDConfiguration(c *check.C) { func (s *K8sSuite) TestCRDConfiguration() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-crd.json", "8000")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-crd.json", "8000")
} }
func (s *K8sSuite) TestCRDLabelSelector(c *check.C) { func (s *K8sSuite) TestCRDLabelSelector() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_crd_label_selector.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-crd-label-selector.json", "8000")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-crd-label-selector.json", "8000")
} }
func (s *K8sSuite) TestGatewayConfiguration(c *check.C) { func (s *K8sSuite) TestGatewayConfiguration() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_gateway.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-gateway.json", "8080")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-gateway.json", "8080")
} }
func (s *K8sSuite) TestIngressclass(c *check.C) { func (s *K8sSuite) TestIngressclass() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml")) s.traefikCmd(withConfigFile("fixtures/k8s_ingressclass.toml"))
defer display(c)
err := cmd.Start() s.testConfiguration("testdata/rawdata-ingressclass.json", "8080")
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
testConfiguration(c, "testdata/rawdata-ingressclass.json", "8080")
} }
func testConfiguration(c *check.C, path, apiPort string) { func (s *K8sSuite) testConfiguration(path, apiPort string) {
err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`)) err := try.GetRequest("http://127.0.0.1:"+apiPort+"/api/entrypoints", 20*time.Second, try.BodyContains(`"name":"web"`))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash(path) expectedJSON := filepath.FromSlash(path)
if *updateExpected { if *updateExpected {
fi, err := os.Create(expectedJSON) fi, err := os.Create(expectedJSON)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = fi.Close() err = fi.Close()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
var buf bytes.Buffer var buf bytes.Buffer
err = try.GetRequest("http://127.0.0.1:"+apiPort+"/api/rawdata", 1*time.Minute, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf)) err = try.GetRequest("http://127.0.0.1:"+apiPort+"/api/rawdata", 1*time.Minute, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf))
if !*updateExpected { if !*updateExpected {
if err != nil { require.NoError(s.T(), err)
c.Error(err)
}
return return
} }
if err != nil { if err != nil {
c.Logf("In file update mode, got expected error: %v", err) log.WithoutContext().Infof("In file update mode, got expected error: %v", err)
} }
var rtRepr api.RunTimeRepresentation var rtRepr api.RunTimeRepresentation
err = json.Unmarshal(buf.Bytes(), &rtRepr) err = json.Unmarshal(buf.Bytes(), &rtRepr)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
newJSON, err := json.MarshalIndent(rtRepr, "", "\t") newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = os.WriteFile(expectedJSON, newJSON, 0o644) err = os.WriteFile(expectedJSON, newJSON, 0o644)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Errorf("We do not want a passing test in file update mode")
s.T().Fatal("We do not want a passing test in file update mode")
} }
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition { func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {

View file

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

View file

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

View file

@ -2,13 +2,13 @@ package integration
import ( import (
"net/http" "net/http"
"os" "testing"
"time" "time"
"github.com/gambol99/go-marathon" "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" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
// Marathon test suites. // Marathon test suites.
@ -17,42 +17,46 @@ type MarathonSuite15 struct {
marathonURL string marathonURL string
} }
func (s *MarathonSuite15) SetUpSuite(c *check.C) { func TestMarathonSuite15(t *testing.T) {
s.createComposeProject(c, "marathon15") suite.Run(t, new(MarathonSuite))
s.composeUp(c) }
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 // Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the // don't run into the "all cluster members down" state right from the
// start. // start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) { func (s *MarathonSuite15) TearDownSuite() {
c.Skip("doesn't work") s.BaseSuite.TearDownSuite()
}
func (s *MarathonSuite15) TestConfigurationUpdate() {
s.T().Skip("doesn't work")
// Start Traefik. // Start Traefik.
file := s.adaptFile(c, "fixtures/marathon/simple.toml", struct { file := s.adaptFile("fixtures/marathon/simple.toml", struct {
MarathonURL string MarathonURL string
}{s.marathonURL}) }{s.marathonURL})
defer os.Remove(file) s.traefikCmd(withConfigFile(file))
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Wait for Traefik to turn ready. // Wait for Traefik to turn ready.
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Prepare Marathon client. // Prepare Marathon client.
config := marathon.NewDefaultConfig() config := marathon.NewDefaultConfig()
config.URL = s.marathonURL config.URL = s.marathonURL
client, err := marathon.NewClient(config) client, err := marathon.NewClient(config)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Create test application to be deployed. // Create test application to be deployed.
app := marathon.NewDockerApplication(). app := marathon.NewDockerApplication().
@ -68,11 +72,11 @@ func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {
*app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork()) *app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork())
// Deploy the test application. // Deploy the test application.
deployApplication(c, client, app) s.deployApplication(client, app)
// Query application via Traefik. // Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/service", 30*time.Second, try.StatusCodeIs(http.StatusOK)) 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. // Create test application with services to be deployed.
app = marathon.NewDockerApplication(). app = marathon.NewDockerApplication().
@ -88,9 +92,9 @@ func (s *MarathonSuite15) TestConfigurationUpdate(c *check.C) {
*app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork()) *app.Networks = append(*app.Networks, *marathon.NewBridgePodNetwork())
// Deploy the test application. // Deploy the test application.
deployApplication(c, client, app) s.deployApplication(client, app)
// Query application via Traefik. // Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/app", 30*time.Second, try.StatusCodeIs(http.StatusOK)) 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 ( import (
"net/http" "net/http"
"os" "testing"
"time" "time"
"github.com/gambol99/go-marathon" "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" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
const containerNameMarathon = "marathon" const containerNameMarathon = "marathon"
@ -19,50 +19,55 @@ type MarathonSuite struct {
marathonURL string marathonURL string
} }
func (s *MarathonSuite) SetUpSuite(c *check.C) { func TestMarathonSuite(t *testing.T) {
s.createComposeProject(c, "marathon") suite.Run(t, new(MarathonSuite))
s.composeUp(c) }
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 // Wait for Marathon readiness prior to creating the client so that we
// don't run into the "all cluster members down" state right from the // don't run into the "all cluster members down" state right from the
// start. // start.
err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest(s.marathonURL+"/v2/leader", 1*time.Minute, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) 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) deploy, err := client.UpdateApplication(application, false)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Wait for deployment to complete. // 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) { func (s *MarathonSuite) TestConfigurationUpdate() {
c.Skip("doesn't work") s.T().Skip("doesn't work")
// Start Traefik. // Start Traefik.
file := s.adaptFile(c, "fixtures/marathon/simple.toml", struct { file := s.adaptFile("fixtures/marathon/simple.toml", struct {
MarathonURL string MarathonURL string
}{s.marathonURL}) }{s.marathonURL})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Wait for Traefik to turn ready. // Wait for Traefik to turn ready.
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Prepare Marathon client. // Prepare Marathon client.
config := marathon.NewDefaultConfig() config := marathon.NewDefaultConfig()
config.URL = s.marathonURL config.URL = s.marathonURL
client, err := marathon.NewClient(config) client, err := marathon.NewClient(config)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Create test application to be deployed. // Create test application to be deployed.
app := marathon.NewDockerApplication(). app := marathon.NewDockerApplication().
@ -75,11 +80,11 @@ func (s *MarathonSuite) TestConfigurationUpdate(c *check.C) {
Container("traefik/whoami") Container("traefik/whoami")
// Deploy the test application. // Deploy the test application.
deployApplication(c, client, app) s.deployApplication(client, app)
// Query application via Traefik. // Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/service", 30*time.Second, try.StatusCodeIs(http.StatusOK)) 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. // Create test application with services to be deployed.
app = marathon.NewDockerApplication(). app = marathon.NewDockerApplication().
@ -92,9 +97,9 @@ func (s *MarathonSuite) TestConfigurationUpdate(c *check.C) {
Container("traefik/whoami") Container("traefik/whoami")
// Deploy the test application. // Deploy the test application.
deployApplication(c, client, app) s.deployApplication(client, app)
// Query application via Traefik. // Query application via Traefik.
err = try.GetRequest("http://127.0.0.1:8000/app", 30*time.Second, try.StatusCodeIs(http.StatusOK)) 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 package integration
import ( import (
"net/http" "bufio"
"os" "net"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/pires/go-proxyproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
type ProxyProtocolSuite struct { type ProxyProtocolSuite struct {
BaseSuite BaseSuite
gatewayIP string
haproxyIP string
whoamiIP string whoamiIP string
} }
func (s *ProxyProtocolSuite) SetUpSuite(c *check.C) { func TestProxyProtocolSuite(t *testing.T) {
s.createComposeProject(c, "proxy-protocol") suite.Run(t, new(ProxyProtocolSuite))
s.composeUp(c)
s.gatewayIP = s.getContainerIP(c, "traefik")
s.haproxyIP = s.getComposeServiceIP(c, "haproxy")
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
} }
func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) { func (s *ProxyProtocolSuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct { s.BaseSuite.SetupSuite()
s.createComposeProject("proxy-protocol")
s.composeUp()
s.whoamiIP = s.getComposeServiceIP("whoami")
}
func (s *ProxyProtocolSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *ProxyProtocolSuite) TestProxyProtocolTrusted() {
file := s.adaptFile("fixtures/proxy-protocol/proxy-protocol.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP}) }{WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second, err := try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second)
try.StatusCodeIs(http.StatusOK), require.NoError(s.T(), err)
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil) content, err := proxyProtoRequest("127.0.0.1:8000", 1)
require.NoError(s.T(), err)
assert.Contains(s.T(), content, "X-Forwarded-For: 1.2.3.4")
content, err = proxyProtoRequest("127.0.0.1:8000", 2)
require.NoError(s.T(), err)
assert.Contains(s.T(), content, "X-Forwarded-For: 1.2.3.4")
} }
func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) { func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted() {
file := s.adaptFile(c, "fixtures/proxy-protocol/with.toml", struct { file := s.adaptFile("fixtures/proxy-protocol/proxy-protocol.toml", struct {
HaproxyIP string HaproxyIP string
WhoamiIP string WhoamiIP string
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP}) }{WhoamiIP: s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second, err := try.GetRequest("http://127.0.0.1:9000/whoami", 10*time.Second)
try.StatusCodeIs(http.StatusOK), require.NoError(s.T(), err)
try.BodyContains("X-Forwarded-For: "+s.gatewayIP))
c.Assert(err, checker.IsNil) content, err := proxyProtoRequest("127.0.0.1:9000", 1)
require.NoError(s.T(), err)
assert.Contains(s.T(), content, "X-Forwarded-For: 127.0.0.1")
content, err = proxyProtoRequest("127.0.0.1:9000", 2)
require.NoError(s.T(), err)
assert.Contains(s.T(), content, "X-Forwarded-For: 127.0.0.1")
} }
func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) { func proxyProtoRequest(address string, version byte) (string, error) {
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct { // Open a TCP connection to the server
HaproxyIP string conn, err := net.Dial("tcp", address)
WhoamiIP string if err != nil {
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP}) return "", err
defer os.Remove(file) }
defer conn.Close()
cmd, display := s.traefikCmd(withConfigFile(file)) // Create a Proxy Protocol header with v1
defer display(c) proxyHeader := &proxyproto.Header{
err := cmd.Start() Version: version,
c.Assert(err, checker.IsNil) Command: proxyproto.PROXY,
defer s.killCmd(cmd) TransportProtocol: proxyproto.TCPv4,
DestinationAddr: &net.TCPAddr{
err = try.GetRequest("http://"+s.haproxyIP+"/whoami", 1*time.Second, IP: net.ParseIP("127.0.0.1"),
try.StatusCodeIs(http.StatusOK), Port: 8000,
try.BodyContains("X-Forwarded-For: "+s.haproxyIP)) },
c.Assert(err, checker.IsNil) SourceAddr: &net.TCPAddr{
IP: net.ParseIP("1.2.3.4"),
Port: 62541,
},
} }
func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) { // After the connection was created write the proxy headers first
file := s.adaptFile(c, "fixtures/proxy-protocol/without.toml", struct { _, err = proxyHeader.WriteTo(conn)
HaproxyIP string if err != nil {
WhoamiIP string return "", err
}{HaproxyIP: s.haproxyIP, WhoamiIP: s.whoamiIP}) }
defer os.Remove(file)
// Create an HTTP request
cmd, display := s.traefikCmd(withConfigFile(file)) request := "GET /whoami HTTP/1.1\r\n" +
defer display(c) "Host: 127.0.0.1\r\n" +
err := cmd.Start() "Connection: close\r\n" +
c.Assert(err, checker.IsNil) "\r\n"
defer s.killCmd(cmd)
// Write the HTTP request to the TCP connection
err = try.GetRequest("http://"+s.haproxyIP+":81/whoami", 1*time.Second, writer := bufio.NewWriter(conn)
try.StatusCodeIs(http.StatusOK), _, err = writer.WriteString(request)
try.BodyContains("X-Forwarded-For: "+s.haproxyIP)) if err != nil {
c.Assert(err, checker.IsNil) 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 ( import (
"net/http" "net/http"
"os" "testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
type RateLimitSuite struct { type RateLimitSuite struct {
@ -15,33 +15,38 @@ type RateLimitSuite struct {
ServerIP string ServerIP string
} }
func (s *RateLimitSuite) SetUpSuite(c *check.C) { func TestRateLimitSuite(t *testing.T) {
s.createComposeProject(c, "ratelimit") suite.Run(t, new(RateLimitSuite))
s.composeUp(c)
s.ServerIP = s.getComposeServiceIP(c, "whoami1")
} }
func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) { func (s *RateLimitSuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/ratelimit/simple.toml", struct { s.BaseSuite.SetupSuite()
s.createComposeProject("ratelimit")
s.composeUp()
s.ServerIP = s.getComposeServiceIP("whoami1")
}
func (s *RateLimitSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *RateLimitSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/ratelimit/simple.toml", struct {
Server1 string Server1 string
}{s.ServerIP}) }{s.ServerIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("ratelimit"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
start := time.Now() start := time.Now()
count := 0 count := 0
for { for {
err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err = try.GetRequest("http://127.0.0.1:8081/", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
count++ count++
if count > 100 { if count > 100 {
break break
@ -50,6 +55,6 @@ func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {
stop := time.Now() stop := time.Now()
elapsed := stop.Sub(start) elapsed := stop.Sub(start)
if elapsed < time.Second*99/100 { if elapsed < time.Second*99/100 {
c.Fatalf("requests throughput was too fast wrt to rate limiting: 100 requests in %v", elapsed) s.T().Fatalf("requests throughput was too fast wrt to rate limiting: 100 requests in %v", elapsed)
} }
} }

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" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt"
"io/fs"
"net" "net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template" "testing"
"time" "time"
"github.com/fatih/structs"
"github.com/go-check/check"
"github.com/kvtools/redis" "github.com/kvtools/redis"
"github.com/kvtools/valkeyrie" "github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store" "github.com/kvtools/valkeyrie/store"
"github.com/pmezard/go-difflib/difflib" "github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/api" "github.com/traefik/traefik/v2/pkg/api"
checker "github.com/vdemeester/shakers"
) )
// Redis test suites. // Redis test suites.
@ -33,23 +29,18 @@ type RedisSuite struct {
redisEndpoints []string redisEndpoints []string
} }
func (s *RedisSuite) TearDownSuite(c *check.C) { func TestRedisSuite(t *testing.T) {
s.composeDown(c) suite.Run(t, new(RedisSuite))
for _, filename := range []string{"sentinel1.conf", "sentinel2.conf", "sentinel3.conf"} {
err := os.Remove(filepath.Join(".", "resources", "compose", "config", filename))
if err != nil && !errors.Is(err, fs.ErrNotExist) {
c.Fatal("unable to clean configuration file for sentinel: ", err)
}
}
} }
func (s *RedisSuite) setupStore(c *check.C) { func (s *RedisSuite) SetupSuite() {
s.createComposeProject(c, "redis") s.BaseSuite.SetupSuite()
s.composeUp(c)
s.createComposeProject("redis")
s.composeUp()
s.redisEndpoints = []string{} s.redisEndpoints = []string{}
s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP(c, "redis"), "6379")) s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP("redis"), "6379"))
kv, err := valkeyrie.NewStore( kv, err := valkeyrie.NewStore(
context.Background(), context.Background(),
@ -57,23 +48,23 @@ func (s *RedisSuite) setupStore(c *check.C) {
s.redisEndpoints, s.redisEndpoints,
&redis.Config{}, &redis.Config{},
) )
if err != nil { require.NoError(s.T(), err, "Cannot create store redis")
c.Fatal("Cannot create store redis: ", err)
}
s.kvClient = kv s.kvClient = kv
// wait for redis // wait for redis
err = try.Do(60*time.Second, try.KVExists(kv, "test")) err = try.Do(60*time.Second, try.KVExists(kv, "test"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *RedisSuite) TestSimpleConfiguration(c *check.C) { func (s *RedisSuite) TearDownSuite() {
s.setupStore(c) s.BaseSuite.TearDownSuite()
}
file := s.adaptFile(c, "fixtures/redis/simple.toml", struct{ RedisAddress string }{ func (s *RedisSuite) TestSimpleConfiguration() {
file := s.adaptFile("fixtures/redis/simple.toml", struct{ RedisAddress string }{
RedisAddress: strings.Join(s.redisEndpoints, ","), RedisAddress: strings.Join(s.redisEndpoints, ","),
}) })
defer os.Remove(file)
data := map[string]string{ data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web", "traefik/http/routers/Router0/entryPoints/0": "web",
@ -123,39 +114,35 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
for k, v := range data { for k, v := range data {
err := s.kvClient.Put(context.Background(), k, []byte(v), nil) err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`), try.BodyContains(`"striper@redis":`, `"compressor@redis":`, `"srvcA@redis":`, `"srvcB@redis":`),
) )
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata") resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
var obtained api.RunTimeRepresentation var obtained api.RunTimeRepresentation
err = json.NewDecoder(resp.Body).Decode(&obtained) err = json.NewDecoder(resp.Body).Decode(&obtained)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
got, err := json.MarshalIndent(obtained, "", " ") got, err := json.MarshalIndent(obtained, "", " ")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json") expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json")
if *updateExpected { if *updateExpected {
err = os.WriteFile(expectedJSON, got, 0o666) err = os.WriteFile(expectedJSON, got, 0o666)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
expected, err := os.ReadFile(expectedJSON) expected, err := os.ReadFile(expectedJSON)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
if !bytes.Equal(expected, got) { if !bytes.Equal(expected, got) {
diff := difflib.UnifiedDiff{ diff := difflib.UnifiedDiff{
@ -167,171 +154,6 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) {
} }
text, err := difflib.GetUnifiedDiffString(diff) text, err := difflib.GetUnifiedDiffString(diff)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err, text)
c.Error(text)
}
}
func (s *RedisSuite) setupSentinelStore(c *check.C) {
s.setupSentinelConfiguration(c, []string{"26379", "36379", "46379"})
s.createComposeProject(c, "redis_sentinel")
s.composeUp(c)
s.redisEndpoints = []string{
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel1"), "26379"),
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel2"), "36379"),
net.JoinHostPort(s.getComposeServiceIP(c, "sentinel3"), "46379"),
}
kv, err := valkeyrie.NewStore(
context.Background(),
redis.StoreName,
s.redisEndpoints,
&redis.Config{
Sentinel: &redis.Sentinel{
MasterName: "mymaster",
},
},
)
if err != nil {
c.Fatal("Cannot create store redis sentinel")
}
s.kvClient = kv
// wait for redis
err = try.Do(60*time.Second, try.KVExists(kv, "test"))
c.Assert(err, checker.IsNil)
}
func (s *RedisSuite) setupSentinelConfiguration(c *check.C, ports []string) {
for i, port := range ports {
templateValue := struct{ SentinelPort string }{SentinelPort: port}
// Load file
templateFile := "resources/compose/config/sentinel_template.conf"
tmpl, err := template.ParseFiles(templateFile)
c.Assert(err, checker.IsNil)
folder, prefix := filepath.Split(templateFile)
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
tmpFile, err := os.Create(fileName)
c.Assert(err, checker.IsNil)
defer tmpFile.Close()
model := structs.Map(templateValue)
model["SelfFilename"] = tmpFile.Name()
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
c.Assert(err, checker.IsNil)
err = tmpFile.Sync()
c.Assert(err, checker.IsNil)
}
}
func (s *RedisSuite) TestSentinelConfiguration(c *check.C) {
s.setupSentinelStore(c)
file := s.adaptFile(c, "fixtures/redis/sentinel.toml", struct{ RedisAddress string }{
RedisAddress: strings.Join(s.redisEndpoints, `","`),
})
defer os.Remove(file)
data := map[string]string{
"traefik/http/routers/Router0/entryPoints/0": "web",
"traefik/http/routers/Router0/middlewares/0": "compressor",
"traefik/http/routers/Router0/middlewares/1": "striper",
"traefik/http/routers/Router0/service": "simplesvc",
"traefik/http/routers/Router0/rule": "Host(`kv1.localhost`)",
"traefik/http/routers/Router0/priority": "42",
"traefik/http/routers/Router0/tls": "true",
"traefik/http/routers/Router1/rule": "Host(`kv2.localhost`)",
"traefik/http/routers/Router1/priority": "42",
"traefik/http/routers/Router1/tls/domains/0/main": "aaa.localhost",
"traefik/http/routers/Router1/tls/domains/0/sans/0": "aaa.aaa.localhost",
"traefik/http/routers/Router1/tls/domains/0/sans/1": "bbb.aaa.localhost",
"traefik/http/routers/Router1/tls/domains/1/main": "bbb.localhost",
"traefik/http/routers/Router1/tls/domains/1/sans/0": "aaa.bbb.localhost",
"traefik/http/routers/Router1/tls/domains/1/sans/1": "bbb.bbb.localhost",
"traefik/http/routers/Router1/entryPoints/0": "web",
"traefik/http/routers/Router1/service": "simplesvc",
"traefik/http/services/simplesvc/loadBalancer/servers/0/url": "http://10.0.1.1:8888",
"traefik/http/services/simplesvc/loadBalancer/servers/1/url": "http://10.0.1.1:8889",
"traefik/http/services/srvcA/loadBalancer/servers/0/url": "http://10.0.1.2:8888",
"traefik/http/services/srvcA/loadBalancer/servers/1/url": "http://10.0.1.2:8889",
"traefik/http/services/srvcB/loadBalancer/servers/0/url": "http://10.0.1.3:8888",
"traefik/http/services/srvcB/loadBalancer/servers/1/url": "http://10.0.1.3:8889",
"traefik/http/services/mirror/mirroring/service": "simplesvc",
"traefik/http/services/mirror/mirroring/mirrors/0/name": "srvcA",
"traefik/http/services/mirror/mirroring/mirrors/0/percent": "42",
"traefik/http/services/mirror/mirroring/mirrors/1/name": "srvcB",
"traefik/http/services/mirror/mirroring/mirrors/1/percent": "42",
"traefik/http/services/Service03/weighted/services/0/name": "srvcA",
"traefik/http/services/Service03/weighted/services/0/weight": "42",
"traefik/http/services/Service03/weighted/services/1/name": "srvcB",
"traefik/http/services/Service03/weighted/services/1/weight": "42",
"traefik/http/middlewares/compressor/compress": "true",
"traefik/http/middlewares/striper/stripPrefix/prefixes/0": "foo",
"traefik/http/middlewares/striper/stripPrefix/prefixes/1": "bar",
"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)
} }
} }

View file

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

View file

@ -34,8 +34,3 @@ services:
traefik.http.routers.rt4.middlewares: wl4 traefik.http.routers.rt4.middlewares: wl4
traefik.http.middlewares.wl4.ipallowlist.sourceRange: 8.8.8.8 traefik.http.middlewares.wl4.ipallowlist.sourceRange: 8.8.8.8
traefik.http.middlewares.wl4.ipallowlist.ipStrategy.excludedIPs: 10.0.0.1,10.0.0.2 traefik.http.middlewares.wl4.ipallowlist.ipStrategy.excludedIPs: 10.0.0.1,10.0.0.2
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

@ -2,11 +2,24 @@ version: "3.8"
services: services:
consul: consul:
image: consul:1.6.2 image: consul:1.6.2
command: agent -server -bootstrap -ui -client 0.0.0.0 -hcl 'connect { enabled = true }' command:
- agent
- -server
- -bootstrap
- -ui
- -client
- 0.0.0.0
- -hcl
- 'connect { enabled = true }'
consul-agent: consul-agent:
image: consul:1.6.2 image: consul:1.6.2
command: agent -retry-join consul -client 0.0.0.0 command:
- agent
- -retry-join
- consul
- -client
- 0.0.0.0
whoami1: whoami1:
image: traefik/whoami image: traefik/whoami
@ -30,8 +43,3 @@ services:
PORT: 443 PORT: 443
BIND: 0.0.0.0 BIND: 0.0.0.0
CONSUL_HTTP_ADDR: http://consul:8500 CONSUL_HTTP_ADDR: http://consul:8500
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,9 +2,23 @@ version: "3.8"
services: services:
server: server:
image: rancher/k3s:v1.20.15-k3s1 image: rancher/k3s:v1.20.15-k3s1
command: server --disable-agent --no-deploy coredns --no-deploy servicelb --no-deploy traefik --no-deploy local-storage --no-deploy metrics-server --log /output/k3s.log --bind-address=server --tls-san=server privileged: true
command:
- server
- --disable-agent
- --disable=coredns
- --disable=servicelb
- --disable=traefik
- --disable=local-storage
- --disable=metrics-server
- --log=/output/k3s.log
- --bind-address=server
- --tls-san=server
- --tls-san=172.31.42.3
- --tls-san=172.31.42.4
environment: environment:
K3S_CLUSTER_SECRET: somethingtotallyrandom K3S_CLUSTER_SECRET: somethingtotallyrandom
K3S_TOKEN: somethingtotallyrandom
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
K3S_KUBECONFIG_MODE: 666 K3S_KUBECONFIG_MODE: 666
volumes: volumes:
@ -15,10 +29,6 @@ services:
image: rancher/k3s:v1.20.15-k3s1 image: rancher/k3s:v1.20.15-k3s1
privileged: true privileged: true
environment: environment:
K3S_TOKEN: somethingtotallyrandom
K3S_URL: https://server:6443 K3S_URL: https://server:6443
K3S_CLUSTER_SECRET: somethingtotallyrandom K3S_CLUSTER_SECRET: somethingtotallyrandom
networks:
default:
name: traefik-test-network
external: true

View file

@ -3,12 +3,9 @@ services:
whoami1: whoami1:
image: traefik/whoami image: traefik/whoami
labels: labels:
traefik.http.Routers.RouterMini.Rule: PathPrefix(`/whoami`) traefik.http.routers.router-mini.Rule: PathPrefix(`/whoami`)
traefik.http.routers.router-mini.service: service-mini
traefik.http.services.service-mini.loadbalancer.server.port: 80
traefik.enable: true traefik.enable: true
deploy: deploy:
replicas: 2 replicas: 2
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,8 +5,3 @@ services:
whoami2: whoami2:
image: traefik/whoami 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 cap_add: # Required for tailscale to work
- net_admin - net_admin
- sys_module - sys_module
command: tailscaled command:
- tailscaled
networks:
default:
name: traefik-test-network
external: true

View file

@ -2,38 +2,58 @@ version: "3.8"
services: services:
whoami-a: whoami-a:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-a -certFile /certs/whoami-a.crt -keyFile /certs/whoami-a.key command:
- -name
- whoami-a
- -certFile
- /certs/whoami-a.crt
- -keyFile
- /certs/whoami-a.key
volumes: volumes:
- ./fixtures/tcp:/certs - ./fixtures/tcp:/certs
whoami-b: whoami-b:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-b -certFile /certs/whoami-b.crt -keyFile /certs/whoami-b.key command:
- -name
- whoami-b
- -certFile
- /certs/whoami-b.crt
- -keyFile
- /certs/whoami-b.key
volumes: volumes:
- ./fixtures/tcp:/certs - ./fixtures/tcp:/certs
whoami-ab: whoami-ab:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-ab -certFile /certs/whoami-b.crt -keyFile /certs/whoami-b.key command:
- -name
- whoami-ab
- -certFile
- /certs/whoami-b.crt
- -keyFile
- /certs/whoami-b.key
volumes: volumes:
- ./fixtures/tcp:/certs - ./fixtures/tcp:/certs
whoami-no-cert: whoami-no-cert:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-no-cert command:
- -name
- whoami-no-cert
whoami-no-tls: whoami-no-tls:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-no-tls command:
- -name
- whoami-no-tls
whoami: whoami:
image: traefik/whoami image: traefik/whoami
whoami-banner: whoami-banner:
image: traefik/whoamitcp image: traefik/whoamitcp
command: -name whoami-banner --banner command:
- -name
networks: - whoami-banner
default: - --banner
name: traefik-test-network
external: true

View file

@ -5,8 +5,3 @@ services:
environment: environment:
PROTO: http PROTO: http
PORT: 9000 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.middlewares: passtls
traefik.http.routers.route1.tls: true traefik.http.routers.route1.tls: true
traefik.http.middlewares.passtls.passtlsclientcert.pem: true traefik.http.middlewares.passtls.passtlsclientcert.pem: true
networks:
default:
name: traefik-test-network
external: true

View file

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

View file

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

View file

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

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" "encoding/json"
"net" "net"
"net/http" "net/http"
"os"
"strings" "strings"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
"github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/dynamic"
checker "github.com/vdemeester/shakers"
) )
type RestSuite struct { type RestSuite struct {
@ -20,28 +21,33 @@ type RestSuite struct {
whoamiAddr string whoamiAddr string
} }
func (s *RestSuite) SetUpSuite(c *check.C) { func TestRestSuite(t *testing.T) {
s.createComposeProject(c, "rest") suite.Run(t, new(RestSuite))
s.composeUp(c)
s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP(c, "whoami1"), "80")
} }
func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) { func (s *RestSuite) SetupSuite() {
cmd, display := s.traefikCmd(withConfigFile("fixtures/rest/simple.toml")) s.BaseSuite.SetupSuite()
defer display(c) s.createComposeProject("rest")
err := cmd.Start() s.composeUp()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) s.whoamiAddr = net.JoinHostPort(s.getComposeServiceIP("whoami1"), "80")
}
func (s *RestSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *RestSuite) TestSimpleConfigurationInsecure() {
s.traefikCmd(withConfigFile("fixtures/rest/simple.toml"))
// wait for Traefik // wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("rest@internal"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Expected a 404 as we did not configure anything. // Expected a 404 as we did not configure anything.
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
testCase := []struct { testCase := []struct {
desc string desc string
@ -105,47 +111,41 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
for _, test := range testCase { for _, test := range testCase {
data, err := json.Marshal(test.config) data, err := json.Marshal(test.config)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data)) request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", bytes.NewReader(data))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request) response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, response.StatusCode)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 3*time.Second, try.BodyContains(test.ruleMatch)) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 3*time.Second, try.BodyContains(test.ruleMatch))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
} }
func (s *RestSuite) TestSimpleConfiguration(c *check.C) { func (s *RestSuite) TestSimpleConfiguration() {
file := s.adaptFile(c, "fixtures/rest/simple_secure.toml", struct{}{}) file := s.adaptFile("fixtures/rest/simple_secure.toml", struct{}{})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
// Expected a 404 as we did not configure anything. // Expected a 404 as we did not configure anything.
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err := try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2000*time.Millisecond, try.BodyContains("PathPrefix(`/secure`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2000*time.Millisecond, try.BodyContains("PathPrefix(`/secure`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", strings.NewReader("{}")) request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8080/api/providers/rest", strings.NewReader("{}"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request) response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusNotFound) assert.Equal(s.T(), http.StatusNotFound, response.StatusCode)
testCase := []struct { testCase := []struct {
desc string desc string
@ -209,19 +209,19 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
for _, test := range testCase { for _, test := range testCase {
data, err := json.Marshal(test.config) data, err := json.Marshal(test.config)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/secure/api/providers/rest", bytes.NewReader(data)) request, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/secure/api/providers/rest", bytes.NewReader(data))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(request) response, err := http.DefaultClient.Do(request)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, response.StatusCode)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains(test.ruleMatch)) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains(test.ruleMatch))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusOK)) err = try.GetRequest("http://127.0.0.1:8000/", time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
} }

View file

@ -3,13 +3,14 @@ package integration
import ( import (
"io" "io"
"net/http" "net/http"
"os" "testing"
"time" "time"
"github.com/go-check/check"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
type RetrySuite struct { type RetrySuite struct {
@ -17,96 +18,86 @@ type RetrySuite struct {
whoamiIP string whoamiIP string
} }
func (s *RetrySuite) SetUpSuite(c *check.C) { func TestRetrySuite(t *testing.T) {
s.createComposeProject(c, "retry") suite.Run(t, new(RetrySuite))
s.composeUp(c)
s.whoamiIP = s.getComposeServiceIP(c, "whoami")
} }
func (s *RetrySuite) TestRetry(c *check.C) { func (s *RetrySuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP}) s.BaseSuite.SetupSuite()
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.createComposeProject("retry")
defer display(c) s.composeUp()
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)")) s.whoamiIP = s.getComposeServiceIP("whoami")
c.Assert(err, checker.IsNil) }
func (s *RetrySuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *RetrySuite) TestRetry() {
file := s.adaptFile("fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
s.traefikCmd(withConfigFile(file))
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
require.NoError(s.T(), err)
response, err := http.Get("http://127.0.0.1:8000/") response, err := http.Get("http://127.0.0.1:8000/")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// The test only verifies that the retry middleware makes sure that the working service is eventually reached. // The test only verifies that the retry middleware makes sure that the working service is eventually reached.
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, response.StatusCode)
} }
func (s *RetrySuite) TestRetryBackoff(c *check.C) { func (s *RetrySuite) TestRetryBackoff() {
file := s.adaptFile(c, "fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP}) file := s.adaptFile("fixtures/retry/backoff.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.Get("http://127.0.0.1:8000/") response, err := http.Get("http://127.0.0.1:8000/")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// The test only verifies that the retry middleware allows finally to reach the working service. // The test only verifies that the retry middleware allows finally to reach the working service.
c.Assert(response.StatusCode, checker.Equals, http.StatusOK) assert.Equal(s.T(), http.StatusOK, response.StatusCode)
} }
func (s *RetrySuite) TestRetryWebsocket(c *check.C) { func (s *RetrySuite) TestRetryWebsocket() {
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP}) file := s.adaptFile("fixtures/retry/simple.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// The test only verifies that the retry middleware makes sure that the working service is eventually reached. // The test only verifies that the retry middleware makes sure that the working service is eventually reached.
_, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) _, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
// The test verifies a second time that the working service is eventually reached. // The test verifies a second time that the working service is eventually reached.
_, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) _, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) assert.Equal(s.T(), http.StatusSwitchingProtocols, response.StatusCode)
} }
func (s *RetrySuite) TestRetryWithStripPrefix(c *check.C) { func (s *RetrySuite) TestRetryWithStripPrefix() {
file := s.adaptFile(c, "fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP}) file := s.adaptFile("fixtures/retry/strip_prefix.toml", struct{ WhoamiIP string }{s.whoamiIP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
response, err := http.Get("http://127.0.0.1:8000/test") response, err := http.Get("http://127.0.0.1:8000/test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
body, err := io.ReadAll(response.Body) body, err := io.ReadAll(response.Body)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(body), checker.Contains, "GET / HTTP/1.1") assert.Contains(s.T(), string(body), "GET / HTTP/1.1")
c.Assert(string(body), checker.Contains, "X-Forwarded-Prefix: /test") assert.Contains(s.T(), string(body), "X-Forwarded-Prefix: /test")
} }

File diff suppressed because it is too large Load diff

View file

@ -5,63 +5,69 @@ import (
"crypto/x509" "crypto/x509"
"errors" "errors"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"strings" "strings"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
type TCPSuite struct{ BaseSuite } type TCPSuite struct{ BaseSuite }
func (s *TCPSuite) SetUpSuite(c *check.C) { func TestTCPSuite(t *testing.T) {
s.createComposeProject(c, "tcp") suite.Run(t, new(TCPSuite))
s.composeUp(c)
} }
func (s *TCPSuite) TestMixed(c *check.C) { func (s *TCPSuite) SetupSuite() {
file := s.adaptFile(c, "fixtures/tcp/mixed.toml", struct { s.BaseSuite.SetupSuite()
s.createComposeProject("tcp")
s.composeUp()
}
func (s *TCPSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *TCPSuite) TestMixed() {
file := s.adaptFile("fixtures/tcp/mixed.toml", struct {
Whoami string Whoami string
WhoamiA string WhoamiA string
WhoamiB string WhoamiB string
WhoamiNoCert string WhoamiNoCert string
}{ }{
Whoami: "http://" + s.getComposeServiceIP(c, "whoami") + ":80", Whoami: "http://" + s.getComposeServiceIP("whoami") + ":80",
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080", WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080", WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080", WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a // Traefik passes through, termination handled by whoami-a
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test") out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-a") assert.Contains(s.T(), out, "whoami-a")
// Traefik passes through, termination handled by whoami-b // Traefik passes through, termination handled by whoami-b
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test") out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-b") assert.Contains(s.T(), out, "whoami-b")
// Termination handled by traefik // Termination handled by traefik
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true) out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-cert") assert.Contains(s.T(), out, "whoami-no-cert")
tr1 := &http.Transport{ tr1 := &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
@ -69,174 +75,143 @@ func (s *TCPSuite) TestMixed(c *check.C) {
}, },
} }
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/whoami/", nil) req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/whoami/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusOK)) err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/not-found/", nil) req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:8093/not-found/", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusNotFound)) err = try.RequestWithTransport(req, 10*time.Second, tr1, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8093/test", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) err = try.GetRequest("http://127.0.0.1:8093/test", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8093/not-found", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) err = try.GetRequest("http://127.0.0.1:8093/not-found", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *TCPSuite) TestTLSOptions(c *check.C) { func (s *TCPSuite) TestTLSOptions() {
file := s.adaptFile(c, "fixtures/tcp/multi-tls-options.toml", struct { file := s.adaptFile("fixtures/tcp/multi-tls-options.toml", struct {
WhoamiNoCert string WhoamiNoCert string
}{ }{
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080", WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-c.test`)"))
c.Assert(err, checker.IsNil)
// Check that we can use a client tls version <= 1.2 with hostSNI 'whoami-c.test' // Check that we can use a client tls version <= 1.2 with hostSNI 'whoami-c.test'
out, err := guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-c.test", true, tls.VersionTLS12) out, err := guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-c.test", true, tls.VersionTLS12)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-cert") assert.Contains(s.T(), out, "whoami-no-cert")
// Check that we can use a client tls version <= 1.3 with hostSNI 'whoami-d.test' // Check that we can use a client tls version <= 1.3 with hostSNI 'whoami-d.test'
out, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS13) out, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS13)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-cert") assert.Contains(s.T(), out, "whoami-no-cert")
// Check that we cannot use a client tls version <= 1.2 with hostSNI 'whoami-d.test' // Check that we cannot use a client tls version <= 1.2 with hostSNI 'whoami-d.test'
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12) _, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
c.Assert(err, checker.NotNil) assert.ErrorContains(s.T(), err, "protocol version not supported")
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
// Check that we can't reach a route with an invalid mTLS configuration. // Check that we can't reach a route with an invalid mTLS configuration.
conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{ conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
ServerName: "whoami-i.test", ServerName: "whoami-i.test",
InsecureSkipVerify: true, InsecureSkipVerify: true,
}) })
c.Assert(conn, checker.IsNil) assert.Nil(s.T(), conn)
c.Assert(err, checker.NotNil) assert.Error(s.T(), err)
} }
func (s *TCPSuite) TestNonTLSFallback(c *check.C) { func (s *TCPSuite) TestNonTLSFallback() {
file := s.adaptFile(c, "fixtures/tcp/non-tls-fallback.toml", struct { file := s.adaptFile("fixtures/tcp/non-tls-fallback.toml", struct {
WhoamiA string WhoamiA string
WhoamiB string WhoamiB string
WhoamiNoCert string WhoamiNoCert string
WhoamiNoTLS string WhoamiNoTLS string
}{ }{
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080", WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080", WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiNoCert: s.getComposeServiceIP(c, "whoami-no-cert") + ":8080", WhoamiNoCert: s.getComposeServiceIP("whoami-no-cert") + ":8080",
WhoamiNoTLS: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080", WhoamiNoTLS: s.getComposeServiceIP("whoami-no-tls") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
// Traefik passes through, termination handled by whoami-a // Traefik passes through, termination handled by whoami-a
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test") out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-a") assert.Contains(s.T(), out, "whoami-a")
// Traefik passes through, termination handled by whoami-b // Traefik passes through, termination handled by whoami-b
out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test") out, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-b") assert.Contains(s.T(), out, "whoami-b")
// Termination handled by traefik // Termination handled by traefik
out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true) out, err = guessWho("127.0.0.1:8093", "whoami-c.test", true)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-cert") assert.Contains(s.T(), out, "whoami-no-cert")
out, err = guessWho("127.0.0.1:8093", "", false) out, err = guessWho("127.0.0.1:8093", "", false)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-tls") assert.Contains(s.T(), out, "whoami-no-tls")
} }
func (s *TCPSuite) TestNonTlsTcp(c *check.C) { func (s *TCPSuite) TestNonTlsTcp() {
file := s.adaptFile(c, "fixtures/tcp/non-tls.toml", struct { file := s.adaptFile("fixtures/tcp/non-tls.toml", struct {
WhoamiNoTLS string WhoamiNoTLS string
}{ }{
WhoamiNoTLS: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080", WhoamiNoTLS: s.getComposeServiceIP("whoami-no-tls") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
// Traefik will forward every requests on the given port to whoami-no-tls // Traefik will forward every requests on the given port to whoami-no-tls
out, err := guessWho("127.0.0.1:8093", "", false) out, err := guessWho("127.0.0.1:8093", "", false)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-no-tls") assert.Contains(s.T(), out, "whoami-no-tls")
} }
func (s *TCPSuite) TestCatchAllNoTLS(c *check.C) { func (s *TCPSuite) TestCatchAllNoTLS() {
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls.toml", struct { file := s.adaptFile("fixtures/tcp/catch-all-no-tls.toml", struct {
WhoamiBannerAddress string WhoamiBannerAddress string
}{ }{
WhoamiBannerAddress: s.getComposeServiceIP(c, "whoami-banner") + ":8080", WhoamiBannerAddress: s.getComposeServiceIP("whoami-banner") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
// Traefik will forward every requests on the given port to whoami-no-tls // Traefik will forward every requests on the given port to whoami-no-tls
out, err := welcome("127.0.0.1:8093") out, err := welcome("127.0.0.1:8093")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "Welcome") assert.Contains(s.T(), out, "Welcome")
} }
func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) { func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS() {
file := s.adaptFile(c, "fixtures/tcp/catch-all-no-tls-with-https.toml", struct { file := s.adaptFile("fixtures/tcp/catch-all-no-tls-with-https.toml", struct {
WhoamiNoTLSAddress string WhoamiNoTLSAddress string
WhoamiURL string WhoamiURL string
}{ }{
WhoamiNoTLSAddress: s.getComposeServiceIP(c, "whoami-no-tls") + ":8080", WhoamiNoTLSAddress: s.getComposeServiceIP("whoami-no-tls") + ":8080",
WhoamiURL: "http://" + s.getComposeServiceIP(c, "whoami") + ":80", WhoamiURL: "http://" + s.getComposeServiceIP("whoami") + ":80",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`*`)"))
c.Assert(err, checker.IsNil)
req := httptest.NewRequest(http.MethodGet, "https://127.0.0.1:8093/test", nil) req := httptest.NewRequest(http.MethodGet, "https://127.0.0.1:8093/test", nil)
req.RequestURI = "" req.RequestURI = ""
@ -246,64 +221,52 @@ func (s *TCPSuite) TestCatchAllNoTLSWithHTTPS(c *check.C) {
InsecureSkipVerify: true, InsecureSkipVerify: true,
}, },
}, try.StatusCodeIs(http.StatusOK)) }, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }
func (s *TCPSuite) TestMiddlewareAllowList(c *check.C) { func (s *TCPSuite) TestMiddlewareAllowList() {
file := s.adaptFile(c, "fixtures/tcp/ip-allowlist.toml", struct { file := s.adaptFile("fixtures/tcp/ip-allowlist.toml", struct {
WhoamiA string WhoamiA string
WhoamiB string WhoamiB string
}{ }{
WhoamiA: s.getComposeServiceIP(c, "whoami-a") + ":8080", WhoamiA: s.getComposeServiceIP("whoami-a") + ":8080",
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080", WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-a.test`)"))
c.Assert(err, checker.IsNil)
// Traefik not passes through, ipWhitelist closes connection // Traefik not passes through, ipWhitelist closes connection
_, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test") _, err = guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-a.test")
c.Assert(err, checker.ErrorMatches, "EOF") assert.ErrorIs(s.T(), err, io.EOF)
// Traefik passes through, termination handled by whoami-b // Traefik passes through, termination handled by whoami-b
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test") out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(out, checker.Contains, "whoami-b") assert.Contains(s.T(), out, "whoami-b")
} }
func (s *TCPSuite) TestWRR(c *check.C) { func (s *TCPSuite) TestWRR() {
file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct { file := s.adaptFile("fixtures/tcp/wrr.toml", struct {
WhoamiB string WhoamiB string
WhoamiAB string WhoamiAB string
}{ }{
WhoamiB: s.getComposeServiceIP(c, "whoami-b") + ":8080", WhoamiB: s.getComposeServiceIP("whoami-b") + ":8080",
WhoamiAB: s.getComposeServiceIP(c, "whoami-ab") + ":8080", WhoamiAB: s.getComposeServiceIP("whoami-ab") + ":8080",
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start() err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-b.test`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`whoami-b.test`)"))
c.Assert(err, checker.IsNil)
call := map[string]int{} call := map[string]int{}
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
// Traefik passes through, termination handled by whoami-b or whoami-bb // Traefik passes through, termination handled by whoami-b or whoami-bb
out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test") out, err := guessWhoTLSPassthrough("127.0.0.1:8093", "whoami-b.test")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
switch { switch {
case strings.Contains(out, "whoami-b"): case strings.Contains(out, "whoami-b"):
call["whoami-b"]++ call["whoami-b"]++
@ -315,7 +278,7 @@ func (s *TCPSuite) TestWRR(c *check.C) {
time.Sleep(time.Second) time.Sleep(time.Second)
} }
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-b": 3, "whoami-ab": 1}) assert.EqualValues(s.T(), call, map[string]int{"whoami-b": 3, "whoami-ab": 1})
} }
func welcome(addr string) (string, error) { func welcome(addr string) (string, error) {

View file

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

View file

@ -4,11 +4,13 @@ import (
"crypto/tls" "crypto/tls"
"net/http" "net/http"
"os" "os"
"testing"
"time" "time"
"github.com/go-check/check" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
) )
const ( const (
@ -19,20 +21,30 @@ const (
type TLSClientHeadersSuite struct{ BaseSuite } type TLSClientHeadersSuite struct{ BaseSuite }
func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) { func TestTLSClientHeadersSuite(t *testing.T) {
s.createComposeProject(c, "tlsclientheaders") suite.Run(t, new(TLSClientHeadersSuite))
s.composeUp(c)
} }
func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) { func (s *TLSClientHeadersSuite) SetupSuite() {
rootCertContent, err := os.ReadFile(rootCertPath) s.BaseSuite.SetupSuite()
c.Assert(err, check.IsNil)
serverCertContent, err := os.ReadFile(certPemPath)
c.Assert(err, check.IsNil)
ServerKeyContent, err := os.ReadFile(certKeyPath)
c.Assert(err, check.IsNil)
file := s.adaptFile(c, "fixtures/tlsclientheaders/simple.toml", struct { s.createComposeProject("tlsclientheaders")
s.composeUp()
}
func (s *TLSClientHeadersSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
}
func (s *TLSClientHeadersSuite) TestTLSClientHeaders() {
rootCertContent, err := os.ReadFile(rootCertPath)
assert.NoError(s.T(), err)
serverCertContent, err := os.ReadFile(certPemPath)
assert.NoError(s.T(), err)
ServerKeyContent, err := os.ReadFile(certKeyPath)
assert.NoError(s.T(), err)
file := s.adaptFile("fixtures/tlsclientheaders/simple.toml", struct {
RootCertContent string RootCertContent string
ServerCertContent string ServerCertContent string
ServerKeyContent string ServerKeyContent string
@ -41,22 +53,17 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
ServerCertContent: string(serverCertContent), ServerCertContent: string(serverCertContent),
ServerKeyContent: string(ServerKeyContent), ServerKeyContent: string(ServerKeyContent),
}) })
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
defer display(c)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer s.killCmd(cmd)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil) request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath) certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
tr := &http.Transport{ tr := &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
@ -66,5 +73,5 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
} }
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA+bBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x/lhKLlv4amSDHDx8Cmz/6blOUM8XOfMRW1xx++AgChWN9dx/kf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL/aaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V+XyVkIPmzfPrRV+4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW+B80gMhpLdxXD+KO0/Wgc5xpB/wLSirNtRQyxAa3+EEcIwJv/wdh8EyjlDLSpFm/8ghntrKhkOfIOPDFE41M5HNfx/Fuh5btKEenOL/XdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k+3QydO1lqB5JJmcrs59BEveTqaJ3eeh/0I4OOab6OkTTZ0JNjJp1573oxO+fce/bfGud8xHY5gSN9huU7U6RsgvO7Dhmal/sDNl8XC8oU90hVDVXZdA7ewh4jjaoIv")) err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA+bBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x/lhKLlv4amSDHDx8Cmz/6blOUM8XOfMRW1xx++AgChWN9dx/kf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL/aaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V+XyVkIPmzfPrRV+4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW+B80gMhpLdxXD+KO0/Wgc5xpB/wLSirNtRQyxAa3+EEcIwJv/wdh8EyjlDLSpFm/8ghntrKhkOfIOPDFE41M5HNfx/Fuh5btKEenOL/XdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k+3QydO1lqB5JJmcrs59BEveTqaJ3eeh/0I4OOab6OkTTZ0JNjJp1573oxO+fce/bfGud8xHY5gSN9huU7U6RsgvO7Dhmal/sDNl8XC8oU90hVDVXZdA7ewh4jjaoIv"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
} }

View file

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

View file

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

View file

@ -8,19 +8,25 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"testing"
"time" "time"
"github.com/go-check/check"
gorillawebsocket "github.com/gorilla/websocket" gorillawebsocket "github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v2/integration/try" "github.com/traefik/traefik/v2/integration/try"
checker "github.com/vdemeester/shakers"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
) )
// WebsocketSuite tests suite. // WebsocketSuite tests suite.
type WebsocketSuite struct{ BaseSuite } type WebsocketSuite struct{ BaseSuite }
func (s *WebsocketSuite) TestBase(c *check.C) { func TestWebsocketSuite(t *testing.T) {
suite.Run(t, new(WebsocketSuite))
}
func (s *WebsocketSuite) TestBase() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -41,36 +47,30 @@ func (s *WebsocketSuite) TestBase(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil) conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestWrongOrigin(c *check.C) { func (s *WebsocketSuite) TestWrongOrigin() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -91,35 +91,28 @@ func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800") config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second) conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, err = websocket.NewClient(config, conn) _, err = websocket.NewClient(config, conn)
c.Assert(err, checker.NotNil) assert.ErrorContains(s.T(), err, "bad status")
c.Assert(err, checker.ErrorMatches, "bad status")
} }
func (s *WebsocketSuite) TestOrigin(c *check.C) { func (s *WebsocketSuite) TestOrigin() {
// use default options // use default options
upgrader := gorillawebsocket.Upgrader{} upgrader := gorillawebsocket.Upgrader{}
@ -141,44 +134,38 @@ func (s *WebsocketSuite) TestOrigin(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000") config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second) conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn) client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
n, err := client.Write([]byte("OK")) n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
msg := make([]byte, 2) msg := make([]byte, 2)
n, err = client.Read(msg) n, err = client.Read(msg)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) { func (s *WebsocketSuite) TestWrongOriginIgnoredByServer() {
upgrader := gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool { upgrader := gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
return true return true
}} }}
@ -201,44 +188,38 @@ func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80") config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80")
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second) conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn) client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
n, err := client.Write([]byte("OK")) n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
msg := make([]byte, 2) msg := make([]byte, 2)
n, err = client.Read(msg) n, err = client.Read(msg)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestSSLTermination(c *check.C) { func (s *WebsocketSuite) TestSSLTermination() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -258,44 +239,38 @@ func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
} }
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct { file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Add client self-signed cert // Add client self-signed cert
roots := x509.NewCertPool() roots := x509.NewCertPool()
certContent, err := os.ReadFile("./resources/tls/local.cert") certContent, err := os.ReadFile("./resources/tls/local.cert")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
roots.AppendCertsFromPEM(certContent) roots.AppendCertsFromPEM(certContent)
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{ gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
RootCAs: roots, RootCAs: roots,
} }
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/ws", nil) conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/ws", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestBasicAuth(c *check.C) { func (s *WebsocketSuite) TestBasicAuth() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -306,8 +281,8 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
defer conn.Close() defer conn.Close()
user, password, _ := r.BasicAuth() user, password, _ := r.BasicAuth()
c.Assert(user, check.Equals, "traefiker") assert.Equal(s.T(), "traefiker", user)
c.Assert(password, check.Equals, "secret") assert.Equal(s.T(), "secret", password)
for { for {
mt, message, err := conn.ReadMessage() mt, message, err := conn.ReadMessage()
@ -320,78 +295,66 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
} }
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000") config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
auth := "traefiker:secret" auth := "traefiker:secret"
config.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) config.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
c.Assert(err, check.IsNil) assert.NoError(s.T(), err)
conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second) conn, err := net.DialTimeout("tcp", "127.0.0.1:8000", time.Second)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
client, err := websocket.NewClient(config, conn) client, err := websocket.NewClient(config, conn)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
n, err := client.Write([]byte("OK")) n, err := client.Write([]byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
msg := make([]byte, 2) msg := make([]byte, 2)
n, err = client.Read(msg) n, err = client.Read(msg)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(n, checker.Equals, 2) assert.Equal(s.T(), 2, n)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestSpecificResponseFromBackend(c *check.C) { func (s *WebsocketSuite) TestSpecificResponseFromBackend() {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil) _, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
c.Assert(err, checker.NotNil) assert.Error(s.T(), err)
c.Assert(resp.StatusCode, check.Equals, http.StatusUnauthorized) assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
} }
func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) { func (s *WebsocketSuite) TestURLWithURLEncodedChar() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Assert(r.URL.EscapedPath(), check.Equals, "/ws/http%3A%2F%2Ftest") assert.Equal(s.T(), "/ws/http%3A%2F%2Ftest", r.URL.EscapedPath())
conn, err := upgrader.Upgrade(w, r, nil) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
@ -409,36 +372,30 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil) conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestSSLhttp2(c *check.C) { func (s *WebsocketSuite) TestSSLhttp2() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -463,48 +420,42 @@ func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
ts.TLS.NextProtos = append(ts.TLS.NextProtos, `h2`, `http/1.1`) ts.TLS.NextProtos = append(ts.TLS.NextProtos, `h2`, `http/1.1`)
ts.StartTLS() ts.StartTLS()
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct { file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: ts.URL, WebsocketServer: ts.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
// Add client self-signed cert // Add client self-signed cert
roots := x509.NewCertPool() roots := x509.NewCertPool()
certContent, err := os.ReadFile("./resources/tls/local.cert") certContent, err := os.ReadFile("./resources/tls/local.cert")
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
roots.AppendCertsFromPEM(certContent) roots.AppendCertsFromPEM(certContent)
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{ gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
RootCAs: roots, RootCAs: roots,
} }
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/echo", nil) conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/echo", nil)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }
func (s *WebsocketSuite) TestHeaderAreForwarded(c *check.C) { func (s *WebsocketSuite) TestHeaderAreForwarded() {
upgrader := gorillawebsocket.Upgrader{} // use default options upgrader := gorillawebsocket.Upgrader{} // use default options
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Assert(r.Header.Get("X-Token"), check.Equals, "my-token") assert.Equal(s.T(), "my-token", r.Header.Get("X-Token"))
c, err := upgrader.Upgrade(w, r, nil) c, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
return return
@ -522,33 +473,27 @@ func (s *WebsocketSuite) TestHeaderAreForwarded(c *check.C) {
} }
})) }))
file := s.adaptFile(c, "fixtures/websocket/config.toml", struct { file := s.adaptFile("fixtures/websocket/config.toml", struct {
WebsocketServer string WebsocketServer string
}{ }{
WebsocketServer: srv.URL, WebsocketServer: srv.URL,
}) })
defer os.Remove(file) s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
cmd, display := s.traefikCmd(withConfigFile(file), "--log.level=DEBUG")
defer display(c)
err := cmd.Start()
c.Assert(err, check.IsNil)
defer s.killCmd(cmd)
// wait for traefik // wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1")) err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
headers := http.Header{} headers := http.Header{}
headers.Add("X-Token", "my-token") headers.Add("X-Token", "my-token")
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", headers) conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", headers)
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage()
c.Assert(err, checker.IsNil) require.NoError(s.T(), err)
c.Assert(string(msg), checker.Equals, "OK") assert.Equal(s.T(), "OK", string(msg))
} }

View file

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

View file

@ -5,7 +5,7 @@ set -e -o pipefail
source /go/src/k8s.io/code-generator/kube_codegen.sh source /go/src/k8s.io/code-generator/kube_codegen.sh
git config --global --add safe.directory /go/src/${PROJECT_MODULE} git config --global --add safe.directory "/go/src/${PROJECT_MODULE}"
rm -rf "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}" rm -rf "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}"
mkdir -p "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}/" mkdir -p "/go/src/${PROJECT_MODULE}/${MODULE_VERSION}/"

View file

@ -3,18 +3,9 @@ set -e
export DEST=. export DEST=.
TESTFLAGS+=("-test.timeout=20m" -check.v)
if [ -n "${VERBOSE}" ]; then
TESTFLAGS+=(-v)
elif [ -n "${VERBOSE_INTEGRATION}" ]; then
TESTFLAGS+=(-v)
fi
cd integration
echo "Testing against..." echo "Testing against..."
docker version docker version
# shellcheck disable=SC2086 # shellcheck disable=SC2086
# shellcheck disable=SC2048 # shellcheck disable=SC2048
CGO_ENABLED=0 go test -integration ${TESTFLAGS[*]} go test ./integration -test.timeout=20m -failfast -v ${TESTFLAGS[*]}