mirror of
https://github.com/containous/traefik.git
synced 2025-10-03 01:44:23 +03:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b7be71c02a | ||
|
6e9d713668 | ||
|
ddb32ef86f | ||
|
2087e11f55 | ||
|
a5d46fc6ef | ||
|
84742275a4 | ||
|
54a2d657f3 | ||
|
08b90ade94 | ||
|
8ba99adc50 | ||
|
b02946147d | ||
|
137c632793 | ||
|
e76b65f44d | ||
|
55ebaee4a7 | ||
|
4ff76e13c4 | ||
|
14e400bcd0 | ||
|
7cfd10db62 | ||
|
9d8a42111f | ||
|
0dfd12ee61 | ||
|
a3fd484728 | ||
|
f196de90e1 | ||
|
c2a294c872 | ||
|
8e5d4c6ae9 | ||
|
eb07a5ca1a | ||
|
84e20aa9c3 | ||
|
4e441d09ed | ||
|
8f5dd7bd9d | ||
|
86315e0f18 | ||
|
c20af070e3 | ||
|
8593581cbf | ||
|
8103992977 | ||
|
c5b92b5260 | ||
|
c19cf125e8 | ||
|
435d28c790 | ||
|
8272be0eda | ||
|
d2414feaff | ||
|
1aa450c028 | ||
|
f9ff6049d3 | ||
|
ee8305549a | ||
|
a31b026364 | ||
|
20d496268c | ||
|
5f3c30e37b | ||
|
38ac1e75a2 | ||
|
109a8712cc | ||
|
278e739242 | ||
|
db31a4c961 | ||
|
35ce6baaae | ||
|
95f20fc753 | ||
|
1c0094048b | ||
|
590ddfc990 | ||
|
39d7b77609 | ||
|
74e0abf8bf | ||
|
cc14c165c0 | ||
|
f2ba4353b2 | ||
|
42df9afeaf | ||
|
2df655cefe | ||
|
c120b70483 | ||
|
8eadfbb990 | ||
|
cc80568d9e | ||
|
8ffd1854db | ||
|
6baa110adb | ||
|
5658c8ac06 | ||
|
1c80f12bc2 | ||
|
ef5f1b1508 | ||
|
8c19652361 | ||
|
e5c80637fc | ||
|
f437fb4230 | ||
|
9c50129520 | ||
|
00a5f4c401 | ||
|
a79cdd1dfa | ||
|
2096fd7081 | ||
|
6f18344c56 |
36
.github/workflows/build.yaml
vendored
36
.github/workflows/build.yaml
vendored
@@ -16,36 +16,7 @@ env:
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Package webui
|
||||
run: |
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -90,6 +61,7 @@ jobs:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -97,7 +69,9 @@ jobs:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: tar xvf webui.tar.gz
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
32
.github/workflows/experimental.yaml
vendored
32
.github/workflows/experimental.yaml
vendored
@@ -12,36 +12,28 @@ env:
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
experimental:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
name: Build experimental image on branch
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Build
|
||||
run: make generate binary
|
||||
@@ -61,6 +53,16 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build docker experimental image
|
||||
env:
|
||||
DOCKER_BUILDX_ARGS: "--push"
|
||||
|
138
.github/workflows/release.yaml
vendored
Normal file
138
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ github.ref_name }}
|
||||
TRAEFIKER_EMAIL: "traefiker@traefik.io"
|
||||
CODENAME: mimolette
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin, windows-amd64, windows-arm64, windows-386, freebsd, openbsd ]
|
||||
needs:
|
||||
- build-webui
|
||||
|
||||
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
|
||||
env:
|
||||
# Ensure cache consistency on Linux, see https://github.com/actions/setup-go/pull/383
|
||||
ImageOS: ${{ matrix.os }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Go generate
|
||||
run: go generate
|
||||
|
||||
|
||||
- name: Generate goreleaser file
|
||||
run: |
|
||||
GORELEASER_CONFIG_FILE_PATH=$(go run ./internal/release "${{ matrix.os }}")
|
||||
echo "GORELEASER_CONFIG_FILE_PATH=$GORELEASER_CONFIG_FILE_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with goreleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
# 'latest', 'nightly', or a semver
|
||||
version: '~> v2'
|
||||
args: release --clean --timeout="90m" --config "${{ env.GORELEASER_CONFIG_FILE_PATH }}"
|
||||
|
||||
- name: Artifact binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-binaries
|
||||
path: |
|
||||
dist/**/*_checksums.txt
|
||||
dist/**/*.tar.gz
|
||||
dist/**/*.zip
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Retrieve the secret and decode it to a file
|
||||
env:
|
||||
TRAEFIKER_RSA: ${{ secrets.TRAEFIKER_RSA }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${TRAEFIKER_RSA}" | base64 --decode > ~/.ssh/traefiker_rsa
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist/
|
||||
pattern: "*-binaries"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
cat dist/**/*_checksums.txt >> "dist/traefik_${VERSION}_checksums.txt"
|
||||
rm dist/**/*_checksums.txt
|
||||
tar cfz "dist/traefik-${VERSION}.src.tar.gz" \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--exclude .travis \
|
||||
--exclude .semaphoreci \
|
||||
--exclude .github \
|
||||
--exclude dist .
|
||||
|
||||
chown -R "$(id -u)":"$(id -g)" dist/
|
||||
gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION}
|
||||
|
||||
./script/deploy.sh
|
||||
|
37
.github/workflows/template-webui.yaml
vendored
Normal file
37
.github/workflows/template-webui.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Build Web UI
|
||||
on:
|
||||
workflow_call: {}
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Package webui
|
||||
run: |
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
retention-days: 1
|
2
.github/workflows/test-integration.yaml
vendored
2
.github/workflows/test-integration.yaml
vendored
@@ -28,6 +28,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
@@ -55,6 +56,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
1
.github/workflows/test-unit.yaml
vendored
1
.github/workflows/test-unit.yaml
vendored
@@ -27,6 +27,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
5
.github/workflows/validate.yaml
vendored
5
.github/workflows/validate.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GOLANGCI_LINT_VERSION: v1.61.0
|
||||
GOLANGCI_LINT_VERSION: v1.64.2
|
||||
MISSPELL_VERSION: v0.6.0
|
||||
|
||||
jobs:
|
||||
@@ -25,6 +25,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
@@ -44,6 +45,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Install misspell ${{ env.MISSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSPELL_VERSION}
|
||||
@@ -67,6 +69,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: go generate
|
||||
run: |
|
||||
|
@@ -1,5 +1,6 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
relative-path-mode: cfg
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
@@ -141,6 +142,9 @@ linters-settings:
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
gomoddirectives:
|
||||
tool-forbidden: true
|
||||
toolchain-pattern: 'go1\.\d+\.\d+$'
|
||||
go-version-pattern: '^1\.\d+(\.0)?$'
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/gorilla/mux
|
||||
@@ -159,8 +163,7 @@ linters-settings:
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- execinquery # deprecated
|
||||
- gomnd # deprecated
|
||||
- tenv # Deprecated
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- cyclop # duplicate of gocyclo
|
||||
@@ -197,7 +200,6 @@ linters:
|
||||
- maintidx # kind of duplicate of gocyclo
|
||||
- nonamedreturns # Too strict
|
||||
- gosmopolitan # not relevant
|
||||
- exportloopref # Not relevant since go1.22
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
@@ -273,3 +275,14 @@ issues:
|
||||
- path: pkg/provider/acme/local_store.go
|
||||
linters:
|
||||
- musttag
|
||||
- path: pkg/tls/certificate.go
|
||||
text: 'the methods of "Certificates" use pointer receiver and non-pointer receiver.'
|
||||
linters:
|
||||
- recvcheck
|
||||
|
||||
output:
|
||||
show-stats: true
|
||||
sort-results: true
|
||||
sort-order:
|
||||
- linter
|
||||
- file
|
||||
|
@@ -1,12 +1,11 @@
|
||||
project_name: traefik
|
||||
version: 2
|
||||
|
||||
[[if .GOARCH]]
|
||||
dist: "./dist/[[ .GOOS ]]-[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
dist: "./dist/[[ .GOOS ]]"
|
||||
|
||||
[[ if eq .GOOS "linux" ]]
|
||||
before:
|
||||
hooks:
|
||||
- go generate
|
||||
[[ end ]]
|
||||
[[end]]
|
||||
|
||||
builds:
|
||||
- binary: traefik
|
||||
@@ -21,6 +20,9 @@ builds:
|
||||
goos:
|
||||
- "[[ .GOOS ]]"
|
||||
goarch:
|
||||
[[if .GOARCH]]
|
||||
- "[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
- amd64
|
||||
- '386'
|
||||
- arm
|
||||
@@ -28,6 +30,7 @@ builds:
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
[[end]]
|
||||
goarm:
|
||||
- '7'
|
||||
- '6'
|
||||
|
@@ -1,63 +1,13 @@
|
||||
version: v1.0
|
||||
name: Traefik
|
||||
name: Traefik Release - deprecated
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-4
|
||||
os_image: ubuntu2004
|
||||
|
||||
fail_fast:
|
||||
stop:
|
||||
when: "branch != 'master'"
|
||||
|
||||
auto_cancel:
|
||||
queued:
|
||||
when: "branch != 'master'"
|
||||
running:
|
||||
when: "branch != 'master'"
|
||||
|
||||
global_job_config:
|
||||
prologue:
|
||||
commands:
|
||||
- curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin"
|
||||
- sudo semgo go1.23
|
||||
- export "GOPATH=$(go env GOPATH)"
|
||||
- export "SEMAPHORE_GIT_DIR=${GOPATH}/src/github.com/traefik/${SEMAPHORE_PROJECT_NAME}"
|
||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||
- export GOPROXY=https://proxy.golang.org,direct
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.61.0
|
||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
type: f1-standard-2
|
||||
os_image: ubuntu2204
|
||||
blocks:
|
||||
- name: Release
|
||||
dependencies: []
|
||||
run:
|
||||
when: "tag =~ '.*'"
|
||||
- name: 'Do nothing'
|
||||
task:
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-8
|
||||
os_image: ubuntu2004
|
||||
secrets:
|
||||
- name: traefik
|
||||
env_vars:
|
||||
- name: GH_VERSION
|
||||
value: 2.32.1
|
||||
- name: CODENAME
|
||||
value: "mimolette"
|
||||
prologue:
|
||||
commands:
|
||||
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
||||
- curl -sSL -o /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz
|
||||
- tar -zxvf /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz -C /tmp
|
||||
- sudo mv /tmp/gh_${GH_VERSION}_linux_amd64/bin/gh /usr/local/bin/gh
|
||||
- sudo rm -rf ~/.phpbrew ~/.kerl ~/.sbt ~/.nvm ~/.npm ~/.kiex /usr/lib/jvm /opt/az /opt/firefox /usr/lib/google-cloud-sdk ~/.rbenv ~/.pip_download_cache # Remove unnecessary data.
|
||||
- sudo service docker stop && sudo umount /var/lib/docker && sudo service docker start # Unmounts the docker disk and the whole system disk is usable.
|
||||
jobs:
|
||||
- name: Release
|
||||
- name: 'Do nothing'
|
||||
commands:
|
||||
- make release-packages
|
||||
- gh release create ${SEMAPHORE_GIT_TAG_NAME} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${SEMAPHORE_GIT_TAG_NAME} --notes ${SEMAPHORE_GIT_TAG_NAME}
|
||||
- ./script/deploy.sh
|
||||
- echo "Do nothing"
|
||||
|
102
CHANGELOG.md
102
CHANGELOG.md
@@ -1,3 +1,105 @@
|
||||
## [v2.11.22](https://github.com/traefik/traefik/tree/v2.11.22) (2025-03-31)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.21...v2.11.22)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[ecs,logs]** Bump AWS SDK to v2 ([#11359](https://github.com/traefik/traefik/pull/11359) by [Juneezee](https://github.com/Juneezee))
|
||||
- **[logs,tls]** Error level log for configuration-related TLS errors with backends ([#11611](https://github.com/traefik/traefik/pull/11611) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[rules]** Allow underscore character in HostSNI matcher ([#11557](https://github.com/traefik/traefik/pull/11557) by [rohitlohar45](https://github.com/rohitlohar45))
|
||||
- **[server]** Bump github.com/vulcand/oxy/v2 to v2.0.3 ([#11649](https://github.com/traefik/traefik/pull/11649) by [adamvduke](https://github.com/adamvduke))
|
||||
- **[server]** Bump golang.org/x/net to v0.37.0 ([#11632](https://github.com/traefik/traefik/pull/11632) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Change boolean module properties default value to undefined ([#11639](https://github.com/traefik/traefik/pull/11639) by [rtribotte](https://github.com/rtribotte))
|
||||
- Bump github.com/golang-jwt/jwt to v4.5.2 and v5.2.2 ([#11634](https://github.com/traefik/traefik/pull/11634) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Bump github.com/redis/go-redis/v9 to v9.6.3 ([#11633](https://github.com/traefik/traefik/pull/11633) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Bump golang.org/x/net to v0.36.0 ([#11608](https://github.com/traefik/traefik/pull/11608) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Bump github.com/go-jose/go-jose/v4 to v4.0.5 ([#11571](https://github.com/traefik/traefik/pull/11571) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
**Documentation:**
|
||||
- **[accesslogs]** Remove documentation for OriginStatusLine and DownstreamStatusLine accessLogs fields ([#11599](https://github.com/traefik/traefik/pull/11599) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware]** Clarifies that retry middleware uses TCP, not HTTP status codes ([#11603](https://github.com/traefik/traefik/pull/11603) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[redis]** Add tip for dynamic configuration updates of Redis ([#11577](https://github.com/traefik/traefik/pull/11577) by [Alanxtl](https://github.com/Alanxtl))
|
||||
- Add Security Support ([#11610](https://github.com/traefik/traefik/pull/11610) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
# [v2.11.21](https://github.com/traefik/traefik/tree/v2.11.21) (2025-02-24)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.20...v2.11.21)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Bump github.com/go-acme/lego/v4 to v4.22.2 ([#11537](https://github.com/traefik/traefik/pull/11537) by [ldez](https://github.com/ldez))
|
||||
- **[cli]** Bump github.com/traefik/paerser to v0.2.2 ([#11530](https://github.com/traefik/traefik/pull/11530) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[middleware]** Enable the retry middleware in the proxy ([#11536](https://github.com/traefik/traefik/pull/11536) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[middleware]** Retry should send headers on Write ([#11534](https://github.com/traefik/traefik/pull/11534) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.20](https://github.com/traefik/traefik/tree/v2.11.20) (2025-01-31)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.19...v2.11.20)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Graceful shutdown for ACME JSON write operation ([#11497](https://github.com/traefik/traefik/pull/11497) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- Change docker-compose to docker compose ([#11496](https://github.com/traefik/traefik/pull/11496) by [khai-pi](https://github.com/khai-pi))
|
||||
|
||||
## [v2.11.19](https://github.com/traefik/traefik/tree/v2.11.19) (2025-01-29)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.18...v2.11.19)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware]** Changing log message when client cert is not available to debug ([#11453](https://github.com/traefik/traefik/pull/11453) by [Nelwhix](https://github.com/Nelwhix))
|
||||
- **[service]** Do not create a logger instance for each proxy ([#11487](https://github.com/traefik/traefik/pull/11487) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Fix auto refresh not clearing on component unmount ([#11477](https://github.com/traefik/traefik/pull/11477) by [DoubleREW](https://github.com/DoubleREW))
|
||||
|
||||
**Documentation:**
|
||||
- Remove awesome.traefik.io reference in documentation section ([#11435](https://github.com/traefik/traefik/pull/11435) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.18](https://github.com/traefik/traefik/tree/v2.11.18) (2025-01-07)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.17...v2.11.18)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[websocket,server]** Disable http2 connect setting for websocket by default ([#11412](https://github.com/traefik/traefik/pull/11412) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v2.11.17](https://github.com/traefik/traefik/tree/v2.11.17) (2025-01-06)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.16...v2.11.17)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.21.0 ([#11368](https://github.com/traefik/traefik/pull/11368) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix typo in basicauth note ([#11397](https://github.com/traefik/traefik/pull/11397) by [tieje](https://github.com/tieje))
|
||||
- **[service]** Configure ErrorLog in httputil.ReverseProxy ([#11344](https://github.com/traefik/traefik/pull/11344) by [peacewalker122](https://github.com/peacewalker122))
|
||||
- Bump golang.org/x/net to v0.33.0 ([#11365](https://github.com/traefik/traefik/pull/11365) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix allowACMEByPass TOML example ([#11370](https://github.com/traefik/traefik/pull/11370) by [hannesbraun](https://github.com/hannesbraun))
|
||||
- **[k8s/crd]** Update copyright for 2025 ([#11383](https://github.com/traefik/traefik/pull/11383) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.16](https://github.com/traefik/traefik/tree/v2.11.16) (2024-12-16)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.15...v2.11.16)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[server]** Update golang.org/x dependencies ([#11336](https://github.com/traefik/traefik/pull/11336) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v2.11.15](https://github.com/traefik/traefik/tree/v2.11.15) (2024-12-06)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.14...v2.11.15)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.20.4 ([#11295](https://github.com/traefik/traefik/pull/11295) by [ldez](https://github.com/ldez))
|
||||
- **[http3]** Update github.com/quic-go/quic-go to v0.48.2 ([#11320](https://github.com/traefik/traefik/pull/11320) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.14](https://github.com/traefik/traefik/tree/v2.11.14) (2024-11-20)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.13...v2.11.14)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.20.2 ([#11263](https://github.com/traefik/traefik/pull/11263) by [ldez](https://github.com/ldez))
|
||||
- **[logs,server]** Change level of peeking first byte error log to DEBUG ([#11254](https://github.com/traefik/traefik/pull/11254) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware,server]** Drop untrusted X-Forwarded-Prefix header ([#11253](https://github.com/traefik/traefik/pull/11253) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[server]** Apply keepalive config to h2c entrypoints ([#11276](https://github.com/traefik/traefik/pull/11276) by [davefu113](https://github.com/davefu113))
|
||||
- **[service]** Fix internal handlers ServiceBuilder composition ([#11281](https://github.com/traefik/traefik/pull/11281) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[accesslogs]** Update access-logs.md, add examples for accesslog.format ([#11275](https://github.com/traefik/traefik/pull/11275) by [bluepuma77](https://github.com/bluepuma77))
|
||||
- Fix the defaultRule CLI examples ([#11282](https://github.com/traefik/traefik/pull/11282) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Fix spelling, grammar, and rephrase sections for clarity in some documentation pages ([#11280](https://github.com/traefik/traefik/pull/11280) by [AntoineDeveloper](https://github.com/AntoineDeveloper))
|
||||
- Fix absolute link in the migration guide ([#11269](https://github.com/traefik/traefik/pull/11269) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Add X-Forwarded-Prefix to the migration guide ([#11267](https://github.com/traefik/traefik/pull/11267) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Fix a small typo in entrypoints documentation ([#11261](https://github.com/traefik/traefik/pull/11261) by [quiode](https://github.com/quiode))
|
||||
- Add a warning about environment variables casing for static configuration ([#11226](https://github.com/traefik/traefik/pull/11226) by [anchal00](https://github.com/anchal00))
|
||||
- Improve documentation on dashboard ([#11220](https://github.com/traefik/traefik/pull/11220) by [mloiseleur](https://github.com/mloiseleur))
|
||||
|
||||
## [v2.11.13](https://github.com/traefik/traefik/tree/v2.11.13) (2024-10-28)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.12...v2.11.13)
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1.2
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache --no-progress ca-certificates tzdata
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
2
Makefile
2
Makefile
@@ -169,7 +169,7 @@ docs-pull-images:
|
||||
.PHONY: generate-crd
|
||||
#? generate-crd: Generate CRD clientset and CRD manifests
|
||||
generate-crd:
|
||||
@$(CURDIR)/script/code-gen-docker.sh
|
||||
@$(CURDIR)/script/code-gen.sh
|
||||
|
||||
.PHONY: generate-genconf
|
||||
#? generate-genconf: Generate code from dynamic configuration github.com/traefik/genconf
|
||||
|
@@ -61,7 +61,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- WebSocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- Fast
|
||||
@@ -91,8 +91,6 @@ You can access the simple HTML frontend of Traefik.
|
||||
|
||||
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||
|
||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
|
||||
## Support
|
||||
|
||||
To get community support, you can:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Security Policy
|
||||
|
||||
You can join our security mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
|
@@ -187,7 +187,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
||||
acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider, routinesPool)
|
||||
|
||||
// Entrypoints
|
||||
|
||||
@@ -365,7 +365,7 @@ func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP serv
|
||||
}
|
||||
|
||||
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider, routinesPool *safe.Pool) []*acme.Provider {
|
||||
localStores := map[string]*acme.LocalStore{}
|
||||
|
||||
var resolvers []*acme.Provider
|
||||
@@ -375,7 +375,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
||||
}
|
||||
|
||||
if localStores[resolver.ACME.Storage] == nil {
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage)
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage, routinesPool)
|
||||
}
|
||||
|
||||
p := &acme.Provider{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
build-base \
|
||||
@@ -14,8 +14,8 @@ RUN apk --no-cache --no-progress add \
|
||||
ruby-json \
|
||||
zlib-dev
|
||||
|
||||
RUN gem install nokogiri --version 1.15.3 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 5.0.7 --no-document -- --use-system-libraries
|
||||
RUN gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries
|
||||
|
||||
# After Ruby, some NodeJS YAY!
|
||||
RUN apk --no-cache --no-progress add \
|
||||
|
@@ -92,7 +92,7 @@ For development purposes, you can specify which tests to run by using (only work
|
||||
|
||||
Create `tailscale.secret` file in `integration` directory.
|
||||
|
||||
This file need to contains a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
|
||||
This file needs to contain 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
|
||||
|
@@ -15,13 +15,13 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs").
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Docker](https://www.docker.com/ "Link to website of Docker")
|
||||
- [Docker](https://www.docker.com/ "Link to the website of Docker")
|
||||
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||
|
||||
@@ -51,7 +51,7 @@ $ make docs-build
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Python](https://www.python.org/ "Link to website of Python")
|
||||
- [Python](https://www.python.org/ "Link to the website of Python")
|
||||
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
|
||||
|
||||
```bash
|
||||
|
@@ -32,7 +32,7 @@ The contributor should also meet one or several of the following requirements:
|
||||
including those of other maintainers and contributors.
|
||||
|
||||
- The contributor is active on Traefik Community forums
|
||||
or other technical forums/boards such as K8S slack, Reddit, StackOverflow, hacker news.
|
||||
or other technical forums/boards, such as K8S Slack, Reddit, StackOverflow, and Hacker News.
|
||||
|
||||
Any existing active maintainer can create an issue to discuss promoting a contributor to maintainer.
|
||||
Other maintainers can vote on the issue, and if the quorum is reached, the contributor is promoted to maintainer.
|
||||
|
@@ -17,7 +17,7 @@ or the list of [confirmed bugs](https://github.com/traefik/traefik/labels/kind%2
|
||||
|
||||
## How We Prioritize
|
||||
|
||||
We wish we could review every pull request right away, but because it's a time consuming operation, it's not always possible.
|
||||
We wish we could review every pull request right away, but because it's a time-consuming operation, it's not always possible.
|
||||
|
||||
The PRs we are able to handle the fastest are:
|
||||
|
||||
@@ -128,7 +128,7 @@ This label can be used when:
|
||||
Traefik Proxy is made by the community for the community,
|
||||
as such the goal is to engage the community to make Traefik the best reverse proxy available.
|
||||
Part of this goal is maintaining a lean codebase and ensuring code velocity.
|
||||
unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
Unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
|
||||
Because we respect the work you did, you will always be told why we are closing your pull request.
|
||||
If you do not agree with our decision, do not worry; closed pull requests are effortless to recreate,
|
||||
|
@@ -8,7 +8,7 @@ description: "Security is a key part of Traefik Proxy. Read the technical docume
|
||||
## Security Advisories
|
||||
|
||||
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## CVE
|
||||
|
||||
|
@@ -4,23 +4,31 @@
|
||||
|
||||
Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Community Support |
|
||||
|---------|--------------|--------------------|
|
||||
| 3.1 | Jul 15, 2024 | Yes |
|
||||
| 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 |
|
||||
| 2.11 | Feb 12, 2024 | Ends Apr 29, 2025 |
|
||||
| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 |
|
||||
| 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 |
|
||||
| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 |
|
||||
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 |
|
||||
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 |
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|-------------------|
|
||||
| 3.3 | Jan 06, 2025 | Yes | Yes |
|
||||
| 3.2 | Oct 28, 2024 | Ended Jan 06, 2025 | No |
|
||||
| 3.1 | Jul 15, 2024 | Ended Oct 28, 2024 | No |
|
||||
| 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 | No |
|
||||
| 2.11 | Feb 12, 2024 | Ends Apr 29, 2025 | Ends Feb 01, 2026 |
|
||||
| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 | No |
|
||||
| 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 | No |
|
||||
| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No |
|
||||
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
|
||||
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | No |
|
||||
|
||||
??? example "Active Support / Security Support"
|
||||
|
||||
- **Active support**: Receives any bug fixes.
|
||||
|
||||
- **Security support**: Receives only critical bug and security fixes.
|
||||
|
||||
This page is maintained and updated periodically to reflect our roadmap and any decisions affecting the end of support for Traefik Proxy.
|
||||
|
||||
|
@@ -38,7 +38,7 @@ services:
|
||||
Start your `reverse-proxy` with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d reverse-proxy
|
||||
docker compose up -d reverse-proxy
|
||||
```
|
||||
|
||||
You can open a browser and go to `http://localhost:8080/api/rawdata` to see Traefik's API rawdata (you'll go back there once you have launched a service in step 2).
|
||||
@@ -68,7 +68,7 @@ The above defines `whoami`: a web service that outputs information about the mac
|
||||
Start the `whoami` service with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d whoami
|
||||
docker compose up -d whoami
|
||||
```
|
||||
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new container and updated its own configuration.
|
||||
@@ -92,7 +92,7 @@ IP: 172.27.0.3
|
||||
Run more instances of your `whoami` service with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d --scale whoami=2
|
||||
docker compose up -d --scale whoami=2
|
||||
```
|
||||
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new instance of the container.
|
||||
|
@@ -316,17 +316,17 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|------------------------------------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH`, `ACME_DNS_STORAGE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||
| [ArvanCloud](https://www.arvancloud.ir/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) (DEPRECATED) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) (DEPRECATED) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [AzureDNS](https://azure.microsoft.com/services/dns/) | `azuredns` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_ENVIRONMENT]`, `[AZURE_PRIVATE_ZONE]`, `[AZURE_ZONE_NAME]` | [Additional configuration](https://go-acme.github.io/lego/dns/azuredns) |
|
||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [Brandit](https://www.brandit.com) | `brandit` | `BRANDIT_API_USERNAME`, `BRANDIT_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/brandit) |
|
||||
| [Brandit](https://www.brandit.com) (DEPRECATED) | `brandit` | `BRANDIT_API_USERNAME`, `BRANDIT_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/brandit) |
|
||||
| [Bunny](https://bunny.net) | `bunny` | `BUNNY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/bunny) |
|
||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||
| [Civo](https://www.civo.com/) | `civo` | `CIVO_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/civo) |
|
||||
@@ -334,9 +334,10 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) (DEPRECATED) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||
| [Core-Networks](https://www.core-networks.de) | `corenetworks` | `CORENETWORKS_LOGIN`, `CORENETWORKS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/corenetworks) |
|
||||
| [CPanel and WHM](https://cpanel.net/) | `cpanel` | `CPANEL_MODE`, `CPANEL_USERNAME`, `CPANEL_TOKEN`, `CPANEL_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/cpanel) |
|
||||
| [Derak Cloud](https://derak.cloud/) | `derak` | `DERAK_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/derak) |
|
||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||
@@ -392,9 +393,11 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| [Mail-in-a-Box](https://mailinabox.email) | `mailinabox` | `MAILINABOX_EMAIL`, `MAILINABOX_PASSWORD`, `MAILINABOX_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/mailinabox) |
|
||||
| [ManageEngine CloudDNS](https://clouddns.manageengine.com) | `manageengine` | `MANAGEENGINE_CLIENT_ID`, `MANAGEENGINE_CLIENT_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/manageengine) |
|
||||
| [Metaname](https://metaname.net) | `metaname` | `METANAME_ACCOUNT_REFERENCE`, `METANAME_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/metaname) |
|
||||
| [mijn.host](https://mijn.host/) | `mijnhost` | `MIJNHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/mijnhost) |
|
||||
| [Mittwald](https://www.mittwald.de) | `mittwald` | `MITTWALD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/mittwald) |
|
||||
| [myaddr.{tools,dev,io}](https://myaddr.tools/) | `myaddr` | `MYADDR_PRIVATE_KEYS_MAPPING` | [Additional configuration](https://go-acme.github.io/lego/dns/myaddr) |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
@@ -416,8 +419,10 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [Rainyun/雨云](https://www.rainyun.com) | `rainyun` | `RAINYUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rainyun) |
|
||||
| [RcodeZero](https://www.rcodezero.at) | `rcodezero` | `RCODEZERO_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/rcodezero) |
|
||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||
| [Regfish](https://regfish.de) | `regfish` | `regfish` | [Additional configuration](https://go-acme.github.io/lego/dns/regfish) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
@@ -430,8 +435,11 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Shellrent](https://www.shellrent.com) | `shellrent` | `SHELLRENT_USERNAME`, `SHELLRENT_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/shellrent) |
|
||||
| [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) |
|
||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||
| [Spaceship](https://spaceship.com) | `spaceship` | `SPACESHIP_API_KEY`, `SPACESHIP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/spaceship) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [Technitium](https://technitium.com) | `technitium` | `TECHNITIUM_SERVER_BASE_URL`, `TECHNITIUM_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/technitium) |
|
||||
| [Tencent Cloud DNS](https://cloud.tencent.com/product/cns) | `tencentcloud` | `TENCENTCLOUD_SECRET_ID`, `TENCENTCLOUD_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/tencentcloud) |
|
||||
| [Timeweb Cloud](https://timeweb.cloud) | `timewebcloud` | `TIMEWEBCLOUD_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/timewebcloud) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [UKFast SafeDNS](https://docs.ukfast.co.uk/domains/safedns/index.html) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||
| [Ultradns](https://neustarsecurityservices.com/dns-services) | `ultradns` | `ULTRADNS_USERNAME`, `ULTRADNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/ultradns) |
|
||||
@@ -441,11 +449,13 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
||||
| [VK Cloud](https://mcs.mail.ru/) | `vkcloud` | `VK_CLOUD_PASSWORD`, `VK_CLOUD_PROJECT_ID`, `VK_CLOUD_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/vkcloud) |
|
||||
| [Volcano Engine](https://www.volcengine.com) | `volcengine` | `VOLC_ACCESSKEY`, `VOLC_SECRETKEY` | [Additional configuration](https://go-acme.github.io/lego/dns/volcengine) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [Webnames](https://www.webnames.ru/) | `webnames` | `WEBNAMES_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/webnames) |
|
||||
| [Websupport](https://websupport.sk) | `websupport` | `WEBSUPPORT_API_KEY`, `WEBSUPPORT_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/websupport) |
|
||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
||||
| [West.cn/西部数码](https://www.west.cn) | `westcn` | `WESTCN_USERNAME`, `WESTCN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/westcn) |
|
||||
| [Yandex 360](https://360.yandex.ru) | `yandex360` | `YANDEX360_OAUTH_TOKEN`, `YANDEX360_ORG_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex360) |
|
||||
| [Yandex Cloud](https://cloud.yandex.com/en/) | `yandexcloud` | `YANDEX_CLOUD_FOLDER_ID`, `YANDEX_CLOUD_IAM_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandexcloud) |
|
||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
||||
|
@@ -21,7 +21,7 @@ The BasicAuth middleware grants access to services to authorized users only.
|
||||
# To create user:password pair, it's possible to use this command:
|
||||
# echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
|
||||
#
|
||||
# Also note that dollar signs should NOT be doubled when they not evaluated (e.g. Ansible docker_container module).
|
||||
# Also note that dollar signs should NOT be doubled when they are not being evaluated (e.g. Ansible docker_container module).
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
@@ -12,8 +12,11 @@ Retrying until it Succeeds
|
||||
TODO: add schema
|
||||
-->
|
||||
|
||||
The Retry middleware reissues requests a given number of times to a backend server if that server does not reply.
|
||||
As soon as the server answers, the middleware stops retrying, regardless of the response status.
|
||||
The Retry middleware reissues requests a given number of times when it cannot contact the backend service.
|
||||
This applies at the transport level (TCP).
|
||||
If the service does not respond to the initial connection attempt, the middleware retries.
|
||||
However, once the service responds, regardless of the HTTP status code, the middleware considers it operational and stops retrying.
|
||||
This means that the retry mechanism does not handle HTTP errors; it only retries when there is no response at the TCP level.
|
||||
The Retry middleware has an optional configuration to enable an exponential backoff.
|
||||
|
||||
## Configuration Examples
|
||||
|
@@ -649,3 +649,10 @@ As a consequence, middlewares do not have access to those Connection headers,
|
||||
and a new option has been introduced to specify which ones could go through the middleware chain before being removed: `<entrypoint>.forwardedHeaders.connection`.
|
||||
|
||||
Please check out the [entrypoint forwarded headers connection option configuration](../routing/entrypoints.md#forwarded-headers) documentation.
|
||||
|
||||
## v2.11.14
|
||||
|
||||
### X-Forwarded-Prefix
|
||||
|
||||
In `v2.11.14`, the `X-Forwarded-Prefix` header is now handled like the other `X-Forwarded-*` headers: Traefik removes it when it's sent from an untrusted source.
|
||||
Please refer to the Forwarded headers [documentation](../routing/entrypoints.md#forwarded-headers) for more details.
|
||||
|
@@ -59,6 +59,20 @@ If the given format is unsupported, the default (CLF) is used instead.
|
||||
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <HTTP_status> <content-length> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_router_name>" "<Traefik_server_URL>" <request_duration_in_ms>ms
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
accessLog:
|
||||
format: "json"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[accessLog]
|
||||
format = "json"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--accesslog.format=json
|
||||
```
|
||||
|
||||
### `bufferingSize`
|
||||
|
||||
To write the logs in an asynchronous fashion, specify a `bufferingSize` option.
|
||||
@@ -222,9 +236,7 @@ accessLog:
|
||||
| `OriginDuration` | The time taken (in nanoseconds) by the origin server ('upstream') to return its response. |
|
||||
| `OriginContentSize` | The content length specified by the origin server, or 0 if unspecified. |
|
||||
| `OriginStatus` | The HTTP status code returned by the origin server. If the request was handled by this Traefik instance (e.g. with a redirect), then this value will be absent (0). |
|
||||
| `OriginStatusLine` | `OriginStatus` + Status code explanation |
|
||||
| `DownstreamStatus` | The HTTP status code returned to the client. |
|
||||
| `DownstreamStatusLine` | `DownstreamStatus` + Status code explanation |
|
||||
| `DownstreamContentSize` | The number of bytes in the response entity returned to the client. This is in addition to the "Content-Length" header, which may be present in the origin response. |
|
||||
| `RequestCount` | The number of requests received since the Traefik instance started. |
|
||||
| `GzipRatio` | The response body compression ratio achieved. |
|
||||
|
@@ -70,7 +70,7 @@ And then define a routing configuration on Traefik itself with the
|
||||
|
||||
### `insecure`
|
||||
|
||||
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`.
|
||||
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`, on path `/api`.
|
||||
|
||||
!!! info
|
||||
If the entryPoint named `traefik` is not configured, it will be automatically created on port 8080.
|
||||
|
@@ -37,32 +37,15 @@ Start by enabling the dashboard by using the following option from [Traefik's AP
|
||||
on the [static configuration](../getting-started/configuration-overview.md#the-static-configuration):
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
dashboard: true
|
||||
api: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
dashboard = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
--api.dashboard=true
|
||||
--api=true
|
||||
```
|
||||
|
||||
Then define a routing configuration on Traefik itself,
|
||||
@@ -106,27 +89,47 @@ rule = "Host(`traefik.example.com`) && PathPrefix(`/api`, `/dashboard`)"
|
||||
|
||||
## Insecure Mode
|
||||
|
||||
This mode is not recommended because it does not allow the use of security features.
|
||||
When _insecure_ mode is enabled, one can access the dashboard on the `traefik` port (default: `8080`) of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
|
||||
To enable the "insecure mode", use the following options from [Traefik's API](./api.md#insecure):
|
||||
This mode is **not** recommended because it does not allow security features.
|
||||
For example, it is not possible to add an authentication middleware with this mode.
|
||||
|
||||
It should be used for testing purpose **only**.
|
||||
|
||||
To enable the _insecure_ mode, use the following options from [Traefik's API](./api.md#insecure):
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
dashboard = true
|
||||
insecure = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--api.dashboard=true --api.insecure=true
|
||||
--api.insecure=true
|
||||
```
|
||||
|
||||
You can now access the dashboard on the port `8080` of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
## Disable The Dashboard
|
||||
|
||||
By default, the dashboard is enabled when the API is enabled.
|
||||
If necessary, the dashboard can be disabled by using the following option.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
dashboard: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
dashboard = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--api.dashboard=false
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -525,7 +525,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.consulcatalog.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.consulcatalog.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -466,7 +466,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.docker.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -259,7 +259,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.ecs.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -301,7 +301,8 @@ _Optional_
|
||||
If `region` is not provided, it is resolved from the EC2 metadata endpoint for EC2 tasks.
|
||||
In a FARGATE context it is resolved from the `AWS_REGION` environment variable.
|
||||
|
||||
If `accessKeyID` and `secretAccessKey` are not provided, credentials are resolved in the following order:
|
||||
If `accessKeyID` and `secretAccessKey` are not provided, credentials are resolved in the order specified by the
|
||||
[default credential chain of AWS SDK for Go V2](https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specifying-credentials):
|
||||
|
||||
- Using the environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`.
|
||||
- Using shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to `default` and `~/.aws/credentials`.
|
||||
|
@@ -138,7 +138,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.marathon.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -374,7 +374,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
--providers.nomad.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -121,7 +121,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.rancher.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.rancher.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -10,6 +10,12 @@ A Story of KV store & Containers
|
||||
|
||||
Store your configuration in Redis and let Traefik do the rest!
|
||||
|
||||
!!! tip "Dynamic configuration updates"
|
||||
|
||||
Dynamic configuration updates require Redis [keyspace notifications](https://redis.io/docs/latest/develop/use/keyspace-notifications) to be enabled.
|
||||
Cloud-managed Redis services (e.g., GCP Memorystore, AWS ElastiCache) may disable this by default due to CPU performance issues.
|
||||
For more information, see the [Redis](https://redis.io/docs/latest/develop/use/keyspace-notifications/) documentation or refer to your cloud provider's documentation for specific configuration steps.
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
See the dedicated section in [routing](../routing/providers/kv.md).
|
||||
|
@@ -5,4 +5,23 @@ description: "Reference the environment variables for static configuration in Tr
|
||||
|
||||
# Static Configuration: Environment variables
|
||||
|
||||
!!! warning "Environment Variable Casing"
|
||||
|
||||
Traefik normalizes the environment variable key-value pairs by lowercasing them.
|
||||
This means that when you interpolate a string in an environment variable's name,
|
||||
that string will be treated as lowercase, regardless of its original casing.
|
||||
|
||||
For example, assuming you have set environment variables as follows:
|
||||
|
||||
```bash
|
||||
export TRAEFIK_ENTRYPOINTS_WEB=true
|
||||
export TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
|
||||
|
||||
export TRAEFIK_CERTIFICATESRESOLVERS_myResolver=true
|
||||
export TRAEFIK_CERTIFICATESRESOLVERS_myResolver_ACME_CASERVER=....
|
||||
```
|
||||
|
||||
Although the Entrypoint is named `WEB` and the Certificate Resolver is named `myResolver`,
|
||||
they have to be referenced respectively as `web`, and `myresolver` in the configuration.
|
||||
|
||||
--8<-- "content/reference/static-configuration/env-ref.md"
|
||||
|
@@ -254,8 +254,7 @@ entryPoints:
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints.foo]
|
||||
[entryPoints.foo.allowACMEByPass]
|
||||
allowACMEByPass = true
|
||||
allowACMEByPass = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
@@ -472,7 +471,7 @@ Setting them has no effect for UDP entryPoints.
|
||||
If zero, no timeout exists.
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
We strongly suggest to adapt this value accordingly to the your needs.
|
||||
We strongly suggest adapting this value accordingly to your needs.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
## Static configuration
|
||||
|
@@ -31,7 +31,7 @@ Our starting point is the docker-compose configuration file, to start the k3s cl
|
||||
You can start it with:
|
||||
|
||||
```bash
|
||||
docker-compose -f k3s.yml up
|
||||
docker compose -f k3s.yml up
|
||||
```
|
||||
|
||||
```yaml
|
||||
|
@@ -46,7 +46,7 @@ For the DNS challenge, you'll need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
|
@@ -32,7 +32,7 @@ For the HTTP challenge you will need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
|
@@ -32,7 +32,7 @@ For the TLS challenge you will need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
|
@@ -46,7 +46,7 @@ Create a `docker-compose.yml` file with the following content:
|
||||
|
||||
Replace `whoami.localhost` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
|
||||
|
||||
Now run `docker-compose up -d` within the folder where you created the previous file.
|
||||
Now run `docker compose up -d` within the folder where you created the previous file.
|
||||
This will start Docker Compose in background mode.
|
||||
|
||||
!!! info "This can take a moment"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
ENV PATH="${PATH}:/venv/bin"
|
||||
|
||||
|
@@ -27,7 +27,7 @@ theme:
|
||||
prev: 'Previous'
|
||||
next: 'Next'
|
||||
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2024'
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2025'
|
||||
|
||||
extra_javascript:
|
||||
- assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile
|
||||
|
153
go.mod
153
go.mod
@@ -7,7 +7,13 @@ require (
|
||||
github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61 // No tag on the repo.
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
|
||||
github.com/aws/aws-sdk-go v1.44.327
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.7
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.48
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1
|
||||
github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13
|
||||
github.com/aws/smithy-go v1.22.2
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo.
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
@@ -15,19 +21,19 @@ require (
|
||||
github.com/docker/docker v27.1.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 // No tag on the repo.
|
||||
github.com/go-acme/lego/v4 v4.19.2
|
||||
github.com/go-acme/lego/v4 v4.22.2
|
||||
github.com/go-kit/kit v0.13.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-github/v28 v28.1.1
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/consul/api v1.26.1
|
||||
github.com/hashicorp/go-hclog v1.6.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b // No tag on the repo.
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab // No tag on the repo.
|
||||
@@ -51,29 +57,28 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo.
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/quic-go/quic-go v0.47.0
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac // No tag on the repo.
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 // No tag on the repo.
|
||||
github.com/testcontainers/testcontainers-go v0.32.0
|
||||
github.com/traefik/paerser v0.2.1
|
||||
github.com/traefik/paerser v0.2.2
|
||||
github.com/traefik/yaegi v0.16.1
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||
github.com/unrolled/render v1.0.2
|
||||
github.com/unrolled/secure v1.0.9
|
||||
github.com/vulcand/oxy/v2 v2.0.0
|
||||
github.com/vulcand/oxy/v2 v2.0.3
|
||||
github.com/vulcand/predicate v1.2.0
|
||||
go.elastic.co/apm/module/apmot/v2 v2.4.8
|
||||
go.elastic.co/apm/v2 v2.4.8
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // No tag on the repo.
|
||||
golang.org/x/mod v0.21.0
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/text v0.18.0
|
||||
golang.org/x/time v0.6.0
|
||||
golang.org/x/tools v0.25.0
|
||||
google.golang.org/grpc v1.66.1
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/time v0.8.0
|
||||
golang.org/x/tools v0.28.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.26.3
|
||||
@@ -86,17 +91,17 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.9.3 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.1 // indirect
|
||||
cloud.google.com/go/auth v0.13.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
@@ -124,35 +129,30 @@ require (
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.15 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.40.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.43.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 // indirect
|
||||
github.com/aws/smithy-go v1.20.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect
|
||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.104.0 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
|
||||
github.com/containerd/containerd v1.7.20 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
@@ -168,13 +168,13 @@ require (
|
||||
github.com/elastic/go-windows v1.0.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/exoscale/egoscale/v3 v3.1.5 // indirect
|
||||
github.com/exoscale/egoscale/v3 v3.1.7 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -185,16 +185,15 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.13.1 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
@@ -203,8 +202,8 @@ require (
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.14.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.2 // indirect
|
||||
@@ -218,7 +217,7 @@ require (
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||
@@ -234,7 +233,7 @@ require (
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/linode/linodego v1.40.0 // indirect
|
||||
github.com/linode/linodego v1.44.0 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||
github.com/looplab/fsm v0.1.0 // indirect
|
||||
@@ -261,9 +260,10 @@ require (
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||
github.com/nrdcg/desec v0.8.0 // indirect
|
||||
github.com/nrdcg/desec v0.10.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||
github.com/nrdcg/goacmedns v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.10.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
@@ -275,10 +275,11 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
|
||||
github.com/oracle/oci-go-sdk/v65 v65.73.0 // indirect
|
||||
github.com/oracle/oci-go-sdk/v65 v65.81.1 // indirect
|
||||
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||
github.com/ovh/go-ovh v1.6.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/peterhellberg/link v1.2.0 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
@@ -287,10 +288,11 @@ require (
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.2.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.6.3 // indirect
|
||||
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||
github.com/sacloud/go-http v0.1.8 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.12.0 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
||||
github.com/sacloud/packages-go v0.0.10 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
@@ -298,12 +300,12 @@ require (
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||
github.com/selectel/domains-go v1.1.0 // indirect
|
||||
github.com/selectel/go-selvpcclient/v3 v3.1.1 // indirect
|
||||
github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.5 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.7 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
@@ -313,18 +315,19 @@ require (
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.7.0-20240913052650-970ca9a // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.189 // indirect
|
||||
github.com/vultr/govultr/v3 v3.9.1 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20240911120709-1fa0cb6f47c2 // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20240911121212-e4e74d0d02f5 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.elastic.co/apm/module/apmhttp/v2 v2.4.8 // indirect
|
||||
go.elastic.co/fastjson v1.1.0 // indirect
|
||||
@@ -332,7 +335,6 @@ require (
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.10 // indirect
|
||||
go.mongodb.org/mongo-driver v1.12.1 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
@@ -345,20 +347,21 @@ require (
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.197.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/api v0.214.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.12.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||
|
@@ -9,24 +9,24 @@
|
||||
# Use certificate in net/internal/testcert.go
|
||||
rootCAs = [ """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
|
||||
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
|
||||
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
|
||||
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
|
||||
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
|
||||
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||||
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||
WkBKOclmOV2xlTVuPw==
|
||||
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||||
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
|
||||
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
|
||||
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
|
||||
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
|
||||
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
|
||||
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
|
||||
-----END CERTIFICATE-----
|
||||
"""]
|
||||
|
||||
|
@@ -1,20 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
|
||||
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
|
||||
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
|
||||
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
|
||||
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
|
||||
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||||
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||
WkBKOclmOV2xlTVuPw==
|
||||
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||||
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
|
||||
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
|
||||
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
|
||||
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
|
||||
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
|
||||
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
|
||||
-----END CERTIFICATE-----
|
||||
|
@@ -41,7 +41,7 @@ func (s *HTTPSuite) TestSimpleConfiguration() {
|
||||
Services: map[string]*dynamic.Service{
|
||||
"serviceHTTP": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: boolRef(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://bacon:80",
|
||||
@@ -81,6 +81,4 @@ func startTestServerWithResponse(response []byte) (ts *httptest.Server) {
|
||||
return ts
|
||||
}
|
||||
|
||||
func boolRef(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/traefik/traefik/v2/integration/try"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
@@ -451,6 +452,44 @@ func (s *WebsocketSuite) TestSSLhttp2() {
|
||||
assert.Equal(s.T(), "OK", string(msg))
|
||||
}
|
||||
|
||||
func (s *WebsocketSuite) TestSettingEnableConnectProtocol() {
|
||||
file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
|
||||
WebsocketServer string
|
||||
}{
|
||||
WebsocketServer: "http://127.0.0.1",
|
||||
})
|
||||
|
||||
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
|
||||
|
||||
// Wait for traefik.
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// Add client self-signed cert.
|
||||
roots := x509.NewCertPool()
|
||||
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
roots.AppendCertsFromPEM(certContent)
|
||||
|
||||
// Open a connection to inspect SettingsFrame.
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:8000", &tls.Config{
|
||||
RootCAs: roots,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
framer := http2.NewFramer(nil, conn)
|
||||
frame, err := framer.ReadFrame()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
fr, ok := frame.(*http2.SettingsFrame)
|
||||
require.True(s.T(), ok)
|
||||
|
||||
_, ok = fr.Value(http2.SettingEnableConnectProtocol)
|
||||
assert.False(s.T(), ok)
|
||||
}
|
||||
|
||||
func (s *WebsocketSuite) TestHeaderAreForwarded() {
|
||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||
|
||||
|
@@ -25,14 +25,22 @@ func main() {
|
||||
ParseFiles("./.goreleaser.yml.tmpl"),
|
||||
)
|
||||
|
||||
outputPath := path.Join(os.TempDir(), fmt.Sprintf(".goreleaser_%s.yml", goos))
|
||||
goarch := ""
|
||||
outputFileName := fmt.Sprintf(".goreleaser_%s.yml", goos)
|
||||
if strings.Contains(goos, "-") {
|
||||
split := strings.Split(goos, "-")
|
||||
goos = split[0]
|
||||
goarch = split[1]
|
||||
}
|
||||
|
||||
outputPath := path.Join(os.TempDir(), outputFileName)
|
||||
|
||||
output, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(output, map[string]string{"GOOS": goos})
|
||||
err = tmpl.Execute(output, map[string]string{"GOOS": goos, "GOARCH": goarch})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package dashboard
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/traefik/traefik/v2/webui"
|
||||
@@ -25,7 +25,8 @@ func Append(router *mux.Router, customAssets fs.FS) {
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(resp, req, safePrefix(req)+"/dashboard/", http.StatusFound)
|
||||
prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/")
|
||||
http.Redirect(resp, req, prefix+"/dashboard/", http.StatusFound)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
@@ -48,21 +49,3 @@ func (g Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||
http.FileServerFS(assets).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func safePrefix(req *http.Request) string {
|
||||
prefix := req.Header.Get("X-Forwarded-Prefix")
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parse, err := url.Parse(prefix)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parse.Host != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parse.Path
|
||||
}
|
||||
|
@@ -10,53 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_safePrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "host",
|
||||
value: "https://example.com",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "host with path",
|
||||
value: "https://example.com/foo/bar?test",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "path",
|
||||
value: "/foo/bar",
|
||||
expected: "/foo/bar",
|
||||
},
|
||||
{
|
||||
desc: "path without leading slash",
|
||||
value: "foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set("X-Forwarded-Prefix", test.value)
|
||||
|
||||
prefix := safePrefix(req)
|
||||
|
||||
assert.Equal(t, test.expected, prefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ContentSecurityPolicy(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
)
|
||||
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestHandler_HTTP(t *testing.T) {
|
||||
type expected struct {
|
||||
@@ -337,7 +337,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -354,7 +354,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -423,7 +423,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -440,7 +440,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -457,7 +457,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.3",
|
||||
@@ -487,7 +487,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -505,7 +505,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -536,7 +536,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -554,7 +554,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -585,7 +585,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -614,7 +614,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -643,7 +643,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
|
@@ -38,7 +38,7 @@ func TestHandler_RawData(t *testing.T) {
|
||||
"foo-service@myprovider": {
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
|
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestDecodeConfiguration(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"traefik.http.middlewares.Middleware0.addprefix.prefix": "foobar",
|
||||
@@ -265,7 +267,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
ProxyProtocol: &dynamic.ProxyProtocol{Version: 42},
|
||||
},
|
||||
},
|
||||
@@ -276,7 +278,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
ProxyProtocol: &dynamic.ProxyProtocol{Version: 2},
|
||||
},
|
||||
},
|
||||
@@ -665,9 +667,9 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
"name0": "foobar",
|
||||
"name1": "foobar",
|
||||
},
|
||||
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||
FollowRedirects: pointer(true),
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -693,9 +695,9 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
"name0": "foobar",
|
||||
"name1": "foobar",
|
||||
},
|
||||
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||
FollowRedirects: pointer(true),
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -773,7 +775,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
},
|
||||
},
|
||||
"Service1": {
|
||||
@@ -783,7 +785,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1170,7 +1172,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
"name1": "foobar",
|
||||
},
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -1197,7 +1199,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
"name1": "foobar",
|
||||
},
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
|
@@ -24,14 +24,14 @@ type EntryPoint struct {
|
||||
|
||||
// GetAddress strips any potential protocol part of the address field of the
|
||||
// entry point, in order to return the actual address.
|
||||
func (ep EntryPoint) GetAddress() string {
|
||||
func (ep *EntryPoint) GetAddress() string {
|
||||
splitN := strings.SplitN(ep.Address, "/", 2)
|
||||
return splitN[0]
|
||||
}
|
||||
|
||||
// GetProtocol returns the protocol part of the address field of the entry point.
|
||||
// If none is specified, it defaults to "tcp".
|
||||
func (ep EntryPoint) GetProtocol() (string, error) {
|
||||
func (ep *EntryPoint) GetProtocol() (string, error) {
|
||||
splitN := strings.SplitN(ep.Address, "/", 2)
|
||||
if len(splitN) < 2 {
|
||||
return "tcp", nil
|
||||
|
@@ -278,7 +278,7 @@ func TestNewRequest(t *testing.T) {
|
||||
|
||||
if test.expected.err {
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, nil)
|
||||
assert.Nil(t, req)
|
||||
} else {
|
||||
require.NoError(t, err, "failed to create new backend request")
|
||||
require.NotNil(t, req)
|
||||
|
@@ -12,7 +12,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -809,16 +808,16 @@ func assertValidLogData(t *testing.T, expected string, logData []byte) {
|
||||
assert.Equal(t, resultExpected[OriginContentSize], result[OriginContentSize], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestRefererHeader], result[RequestRefererHeader], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestUserAgentHeader], result[RequestUserAgentHeader], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*`), result[RequestCount], formatErrMessage)
|
||||
assert.Regexp(t, `\d*`, result[RequestCount], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RouterName], result[RouterName], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[ServiceURL], result[ServiceURL], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*ms`), result[Duration], formatErrMessage)
|
||||
assert.Regexp(t, `\d*ms`, result[Duration], formatErrMessage)
|
||||
}
|
||||
|
||||
func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
|
||||
t.Helper()
|
||||
|
||||
file, err := os.CreateTemp("", "testlogger")
|
||||
file, err := os.CreateTemp(t.TempDir(), "testlogger")
|
||||
require.NoError(t, err, "failed to create temp file")
|
||||
|
||||
original := os.Stdout
|
||||
|
@@ -20,6 +20,7 @@ const (
|
||||
xForwardedServer = "X-Forwarded-Server"
|
||||
xForwardedURI = "X-Forwarded-Uri"
|
||||
xForwardedMethod = "X-Forwarded-Method"
|
||||
xForwardedPrefix = "X-Forwarded-Prefix"
|
||||
xForwardedTLSClientCert = "X-Forwarded-Tls-Client-Cert"
|
||||
xForwardedTLSClientCertInfo = "X-Forwarded-Tls-Client-Cert-Info"
|
||||
xRealIP = "X-Real-Ip"
|
||||
@@ -35,6 +36,7 @@ var xHeaders = []string{
|
||||
xForwardedServer,
|
||||
xForwardedURI,
|
||||
xForwardedMethod,
|
||||
xForwardedPrefix,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xRealIP,
|
||||
|
@@ -48,6 +48,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -55,6 +56,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -68,6 +70,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -75,6 +78,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -88,6 +92,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -95,6 +100,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -108,6 +114,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -115,6 +122,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -128,6 +136,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -135,6 +144,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -148,6 +158,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -155,6 +166,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -283,6 +295,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -293,6 +306,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -304,6 +318,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "80",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
xRealIP: "",
|
||||
connection: "",
|
||||
},
|
||||
@@ -321,6 +336,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -331,6 +347,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -342,6 +359,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "foo",
|
||||
xForwardedTLSClientCert: "foo",
|
||||
xForwardedTLSClientCertInfo: "foo",
|
||||
xForwardedPrefix: "foo",
|
||||
xRealIP: "foo",
|
||||
connection: "",
|
||||
},
|
||||
@@ -358,6 +376,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
incomingHeaders: map[string][]string{
|
||||
@@ -370,6 +389,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -380,6 +400,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -391,6 +412,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "80",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
xRealIP: "",
|
||||
connection: "",
|
||||
},
|
||||
@@ -407,6 +429,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
incomingHeaders: map[string][]string{
|
||||
@@ -419,6 +442,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -429,6 +453,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -440,6 +465,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "foo",
|
||||
xForwardedTLSClientCert: "foo",
|
||||
xForwardedTLSClientCertInfo: "foo",
|
||||
xForwardedPrefix: "foo",
|
||||
xRealIP: "foo",
|
||||
connection: "",
|
||||
},
|
||||
|
@@ -152,7 +152,7 @@ func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
if req.TLS != nil && len(req.TLS.PeerCertificates) > 0 {
|
||||
req.Header.Set(xForwardedTLSClientCert, getCertificates(ctx, req.TLS.PeerCertificates))
|
||||
} else {
|
||||
logger.Warn("Tried to extract a certificate on a request without mutual TLS")
|
||||
logger.Debug("Tried to extract a certificate on a request without mutual TLS")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
headerContent := p.getCertInfo(ctx, req.TLS.PeerCertificates)
|
||||
req.Header.Set(xForwardedTLSClientCertInfo, url.QueryEscape(headerContent))
|
||||
} else {
|
||||
logger.Warn("Tried to extract a certificate on a request without mutual TLS")
|
||||
logger.Debug("Tried to extract a certificate on a request without mutual TLS")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,6 +37,48 @@ type Listener interface {
|
||||
// each of them about a retry attempt.
|
||||
type Listeners []Listener
|
||||
|
||||
// Retried exists to implement the Listener interface. It calls Retried on each of its slice entries.
|
||||
func (l Listeners) Retried(req *http.Request, attempt int) {
|
||||
for _, listener := range l {
|
||||
listener.Retried(req, attempt)
|
||||
}
|
||||
}
|
||||
|
||||
type shouldRetryContextKey struct{}
|
||||
|
||||
// ShouldRetry is a function allowing to enable/disable the retry middleware mechanism.
|
||||
type ShouldRetry func(shouldRetry bool)
|
||||
|
||||
// ContextShouldRetry returns the ShouldRetry function if it has been set by the Retry middleware in the chain.
|
||||
func ContextShouldRetry(ctx context.Context) ShouldRetry {
|
||||
f, _ := ctx.Value(shouldRetryContextKey{}).(ShouldRetry)
|
||||
return f
|
||||
}
|
||||
|
||||
// WrapHandler wraps a given http.Handler to inject the httptrace.ClientTrace in the request context when it is needed
|
||||
// by the retry middleware.
|
||||
func WrapHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if shouldRetry := ContextShouldRetry(req.Context()); shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
|
||||
trace := &httptrace.ClientTrace{
|
||||
WroteHeaders: func() {
|
||||
shouldRetry(false)
|
||||
},
|
||||
WroteRequest: func(httptrace.WroteRequestInfo) {
|
||||
shouldRetry(false)
|
||||
},
|
||||
}
|
||||
newCtx := httptrace.WithClientTrace(req.Context(), trace)
|
||||
next.ServeHTTP(rw, req.WithContext(newCtx))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
// retry is a middleware that retries requests.
|
||||
type retry struct {
|
||||
attempts int
|
||||
@@ -83,19 +125,13 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
attempts := 1
|
||||
|
||||
operation := func() error {
|
||||
shouldRetry := attempts < r.attempts
|
||||
retryResponseWriter := newResponseWriter(rw, shouldRetry)
|
||||
remainAttempts := attempts < r.attempts
|
||||
retryResponseWriter := newResponseWriter(rw)
|
||||
|
||||
// Disable retries when the backend already received request data
|
||||
trace := &httptrace.ClientTrace{
|
||||
WroteHeaders: func() {
|
||||
retryResponseWriter.DisableRetries()
|
||||
},
|
||||
WroteRequest: func(httptrace.WroteRequestInfo) {
|
||||
retryResponseWriter.DisableRetries()
|
||||
},
|
||||
var shouldRetry ShouldRetry = func(shouldRetry bool) {
|
||||
retryResponseWriter.SetShouldRetry(remainAttempts && shouldRetry)
|
||||
}
|
||||
newCtx := httptrace.WithClientTrace(req.Context(), trace)
|
||||
newCtx := context.WithValue(req.Context(), shouldRetryContextKey{}, shouldRetry)
|
||||
|
||||
r.next.ServeHTTP(retryResponseWriter, req.Clone(newCtx))
|
||||
|
||||
@@ -142,25 +178,17 @@ func (r *retry) newBackOff() backoff.BackOff {
|
||||
return b
|
||||
}
|
||||
|
||||
// Retried exists to implement the Listener interface. It calls Retried on each of its slice entries.
|
||||
func (l Listeners) Retried(req *http.Request, attempt int) {
|
||||
for _, listener := range l {
|
||||
listener.Retried(req, attempt)
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
ShouldRetry() bool
|
||||
DisableRetries()
|
||||
SetShouldRetry(shouldRetry bool)
|
||||
}
|
||||
|
||||
func newResponseWriter(rw http.ResponseWriter, shouldRetry bool) responseWriter {
|
||||
func newResponseWriter(rw http.ResponseWriter) responseWriter {
|
||||
responseWriter := &responseWriterWithoutCloseNotify{
|
||||
responseWriter: rw,
|
||||
headers: make(http.Header),
|
||||
shouldRetry: shouldRetry,
|
||||
}
|
||||
if _, ok := rw.(http.CloseNotifier); ok {
|
||||
return &responseWriterWithCloseNotify{
|
||||
@@ -181,8 +209,8 @@ func (r *responseWriterWithoutCloseNotify) ShouldRetry() bool {
|
||||
return r.shouldRetry
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) DisableRetries() {
|
||||
r.shouldRetry = false
|
||||
func (r *responseWriterWithoutCloseNotify) SetShouldRetry(shouldRetry bool) {
|
||||
r.shouldRetry = shouldRetry
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) Header() http.Header {
|
||||
@@ -193,23 +221,17 @@ func (r *responseWriterWithoutCloseNotify) Header() http.Header {
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||
if r.ShouldRetry() {
|
||||
if r.shouldRetry {
|
||||
return len(buf), nil
|
||||
}
|
||||
if !r.written {
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return r.responseWriter.Write(buf)
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||
if r.ShouldRetry() && code == http.StatusServiceUnavailable {
|
||||
// We get a 503 HTTP Status Code when there is no backend server in the pool
|
||||
// to which the request could be sent. Also, note that r.ShouldRetry()
|
||||
// will never return true in case there was a connection established to
|
||||
// the backend server and so we can be sure that the 503 was produced
|
||||
// inside Traefik already and we don't have to retry in this cases.
|
||||
r.DisableRetries()
|
||||
}
|
||||
|
||||
if r.ShouldRetry() || r.written {
|
||||
if r.shouldRetry || r.written {
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -105,12 +105,21 @@ func TestRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryAttempts := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
// This signals that a connection will be established with the backend
|
||||
// to enable the Retry middleware mechanism.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
retryAttempts++
|
||||
|
||||
if retryAttempts > test.amountFaultyEndpoints {
|
||||
// calls WroteHeaders on httptrace.
|
||||
_ = r.Write(io.Discard)
|
||||
// This signals that request headers have been sent to the backend.
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return
|
||||
@@ -152,27 +161,16 @@ func TestRetryEmptyServerList(t *testing.T) {
|
||||
assert.Equal(t, 0, retryListener.timesCalled)
|
||||
}
|
||||
|
||||
func TestRetryListeners(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
retryListeners := Listeners{&countingRetryListener{}, &countingRetryListener{}}
|
||||
|
||||
retryListeners.Retried(req, 1)
|
||||
retryListeners.Retried(req, 1)
|
||||
|
||||
for _, retryListener := range retryListeners {
|
||||
listener := retryListener.(*countingRetryListener)
|
||||
if listener.timesCalled != 2 {
|
||||
t.Errorf("retry listener was called %d time(s), want %d time(s)", listener.timesCalled, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) {
|
||||
attempt := 0
|
||||
expectedHeaderName := "X-Foo-Test-2"
|
||||
expectedHeaderValue := "bar"
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
headerName := fmt.Sprintf("X-Foo-Test-%d", attempt)
|
||||
rw.Header().Add(headerName, expectedHeaderValue)
|
||||
if attempt < 2 {
|
||||
@@ -181,43 +179,54 @@ func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) {
|
||||
}
|
||||
|
||||
// Request has been successfully written to backend
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
trace.WroteHeaders()
|
||||
shouldRetry(false)
|
||||
|
||||
// And we decide to answer to client
|
||||
// And we decide to answer to client.
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
retry.ServeHTTP(responseRecorder, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
res := httptest.NewRecorder()
|
||||
retry.ServeHTTP(res, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
|
||||
headerValue := responseRecorder.Header().Get(expectedHeaderName)
|
||||
|
||||
// Validate if we have the correct header
|
||||
if headerValue != expectedHeaderValue {
|
||||
t.Errorf("Expected to have %s for header %s, got %s", expectedHeaderValue, expectedHeaderName, headerValue)
|
||||
}
|
||||
// The third header attempt is kept.
|
||||
headerValue := res.Header().Get("X-Foo-Test-2")
|
||||
assert.Equal(t, expectedHeaderValue, headerValue)
|
||||
|
||||
// Validate that we don't have headers from previous attempts
|
||||
for i := range attempt {
|
||||
headerName := fmt.Sprintf("X-Foo-Test-%d", i)
|
||||
headerValue = responseRecorder.Header().Get("headerName")
|
||||
headerValue = res.Header().Get(headerName)
|
||||
if headerValue != "" {
|
||||
t.Errorf("Expected no value for header %s, got %s", headerName, headerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// countingRetryListener is a Listener implementation to count the times the Retried fn is called.
|
||||
type countingRetryListener struct {
|
||||
timesCalled int
|
||||
}
|
||||
func TestRetryShouldNotLooseHeadersOnWrite(t *testing.T) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Add("X-Foo-Test", "bar")
|
||||
|
||||
func (l *countingRetryListener) Retried(req *http.Request, attempt int) {
|
||||
l.timesCalled++
|
||||
// Request has been successfully written to backend.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
// And we decide to answer to client without calling WriteHeader.
|
||||
_, err := rw.Write([]byte("bar"))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
res := httptest.NewRecorder()
|
||||
retry.ServeHTTP(res, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
|
||||
headerValue := res.Header().Get("X-Foo-Test")
|
||||
assert.Equal(t, "bar", headerValue)
|
||||
}
|
||||
|
||||
func TestRetryWithFlush(t *testing.T) {
|
||||
@@ -275,12 +284,24 @@ func TestRetryWebsocket(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryAttempts := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
// This signals that a connection will be established with the backend
|
||||
// to enable the Retry middleware mechanism.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
retryAttempts++
|
||||
|
||||
if retryAttempts > test.amountFaultyEndpoints {
|
||||
// This signals that request headers have been sent to the backend.
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{}
|
||||
_, err := upgrader.Upgrade(rw, r, nil)
|
||||
_, err := upgrader.Upgrade(rw, req, nil)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -387,3 +408,12 @@ func Test1xxResponses(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, retryListener.timesCalled)
|
||||
}
|
||||
|
||||
// countingRetryListener is a Listener implementation to count the times the Retried fn is called.
|
||||
type countingRetryListener struct {
|
||||
timesCalled int
|
||||
}
|
||||
|
||||
func (l *countingRetryListener) Retried(req *http.Request, attempt int) {
|
||||
l.timesCalled++
|
||||
}
|
||||
|
@@ -38,24 +38,24 @@ type MockSpan struct {
|
||||
Tags map[string]interface{}
|
||||
}
|
||||
|
||||
func (n MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
|
||||
func (n MockSpan) SetBaggageItem(key, val string) opentracing.Span {
|
||||
return MockSpan{Tags: make(map[string]interface{})}
|
||||
func (n *MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
|
||||
func (n *MockSpan) SetBaggageItem(key, val string) opentracing.Span {
|
||||
return &MockSpan{Tags: make(map[string]interface{})}
|
||||
}
|
||||
func (n MockSpan) BaggageItem(key string) string { return "" }
|
||||
func (n MockSpan) SetTag(key string, value interface{}) opentracing.Span {
|
||||
func (n *MockSpan) BaggageItem(key string) string { return "" }
|
||||
func (n *MockSpan) SetTag(key string, value interface{}) opentracing.Span {
|
||||
n.Tags[key] = value
|
||||
return n
|
||||
}
|
||||
func (n MockSpan) LogFields(fields ...log.Field) {}
|
||||
func (n MockSpan) LogKV(keyVals ...interface{}) {}
|
||||
func (n MockSpan) Finish() {}
|
||||
func (n MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {}
|
||||
func (n MockSpan) SetOperationName(operationName string) opentracing.Span { return n }
|
||||
func (n MockSpan) Tracer() opentracing.Tracer { return MockTracer{} }
|
||||
func (n MockSpan) LogEvent(event string) {}
|
||||
func (n MockSpan) LogEventWithPayload(event string, payload interface{}) {}
|
||||
func (n MockSpan) Log(data opentracing.LogData) {}
|
||||
func (n *MockSpan) LogFields(fields ...log.Field) {}
|
||||
func (n *MockSpan) LogKV(keyVals ...interface{}) {}
|
||||
func (n *MockSpan) Finish() {}
|
||||
func (n *MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {}
|
||||
func (n *MockSpan) SetOperationName(operationName string) opentracing.Span { return n }
|
||||
func (n *MockSpan) Tracer() opentracing.Tracer { return MockTracer{} }
|
||||
func (n *MockSpan) LogEvent(event string) {}
|
||||
func (n *MockSpan) LogEventWithPayload(event string, payload interface{}) {}
|
||||
func (n *MockSpan) Log(data opentracing.LogData) {}
|
||||
func (n *MockSpan) Reset() {
|
||||
n.Tags = make(map[string]interface{})
|
||||
}
|
||||
|
@@ -102,7 +102,7 @@ func NewMuxer() (*Muxer, error) {
|
||||
|
||||
// Match returns the handler of the first route matching the connection metadata,
|
||||
// and whether the match is exactly from the rule HostSNI(*).
|
||||
func (m Muxer) Match(meta ConnData) (tcp.Handler, bool) {
|
||||
func (m *Muxer) Match(meta ConnData) (tcp.Handler, bool) {
|
||||
for _, route := range m.routes {
|
||||
if route.matchers.match(meta) {
|
||||
return route.handler, route.catchAll
|
||||
@@ -310,7 +310,7 @@ func alpn(tree *matchersTree, protos ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hostOrIP = regexp.MustCompile(`^[[:alnum:]\.\-\:]+$`)
|
||||
var hostOrIP = regexp.MustCompile(`^[[:word:]\.\-\:]+$`)
|
||||
|
||||
// hostSNI checks if the SNI Host of the connection match the matcher host.
|
||||
func hostSNI(tree *matchersTree, hosts ...string) error {
|
||||
|
@@ -743,6 +743,11 @@ func Test_HostSNI(t *testing.T) {
|
||||
ruleHosts: []string{"foo.bar"},
|
||||
serverName: "foo.bar",
|
||||
},
|
||||
{
|
||||
desc: "Matching hosts with subdomains with _",
|
||||
ruleHosts: []string{"foo_bar.example.com"},
|
||||
serverName: "foo_bar.example.com",
|
||||
},
|
||||
{
|
||||
desc: "Matching IPv4",
|
||||
ruleHosts: []string{"127.0.0.1"},
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
@@ -22,9 +23,9 @@ type LocalStore struct {
|
||||
}
|
||||
|
||||
// NewLocalStore initializes a new LocalStore with a file name.
|
||||
func NewLocalStore(filename string) *LocalStore {
|
||||
func NewLocalStore(filename string, routinesPool *safe.Pool) *LocalStore {
|
||||
store := &LocalStore{filename: filename, saveDataChan: make(chan map[string]*StoredData)}
|
||||
store.listenSaveAction()
|
||||
store.listenSaveAction(routinesPool)
|
||||
return store
|
||||
}
|
||||
|
||||
@@ -99,18 +100,31 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
|
||||
}
|
||||
|
||||
// listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`.
|
||||
func (s *LocalStore) listenSaveAction() {
|
||||
safe.Go(func() {
|
||||
func (s *LocalStore) listenSaveAction(routinesPool *safe.Pool) {
|
||||
routinesPool.GoCtx(func(ctx context.Context) {
|
||||
logger := log.WithoutContext().WithField(log.ProviderName, "acme")
|
||||
for object := range s.saveDataChan {
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
err = os.WriteFile(s.filename, data, 0o600)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
case object := <-s.saveDataChan:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Stop handling events because Traefik is shutting down.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(s.filename, data, 0o600)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
)
|
||||
|
||||
func TestLocalStore_GetAccount(t *testing.T) {
|
||||
@@ -45,7 +47,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
s := NewLocalStore(test.filename)
|
||||
s := NewLocalStore(test.filename, safe.NewPool(context.Background()))
|
||||
|
||||
account, err := s.GetAccount("test")
|
||||
require.NoError(t, err)
|
||||
@@ -58,7 +60,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
|
||||
func TestLocalStore_SaveAccount(t *testing.T) {
|
||||
acmeFile := filepath.Join(t.TempDir(), "acme.json")
|
||||
|
||||
s := NewLocalStore(acmeFile)
|
||||
s := NewLocalStore(acmeFile, safe.NewPool(context.Background()))
|
||||
|
||||
email := "some@email.com"
|
||||
|
||||
|
@@ -67,8 +67,8 @@ type ProviderAggregator struct {
|
||||
}
|
||||
|
||||
// NewProviderAggregator returns an aggregate of all the providers configured in the static configuration.
|
||||
func NewProviderAggregator(conf static.Providers) ProviderAggregator {
|
||||
p := ProviderAggregator{
|
||||
func NewProviderAggregator(conf static.Providers) *ProviderAggregator {
|
||||
p := &ProviderAggregator{
|
||||
providersThrottleDuration: time.Duration(conf.ProvidersThrottleDuration),
|
||||
}
|
||||
|
||||
@@ -172,12 +172,12 @@ func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
|
||||
}
|
||||
|
||||
// Init the provider.
|
||||
func (p ProviderAggregator) Init() error {
|
||||
func (p *ProviderAggregator) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provide calls the provide method of every providers.
|
||||
func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
func (p *ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
if p.fileProvider != nil {
|
||||
p.launchProvider(configurationChan, pool, p.fileProvider)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
|
||||
func (p *ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
|
||||
jsonConf, err := redactor.RemoveCredentials(prd)
|
||||
if err != nil {
|
||||
log.WithoutContext().Debugf("Cannot marshal the provider configuration %T: %v", prd, err)
|
||||
|
@@ -3,7 +3,9 @@ package provider
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -13,7 +15,6 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// Merge merges multiple configurations.
|
||||
@@ -384,7 +385,7 @@ func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCP
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -406,7 +407,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -453,7 +454,7 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@@ -13,8 +13,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
func Int(v int) *int { return &v }
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestDefaultRule(t *testing.T) {
|
||||
testCases := []struct {
|
||||
@@ -64,7 +63,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -118,7 +117,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -164,7 +163,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -210,7 +209,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -262,7 +261,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -350,7 +349,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -407,7 +406,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.1:443",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-dev-Test",
|
||||
},
|
||||
},
|
||||
@@ -497,7 +496,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.2:444",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-dev-Test",
|
||||
},
|
||||
},
|
||||
@@ -578,7 +577,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -588,7 +587,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -651,7 +650,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -711,7 +710,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -774,7 +773,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -826,7 +825,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -879,7 +878,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -924,7 +923,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -982,7 +981,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1030,7 +1029,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1040,7 +1039,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1216,7 +1215,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1267,7 +1266,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1344,7 +1343,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1408,7 +1407,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1488,7 +1487,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1548,7 +1547,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1622,7 +1621,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1688,7 +1687,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1740,7 +1739,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1793,7 +1792,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1840,7 +1839,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1850,7 +1849,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2074,7 +2073,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2137,7 +2136,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2188,7 +2187,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2241,7 +2240,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2337,7 +2336,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2393,7 +2392,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2515,7 +2514,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2544,7 +2543,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2631,7 +2630,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2668,7 +2667,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2760,7 +2759,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(200),
|
||||
TerminationDelay: pointer(200),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2844,7 +2843,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-Test",
|
||||
},
|
||||
},
|
||||
@@ -2855,7 +2854,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-Test",
|
||||
},
|
||||
},
|
||||
@@ -2933,7 +2932,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Servers: []dynamic.TCPServer{
|
||||
{Address: "127.0.0.1:80"},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
"Test-17573747155436217342": {
|
||||
@@ -2941,7 +2940,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Servers: []dynamic.TCPServer{
|
||||
{Address: "127.0.0.2:80"},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3136,7 +3135,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -307,7 +307,7 @@ func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPo
|
||||
return ip, port, nil
|
||||
}
|
||||
|
||||
func (p Provider) getIPAddress(ctx context.Context, container dockerData) string {
|
||||
func (p *Provider) getIPAddress(ctx context.Context, container dockerData) string {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
netNotFound := false
|
||||
|
@@ -71,7 +71,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -130,7 +130,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -191,7 +191,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -244,7 +244,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -297,7 +297,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -356,7 +356,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -579,7 +579,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -658,7 +658,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -668,7 +668,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -747,7 +747,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -807,7 +807,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -868,7 +868,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -921,7 +921,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -987,7 +987,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1043,7 +1043,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1053,7 +1053,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1113,7 +1113,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1353,7 +1353,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1412,7 +1412,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1508,7 +1508,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1591,7 +1591,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1696,7 +1696,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1773,7 +1773,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1872,7 +1872,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1954,7 +1954,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2026,7 +2026,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -2036,7 +2036,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2096,7 +2096,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2157,7 +2157,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2212,7 +2212,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -2222,7 +2222,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2418,7 +2418,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Services: map[string]*dynamic.Service{
|
||||
"Test": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2487,7 +2487,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Services: map[string]*dynamic.TCPService{
|
||||
"Test": {
|
||||
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2676,7 +2676,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2747,7 +2747,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2806,7 +2806,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2867,7 +2867,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2979,7 +2979,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3043,7 +3043,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3215,7 +3215,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3314,7 +3314,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(200),
|
||||
TerminationDelay: pointer(200),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3391,7 +3391,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://192.168.0.1:8081",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3451,7 +3451,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:79",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3958,6 +3958,4 @@ func TestSwarmGetPort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Int(v int) *int { return &v }
|
||||
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
@@ -1,10 +1,13 @@
|
||||
package ecs
|
||||
|
||||
import "github.com/aws/aws-sdk-go/service/ecs"
|
||||
import (
|
||||
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
|
||||
)
|
||||
|
||||
func instance(ops ...func(*ecsInstance)) ecsInstance {
|
||||
e := &ecsInstance{
|
||||
containerDefinition: &ecs.ContainerDefinition{},
|
||||
containerDefinition: &ecstypes.ContainerDefinition{},
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
@@ -36,7 +39,7 @@ func iMachine(opts ...func(*machine)) func(*ecsInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
func mState(state string) func(*machine) {
|
||||
func mState(state ec2types.InstanceStateName) func(*machine) {
|
||||
return func(m *machine) {
|
||||
m.state = state
|
||||
}
|
||||
@@ -48,7 +51,7 @@ func mPrivateIP(ip string) func(*machine) {
|
||||
}
|
||||
}
|
||||
|
||||
func mHealthStatus(status string) func(*machine) {
|
||||
func mHealthStatus(status ecstypes.HealthStatus) func(*machine) {
|
||||
return func(m *machine) {
|
||||
m.healthStatus = status
|
||||
}
|
||||
@@ -64,10 +67,10 @@ func mPorts(opts ...func(*portMapping)) func(*machine) {
|
||||
}
|
||||
}
|
||||
|
||||
func mPort(containerPort, hostPort int32, protocol string) func(*portMapping) {
|
||||
func mPort(containerPort, hostPort int32, protocol ecstypes.TransportProtocol) func(*portMapping) {
|
||||
return func(pm *portMapping) {
|
||||
pm.containerPort = int64(containerPort)
|
||||
pm.hostPort = int64(hostPort)
|
||||
pm.containerPort = containerPort
|
||||
pm.hostPort = hostPort
|
||||
pm.protocol = protocol
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/label"
|
||||
@@ -163,12 +163,12 @@ func (p *Provider) filterInstance(ctx context.Context, instance ecsInstance) boo
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.ToLower(instance.machine.state) != ec2.InstanceStateNameRunning {
|
||||
if instance.machine.state != ec2types.InstanceStateNameRunning {
|
||||
logger.Debugf("Filtering ecs instance with an incorrect state %s (%s) (state = %s)", instance.Name, instance.ID, instance.machine.state)
|
||||
return false
|
||||
}
|
||||
|
||||
if instance.machine.healthStatus == "UNHEALTHY" {
|
||||
if instance.machine.healthStatus == ecstypes.HealthStatusUnhealthy {
|
||||
logger.Debugf("Filtering unhealthy ecs instance %s (%s)", instance.Name, instance.ID)
|
||||
return false
|
||||
}
|
||||
@@ -293,9 +293,9 @@ func (p *Provider) getIPPort(instance ecsInstance, serverPort string) (string, s
|
||||
func getPort(instance ecsInstance, serverPort string) string {
|
||||
if len(serverPort) > 0 {
|
||||
for _, port := range instance.machine.ports {
|
||||
containerPort := strconv.FormatInt(port.containerPort, 10)
|
||||
containerPort := strconv.FormatInt(int64(port.containerPort), 10)
|
||||
if serverPort == containerPort {
|
||||
return strconv.FormatInt(port.hostPort, 10)
|
||||
return strconv.FormatInt(int64(port.hostPort), 10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ func getPort(instance ecsInstance, serverPort string) string {
|
||||
|
||||
var ports []nat.Port
|
||||
for _, port := range instance.machine.ports {
|
||||
natPort, err := nat.NewPort(port.protocol, strconv.FormatInt(port.hostPort, 10))
|
||||
natPort, err := nat.NewPort(string(port.protocol), strconv.FormatInt(int64(port.hostPort), 10))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -3,21 +3,22 @@ package ecs
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"iter"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ecs"
|
||||
"github.com/aws/aws-sdk-go/service/ssm"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ecs"
|
||||
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ssm"
|
||||
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
@@ -47,29 +48,29 @@ type Provider struct {
|
||||
type ecsInstance struct {
|
||||
Name string
|
||||
ID string
|
||||
containerDefinition *ecs.ContainerDefinition
|
||||
containerDefinition *ecstypes.ContainerDefinition
|
||||
machine *machine
|
||||
Labels map[string]string
|
||||
ExtraConf configuration
|
||||
}
|
||||
|
||||
type portMapping struct {
|
||||
containerPort int64
|
||||
hostPort int64
|
||||
protocol string
|
||||
containerPort int32
|
||||
hostPort int32
|
||||
protocol ecstypes.TransportProtocol
|
||||
}
|
||||
|
||||
type machine struct {
|
||||
state string
|
||||
state ec2types.InstanceStateName
|
||||
privateIP string
|
||||
ports []portMapping
|
||||
healthStatus string
|
||||
healthStatus ecstypes.HealthStatus
|
||||
}
|
||||
|
||||
type awsClient struct {
|
||||
ecs *ecs.ECS
|
||||
ec2 *ec2.EC2
|
||||
ssm *ssm.SSM
|
||||
ecs *ecs.Client
|
||||
ec2 *ec2.Client
|
||||
ssm *ssm.Client
|
||||
}
|
||||
|
||||
// DefaultTemplateRule The default template for the default rule.
|
||||
@@ -100,56 +101,40 @@ func (p *Provider) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
})
|
||||
func (p *Provider) createClient(ctx context.Context, logger log.Logger) (*awsClient, error) {
|
||||
optFns := []func(*config.LoadOptions) error{
|
||||
config.WithLogger(logging.LoggerFunc(func(_ logging.Classification, format string, args ...interface{}) {
|
||||
logger.Debugf(format, args...)
|
||||
})),
|
||||
}
|
||||
if p.Region != "" {
|
||||
optFns = append(optFns, config.WithRegion(p.Region))
|
||||
} else {
|
||||
logger.Infoln("No region provided, will retrieve region from the EC2 Metadata service")
|
||||
optFns = append(optFns, config.WithEC2IMDSRegion())
|
||||
}
|
||||
|
||||
if p.AccessKeyID != "" && p.SecretAccessKey != "" {
|
||||
// From https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specify-credentials-programmatically:
|
||||
// "If you explicitly provide credentials, as in this example, the SDK uses only those credentials."
|
||||
// this makes sure that user-defined credentials always have the highest priority
|
||||
staticCreds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(p.AccessKeyID, p.SecretAccessKey, ""))
|
||||
optFns = append(optFns, config.WithCredentialsProvider(staticCreds))
|
||||
|
||||
// If the access key and secret access key are not provided, config.LoadDefaultConfig
|
||||
// will look for the credentials in the default credential chain.
|
||||
// See https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specifying-credentials.
|
||||
}
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx, optFns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ec2meta := ec2metadata.New(sess)
|
||||
if p.Region == "" && ec2meta.Available() {
|
||||
logger.Infoln("No region provided, querying instance metadata endpoint...")
|
||||
identity, err := ec2meta.GetInstanceIdentityDocument()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Region = identity.Region
|
||||
}
|
||||
|
||||
cfg := aws.NewConfig().
|
||||
WithCredentials(credentials.NewChainCredentials([]credentials.Provider{
|
||||
&credentials.StaticProvider{
|
||||
Value: credentials.Value{
|
||||
AccessKeyID: p.AccessKeyID,
|
||||
SecretAccessKey: p.SecretAccessKey,
|
||||
},
|
||||
},
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{},
|
||||
defaults.RemoteCredProvider(*(defaults.Config()), defaults.Handlers()),
|
||||
stscreds.NewWebIdentityRoleProviderWithOptions(
|
||||
sts.New(sess),
|
||||
os.Getenv("AWS_ROLE_ARN"),
|
||||
"",
|
||||
stscreds.FetchTokenPath(os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE")),
|
||||
),
|
||||
}))
|
||||
|
||||
// Set the region if it is defined by the user or resolved from the EC2 metadata.
|
||||
if p.Region != "" {
|
||||
cfg.Region = &p.Region
|
||||
}
|
||||
|
||||
cfg.WithLogger(aws.LoggerFunc(func(args ...interface{}) {
|
||||
logger.Debug(args...)
|
||||
}))
|
||||
|
||||
return &awsClient{
|
||||
ecs.New(sess, cfg),
|
||||
ec2.New(sess, cfg),
|
||||
ssm.New(sess, cfg),
|
||||
ecs.NewFromConfig(cfg),
|
||||
ec2.NewFromConfig(cfg),
|
||||
ssm.NewFromConfig(cfg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -160,7 +145,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
||||
logger := log.FromContext(ctxLog)
|
||||
|
||||
operation := func() error {
|
||||
awsClient, err := p.createClient(logger)
|
||||
awsClient, err := p.createClient(ctxLog, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create AWS client: %w", err)
|
||||
}
|
||||
@@ -218,28 +203,19 @@ func (p *Provider) loadConfiguration(ctx context.Context, client *awsClient, con
|
||||
func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsInstance, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
var clustersArn []*string
|
||||
var clusters []string
|
||||
|
||||
if p.AutoDiscoverClusters {
|
||||
input := &ecs.ListClustersInput{}
|
||||
for {
|
||||
result, err := client.ecs.ListClusters(input)
|
||||
|
||||
paginator := ecs.NewListClustersPaginator(client.ecs, input)
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result != nil {
|
||||
clustersArn = append(clustersArn, result.ClusterArns...)
|
||||
input.NextToken = result.NextToken
|
||||
if result.NextToken == nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, cArn := range clustersArn {
|
||||
clusters = append(clusters, *cArn)
|
||||
|
||||
clusters = append(clusters, page.ClusterArns...)
|
||||
}
|
||||
} else {
|
||||
clusters = p.Clusters
|
||||
@@ -251,13 +227,19 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
for _, c := range clusters {
|
||||
input := &ecs.ListTasksInput{
|
||||
Cluster: &c,
|
||||
DesiredStatus: aws.String(ecs.DesiredStatusRunning),
|
||||
DesiredStatus: ecstypes.DesiredStatusRunning,
|
||||
}
|
||||
|
||||
tasks := make(map[string]*ecs.Task)
|
||||
err := client.ecs.ListTasksPagesWithContext(ctx, input, func(page *ecs.ListTasksOutput, lastPage bool) bool {
|
||||
tasks := make(map[string]ecstypes.Task)
|
||||
|
||||
paginator := ecs.NewListTasksPaginator(client.ecs, input)
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing tasks: %w", err)
|
||||
}
|
||||
if len(page.TaskArns) > 0 {
|
||||
resp, err := client.ecs.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{
|
||||
resp, err := client.ecs.DescribeTasks(ctx, &ecs.DescribeTasksInput{
|
||||
Tasks: page.TaskArns,
|
||||
Cluster: &c,
|
||||
})
|
||||
@@ -265,16 +247,12 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
logger.Errorf("Unable to describe tasks for %v", page.TaskArns)
|
||||
} else {
|
||||
for _, t := range resp.Tasks {
|
||||
if aws.StringValue(t.LastStatus) == ecs.DesiredStatusRunning {
|
||||
tasks[aws.StringValue(t.TaskArn)] = t
|
||||
if aws.ToString(t.LastStatus) == string(ecstypes.DesiredStatusRunning) {
|
||||
tasks[aws.ToString(t.TaskArn)] = t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !lastPage
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing tasks: %w", err)
|
||||
}
|
||||
|
||||
// Skip to the next cluster if there are no tasks found on
|
||||
@@ -288,7 +266,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
return nil, err
|
||||
}
|
||||
|
||||
miInstances := make(map[string]*ssm.InstanceInformation)
|
||||
miInstances := make(map[string]ssmtypes.InstanceInformation)
|
||||
if p.ECSAnywhere {
|
||||
// Try looking up for instances on ECS Anywhere
|
||||
miInstances, err = p.lookupMiInstances(ctx, client, &c, tasks)
|
||||
@@ -303,74 +281,67 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
}
|
||||
|
||||
for key, task := range tasks {
|
||||
containerInstance := ec2Instances[aws.StringValue(task.ContainerInstanceArn)]
|
||||
containerInstance, hasContainerInstance := ec2Instances[aws.ToString(task.ContainerInstanceArn)]
|
||||
taskDef := taskDefinitions[key]
|
||||
|
||||
for _, container := range task.Containers {
|
||||
var containerDefinition *ecs.ContainerDefinition
|
||||
var containerDefinition *ecstypes.ContainerDefinition
|
||||
for _, def := range taskDef.ContainerDefinitions {
|
||||
if aws.StringValue(container.Name) == aws.StringValue(def.Name) {
|
||||
containerDefinition = def
|
||||
if aws.ToString(container.Name) == aws.ToString(def.Name) {
|
||||
containerDefinition = &def
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if containerDefinition == nil {
|
||||
logger.Debugf("Unable to find container definition for %s", aws.StringValue(container.Name))
|
||||
logger.Debugf("Unable to find container definition for %s", aws.ToString(container.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var mach *machine
|
||||
if aws.StringValue(taskDef.NetworkMode) == "awsvpc" && len(task.Attachments) != 0 {
|
||||
if taskDef.NetworkMode == ecstypes.NetworkModeAwsvpc && len(task.Attachments) != 0 {
|
||||
if len(container.NetworkInterfaces) == 0 {
|
||||
logger.Errorf("Skip container %s: no network interfaces", aws.StringValue(container.Name))
|
||||
logger.Errorf("Skip container %s: no network interfaces", aws.ToString(container.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []portMapping
|
||||
for _, mapping := range containerDefinition.PortMappings {
|
||||
if mapping != nil {
|
||||
protocol := "TCP"
|
||||
if aws.StringValue(mapping.Protocol) == "udp" {
|
||||
protocol = "UDP"
|
||||
}
|
||||
|
||||
ports = append(ports, portMapping{
|
||||
hostPort: aws.Int64Value(mapping.HostPort),
|
||||
containerPort: aws.Int64Value(mapping.ContainerPort),
|
||||
protocol: protocol,
|
||||
})
|
||||
}
|
||||
ports = append(ports, portMapping{
|
||||
hostPort: aws.ToInt32(mapping.HostPort),
|
||||
containerPort: aws.ToInt32(mapping.ContainerPort),
|
||||
protocol: mapping.Protocol,
|
||||
})
|
||||
}
|
||||
mach = &machine{
|
||||
privateIP: aws.StringValue(container.NetworkInterfaces[0].PrivateIpv4Address),
|
||||
privateIP: aws.ToString(container.NetworkInterfaces[0].PrivateIpv4Address),
|
||||
ports: ports,
|
||||
state: aws.StringValue(task.LastStatus),
|
||||
healthStatus: aws.StringValue(task.HealthStatus),
|
||||
state: ec2types.InstanceStateName(strings.ToLower(aws.ToString(task.LastStatus))),
|
||||
healthStatus: task.HealthStatus,
|
||||
}
|
||||
} else {
|
||||
miContainerInstance := miInstances[aws.StringValue(task.ContainerInstanceArn)]
|
||||
if containerInstance == nil && miContainerInstance == nil {
|
||||
logger.Errorf("Unable to find container instance information for %s", aws.StringValue(container.Name))
|
||||
miContainerInstance, hasMiContainerInstance := miInstances[aws.ToString(task.ContainerInstanceArn)]
|
||||
if !hasContainerInstance && !hasMiContainerInstance {
|
||||
logger.Errorf("Unable to find container instance information for %s", aws.ToString(container.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []portMapping
|
||||
for _, mapping := range container.NetworkBindings {
|
||||
if mapping != nil {
|
||||
ports = append(ports, portMapping{
|
||||
hostPort: aws.Int64Value(mapping.HostPort),
|
||||
containerPort: aws.Int64Value(mapping.ContainerPort),
|
||||
})
|
||||
}
|
||||
ports = append(ports, portMapping{
|
||||
hostPort: aws.ToInt32(mapping.HostPort),
|
||||
containerPort: aws.ToInt32(mapping.ContainerPort),
|
||||
protocol: mapping.Protocol,
|
||||
})
|
||||
}
|
||||
var privateIPAddress, stateName string
|
||||
if containerInstance != nil {
|
||||
privateIPAddress = aws.StringValue(containerInstance.PrivateIpAddress)
|
||||
stateName = aws.StringValue(containerInstance.State.Name)
|
||||
} else if miContainerInstance != nil {
|
||||
privateIPAddress = aws.StringValue(miContainerInstance.IPAddress)
|
||||
stateName = aws.StringValue(task.LastStatus)
|
||||
var privateIPAddress string
|
||||
var stateName ec2types.InstanceStateName
|
||||
if hasContainerInstance {
|
||||
privateIPAddress = aws.ToString(containerInstance.PrivateIpAddress)
|
||||
stateName = containerInstance.State.Name
|
||||
} else if hasMiContainerInstance {
|
||||
privateIPAddress = aws.ToString(miContainerInstance.IPAddress)
|
||||
stateName = ec2types.InstanceStateName(strings.ToLower(aws.ToString(task.LastStatus)))
|
||||
}
|
||||
|
||||
mach = &machine{
|
||||
@@ -381,11 +352,11 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
}
|
||||
|
||||
instance := ecsInstance{
|
||||
Name: fmt.Sprintf("%s-%s", strings.Replace(aws.StringValue(task.Group), ":", "-", 1), *container.Name),
|
||||
Name: fmt.Sprintf("%s-%s", strings.Replace(aws.ToString(task.Group), ":", "-", 1), aws.ToString(container.Name)),
|
||||
ID: key[len(key)-12:],
|
||||
containerDefinition: containerDefinition,
|
||||
machine: mach,
|
||||
Labels: aws.StringValueMap(containerDefinition.DockerLabels),
|
||||
Labels: containerDefinition.DockerLabels,
|
||||
}
|
||||
|
||||
extraConf, err := p.getConfiguration(instance)
|
||||
@@ -403,21 +374,21 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]*ecs.Task) (map[string]*ssm.InstanceInformation, error) {
|
||||
func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]ecstypes.Task) (map[string]ssmtypes.InstanceInformation, error) {
|
||||
instanceIDs := make(map[string]string)
|
||||
miInstances := make(map[string]*ssm.InstanceInformation)
|
||||
miInstances := make(map[string]ssmtypes.InstanceInformation)
|
||||
|
||||
var containerInstancesArns []*string
|
||||
var instanceArns []*string
|
||||
var containerInstancesArns []string
|
||||
var instanceArns []string
|
||||
|
||||
for _, task := range ecsDatas {
|
||||
if task.ContainerInstanceArn != nil {
|
||||
containerInstancesArns = append(containerInstancesArns, task.ContainerInstanceArn)
|
||||
containerInstancesArns = append(containerInstancesArns, *task.ContainerInstanceArn)
|
||||
}
|
||||
}
|
||||
|
||||
for _, arns := range p.chunkIDs(containerInstancesArns) {
|
||||
resp, err := client.ecs.DescribeContainerInstancesWithContext(ctx, &ecs.DescribeContainerInstancesInput{
|
||||
for arns := range chunkIDs(containerInstancesArns) {
|
||||
resp, err := client.ecs.DescribeContainerInstances(ctx, &ecs.DescribeContainerInstancesInput{
|
||||
ContainerInstances: arns,
|
||||
Cluster: clusterName,
|
||||
})
|
||||
@@ -426,23 +397,21 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu
|
||||
}
|
||||
|
||||
for _, container := range resp.ContainerInstances {
|
||||
instanceIDs[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn)
|
||||
instanceIDs[aws.ToString(container.Ec2InstanceId)] = aws.ToString(container.ContainerInstanceArn)
|
||||
|
||||
// Disallow EC2 Instance IDs
|
||||
// This prevents considering EC2 instances in ECS
|
||||
// and getting InvalidInstanceID.Malformed error when calling the describe-instances endpoint.
|
||||
if !strings.HasPrefix(aws.StringValue(container.Ec2InstanceId), "mi-") {
|
||||
continue
|
||||
if strings.HasPrefix(aws.ToString(container.Ec2InstanceId), "mi-") {
|
||||
instanceArns = append(instanceArns, *container.Ec2InstanceId)
|
||||
}
|
||||
|
||||
instanceArns = append(instanceArns, container.Ec2InstanceId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(instanceArns) > 0 {
|
||||
for _, ids := range p.chunkIDs(instanceArns) {
|
||||
for ids := range chunkIDs(instanceArns) {
|
||||
input := &ssm.DescribeInstanceInformationInput{
|
||||
Filters: []*ssm.InstanceInformationStringFilter{
|
||||
Filters: []ssmtypes.InstanceInformationStringFilter{
|
||||
{
|
||||
Key: aws.String("InstanceIds"),
|
||||
Values: ids,
|
||||
@@ -450,18 +419,18 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu
|
||||
},
|
||||
}
|
||||
|
||||
err := client.ssm.DescribeInstanceInformationPagesWithContext(ctx, input, func(page *ssm.DescribeInstanceInformationOutput, lastPage bool) bool {
|
||||
if len(page.InstanceInformationList) > 0 {
|
||||
for _, i := range page.InstanceInformationList {
|
||||
if i.InstanceId != nil {
|
||||
miInstances[instanceIDs[aws.StringValue(i.InstanceId)]] = i
|
||||
}
|
||||
paginator := ssm.NewDescribeInstanceInformationPaginator(client.ssm, input)
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("describing instances: %w", err)
|
||||
}
|
||||
|
||||
for _, i := range page.InstanceInformationList {
|
||||
if i.InstanceId != nil {
|
||||
miInstances[instanceIDs[aws.ToString(i.InstanceId)]] = i
|
||||
}
|
||||
}
|
||||
return !lastPage
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("describing instances: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -469,21 +438,21 @@ func (p *Provider) lookupMiInstances(ctx context.Context, client *awsClient, clu
|
||||
return miInstances, nil
|
||||
}
|
||||
|
||||
func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]*ecs.Task) (map[string]*ec2.Instance, error) {
|
||||
func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, clusterName *string, ecsDatas map[string]ecstypes.Task) (map[string]ec2types.Instance, error) {
|
||||
instanceIDs := make(map[string]string)
|
||||
ec2Instances := make(map[string]*ec2.Instance)
|
||||
ec2Instances := make(map[string]ec2types.Instance)
|
||||
|
||||
var containerInstancesArns []*string
|
||||
var instanceArns []*string
|
||||
var containerInstancesArns []string
|
||||
var instanceArns []string
|
||||
|
||||
for _, task := range ecsDatas {
|
||||
if task.ContainerInstanceArn != nil {
|
||||
containerInstancesArns = append(containerInstancesArns, task.ContainerInstanceArn)
|
||||
containerInstancesArns = append(containerInstancesArns, *task.ContainerInstanceArn)
|
||||
}
|
||||
}
|
||||
|
||||
for _, arns := range p.chunkIDs(containerInstancesArns) {
|
||||
resp, err := client.ecs.DescribeContainerInstancesWithContext(ctx, &ecs.DescribeContainerInstancesInput{
|
||||
for arns := range chunkIDs(containerInstancesArns) {
|
||||
resp, err := client.ecs.DescribeContainerInstances(ctx, &ecs.DescribeContainerInstancesInput{
|
||||
ContainerInstances: arns,
|
||||
Cluster: clusterName,
|
||||
})
|
||||
@@ -492,38 +461,38 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
|
||||
}
|
||||
|
||||
for _, container := range resp.ContainerInstances {
|
||||
instanceIDs[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn)
|
||||
instanceIDs[aws.ToString(container.Ec2InstanceId)] = aws.ToString(container.ContainerInstanceArn)
|
||||
// Disallow Instance IDs of the form mi-*
|
||||
// This prevents considering external instances in ECS Anywhere setups
|
||||
// and getting InvalidInstanceID.Malformed error when calling the describe-instances endpoint.
|
||||
if strings.HasPrefix(aws.StringValue(container.Ec2InstanceId), "mi-") {
|
||||
if strings.HasPrefix(aws.ToString(container.Ec2InstanceId), "mi-") {
|
||||
continue
|
||||
}
|
||||
|
||||
instanceArns = append(instanceArns, container.Ec2InstanceId)
|
||||
if container.Ec2InstanceId != nil {
|
||||
instanceArns = append(instanceArns, *container.Ec2InstanceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(instanceArns) > 0 {
|
||||
for _, ids := range p.chunkIDs(instanceArns) {
|
||||
for ids := range chunkIDs(instanceArns) {
|
||||
input := &ec2.DescribeInstancesInput{
|
||||
InstanceIds: ids,
|
||||
}
|
||||
|
||||
err := client.ec2.DescribeInstancesPagesWithContext(ctx, input, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
||||
if len(page.Reservations) > 0 {
|
||||
for _, r := range page.Reservations {
|
||||
for _, i := range r.Instances {
|
||||
if i.InstanceId != nil {
|
||||
ec2Instances[instanceIDs[aws.StringValue(i.InstanceId)]] = i
|
||||
}
|
||||
paginator := ec2.NewDescribeInstancesPaginator(client.ec2, input)
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("describing instances: %w", err)
|
||||
}
|
||||
for _, r := range page.Reservations {
|
||||
for _, i := range r.Instances {
|
||||
if i.InstanceId != nil {
|
||||
ec2Instances[instanceIDs[aws.ToString(i.InstanceId)]] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
return !lastPage
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("describing instances: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,16 +500,16 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
|
||||
return ec2Instances, nil
|
||||
}
|
||||
|
||||
func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient, taskDefArns map[string]*ecs.Task) (map[string]*ecs.TaskDefinition, error) {
|
||||
func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient, taskDefArns map[string]ecstypes.Task) (map[string]*ecstypes.TaskDefinition, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
taskDef := make(map[string]*ecs.TaskDefinition)
|
||||
taskDef := make(map[string]*ecstypes.TaskDefinition)
|
||||
|
||||
for arn, task := range taskDefArns {
|
||||
if definition, ok := existingTaskDefCache.Get(arn); ok {
|
||||
taskDef[arn] = definition.(*ecs.TaskDefinition)
|
||||
taskDef[arn] = definition.(*ecstypes.TaskDefinition)
|
||||
logger.Debugf("Found cached task definition for %s. Skipping the call", arn)
|
||||
} else {
|
||||
resp, err := client.ecs.DescribeTaskDefinitionWithContext(ctx, &ecs.DescribeTaskDefinitionInput{
|
||||
resp, err := client.ecs.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{
|
||||
TaskDefinition: task.TaskDefinitionArn,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -556,16 +525,6 @@ func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient,
|
||||
|
||||
// chunkIDs ECS expects no more than 100 parameters be passed to a API call;
|
||||
// thus, pack each string into an array capped at 100 elements.
|
||||
func (p *Provider) chunkIDs(ids []*string) [][]*string {
|
||||
var chunked [][]*string
|
||||
for i := 0; i < len(ids); i += 100 {
|
||||
var sliceEnd int
|
||||
if i+100 < len(ids) {
|
||||
sliceEnd = i + 100
|
||||
} else {
|
||||
sliceEnd = len(ids)
|
||||
}
|
||||
chunked = append(chunked, ids[i:sliceEnd])
|
||||
}
|
||||
return chunked
|
||||
func chunkIDs(ids []string) iter.Seq[[]string] {
|
||||
return slices.Chunk(ids, 100)
|
||||
}
|
||||
|
@@ -3,13 +3,10 @@ package ecs
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestChunkIDs(t *testing.T) {
|
||||
provider := &Provider{}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
count int
|
||||
@@ -71,13 +68,13 @@ func TestChunkIDs(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var IDs []*string
|
||||
var IDs []string
|
||||
for range test.count {
|
||||
IDs = append(IDs, aws.String("a"))
|
||||
IDs = append(IDs, "a")
|
||||
}
|
||||
|
||||
var outCount []int
|
||||
for _, el := range provider.chunkIDs(IDs) {
|
||||
for el := range chunkIDs(IDs) {
|
||||
outCount = append(outCount, len(el))
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user