mirror of
https://github.com/containous/traefik.git
synced 2025-09-27 05:44:22 +03:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
35a40c8727 | ||
|
7f62667569 | ||
|
fd4ba585ee | ||
|
e73dd31619 | ||
|
ef9b79f85c | ||
|
32d88a977d | ||
|
547c380961 | ||
|
4fc077a5d2 | ||
|
b386964abc | ||
|
319e3065f0 | ||
|
a48a8a97a1 | ||
|
8be434aaad | ||
|
d9fc775084 | ||
|
e12630ef06 | ||
|
bc5e621683 | ||
|
c2c4dc9b58 | ||
|
8d4620dc53 |
82
.github/workflows/build.yaml
vendored
Normal file
82
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Build Binaries
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.16
|
||||
CGO_ENABLED: 0
|
||||
PRE_TARGET: ""
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build webui
|
||||
run: |
|
||||
make generate-webui
|
||||
tar czvf webui.tar.gz ./static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-20.04, macos-latest, windows-latest ]
|
||||
needs:
|
||||
- build-webui
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
'%LocalAppData%\go-build'
|
||||
key: ${{ runner.os }}-build-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-build-go-
|
||||
|
||||
- name: Installing dependencies
|
||||
run: go install github.com/containous/go-bindata/go-bindata@v1.0.0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
- name: Untar webui
|
||||
run: tar xvf webui.tar.gz
|
||||
|
||||
- name: Build
|
||||
run: make binary
|
5
.github/workflows/check_doc.yml
vendored
5
.github/workflows/check_doc.yml
vendored
@@ -2,15 +2,16 @@ name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Check, verify and build documentation
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
10
.github/workflows/documentation.yml
vendored
10
.github/workflows/documentation.yml
vendored
@@ -6,18 +6,18 @@ on:
|
||||
- master
|
||||
- v*
|
||||
|
||||
env:
|
||||
STRUCTOR_VERSION: v1.11.2
|
||||
MIXTUS_VERSION: v0.4.1
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Doc Process
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'traefik/traefik'
|
||||
env:
|
||||
STRUCTOR_VERSION: v1.11.2
|
||||
MIXTUS_VERSION: v0.4.1
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
46
.github/workflows/test-unit.yaml
vendored
Normal file
46
.github/workflows/test-unit.yaml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Test Unit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.16
|
||||
PRE_TARGET: ""
|
||||
|
||||
jobs:
|
||||
|
||||
test-unit:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-test-unit-go-
|
||||
|
||||
- name: Installing dependencies
|
||||
run: go install github.com/containous/go-bindata/go-bindata@v1.0.0
|
||||
|
||||
- name: Tests
|
||||
run: make test-unit
|
100
.github/workflows/validate.yaml
vendored
Normal file
100
.github/workflows/validate.yaml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: Validate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.16
|
||||
GOLANGCI_LINT_VERSION: v1.41.1
|
||||
MISSSPELL_VERSION: v0.3.4
|
||||
PRE_TARGET: ""
|
||||
|
||||
jobs:
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-go-
|
||||
|
||||
- name: Installing dependencies
|
||||
run: go install github.com/containous/go-bindata/go-bindata@v1.0.0
|
||||
|
||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
|
||||
- name: Install missspell ${{ env.MISSSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
|
||||
|
||||
- name: Validate
|
||||
run: make validate
|
||||
|
||||
validate-generate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-generate-go-
|
||||
|
||||
- name: Installing dependencies
|
||||
run: go install github.com/containous/go-bindata/go-bindata@v1.0.0
|
||||
|
||||
- name: go generate
|
||||
run: |
|
||||
go generate
|
||||
git diff --exit-code
|
||||
|
||||
- name: go mod tidy
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code
|
||||
|
||||
- name: make generate-crd
|
||||
run: |
|
||||
make generate-crd
|
||||
git diff --exit-code
|
100
.semaphore/semaphore.yml
Normal file
100
.semaphore/semaphore.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
version: v1.0
|
||||
name: Traefik
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-4
|
||||
os_image: ubuntu1804
|
||||
|
||||
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.16
|
||||
- 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.41.1
|
||||
- curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- go install github.com/containous/go-bindata/go-bindata@v1.0.0
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
blocks:
|
||||
- name: Test Integration Container
|
||||
dependencies: []
|
||||
run:
|
||||
when: "branch =~ '.*' OR pull_request =~'.*'"
|
||||
task:
|
||||
jobs:
|
||||
- name: Test Integration Container
|
||||
commands:
|
||||
- make pull-images
|
||||
- mkdir -p static # Avoid to generate webui
|
||||
- PRE_TARGET="" make binary
|
||||
- make test-integration-container
|
||||
- df -h
|
||||
epilogue:
|
||||
always:
|
||||
commands:
|
||||
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
|
||||
|
||||
- name: Test Integration Host
|
||||
dependencies: []
|
||||
run:
|
||||
when: "branch =~ '.*' OR pull_request =~'.*'"
|
||||
task:
|
||||
env_vars:
|
||||
- name: PRE_TARGET
|
||||
value: ""
|
||||
jobs:
|
||||
- name: Test Integration Host
|
||||
commands:
|
||||
- mkdir -p static # Avoid to generate webui
|
||||
- make test-integration-host
|
||||
epilogue:
|
||||
always:
|
||||
commands:
|
||||
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
|
||||
|
||||
- name: Release
|
||||
dependencies: []
|
||||
run:
|
||||
when: "tag =~ '.*'"
|
||||
task:
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-8
|
||||
os_image: ubuntu1804
|
||||
secrets:
|
||||
- name: traefik
|
||||
env_vars:
|
||||
- name: GH_VERSION
|
||||
value: 1.12.1
|
||||
- name: CODENAME
|
||||
value: "livarot"
|
||||
- name: PRE_TARGET
|
||||
value: ""
|
||||
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
|
||||
jobs:
|
||||
- name: Release
|
||||
commands:
|
||||
- make release-packages
|
||||
- gh release create ${SEMAPHORE_GIT_TAG_NAME} ./dist/traefik*.* --repo traefik/traefik --title ${SEMAPHORE_GIT_TAG_NAME} --notes ${SEMAPHORE_GIT_TAG_NAME}
|
||||
- ./script/deploy.sh
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
sudo rm -rf static
|
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
curl -O https://dl.google.com/go/go"${GO_VERSION}".linux-amd64.tar.gz
|
||||
|
||||
tar -xvf go"${GO_VERSION}".linux-amd64.tar.gz
|
||||
rm -rf go"${GO_VERSION}".linux-amd64.tar.gz
|
||||
|
||||
sudo mkdir -p /usr/local/golang/"${GO_VERSION}"/go
|
||||
sudo mv go /usr/local/golang/"${GO_VERSION}"/
|
||||
|
||||
sudo rm /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/"${GO_VERSION}"/go/bin/go
|
||||
sudo ln -s /usr/local/golang/"${GO_VERSION}"/go/bin/go /usr/local/bin/go
|
||||
|
||||
export GOROOT="/usr/local/golang/${GO_VERSION}/go"
|
||||
export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/go/pkg/tool/linux_amd64"
|
||||
|
||||
go version
|
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make pull-images; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-integration; fi
|
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ci_retry make validate
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-unit; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then make -j"${N_MAKE_JOBS}" crossbinary-default-parallel; fi
|
@@ -1,35 +0,0 @@
|
||||
# For personnal CI
|
||||
# mv /home/runner/workspace/src/github.com/<username>/ /home/runner/workspace/src/github.com/traefik/
|
||||
# cd /home/runner/workspace/src/github.com/traefik/traefik/
|
||||
for s in apache2 cassandra elasticsearch memcached mysql mongod postgresql sphinxsearch rethinkdb rabbitmq-server redis-server; do sudo service $s stop; done
|
||||
sudo swapoff -a
|
||||
sudo dd if=/dev/zero of=/swapfile bs=1M count=3072
|
||||
sudo mkswap /swapfile
|
||||
sudo swapon /swapfile
|
||||
sudo rm -rf /home/runner/.rbenv
|
||||
sudo rm -rf /usr/local/golang/{1.4.3,1.5.4,1.6.4,1.7.6,1.8.6,1.9.7,1.10.3,1.11}
|
||||
#export DOCKER_VERSION=18.06.3
|
||||
source .semaphoreci/vars
|
||||
if [ -z "${PULL_REQUEST_NUMBER}" ]; then SHOULD_TEST="-*-"; else TEMP_STORAGE=$(curl --silent https://patch-diff.githubusercontent.com/raw/traefik/traefik/pull/${PULL_REQUEST_NUMBER}.diff | patch --dry-run -p1 -R || true); fi
|
||||
echo ${SHOULD_TEST}
|
||||
if [ -n "$TEMP_STORAGE" ]; then SHOULD_TEST=$(echo "$TEMP_STORAGE" | grep -Ev '(.md|.yaml|.yml)' || :); fi
|
||||
echo ${TEMP_STORAGE}
|
||||
echo ${SHOULD_TEST}
|
||||
#if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq update; fi
|
||||
#if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*; fi
|
||||
if [ -n "$SHOULD_TEST" ]; then docker version; fi
|
||||
export GO_VERSION=1.13
|
||||
if [ -f "./go.mod" ]; then GO_VERSION="$(grep '^go .*' go.mod | awk '{print $2}')"; export GO_VERSION; fi
|
||||
#if [ "${GO_VERSION}" == '1.15' ]; then export GO_VERSION=1.15rc2; fi
|
||||
echo "Selected Go version: ${GO_VERSION}"
|
||||
|
||||
if [ -f "./.semaphoreci/golang.sh" ]; then ./.semaphoreci/golang.sh; fi
|
||||
if [ -f "./.semaphoreci/golang.sh" ]; then export GOROOT="/usr/local/golang/${GO_VERSION}/go"; fi
|
||||
if [ -f "./.semaphoreci/golang.sh" ]; then export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/go/pkg/tool/linux_amd64"; fi
|
||||
go version
|
||||
|
||||
if [ -f "./go.mod" ]; then export GO111MODULE=on; fi
|
||||
if [ -f "./go.mod" ]; then export GOPROXY=https://proxy.golang.org; fi
|
||||
if [ -f "./go.mod" ]; then go mod download; fi
|
||||
|
||||
df
|
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
export REPO='traefik/traefik'
|
||||
|
||||
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
|
||||
then
|
||||
export VERSION
|
||||
else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=livarot
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
|
||||
function ci_retry {
|
||||
|
||||
local NRETRY=3
|
||||
local NSLEEP=5
|
||||
local n=0
|
||||
|
||||
until [ $n -ge $NRETRY ]
|
||||
do
|
||||
"$@" && break
|
||||
n=$((n+1))
|
||||
echo "${*} failed, attempt ${n}/${NRETRY}"
|
||||
sleep $NSLEEP
|
||||
done
|
||||
|
||||
[ $n -lt $NRETRY ]
|
||||
|
||||
}
|
||||
|
||||
export -f ci_retry
|
49
.travis.yml
49
.travis.yml
@@ -1,49 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
global:
|
||||
- REPO=$TRAVIS_REPO_SLUG
|
||||
- VERSION=$TRAVIS_TAG
|
||||
- CODENAME=livarot
|
||||
- GO111MODULE=on
|
||||
|
||||
script:
|
||||
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
|
||||
|
||||
before_deploy:
|
||||
- >
|
||||
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
|
||||
export BEFORE_DEPLOY_RUN=1;
|
||||
sudo -E apt-get -yq update;
|
||||
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
|
||||
docker version;
|
||||
echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin;
|
||||
make build-image;
|
||||
if [ "$TRAVIS_TAG" ]; then
|
||||
make release-packages;
|
||||
fi;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
- provider: releases
|
||||
api_key: ${GITHUB_TOKEN}
|
||||
file: dist/traefik*
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
on:
|
||||
repo: traefik/traefik
|
||||
tags: true
|
||||
- provider: script
|
||||
script: sh script/deploy.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: traefik/traefik
|
||||
tags: true
|
||||
|
Binary file not shown.
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,3 +1,29 @@
|
||||
## [v2.4.14](https://github.com/traefik/traefik/tree/v2.4.14) (2021-08-16)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.13...v2.4.14)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s/crd,k8s]** Avoid unauthorized middleware cross namespace reference ([#8322](https://github.com/traefik/traefik/pull/8322) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[kv]** Remove unwanted trailing slash in key ([#8335](https://github.com/traefik/traefik/pull/8335) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Redirect: fix comparison when explicit port request and implicit redirect port ([#8348](https://github.com/traefik/traefik/pull/8348) by [tcolgate](https://github.com/tcolgate))
|
||||
|
||||
**Documentation:**
|
||||
- **[kv]** Fix a router's entryPoint definition example for KV provider ([#8357](https://github.com/traefik/traefik/pull/8357) by [avtion](https://github.com/avtion))
|
||||
|
||||
## [v2.4.13](https://github.com/traefik/traefik/tree/v2.4.13) (2021-07-30)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.12...v2.4.13)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[authentication,middleware]** Remove hop-by-hop headers define in connection header beore some middleware ([#8319](https://github.com/traefik/traefik/pull/8319) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.4.12](https://github.com/traefik/traefik/tree/v2.4.12) (2021-07-26)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.11...v2.4.12)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s,k8s/ingress]** Get Kubernetes server version early ([#8286](https://github.com/traefik/traefik/pull/8286) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s,k8s/ingress]** Don't remove ingress config on API call failure ([#8185](https://github.com/traefik/traefik/pull/8185) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Ratelimiter: use correct ttlSeconds value, and always call Set ([#8254](https://github.com/traefik/traefik/pull/8254) by [mpl](https://github.com/mpl))
|
||||
- **[tls]** Check if defaultcertificate is defined in store ([#8274](https://github.com/traefik/traefik/pull/8274) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
## [v2.4.11](https://github.com/traefik/traefik/tree/v2.4.11) (2021-07-15)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.9...v2.4.11)
|
||||
|
||||
|
24
Makefile
24
Makefile
@@ -58,8 +58,9 @@ build-webui-image:
|
||||
docker build -t traefik-webui --build-arg ARG_PLATFORM_URL=$(PLATFORM_URL) -f webui/Dockerfile webui
|
||||
|
||||
## Generate WebUI
|
||||
generate-webui: build-webui-image
|
||||
generate-webui:
|
||||
if [ ! -d "static" ]; then \
|
||||
$(MAKE) build-webui-image; \
|
||||
mkdir -p static; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build:nc; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ../static; \
|
||||
@@ -92,17 +93,16 @@ pull-images:
|
||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
||||
|
||||
## Run the integration tests
|
||||
test-integration: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
|
||||
test-integration: $(PRE_TARGET) binary
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
## Run the container integration tests
|
||||
test-integration-container: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
|
||||
test-integration-container: $(PRE_TARGET) binary
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh test-integration
|
||||
|
||||
## Run the host integration tests
|
||||
test-integration-host: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary
|
||||
test-integration-host: $(PRE_TARGET) binary
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
## Validate code and docs
|
||||
@@ -142,20 +142,20 @@ docs-pull-images:
|
||||
|
||||
## Generate CRD clientset
|
||||
generate-crd:
|
||||
./script/update-generated-crd-code.sh
|
||||
@$(CURDIR)/script/code-gen.sh
|
||||
|
||||
## Create packages for the release
|
||||
release-packages: generate-webui build-dev-image
|
||||
release-packages: generate-webui $(PRE_TARGET)
|
||||
rm -rf dist
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish --timeout="60m"
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish --timeout="60m"
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--exclude .travis \
|
||||
--exclude .semaphoreci \
|
||||
--exclude .github \
|
||||
--exclude dist .
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) chown -R $(shell id -u):$(shell id -g) dist/
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK_NOTTY)) chown -R $(shell id -u):$(shell id -g) dist/
|
||||
|
||||
## Format the Code
|
||||
fmt:
|
||||
|
@@ -28,8 +28,8 @@ A Story of key & values
|
||||
|
||||
| Key (Path) | Value |
|
||||
|-----------------------------------------------|-------------|
|
||||
| `traefik.http.routers.myrouter.entrypoints/0` | `web` |
|
||||
| `traefik.http.routers.myrouter.entrypoints/1` | `websecure` |
|
||||
| `traefik/http/routers/myrouter/entrypoints/0` | `web` |
|
||||
| `traefik/http/routers/myrouter/entrypoints/1` | `websecure` |
|
||||
|
||||
??? info "`traefik/http/routers/<router_name>/middlewares`"
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
mkdocs==1.1
|
||||
mkdocs==1.2.2
|
||||
pymdown-extensions==7.0
|
||||
mkdocs-bootswatch==1.0
|
||||
mkdocs-traefiklabs>=100.0.7
|
||||
|
@@ -1,5 +1,5 @@
|
||||
server:
|
||||
image: rancher/k3s:v1.17.2-k3s1
|
||||
image: rancher/k3s:v1.18.20-k3s1
|
||||
command: server --disable-agent --no-deploy coredns --no-deploy servicelb --no-deploy traefik --no-deploy local-storage --no-deploy metrics-server --log /output/k3s.log
|
||||
environment:
|
||||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||
@@ -12,7 +12,7 @@ server:
|
||||
- 6443:6443
|
||||
|
||||
node:
|
||||
image: rancher/k3s:v1.17.2-k3s1
|
||||
image: rancher/k3s:v1.18.20-k3s1
|
||||
privileged: true
|
||||
links:
|
||||
- server
|
||||
|
@@ -24,7 +24,7 @@ func DecodeToNode(pairs []*store.KVPair, rootName string, filters ...string) (*p
|
||||
return nil, fmt.Errorf("invalid label root %s", rootName)
|
||||
}
|
||||
|
||||
split := strings.Split(pair.Key[len(rootName)+1:], "/")
|
||||
split := strings.FieldsFunc(pair.Key[len(rootName)+1:], func(c rune) bool { return c == '/' })
|
||||
|
||||
parts := []string{rootName}
|
||||
for _, fragment := range split {
|
||||
|
@@ -28,6 +28,7 @@ func TestDecode(t *testing.T) {
|
||||
"traefik/fieldf/Test2": "B",
|
||||
"traefik/fieldg/0/name": "A",
|
||||
"traefik/fieldg/1/name": "B",
|
||||
"traefik/fieldh/": "foo",
|
||||
},
|
||||
expected: &sample{
|
||||
FieldA: "bar",
|
||||
@@ -45,6 +46,7 @@ func TestDecode(t *testing.T) {
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
},
|
||||
FieldH: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -61,6 +63,7 @@ func TestDecode(t *testing.T) {
|
||||
"foo/bar/traefik/fieldf/Test2": "B",
|
||||
"foo/bar/traefik/fieldg/0/name": "A",
|
||||
"foo/bar/traefik/fieldg/1/name": "B",
|
||||
"foo/bar/traefik/fieldh/": "foo",
|
||||
},
|
||||
expected: &sample{
|
||||
FieldA: "bar",
|
||||
@@ -78,6 +81,7 @@ func TestDecode(t *testing.T) {
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
},
|
||||
FieldH: "foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -107,6 +111,7 @@ type sample struct {
|
||||
} `label:"allowEmpty"`
|
||||
FieldF map[string]string
|
||||
FieldG []sub
|
||||
FieldH string
|
||||
}
|
||||
|
||||
type sub struct {
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
@@ -89,7 +90,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
|
||||
fa.authResponseHeadersRegex = re
|
||||
}
|
||||
|
||||
return fa, nil
|
||||
return connectionheader.Remover(fa), nil
|
||||
}
|
||||
|
||||
func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||
|
46
pkg/middlewares/connectionheader/connectionheader.go
Normal file
46
pkg/middlewares/connectionheader/connectionheader.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package connectionheader
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionHeader = "Connection"
|
||||
upgradeHeader = "Upgrade"
|
||||
)
|
||||
|
||||
// Remover removes hop-by-hop headers listed in the "Connection" header.
|
||||
// See RFC 7230, section 6.1.
|
||||
func Remover(next http.Handler) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
var reqUpType string
|
||||
if httpguts.HeaderValuesContainsToken(req.Header[connectionHeader], upgradeHeader) {
|
||||
reqUpType = req.Header.Get(upgradeHeader)
|
||||
}
|
||||
|
||||
removeConnectionHeaders(req.Header)
|
||||
|
||||
if reqUpType != "" {
|
||||
req.Header.Set(connectionHeader, upgradeHeader)
|
||||
req.Header.Set(upgradeHeader, reqUpType)
|
||||
} else {
|
||||
req.Header.Del(connectionHeader)
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func removeConnectionHeaders(h http.Header) {
|
||||
for _, f := range h[connectionHeader] {
|
||||
for _, sf := range strings.Split(f, ",") {
|
||||
if sf = textproto.TrimString(sf); sf != "" {
|
||||
h.Del(sf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
pkg/middlewares/connectionheader/connectionheader_test.go
Normal file
71
pkg/middlewares/connectionheader/connectionheader_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package connectionheader
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRemover(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
reqHeaders map[string]string
|
||||
expected http.Header
|
||||
}{
|
||||
{
|
||||
desc: "simple remove",
|
||||
reqHeaders: map[string]string{
|
||||
"Foo": "bar",
|
||||
connectionHeader: "foo",
|
||||
},
|
||||
expected: http.Header{},
|
||||
},
|
||||
{
|
||||
desc: "remove and Upgrade",
|
||||
reqHeaders: map[string]string{
|
||||
upgradeHeader: "test",
|
||||
"Foo": "bar",
|
||||
connectionHeader: "Upgrade,foo",
|
||||
},
|
||||
expected: http.Header{
|
||||
upgradeHeader: []string{"test"},
|
||||
connectionHeader: []string{"Upgrade"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no remove",
|
||||
reqHeaders: map[string]string{
|
||||
"Foo": "bar",
|
||||
connectionHeader: "fii",
|
||||
},
|
||||
expected: http.Header{
|
||||
"Foo": []string{"bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})
|
||||
|
||||
h := Remover(next)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://localhost", nil)
|
||||
|
||||
for k, v := range test.reqHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
h.ServeHTTP(rw, req)
|
||||
|
||||
assert.Equal(t, test.expected, req.Header)
|
||||
})
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
)
|
||||
|
||||
@@ -58,11 +59,12 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin
|
||||
|
||||
if hasCustomHeaders || hasCorsHeaders {
|
||||
logger.Debugf("Setting up customHeaders/Cors from %v", cfg)
|
||||
var err error
|
||||
handler, err = NewHeader(nextHandler, cfg)
|
||||
h, err := NewHeader(nextHandler, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handler = connectionheader.Remover(h)
|
||||
}
|
||||
|
||||
return &headers{
|
||||
|
@@ -30,7 +30,12 @@ type rateLimiter struct {
|
||||
burst int64
|
||||
// maxDelay is the maximum duration we're willing to wait for a bucket reservation to become effective, in nanoseconds.
|
||||
// For now it is somewhat arbitrarily set to 1/(2*rate).
|
||||
maxDelay time.Duration
|
||||
maxDelay time.Duration
|
||||
// each rate limiter for a given source is stored in the buckets ttlmap.
|
||||
// To keep this ttlmap constrained in size,
|
||||
// each ratelimiter is "garbage collected" when it is considered expired.
|
||||
// It is considered expired after it hasn't been used for ttl seconds.
|
||||
ttl int
|
||||
sourceMatcher utils.SourceExtractor
|
||||
next http.Handler
|
||||
|
||||
@@ -66,12 +71,15 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
|
||||
}
|
||||
|
||||
period := time.Duration(config.Period)
|
||||
if period < 0 {
|
||||
return nil, fmt.Errorf("negative value not valid for period: %v", period)
|
||||
}
|
||||
if period == 0 {
|
||||
period = time.Second
|
||||
}
|
||||
|
||||
// Logically, we should set maxDelay to infinity when config.Average == 0 (because it means no rate limiting),
|
||||
// but since the reservation will give us a delay = 0 anyway in this case, we're good even with any maxDelay >= 0.
|
||||
// if config.Average == 0, in that case,
|
||||
// the value of maxDelay does not matter since the reservation will (buggily) give us a delay of 0 anyway.
|
||||
var maxDelay time.Duration
|
||||
var rtl float64
|
||||
if config.Average > 0 {
|
||||
@@ -86,6 +94,17 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
|
||||
}
|
||||
}
|
||||
|
||||
// Make the ttl inversely proportional to how often a rate limiter is supposed to see any activity (when maxed out),
|
||||
// for low rate limiters.
|
||||
// Otherwise just make it a second for all the high rate limiters.
|
||||
// Add an extra second in both cases for continuity between the two cases.
|
||||
ttl := 1
|
||||
if rtl >= 1 {
|
||||
ttl++
|
||||
} else if rtl > 0 {
|
||||
ttl += int(1 / rtl)
|
||||
}
|
||||
|
||||
return &rateLimiter{
|
||||
name: name,
|
||||
rate: rate.Limit(rtl),
|
||||
@@ -94,6 +113,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
|
||||
next: next,
|
||||
sourceMatcher: sourceMatcher,
|
||||
buckets: buckets,
|
||||
ttl: ttl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -121,13 +141,21 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
bucket = rlSource.(*rate.Limiter)
|
||||
} else {
|
||||
bucket = rate.NewLimiter(rl.rate, int(rl.burst))
|
||||
if err := rl.buckets.Set(source, bucket, int(rl.maxDelay)*10+1); err != nil {
|
||||
logger.Errorf("could not insert bucket: %v", err)
|
||||
http.Error(w, "could not insert bucket", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// We Set even in the case where the source already exists,
|
||||
// because we want to update the expiryTime everytime we get the source,
|
||||
// as the expiryTime is supposed to reflect the activity (or lack thereof) on that source.
|
||||
if err := rl.buckets.Set(source, bucket, rl.ttl); err != nil {
|
||||
logger.Errorf("could not insert/update bucket: %v", err)
|
||||
http.Error(w, "could not insert/update bucket", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// time/rate is bugged, since a rate.Limiter with a 0 Limit not only allows a Reservation to take place,
|
||||
// but also gives a 0 delay below (because of a division by zero, followed by a multiplication that flips into the negatives),
|
||||
// regardless of the current load.
|
||||
// However, for now we take advantage of this behavior to provide the no-limit ratelimiter when config.Average is 0.
|
||||
res := bucket.Reserve()
|
||||
if !res.OK() {
|
||||
http.Error(w, "No bursty traffic allowed", http.StatusTooManyRequests)
|
||||
|
@@ -4,13 +4,17 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
schemeHTTP = "http"
|
||||
schemeHTTPS = "https"
|
||||
)
|
||||
|
||||
type redirect struct {
|
||||
next http.Handler
|
||||
regex *regexp.Regexp
|
||||
@@ -18,10 +22,11 @@ type redirect struct {
|
||||
permanent bool
|
||||
errHandler utils.ErrorHandler
|
||||
name string
|
||||
rawURL func(*http.Request) string
|
||||
}
|
||||
|
||||
// New creates a Redirect middleware.
|
||||
func newRedirect(next http.Handler, regex, replacement string, permanent bool, name string) (http.Handler, error) {
|
||||
func newRedirect(next http.Handler, regex, replacement string, permanent bool, rawURL func(*http.Request) string, name string) (http.Handler, error) {
|
||||
re, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -34,6 +39,7 @@ func newRedirect(next http.Handler, regex, replacement string, permanent bool, n
|
||||
errHandler: utils.DefaultHandler,
|
||||
next: next,
|
||||
name: name,
|
||||
rawURL: rawURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -42,7 +48,7 @@ func (r *redirect) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||
}
|
||||
|
||||
func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
oldURL := rawURL(req)
|
||||
oldURL := r.rawURL(req)
|
||||
|
||||
// If the Regexp doesn't match, skip to the next handler.
|
||||
if !r.regex.MatchString(oldURL) {
|
||||
@@ -98,33 +104,3 @@ func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func rawURL(req *http.Request) string {
|
||||
scheme := "http"
|
||||
host := req.Host
|
||||
port := ""
|
||||
uri := req.RequestURI
|
||||
|
||||
schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$`
|
||||
re, _ := regexp.Compile(schemeRegex)
|
||||
if re.Match([]byte(req.RequestURI)) {
|
||||
match := re.FindStringSubmatch(req.RequestURI)
|
||||
scheme = match[1]
|
||||
|
||||
if len(match[2]) > 0 {
|
||||
host = match[2]
|
||||
}
|
||||
|
||||
if len(match[3]) > 0 {
|
||||
port = match[3]
|
||||
}
|
||||
|
||||
uri = match[4]
|
||||
}
|
||||
|
||||
if req.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
return strings.Join([]string{scheme, "://", host, port, uri}, "")
|
||||
}
|
||||
|
@@ -3,6 +3,8 @@ package redirect
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
@@ -19,5 +21,35 @@ func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.Redir
|
||||
logger.Debug("Creating middleware")
|
||||
logger.Debugf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement)
|
||||
|
||||
return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, name)
|
||||
return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, rawURL, name)
|
||||
}
|
||||
|
||||
func rawURL(req *http.Request) string {
|
||||
scheme := schemeHTTP
|
||||
host := req.Host
|
||||
port := ""
|
||||
uri := req.RequestURI
|
||||
|
||||
schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$`
|
||||
re, _ := regexp.Compile(schemeRegex)
|
||||
if re.Match([]byte(req.RequestURI)) {
|
||||
match := re.FindStringSubmatch(req.RequestURI)
|
||||
scheme = match[1]
|
||||
|
||||
if len(match[2]) > 0 {
|
||||
host = match[2]
|
||||
}
|
||||
|
||||
if len(match[3]) > 0 {
|
||||
port = match[3]
|
||||
}
|
||||
|
||||
uri = match[4]
|
||||
}
|
||||
|
||||
if req.TLS != nil {
|
||||
scheme = schemeHTTPS
|
||||
}
|
||||
|
||||
return strings.Join([]string{scheme, "://", host, port, uri}, "")
|
||||
}
|
||||
|
@@ -3,7 +3,10 @@ package redirect
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
@@ -26,9 +29,47 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi
|
||||
}
|
||||
|
||||
port := ""
|
||||
if len(conf.Port) > 0 && !(conf.Scheme == "http" && conf.Port == "80" || conf.Scheme == "https" && conf.Port == "443") {
|
||||
if len(conf.Port) > 0 && !(conf.Scheme == schemeHTTP && conf.Port == "80" || conf.Scheme == schemeHTTPS && conf.Port == "443") {
|
||||
port = ":" + conf.Port
|
||||
}
|
||||
|
||||
return newRedirect(next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, name)
|
||||
return newRedirect(next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, rawURLScheme, name)
|
||||
}
|
||||
|
||||
func rawURLScheme(req *http.Request) string {
|
||||
scheme := schemeHTTP
|
||||
host, port, err := net.SplitHostPort(req.Host)
|
||||
if err != nil {
|
||||
host = req.Host
|
||||
} else {
|
||||
port = ":" + port
|
||||
}
|
||||
uri := req.RequestURI
|
||||
|
||||
schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$`
|
||||
re, _ := regexp.Compile(schemeRegex)
|
||||
if re.Match([]byte(req.RequestURI)) {
|
||||
match := re.FindStringSubmatch(req.RequestURI)
|
||||
scheme = match[1]
|
||||
|
||||
if len(match[2]) > 0 {
|
||||
host = match[2]
|
||||
}
|
||||
|
||||
if len(match[3]) > 0 {
|
||||
port = match[3]
|
||||
}
|
||||
|
||||
uri = match[4]
|
||||
}
|
||||
|
||||
if req.TLS != nil {
|
||||
scheme = schemeHTTPS
|
||||
}
|
||||
|
||||
if scheme == schemeHTTP && port == ":80" || scheme == schemeHTTPS && port == ":443" || port == "" {
|
||||
port = ""
|
||||
}
|
||||
|
||||
return strings.Join([]string{scheme, "://", host, port, uri}, "")
|
||||
}
|
||||
|
@@ -127,8 +127,18 @@ func TestRedirectSchemeHandler(t *testing.T) {
|
||||
Port: "80",
|
||||
},
|
||||
url: "http://foo:80",
|
||||
expectedURL: "http://foo",
|
||||
expectedStatus: http.StatusFound,
|
||||
expectedURL: "http://foo:80",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "to HTTPS 443",
|
||||
config: dynamic.RedirectScheme{
|
||||
Scheme: "https",
|
||||
Port: "443",
|
||||
},
|
||||
url: "https://foo:443",
|
||||
expectedURL: "https://foo:443",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "HTTP to wss",
|
||||
@@ -248,6 +258,7 @@ func TestRedirectSchemeHandler(t *testing.T) {
|
||||
if test.method != "" {
|
||||
method = test.method
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(method, test.url, nil)
|
||||
|
||||
for k, v := range test.headers {
|
||||
|
@@ -191,6 +191,10 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
|
||||
// TLS stores
|
||||
if len(configuration.TLS.Stores) > 0 {
|
||||
for name, store := range configuration.TLS.Stores {
|
||||
if store.DefaultCertificate == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := store.DefaultCertificate.CertFile.Read()
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Error(err)
|
||||
|
@@ -262,6 +262,13 @@ func getTestCases() []ProvideTestCase {
|
||||
expectedNumRouter: 20,
|
||||
expectedNumService: 20,
|
||||
},
|
||||
{
|
||||
desc: "simple file with empty store yaml",
|
||||
filePath: "./fixtures/yaml/simple_empty_store.yml",
|
||||
expectedNumRouter: 0,
|
||||
expectedNumService: 0,
|
||||
expectedNumTLSConf: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
3
pkg/provider/file/fixtures/yaml/simple_empty_store.yml
Normal file
3
pkg/provider/file/fixtures/yaml/simple_empty_store.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
tls:
|
||||
stores:
|
||||
default: {}
|
@@ -28,6 +28,15 @@ spec:
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: test-errorpage
|
||||
- match: Host(`foo.com`) && PathPrefix(`/bur`)
|
||||
kind: Rule
|
||||
priority: 12
|
||||
services:
|
||||
- name: whoami
|
||||
namespace: default
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: cross-ns-stripprefix@kubernetescrd
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
|
@@ -146,13 +146,23 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
|
||||
var mds []string
|
||||
|
||||
for _, mi := range middlewares {
|
||||
if strings.Contains(mi.Name, providerNamespaceSeparator) {
|
||||
name := mi.Name
|
||||
|
||||
if !p.AllowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) {
|
||||
// Since we are not able to know if another namespace is in the name (namespace-name@kubernetescrd),
|
||||
// if the provider namespace kubernetescrd is used,
|
||||
// we don't allow this format to avoid cross namespace references.
|
||||
return nil, fmt.Errorf("invalid reference to middleware %s: with crossnamespace disallowed, the namespace field needs to be explicitly specified", mi.Name)
|
||||
}
|
||||
|
||||
if strings.Contains(name, providerNamespaceSeparator) {
|
||||
if len(mi.Namespace) > 0 {
|
||||
log.FromContext(ctx).
|
||||
WithField(log.MiddlewareName, mi.Name).
|
||||
Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
|
||||
}
|
||||
mds = append(mds, mi.Name)
|
||||
|
||||
mds = append(mds, name)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -165,7 +175,7 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
|
||||
ns = mi.Namespace
|
||||
}
|
||||
|
||||
mds = append(mds, makeID(ns, mi.Name))
|
||||
mds = append(mds, makeID(ns, name))
|
||||
}
|
||||
|
||||
return mds, nil
|
||||
|
@@ -3986,6 +3986,13 @@ func TestCrossNamespace(t *testing.T) {
|
||||
Priority: 12,
|
||||
Middlewares: []string{"default-test-errorpage"},
|
||||
},
|
||||
"default-test-crossnamespace-route-a1963878aac7331b7950": {
|
||||
EntryPoints: []string{"foo"},
|
||||
Service: "default-test-crossnamespace-route-a1963878aac7331b7950",
|
||||
Rule: "Host(`foo.com`) && PathPrefix(`/bur`)",
|
||||
Priority: 12,
|
||||
Middlewares: []string{"cross-ns-stripprefix@kubernetescrd"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"cross-ns-stripprefix": {
|
||||
@@ -4042,6 +4049,19 @@ func TestCrossNamespace(t *testing.T) {
|
||||
PassHostHeader: Bool(true),
|
||||
},
|
||||
},
|
||||
"default-test-crossnamespace-route-a1963878aac7331b7950": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
|
@@ -60,7 +60,7 @@ type Client interface {
|
||||
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
||||
GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error)
|
||||
UpdateIngressStatus(ing *networkingv1beta1.Ingress, ingStatus []corev1.LoadBalancerIngress) error
|
||||
GetServerVersion() (*version.Version, error)
|
||||
GetServerVersion() *version.Version
|
||||
}
|
||||
|
||||
type clientWrapper struct {
|
||||
@@ -72,6 +72,7 @@ type clientWrapper struct {
|
||||
ingressLabelSelector string
|
||||
isNamespaceAll bool
|
||||
watchedNamespaces []string
|
||||
serverVersion *version.Version
|
||||
}
|
||||
|
||||
// newInClusterClient returns a new Provider client that is expected to run
|
||||
@@ -149,6 +150,19 @@ func newClientImpl(clientset kubernetes.Interface) *clientWrapper {
|
||||
|
||||
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
||||
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||
// Get and store the serverVersion for future use.
|
||||
serverVersionInfo, err := c.clientset.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve server version: %w", err)
|
||||
}
|
||||
|
||||
serverVersion, err := version.NewVersion(serverVersionInfo.GitVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse server version: %w", err)
|
||||
}
|
||||
|
||||
c.serverVersion = serverVersion
|
||||
|
||||
eventCh := make(chan interface{}, 1)
|
||||
eventHandler := &resourceEventHandler{eventCh}
|
||||
|
||||
@@ -208,12 +222,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||
}
|
||||
}
|
||||
|
||||
serverVersion, err := c.GetServerVersion()
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Failed to get server version: %v", err)
|
||||
return eventCh, nil
|
||||
}
|
||||
|
||||
if supportsIngressClass(serverVersion) {
|
||||
c.clusterFactory = informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
|
||||
c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler)
|
||||
@@ -426,13 +434,8 @@ func (c *clientWrapper) lookupNamespace(ns string) string {
|
||||
}
|
||||
|
||||
// GetServerVersion returns the cluster server version, or an error.
|
||||
func (c *clientWrapper) GetServerVersion() (*version.Version, error) {
|
||||
serverVersion, err := c.clientset.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve server version: %w", err)
|
||||
}
|
||||
|
||||
return version.NewVersion(serverVersion.GitVersion)
|
||||
func (c *clientWrapper) GetServerVersion() *version.Version {
|
||||
return c.serverVersion
|
||||
}
|
||||
|
||||
// eventHandlerFunc will pass the obj on to the events channel or drop it.
|
||||
|
@@ -73,8 +73,8 @@ func (c clientMock) GetIngresses() []*networkingv1beta1.Ingress {
|
||||
return c.ingresses
|
||||
}
|
||||
|
||||
func (c clientMock) GetServerVersion() (*version.Version, error) {
|
||||
return c.serverVersion, nil
|
||||
func (c clientMock) GetServerVersion() *version.Version {
|
||||
return c.serverVersion
|
||||
}
|
||||
|
||||
func (c clientMock) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
||||
|
@@ -11,6 +11,8 @@ import (
|
||||
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
fakediscovery "k8s.io/client-go/discovery/fake"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
@@ -149,6 +151,15 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
|
||||
|
||||
fakeDiscovery, ok := kubeClient.Discovery().(*fakediscovery.FakeDiscovery)
|
||||
if !ok {
|
||||
t.Fatalf("couldn't convert Discovery() to *FakeDiscovery")
|
||||
}
|
||||
|
||||
fakeDiscovery.FakedServerVersion = &version.Info{
|
||||
GitVersion: "1.17.0",
|
||||
}
|
||||
|
||||
client := newClientImpl(kubeClient)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
|
@@ -189,11 +189,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
TCP: &dynamic.TCPConfiguration{},
|
||||
}
|
||||
|
||||
serverVersion, err := client.GetServerVersion()
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Errorf("Failed to get server version: %v", err)
|
||||
return conf
|
||||
}
|
||||
serverVersion := client.GetServerVersion()
|
||||
|
||||
var ingressClasses []*networkingv1beta1.IngressClass
|
||||
|
||||
|
24
script/code-gen.sh
Executable file
24
script/code-gen.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
PROJECT_MODULE="github.com/traefik/traefik"
|
||||
MODULE_VERSION="v2"
|
||||
IMAGE_NAME="kubernetes-codegen:latest"
|
||||
|
||||
echo "Building codegen Docker image..."
|
||||
docker build --build-arg KUBE_VERSION=v0.20.2 --build-arg USER=$USER --build-arg UID=$(id -u) --build-arg GID=$(id -g) -f "./script/codegen.Dockerfile" \
|
||||
-t "${IMAGE_NAME}" \
|
||||
"."
|
||||
|
||||
cmd="/go/src/k8s.io/code-generator/generate-groups.sh all ${PROJECT_MODULE}/${MODULE_VERSION}/pkg/provider/kubernetes/crd/generated ${PROJECT_MODULE}/${MODULE_VERSION}/pkg/provider/kubernetes/crd traefik:v1alpha1 --go-header-file=/go/src/${PROJECT_MODULE}/script/boilerplate.go.tmpl"
|
||||
|
||||
echo "Generating Traefik clientSet code ..."
|
||||
echo $(pwd)
|
||||
docker run --rm \
|
||||
-v "$(pwd):/go/src/${PROJECT_MODULE}" \
|
||||
-w "/go/src/${PROJECT_MODULE}" \
|
||||
"${IMAGE_NAME}" $cmd
|
||||
|
||||
cp -r $(pwd)/${MODULE_VERSION}/* $(pwd)
|
||||
rm -rf $(pwd)/${MODULE_VERSION}
|
19
script/codegen.Dockerfile
Normal file
19
script/codegen.Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM golang:1.16
|
||||
|
||||
ARG USER=$USER
|
||||
ARG UID=$UID
|
||||
ARG GID=$GID
|
||||
RUN useradd -m ${USER} --uid=${UID} && echo "${USER}:" chpasswd
|
||||
USER ${UID}:${GID}
|
||||
|
||||
ARG KUBE_VERSION
|
||||
|
||||
RUN go get k8s.io/code-generator@$KUBE_VERSION; exit 0
|
||||
RUN go get k8s.io/apimachinery@$KUBE_VERSION; exit 0
|
||||
|
||||
RUN mkdir -p $GOPATH/src/k8s.io/{code-generator,apimachinery}
|
||||
RUN cp -R $GOPATH/pkg/mod/k8s.io/code-generator@$KUBE_VERSION $GOPATH/src/k8s.io/code-generator
|
||||
RUN cp -R $GOPATH/pkg/mod/k8s.io/apimachinery@$KUBE_VERSION $GOPATH/src/k8s.io/apimachinery
|
||||
RUN chmod +x $GOPATH/src/k8s.io/code-generator/generate-groups.sh
|
||||
|
||||
WORKDIR $GOPATH/src/k8s.io/code-generator
|
@@ -1,33 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ -n "$TRAVIS_TAG" ]; then
|
||||
if [ -n "${SEMAPHORE_GIT_TAG_NAME}" ]; then
|
||||
echo "Deploying..."
|
||||
else
|
||||
echo "Skipping deploy"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config --global user.email "$TRAEFIKER_EMAIL"
|
||||
git config --global user.email "${TRAEFIKER_EMAIL}"
|
||||
git config --global user.name "Traefiker"
|
||||
|
||||
# load ssh key
|
||||
: "${encrypted_83c521e11abe_key:?}" # ensures variable is defined
|
||||
: "${encrypted_83c521e11abe_iv:?}" # same
|
||||
echo "Loading key..."
|
||||
openssl aes-256-cbc -K "$encrypted_83c521e11abe_key" -iv "$encrypted_83c521e11abe_iv" -in .travis/traefiker_rsa.enc -out ~/.ssh/traefiker_rsa -d
|
||||
eval "$(ssh-agent -s)"
|
||||
chmod 600 ~/.ssh/traefiker_rsa
|
||||
ssh-add ~/.ssh/traefiker_rsa
|
||||
chmod 600 /home/semaphore/.ssh/traefiker_rsa
|
||||
ssh-add /home/semaphore/.ssh/traefiker_rsa
|
||||
|
||||
# update traefik-library-image repo (official Docker image)
|
||||
echo "Updating traefik-library-imag repo..."
|
||||
git clone git@github.com:traefik/traefik-library-image.git
|
||||
cd traefik-library-image
|
||||
./updatev2.sh "$VERSION"
|
||||
./updatev2.sh "${VERSION}"
|
||||
git add -A
|
||||
echo "$VERSION" | git commit --file -
|
||||
echo "$VERSION" | git tag -a "$VERSION" --file -
|
||||
echo "${VERSION}" | git commit --file -
|
||||
echo "${VERSION}" | git tag -a "${VERSION}" --file -
|
||||
git push -q --follow-tags -u origin master > /dev/null 2>&1
|
||||
|
||||
cd ..
|
||||
|
@@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||
OutputType = "file"
|
||||
FileName = "traefik_changelog.md"
|
||||
|
||||
# example new bugfix v2.4.11
|
||||
# example new bugfix v2.4.14
|
||||
CurrentRef = "v2.4"
|
||||
PreviousRef = "v2.4.9"
|
||||
PreviousRef = "v2.4.13"
|
||||
BaseBranch = "v2.4"
|
||||
FutureCurrentRefName = "v2.4.11"
|
||||
FutureCurrentRefName = "v2.4.14"
|
||||
|
||||
ThresholdPreviousRef = 10
|
||||
ThresholdCurrentRef = 10
|
||||
|
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
HACK_DIR="$( cd "$( dirname "${0}" )" && pwd -P)"; export HACK_DIR
|
||||
REPO_ROOT=${HACK_DIR}/..
|
||||
TRAEFIK_MODULE_VERSION=v2
|
||||
|
||||
rm -rf "${REPO_ROOT}"/vendor
|
||||
go mod vendor
|
||||
chmod +x "${REPO_ROOT}"/vendor/k8s.io/code-generator/*.sh
|
||||
|
||||
"${REPO_ROOT}"/vendor/k8s.io/code-generator/generate-groups.sh \
|
||||
all \
|
||||
github.com/traefik/traefik/${TRAEFIK_MODULE_VERSION}/pkg/provider/kubernetes/crd/generated \
|
||||
github.com/traefik/traefik/${TRAEFIK_MODULE_VERSION}/pkg/provider/kubernetes/crd \
|
||||
traefik:v1alpha1 \
|
||||
--go-header-file "${HACK_DIR}"/boilerplate.go.tmpl \
|
||||
"$@"
|
||||
|
||||
deepcopy-gen \
|
||||
--input-dirs github.com/traefik/traefik/${TRAEFIK_MODULE_VERSION}/pkg/config/dynamic \
|
||||
--input-dirs github.com/traefik/traefik/${TRAEFIK_MODULE_VERSION}/pkg/tls \
|
||||
--input-dirs github.com/traefik/traefik/${TRAEFIK_MODULE_VERSION}/pkg/types \
|
||||
--output-package github.com/traefik/traefik \
|
||||
-O zz_generated.deepcopy --go-header-file "${HACK_DIR}"/boilerplate.go.tmpl
|
||||
|
||||
cp -r "${REPO_ROOT}"/"${TRAEFIK_MODULE_VERSION:?}"/* "${REPO_ROOT}"; rm -rf "${REPO_ROOT}"/"${TRAEFIK_MODULE_VERSION:?}"
|
||||
|
||||
rm -rf "${REPO_ROOT}"/vendor
|
Reference in New Issue
Block a user