Compare commits

..

21 Commits

Author SHA1 Message Date
Fernandez Ludovic
23ef8f72d1 fix: doc requirements 2022-03-24 23:42:08 +01:00
Romain
3445abe7ac Fix Kubernetes Gateway API documentation links 2021-08-19 11:18:11 +02:00
Romain
35a40c8727 Prepare release v2.4.14 2021-08-16 17:26:14 +02:00
Romain
7f62667569 Update mkdocs dependency version 2021-08-16 12:32:07 +02:00
Avtion
fd4ba585ee fix: an example code error in doc 2021-08-16 10:08:08 +02:00
Tristan Colgate-McFarlane
e73dd31619 redirect: fix comparison when explicit port request and implicit redirect port
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2021-08-11 17:10:12 +02:00
Jean-Baptiste Doumenjou
ef9b79f85c Remove unwanted trailing slash in key
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
2021-08-05 18:02:12 +02:00
Jean-Baptiste Doumenjou
32d88a977d Avoid unauthorized midlleware cross namespace reference
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
2021-08-05 17:42:08 +02:00
Michael
547c380961 fix: change machine type for release 2021-08-05 10:08:06 +02:00
mpl
4fc077a5d2 Prepare release v2.4.13 2021-07-30 16:50:07 +02:00
Ludovic Fernandez
b386964abc fix: remove hop-by-hop headers define in connection header beore some middleware
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
2021-07-30 12:20:07 +02:00
Michael
319e3065f0 fix: upgrade k3s version 2021-07-28 14:28:11 +02:00
Michael
a48a8a97a1 fix: restore cache only once 2021-07-27 19:16:06 +02:00
Jean-Baptiste Doumenjou
8be434aaad Prepare release v2.4.12 2021-07-26 18:08:09 +02:00
mpl
d9fc775084 ratelimiter: use correct ttlSeconds value, and always call Set
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
Co-authored-by: Daniel Tomcej <daniel.tomcej@gmail.com>
2021-07-26 17:20:27 +02:00
Michael
e12630ef06 feat: Add new CI system 2021-07-23 11:00:07 +02:00
Romain
bc5e621683 Get Kubernetes server version early 2021-07-20 13:02:10 +02:00
Daniel Tomcej
c2c4dc9b58 Don't remove ingress config on API call failure 2021-07-19 20:06:07 +02:00
Daniel Tomcej
8d4620dc53 check if defaultcertificate is defined in store 2021-07-19 09:58:14 +02:00
Romain
2a2f7f783f Prepare release v2.4.11 2021-07-15 16:48:11 +02:00
Jean-Baptiste Doumenjou
985f8778e9 fix doc verify script (#8266) 2021-07-15 00:09:51 +02:00
48 changed files with 801 additions and 312 deletions

82
.github/workflows/build.yaml vendored Normal file
View 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

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
set -e
sudo rm -rf static

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,31 @@
## [v2.4.10](https://github.com/traefik/traefik/tree/v2.4.10) (2021-07-13)
[All Commits](https://github.com/traefik/traefik/compare/v2.4.9...v2.4.10)
## [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&#39;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&#39;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)
**Bug fixes:**
- **[k8s,k8s/crd,k8s/ingress]** Disable ExternalName Services by default on Kubernetes providers ([#8261](https://github.com/traefik/traefik/pull/8261) by [dtomcej](https://github.com/dtomcej))
@@ -16,6 +42,11 @@
- Fix maintainers-guidelines page title ([#8216](https://github.com/traefik/traefik/pull/8216) by [kubopanda](https://github.com/kubopanda))
- Typos in contributing section ([#8215](https://github.com/traefik/traefik/pull/8215) by [kubopanda](https://github.com/kubopanda))
## [v2.4.10](https://github.com/traefik/traefik/tree/v2.4.10) (2021-07-13)
[All Commits](https://github.com/traefik/traefik/compare/v2.4.9...v2.4.10)
Release canceled.
## [v2.4.9](https://github.com/traefik/traefik/tree/v2.4.9) (2021-06-21)
[All Commits](https://github.com/traefik/traefik/compare/v2.4.8...v2.4.9)

View File

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

View File

@@ -73,17 +73,17 @@ This provider is proposed as an experimental feature and partially supports the
The Kubernetes Gateway API project provides several guides on how to use the APIs.
These guides can help you to go further than the example above.
The [getting started guide](https://gateway-api.sigs.k8s.io/guides/getting-started/) details how to install the CRDs from their repository.
The [getting started guide](https://gateway-api.sigs.k8s.io/v1alpha1/guides/getting-started/) details how to install the CRDs from their repository.
!!! note ""
Keep in mind that the Traefik Gateway provider only supports the `v0.1.0`.
Keep in mind that the Traefik Gateway provider only supports the `v0.1.0` (v1alpha1).
For now, the Traefik Gateway Provider can be used while following the below guides:
* [Simple Gateway](https://gateway-api.sigs.k8s.io/guides/simple-gateway/)
* [HTTP routing](https://gateway-api.sigs.k8s.io/guides/http-routing/)
* [TLS](https://gateway-api.sigs.k8s.io/guides/tls/) (Partial support: only on listeners with terminate mode)
* [Simple Gateway](https://gateway-api.sigs.k8s.io/v1alpha1/guides/simple-gateway/)
* [HTTP routing](https://gateway-api.sigs.k8s.io/v1alpha1/guides/http-routing/)
* [TLS](https://gateway-api.sigs.k8s.io/v1alpha1/guides/tls/) (Partial support: only on listeners with terminate mode)
## Resource Configuration

View File

@@ -35,14 +35,14 @@ You can find an excerpt of the supported Kubernetes Gateway API resources in the
| Kind | Purpose | Concept Behind |
|------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [GatewayClass](#kind-gatewayclass) | Defines a set of Gateways that share a common configuration and behaviour | [GatewayClass](https://gateway-api.sigs.k8s.io/api-types/gatewayclass) |
| [Gateway](#kind-gateway) | Describes how traffic can be translated to Services within the cluster | [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway) |
| [HTTPRoute](#kind-httproute) | HTTP rules for mapping requests from a Gateway to Kubernetes Services | [Route](https://gateway-api.sigs.k8s.io/api-types/httproute) |
| [GatewayClass](#kind-gatewayclass) | Defines a set of Gateways that share a common configuration and behaviour | [GatewayClass](https://gateway-api.sigs.k8s.io/v1alpha1/api-types/gatewayclass) |
| [Gateway](#kind-gateway) | Describes how traffic can be translated to Services within the cluster | [Gateway](https://gateway-api.sigs.k8s.io/v1alpha1/api-types/gateway) |
| [HTTPRoute](#kind-httproute) | HTTP rules for mapping requests from a Gateway to Kubernetes Services | [Route](https://gateway-api.sigs.k8s.io/v1alpha1/api-types/httproute) |
### Kind: `GatewayClass`
`GatewayClass` is cluster-scoped resource defined by the infrastructure provider. This resource represents a class of Gateways that can be instantiated.
More details on the GatewayClass [official documentation](https://gateway-api.sigs.k8s.io/api-types/gatewayclass/).
More details on the GatewayClass [official documentation](https://gateway-api.sigs.k8s.io/v1alpha1/api-types/gatewayclass/).
The `GatewayClass` should be declared by the infrastructure provider, otherwise please register the `GatewayClass`
[definition](../../reference/dynamic-configuration/kubernetes-gateway.md#definitions) in the Kubernetes cluster before
@@ -65,7 +65,7 @@ creating `GatewayClass` objects.
A `Gateway` is 1:1 with the life cycle of the configuration of infrastructure. When a user creates a Gateway,
some load balancing infrastructure is provisioned or configured by the GatewayClass controller.
More details on the Gateway [official documentation](https://gateway-api.sigs.k8s.io/api-types/gateway/).
More details on the Gateway [official documentation](https://gateway-api.sigs.k8s.io/v1alpha1/api-types/gateway/).
Register the `Gateway` [definition](../../reference/dynamic-configuration/kubernetes-gateway.md#definitions) in the
Kubernetes cluster before creating `Gateway` objects.

View File

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

View File

@@ -1,6 +1,7 @@
mkdocs==1.1
mkdocs==1.2.2
pymdown-extensions==7.0
mkdocs-bootswatch==1.0
mkdocs-traefiklabs>=100.0.7
markdown-include==0.5.1
mkdocs-exclude==1.0.2
Jinja2==3.0.0

View File

@@ -22,7 +22,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \
--alt_ignore="/traefikproxy-vertical-logo-color.svg/" \
--http_status_ignore="0,500,501,503" \
--file_ignore="/404.html/" \
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/" \
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/,/www.alibabacloud.com/" \
'{}' 1>/dev/null
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration

View File

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

View File

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

View File

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

View File

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

View 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)
}
}
}
}

View 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)
})
}
}

View File

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

View File

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

View File

@@ -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}, "")
}

View File

@@ -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}, "")
}

View File

@@ -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}, "")
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
tls:
stores:
default: {}

View File

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

View File

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

View File

@@ -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{},

View File

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

View File

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

View File

@@ -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{})

View File

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

View File

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

View File

@@ -4,11 +4,11 @@ RepositoryName = "traefik"
OutputType = "file"
FileName = "traefik_changelog.md"
# example new bugfix v2.4.10
# example new bugfix v2.4.14
CurrentRef = "v2.4"
PreviousRef = "v2.4.9"
PreviousRef = "v2.4.13"
BaseBranch = "v2.4"
FutureCurrentRefName = "v2.4.10"
FutureCurrentRefName = "v2.4.14"
ThresholdPreviousRef = 10
ThresholdCurrentRef = 10

View File

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