1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-30 17:44:25 +03:00

Compare commits

..

61 Commits

Author SHA1 Message Date
Ludovic Fernandez
422109b82f Prepare release v1.5.3 2018-02-27 12:28:03 +01:00
NicoMen
c864a7297b Add DEBUG log when no provided certificate can check a domain 2018-02-27 11:10:03 +01:00
SALLEYRON Julien
8da038041d Default value for lifecycle 2018-02-27 10:24:03 +01:00
Ludovic Fernandez
dd954f3c0a Fix Duration JSON unmarshal 2018-02-26 22:14:03 +01:00
NicoMen
db483e9d34 Check all the C/N and SANs of provided certificates before to generat… 2018-02-26 11:38:03 +01:00
Ludovic Fernandez
700b7a1b51 Add a CLI help command for Docker. 2018-02-26 10:00:05 +01:00
Ludovic Fernandez
ed65d00574 Infinite entry point redirection. 2018-02-26 09:34:03 +01:00
NicoMen
f460c1990e Starting Træfik even if TLS certificates are in error 2018-02-22 14:38:04 +01:00
Pierre Carru
83381e99cf it's -> its 2018-02-21 17:18:05 +01:00
Michael
31550fd2c9 Replace nginx by whoami in integration tests 2018-02-21 16:28:03 +01:00
Emile Vauge
ba046b4d3a Fix doc cipher suites 2018-02-21 08:00:03 +01:00
Ludovic Fernandez
d675d46930 Multiple issue and pull request templates. 2018-02-20 10:44:03 +01:00
Michael
7ea76929d4 Empty ip address when endpoint mode dnsrr 2018-02-20 08:12:02 +01:00
Ludovic Fernandez
f98c537ec2 Smooth dashboard refresh. 2018-02-16 16:02:03 +01:00
Emile Vauge
083bde64ee Fix traffic pronounce dead link 2018-02-16 13:22:02 +01:00
SALLEYRON Julien
45fe218ee2 Isolate backend with same name on different provider 2018-02-16 11:04:04 +01:00
SALLEYRON Julien
d54777236c Update documentation on onHostRule, ping examples, and web deprecation 2018-02-16 10:32:03 +01:00
Ludovic Fernandez
4f3b06472b Check ping configuration. 2018-02-13 23:42:03 +01:00
Michael
52bad03c8d Prepare release v1.5.2 2018-02-12 11:46:03 +01:00
Ludovic Fernandez
2fde3e8679 Continue refresh the configuration after a failure. 2018-02-12 09:28:03 +01:00
Michael
1e71f52b72 Explain how to write entrypoints definition in a compose file 2018-02-09 18:16:04 +01:00
NicoMen
2b1d2853cd Compress ACME certificates in KV stores. 2018-02-09 10:38:03 +01:00
SALLEYRON Julien
f07e8f58e6 Fix goroutine leaks in websocket 2018-02-08 08:24:03 +01:00
Ludovic Fernandez
7b19cb5631 Migrate to dep 0.4 2018-02-07 23:30:05 +01:00
djeeg
dbd173b4e4 Docs: regex+replacement hints for URL rewriting 2018-02-07 13:42:04 +01:00
Sune Keller
85cfd87c44 Clarify how setting a frontend priority works 2018-02-07 13:00:04 +01:00
Ludovic Fernandez
c867f48f11 Change go-bindata 2018-02-07 12:40:03 +01:00
Timo Reimann
514f9a7215 Reduce oxy round trip logs to debug. 2018-02-07 11:32:03 +01:00
Wilhelm Uschtrin
0b0380b690 Fix typo 2018-02-06 14:30:04 +01:00
Sonu Kumar
4d0c8c189a Fixed typo. 2018-02-06 14:04:03 +01:00
SALLEYRON Julien
afe4c307f9 Traefik still start when Let's encrypt is down 2018-02-05 18:20:04 +01:00
Michael
ce3a0fdd46 Fix dnsrr endpoint mode excluded when not using swarm LB 2018-02-05 11:34:03 +01:00
Ludovic Fernandez
203a5c5c48 Hide the pflag error when displaying help. 2018-02-05 09:12:03 +01:00
Ludovic Fernandez
be4aeaacde Add documentation about entry points definition with CLI. 2018-02-05 08:54:03 +01:00
Ludovic Fernandez
26dc2f4d61 doc: option not available in 1.5. 2018-01-30 17:16:03 +01:00
Alexandre Guédon
6aac78fc36 typo in "i"ngress annotations. 2018-01-29 16:48:05 +01:00
Ludovic Fernandez
f6c53f0450 Rebuild experimental image 2018-01-29 16:08:03 +01:00
NicoMen
54e09b98c7 Prepare release v1.5.1 2018-01-29 15:04:03 +01:00
Ludovic Fernandez
4eebaa1a80 Enhance file provider documentation. 2018-01-29 14:36:03 +01:00
NicoMen
cb9bf3ce68 Fix domain names in dynamic TLS configuration 2018-01-29 10:48:03 +01:00
SALLEYRON Julien
49a8cb76f5 Add note on redirect for ACME http challenge 2018-01-26 09:22:03 +01:00
SALLEYRON Julien
bf12306f17 Change gzipwriter receiver to implement CloseNotifier 2018-01-25 21:46:04 +01:00
SALLEYRON Julien
323b8237a0 Handle undefined entrypoint on ACME config and frontend config 2018-01-25 12:02:04 +01:00
Michael
039ccaf4f1 Fix tar gz source only on tags on travis 2018-01-24 16:10:04 +01:00
Michael
4afb39778a Fix add src.tar.gz in Træfik release 2018-01-24 10:40:04 +01:00
Ludovic Fernandez
751781a3b7 Increase integration tests timeout. 2018-01-24 09:14:02 +01:00
Ludovic Fernandez
f5d150c3b4 Fix the k8s redirection template. 2018-01-24 08:12:03 +01:00
Ludovic Fernandez
ae9342208e Prepare release v1.5.0 2018-01-23 17:34:04 +01:00
Michael
3040d9df0d Build cross binary only on tags in travis 2018-01-23 17:00:06 +01:00
Ludovic Fernandez
00e0571811 Rename TLSConfigurations to TLS. 2018-01-23 16:30:07 +01:00
Ludovic Fernandez
bfb07746fe Deploy pages on all branches. 2018-01-23 14:48:04 +01:00
Ludovic Fernandez
171cda6186 New multi version documentation mechanism 2018-01-23 14:18:03 +01:00
Timo Reimann
4cc17e112f Fix goroutine leak in throttler logic. 2018-01-23 12:44:03 +01:00
Ludovic Fernandez
b6af61fa6e ACME and corporate proxy. 2018-01-23 09:52:03 +01:00
Emile Vauge
4e07d92190 Fix doc dynamic certificates 2018-01-23 09:12:03 +01:00
Roman Pridybailo
fc00e1c228 Don't reload configuration when rancher server is down 2018-01-22 11:00:07 +01:00
Eldon
ae34486b57 Fix some doc links 2018-01-22 10:26:03 +01:00
SALLEYRON Julien
d7b513e9aa Disable websocket compression 2018-01-19 17:34:03 +01:00
SALLEYRON Julien
d8297a055a Fix breaking change in web metrics 2018-01-19 14:30:04 +01:00
SALLEYRON Julien
ced5aa5dc6 Challenge HTTP must ignore deprecated web.path option 2018-01-17 18:46:03 +01:00
Martijn Heemels
adfa3f795c Fix typo in anonymous usage log message. 2018-01-17 12:20:04 +01:00
342 changed files with 4082 additions and 36222 deletions

View File

@@ -22,7 +22,7 @@ If you intend to ask a support question: DO NOT FILE AN ISSUE.
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as more as possible.
- Respect the issue template as much as possible.
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.

68
.github/ISSUE_TEMPLATE/bugs.md vendored Normal file
View File

@@ -0,0 +1,68 @@
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://traefik.herokuapp.com
-->
### Do you want to request a *feature* or report a *bug*?
Bug
### What did you do?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much as possible.
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### What did you expect to see?
### What did you see instead?
### Output of `traefik version`: (_What version of Traefik are you using?_)
<!--
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run traefik version
-->
```
(paste your output here)
```
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
```toml
# (paste your configuration here)
```
<!--
Add more configuration information here.
-->
### If applicable, please paste the log output in debug mode (`--debug` switch)
```
(paste your output here)
```

32
.github/ISSUE_TEMPLATE/features.md vendored Normal file
View File

@@ -0,0 +1,32 @@
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://traefik.herokuapp.com
-->
### Do you want to request a *feature* or report a *bug*?
Feature
### What did you expect to see?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much as possible.
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->

View File

@@ -0,0 +1,7 @@
### What does this PR do?
Merge v{{.Version}} into master
### Motivation
Be sync.

View File

@@ -0,0 +1,7 @@
### What does this PR do?
Prepare release v{{.Version}}.
### Motivation
Create a new release.

View File

@@ -1,6 +1,9 @@
sudo: required
dist: trusty
git:
depth: false
services:
- docker
@@ -21,22 +24,16 @@ before_deploy:
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;
pip install --user -r requirements.txt;
make -j${N_MAKE_JOBS} crossbinary-parallel;
make image-dirty;
mkdocs build --clean;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
make image;
if [ "$TRAVIS_TAG" ]; then
make -j${N_MAKE_JOBS} crossbinary-parallel;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
fi;
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
chmod +x $GOPATH/bin/structor;
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/master/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --exp-branch=master --debug;
fi
deploy:
- provider: pages
edge: true
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true
on:
repo: containous/traefik
tags: true
condition: ${TRAVIS_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$
- provider: releases
api_key: ${GITHUB_TOKEN}
file: dist/traefik*
@@ -56,3 +53,11 @@ deploy:
skip_cleanup: true
on:
repo: containous/traefik
- provider: pages
edge: true
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true
on:
repo: containous/traefik
all_branches: true

View File

@@ -1,5 +1,225 @@
# Change Log
## [v1.5.3](https://github.com/containous/traefik/tree/v1.5.3) (2018-02-27)
[All Commits](https://github.com/containous/traefik/compare/v1.5.2...v1.5.3)
**Bug fixes:**
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates ([#2913](https://github.com/containous/traefik/pull/2913) by [nmengin](https://github.com/nmengin))
- **[docker/swarm]** Empty IP address when use endpoint mode dnsrr ([#2887](https://github.com/containous/traefik/pull/2887) by [mmatur](https://github.com/mmatur))
- **[middleware]** Infinite entry point redirection. ([#2929](https://github.com/containous/traefik/pull/2929) by [ldez](https://github.com/ldez))
- **[provider]** Isolate backend with same name on different provider ([#2862](https://github.com/containous/traefik/pull/2862) by [Juliens](https://github.com/Juliens))
- **[tls]** Starting Træfik even if TLS certificates are in error ([#2909](https://github.com/containous/traefik/pull/2909) by [nmengin](https://github.com/nmengin))
- **[tls]** Add DEBUG log when no provided certificate can check a domain ([#2938](https://github.com/containous/traefik/pull/2938) by [nmengin](https://github.com/nmengin))
- **[webui]** Smooth dashboard refresh. ([#2871](https://github.com/containous/traefik/pull/2871) by [ldez](https://github.com/ldez))
- Fix Duration JSON unmarshal ([#2935](https://github.com/containous/traefik/pull/2935) by [ldez](https://github.com/ldez))
- Default value for lifecycle ([#2934](https://github.com/containous/traefik/pull/2934) by [Juliens](https://github.com/Juliens))
- Check ping configuration. ([#2852](https://github.com/containous/traefik/pull/2852) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** it&#39;s -&gt; its ([#2901](https://github.com/containous/traefik/pull/2901) by [piec](https://github.com/piec))
- **[tls]** Fix doc cipher suites ([#2894](https://github.com/containous/traefik/pull/2894) by [emilevauge](https://github.com/emilevauge))
- Add a CLI help command for Docker. ([#2921](https://github.com/containous/traefik/pull/2921) by [ldez](https://github.com/ldez))
- Fix traffic pronounce dead link ([#2870](https://github.com/containous/traefik/pull/2870) by [emilevauge](https://github.com/emilevauge))
- Update documentation on onHostRule, ping examples, and web deprecation ([#2863](https://github.com/containous/traefik/pull/2863) by [Juliens](https://github.com/Juliens))
## [v1.5.2](https://github.com/containous/traefik/tree/v1.5.2) (2018-02-12)
[All Commits](https://github.com/containous/traefik/compare/v1.5.1...v1.5.2)
**Bug fixes:**
- **[acme,cluster,kv]** Compress ACME certificates in KV stores. ([#2814](https://github.com/containous/traefik/pull/2814) by [nmengin](https://github.com/nmengin))
- **[acme]** Traefik still start when Let&#39;s encrypt is down ([#2794](https://github.com/containous/traefik/pull/2794) by [Juliens](https://github.com/Juliens))
- **[docker]** Fix dnsrr endpoint mode excluded when not using swarm LB ([#2795](https://github.com/containous/traefik/pull/2795) by [mmatur](https://github.com/mmatur))
- **[eureka]** Continue refresh the configuration after a failure. ([#2838](https://github.com/containous/traefik/pull/2838) by [ldez](https://github.com/ldez))
- **[logs]** Reduce oxy round trip logs to debug. ([#2821](https://github.com/containous/traefik/pull/2821) by [timoreimann](https://github.com/timoreimann))
- **[websocket]** Fix goroutine leaks in websocket ([#2825](https://github.com/containous/traefik/pull/2825) by [Juliens](https://github.com/Juliens))
- Hide the pflag error when displaying help. ([#2800](https://github.com/containous/traefik/pull/2800) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** Explain how to write entrypoints definition in a compose file ([#2834](https://github.com/containous/traefik/pull/2834) by [mmatur](https://github.com/mmatur))
- **[docker]** Fix typo ([#2813](https://github.com/containous/traefik/pull/2813) by [uschtwill](https://github.com/uschtwill))
- **[k8s]** typo in &#34;i&#34;ngress annotations. ([#2780](https://github.com/containous/traefik/pull/2780) by [RRAlex](https://github.com/RRAlex))
- Clarify how setting a frontend priority works ([#2818](https://github.com/containous/traefik/pull/2818) by [sirlatrom](https://github.com/sirlatrom))
- Fixed typo. ([#2811](https://github.com/containous/traefik/pull/2811) by [sonus21](https://github.com/sonus21))
- Docs: regex+replacement hints for URL rewriting ([#2802](https://github.com/containous/traefik/pull/2802) by [djeeg](https://github.com/djeeg))
- Add documentation about entry points definition with CLI. ([#2798](https://github.com/containous/traefik/pull/2798) by [ldez](https://github.com/ldez))
## [v1.5.1](https://github.com/containous/traefik/tree/v1.5.1) (2018-01-29)
[All Commits](https://github.com/containous/traefik/compare/v1.5.0...v1.5.1)
**Bug fixes:**
- **[acme]** Handle undefined entrypoint on ACME config and frontend config ([#2756](https://github.com/containous/traefik/pull/2756) by [Juliens](https://github.com/Juliens))
- **[k8s]** Fix the k8s redirection template. ([#2748](https://github.com/containous/traefik/pull/2748) by [ldez](https://github.com/ldez))
- **[middleware]** Change gzipwriter receiver to implement CloseNotifier ([#2766](https://github.com/containous/traefik/pull/2766) by [Juliens](https://github.com/Juliens))
- **[tls]** Fix domain names in dynamic TLS configuration ([#2768](https://github.com/containous/traefik/pull/2768) by [nmengin](https://github.com/nmengin))
**Documentation:**
- **[acme]** Add note on redirect for ACME http challenge ([#2767](https://github.com/containous/traefik/pull/2767) by [Juliens](https://github.com/Juliens))
- **[file]** Enhance file provider documentation. ([#2777](https://github.com/containous/traefik/pull/2777) by [ldez](https://github.com/ldez))
## [v1.5.0](https://github.com/containous/traefik/tree/v1.5.0) (2018-01-23)
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0)
**Enhancements:**
- **[acme,tls]** Rename TLSConfigurations to TLS. ([#2744](https://github.com/containous/traefik/pull/2744) by [ldez](https://github.com/ldez))
- **[acme,provider,docker,tls]** Make the TLS certificates management dynamic. ([#2233](https://github.com/containous/traefik/pull/2233) by [nmengin](https://github.com/nmengin))
- **[acme]** Add Let&#39;s Encrypt HTTP Challenge ([#2701](https://github.com/containous/traefik/pull/2701) by [Juliens](https://github.com/Juliens))
- **[acme]** Update github.com/xenolf/lego to 0.4.1 ([#2304](https://github.com/containous/traefik/pull/2304) by [oldmantaiter](https://github.com/oldmantaiter))
- **[api,healthcheck,metrics,provider,webui]** Split Web into API/Dashboard, ping, metric and Rest Provider ([#2335](https://github.com/containous/traefik/pull/2335) by [Juliens](https://github.com/Juliens))
- **[authentication]** Pass through certain forward auth negative response headers ([#2127](https://github.com/containous/traefik/pull/2127) by [wheresmysocks](https://github.com/wheresmysocks))
- **[cluster,consul,file]** Add file to storeconfig ([#2419](https://github.com/containous/traefik/pull/2419) by [emilevauge](https://github.com/emilevauge))
- **[cluster,provider]** Support Etcd v3, enhance KV support ([#2407](https://github.com/containous/traefik/pull/2407) by [nmengin](https://github.com/nmengin))
- **[docker,k8s,rancher,webui]** Redirect to another entryPoint per frontend ([#2133](https://github.com/containous/traefik/pull/2133) by [SantoDE](https://github.com/SantoDE))
- **[docker,k8s,rancher]** Support regex redirect by frontend ([#2570](https://github.com/containous/traefik/pull/2570) by [ldez](https://github.com/ldez))
- **[docker]** Add Custom header parsing to Docker Provider ([#2030](https://github.com/containous/traefik/pull/2030) by [dtomcej](https://github.com/dtomcej))
- **[docker]** Docker labels ([#2473](https://github.com/containous/traefik/pull/2473) by [ldez](https://github.com/ldez))
- **[docker]** Add docker security headers via labels ([#2334](https://github.com/containous/traefik/pull/2334) by [dtomcej](https://github.com/dtomcej))
- **[docker]** Use Node IP in Swarm Standalone with &#34;host&#34; NetworkMode ([#2274](https://github.com/containous/traefik/pull/2274) by [BlakeMesdag](https://github.com/BlakeMesdag))
- **[ecs]** ECS provider refactoring ([#2050](https://github.com/containous/traefik/pull/2050) by [mmatur](https://github.com/mmatur))
- **[ecs]** Add health check label to ECS ([#2421](https://github.com/containous/traefik/pull/2421) by [oldmantaiter](https://github.com/oldmantaiter))
- **[ecs]** Support Host NetworkMode for ECS provider ([#2320](https://github.com/containous/traefik/pull/2320) by [FriggaHel](https://github.com/FriggaHel))
- **[etcd]** Manage certificates dynamically in kv store ([#2411](https://github.com/containous/traefik/pull/2411) by [dahefanteng](https://github.com/dahefanteng))
- **[healthcheck]** Use health check for systemd watchdog ([#2283](https://github.com/containous/traefik/pull/2283) by [guilhem](https://github.com/guilhem))
- **[k8s]** Kubernetes security header annotations ([#2460](https://github.com/containous/traefik/pull/2460) by [dtomcej](https://github.com/dtomcej))
- **[k8s]** Add labels for `traefik.frontend.entryPoints` &amp; `PassTLSCert` to Kubernetes ([#2324](https://github.com/containous/traefik/pull/2324) by [ryarnyah](https://github.com/ryarnyah))
- **[k8s]** Only listen to configured k8s namespaces. ([#1895](https://github.com/containous/traefik/pull/1895) by [timoreimann](https://github.com/timoreimann))
- **[logs,middleware,consul,docker]** Use constants from http package. ([#2425](https://github.com/containous/traefik/pull/2425) by [ldez](https://github.com/ldez))
- **[logs]** Add json format support for Traefik logs ([#2056](https://github.com/containous/traefik/pull/2056) by [marco-jantke](https://github.com/marco-jantke))
- **[marathon]** Marathon constraints filtering ([#2388](https://github.com/containous/traefik/pull/2388) by [aantono](https://github.com/aantono))
- **[marathon]** Remove unused lightMarathonClient. ([#2383](https://github.com/containous/traefik/pull/2383) by [timoreimann](https://github.com/timoreimann))
- **[metrics]** Add InfluxDB support for traefik metrics ([#2289](https://github.com/containous/traefik/pull/2289) by [adityacs](https://github.com/adityacs))
- **[middleware]** Added ReplacePathRegex middleware ([#2033](https://github.com/containous/traefik/pull/2033) by [Tiscs](https://github.com/Tiscs))
- **[middleware]** Fix custom headers replacement ([#2455](https://github.com/containous/traefik/pull/2455) by [mmatur](https://github.com/mmatur))
- **[oxy]** Resync oxy with original repository ([#2451](https://github.com/containous/traefik/pull/2451) by [Juliens](https://github.com/Juliens))
- **[provider]** Support template as raw string. ([#2413](https://github.com/containous/traefik/pull/2413) by [ldez](https://github.com/ldez))
- **[rancher]** Run Rancher tests cases in parallel. ([#2424](https://github.com/containous/traefik/pull/2424) by [ldez](https://github.com/ldez))
- **[rancher]** Update Rancher API integration to go-rancher client v2. ([#2291](https://github.com/containous/traefik/pull/2291) by [rawmind0](https://github.com/rawmind0))
- **[servicefabric]** Add Service Fabric Provider ([#2117](https://github.com/containous/traefik/pull/2117) by [lawrencegripper](https://github.com/lawrencegripper))
- **[tls]** Allow adding optional Client CA files ([#2306](https://github.com/containous/traefik/pull/2306) by [nmengin](https://github.com/nmengin))
- **[websocket]** Add tests for websocket headers ([#2379](https://github.com/containous/traefik/pull/2379) by [Juliens](https://github.com/Juliens))
- Upgrade libkermit/compose version ([#2071](https://github.com/containous/traefik/pull/2071) by [nmengin](https://github.com/nmengin))
- Add proxy protocol tests ([#2325](https://github.com/containous/traefik/pull/2325) by [emilevauge](https://github.com/emilevauge))
- Register pprof handlers. ([#2428](https://github.com/containous/traefik/pull/2428) by [timoreimann](https://github.com/timoreimann))
- Rate limiting for frontends ([#2034](https://github.com/containous/traefik/pull/2034) by [bparli](https://github.com/bparli))
- Stats collection. ([#2447](https://github.com/containous/traefik/pull/2447) by [ldez](https://github.com/ldez))
- Add request accepting grace period delaying graceful shutdown. ([#1971](https://github.com/containous/traefik/pull/1971) by [timoreimann](https://github.com/timoreimann))
- Put subcommand in dedicated files. ([#2265](https://github.com/containous/traefik/pull/2265) by [ldez](https://github.com/ldez))
**Bug fixes:**
- **[acme,docker]** Modify ACME configuration migration into KV store ([#2598](https://github.com/containous/traefik/pull/2598) by [nmengin](https://github.com/nmengin))
- **[acme,logs]** Modify DEBUG messages to get ACME certificates ([#2685](https://github.com/containous/traefik/pull/2685) by [nmengin](https://github.com/nmengin))
- **[acme]** Modify the ACME renewing logs level ([#2520](https://github.com/containous/traefik/pull/2520) by [nmengin](https://github.com/nmengin))
- **[acme]** ACME and corporate proxy. ([#2738](https://github.com/containous/traefik/pull/2738) by [ldez](https://github.com/ldez))
- **[acme]** Challenge HTTP must ignore deprecated web.path option ([#2719](https://github.com/containous/traefik/pull/2719) by [Juliens](https://github.com/Juliens))
- **[api]** Fix pprof route order. ([#2523](https://github.com/containous/traefik/pull/2523) by [timoreimann](https://github.com/timoreimann))
- **[authentication,middleware]** Fix concurrent map writes on digest auth ([#2695](https://github.com/containous/traefik/pull/2695) by [mmatur](https://github.com/mmatur))
- **[consulcatalog]** Use prefix for sticky and stickiness tags. ([#2624](https://github.com/containous/traefik/pull/2624) by [ldez](https://github.com/ldez))
- **[consulcatalog]** Fix bad Træfik update on Consul Catalog ([#2573](https://github.com/containous/traefik/pull/2573) by [mmatur](https://github.com/mmatur))
- **[consulcatalog]** Reload configuration when port change for one service ([#2574](https://github.com/containous/traefik/pull/2574) by [mmatur](https://github.com/mmatur))
- **[docker,k8s]** Fix Labels/annotation logs and values. ([#2488](https://github.com/containous/traefik/pull/2488) by [ldez](https://github.com/ldez))
- **[docker,k8s]** Change custom headers separator ([#2509](https://github.com/containous/traefik/pull/2509) by [ldez](https://github.com/ldez))
- **[docker]** Fix empty IP for backend when dnsrr in Docker swarm mode ([#2490](https://github.com/containous/traefik/pull/2490) by [mmatur](https://github.com/mmatur))
- **[docker]** Quote template strings ([#2496](https://github.com/containous/traefik/pull/2496) by [dtomcej](https://github.com/dtomcej))
- **[docker]** Return errors from Docker client.Events ([#2689](https://github.com/containous/traefik/pull/2689) by [BlakeMesdag](https://github.com/BlakeMesdag))
- **[docker]** Typo in Docker template. ([#2692](https://github.com/containous/traefik/pull/2692) by [ldez](https://github.com/ldez))
- **[ecs]** Add missing functions for ECS template ([#2312](https://github.com/containous/traefik/pull/2312) by [oldmantaiter](https://github.com/oldmantaiter))
- **[file,tls]** Send empty configuration from file provider ([#2609](https://github.com/containous/traefik/pull/2609) by [nmengin](https://github.com/nmengin))
- **[healthcheck]** Fix health check when web is not specified ([#2529](https://github.com/containous/traefik/pull/2529) by [Juliens](https://github.com/Juliens))
- **[k8s]** Reduce logs with new Kubernetes security annotations ([#2506](https://github.com/containous/traefik/pull/2506) by [ldez](https://github.com/ldez))
- **[k8s]** Add missing entry points template. ([#2594](https://github.com/containous/traefik/pull/2594) by [ldez](https://github.com/ldez))
- **[kv]** Fix stickiness bug due to template syntax error ([#2591](https://github.com/containous/traefik/pull/2591) by [dahefanteng](https://github.com/dahefanteng))
- **[kv]** List entries parsing. ([#2669](https://github.com/containous/traefik/pull/2669) by [ldez](https://github.com/ldez))
- **[logs]** Fix traefik logs to behave like configured ([#2176](https://github.com/containous/traefik/pull/2176) by [marco-jantke](https://github.com/marco-jantke))
- **[marathon]** Update go-marathon ([#2585](https://github.com/containous/traefik/pull/2585) by [timoreimann](https://github.com/timoreimann))
- **[mesos]** Mesos: Use slave.PID.Host as task SlaveIP. ([#2590](https://github.com/containous/traefik/pull/2590) by [nemosupremo](https://github.com/nemosupremo))
- **[metrics]** Fix breaking change in web metrics ([#2725](https://github.com/containous/traefik/pull/2725) by [Juliens](https://github.com/Juliens))
- **[metrics]** Do not ignore web params when web.metrics.prometheus is set ([#2499](https://github.com/containous/traefik/pull/2499) by [Juliens](https://github.com/Juliens))
- **[metrics]** Fix metrics problem on multiple entrypoints ([#2492](https://github.com/containous/traefik/pull/2492) by [Juliens](https://github.com/Juliens))
- **[metrics]** Fix data races. ([#2287](https://github.com/containous/traefik/pull/2287) by [tcolgate](https://github.com/tcolgate))
- **[metrics]** Flaky test Influxdb. ([#2386](https://github.com/containous/traefik/pull/2386) by [ldez](https://github.com/ldez))
- **[middleware,docker,k8s]** Fix custom headers template ([#2621](https://github.com/containous/traefik/pull/2621) by [ldez](https://github.com/ldez))
- **[middleware]** Don&#39;t panic if ResponseWriter does not implement CloseNotify ([#2651](https://github.com/containous/traefik/pull/2651) by [Juliens](https://github.com/Juliens))
- **[middleware]** GzipResponse must implement CloseNotifier if ResponseWriter implement it ([#2657](https://github.com/containous/traefik/pull/2657) by [Juliens](https://github.com/Juliens))
- **[middleware]** Fix RawPath handling in addPrefix ([#2560](https://github.com/containous/traefik/pull/2560) by [risdenk](https://github.com/risdenk))
- **[middleware]** We need to flush the end of the body when retry is streamed ([#2644](https://github.com/containous/traefik/pull/2644) by [Juliens](https://github.com/Juliens))
- **[provider]** Fix typo in frontend.headers.customresponseheaders label ([#2356](https://github.com/containous/traefik/pull/2356) by [nmandery](https://github.com/nmandery))
- **[provider]** Fix concurrent provider config reloads ([#2276](https://github.com/containous/traefik/pull/2276) by [marco-jantke](https://github.com/marco-jantke))
- **[rancher]** Don&#39;t reload configuration when rancher server is down ([#2706](https://github.com/containous/traefik/pull/2706) by [wacken89](https://github.com/wacken89))
- **[rules]** Add non regex pathPrefix ([#2592](https://github.com/containous/traefik/pull/2592) by [emilevauge](https://github.com/emilevauge))
- **[servicefabric]** Fix backend name for Stateful services. (Service Fabric) ([#2559](https://github.com/containous/traefik/pull/2559) by [ldez](https://github.com/ldez))
- **[servicefabric]** Fix isHealthy logic. ([#2577](https://github.com/containous/traefik/pull/2577) by [ldez](https://github.com/ldez))
- **[servicefabric]** Service Fabric &#39;expose&#39; as boolean. ([#2476](https://github.com/containous/traefik/pull/2476) by [ldez](https://github.com/ldez))
- **[tls]** Allow deleting dynamically all TLS certificates from an entryPoint ([#2603](https://github.com/containous/traefik/pull/2603) by [nmengin](https://github.com/nmengin))
- **[websocket]** Disable websocket compression ([#2727](https://github.com/containous/traefik/pull/2727) by [Juliens](https://github.com/Juliens))
- **[websocket]** Add compression and better error handling ([#2702](https://github.com/containous/traefik/pull/2702) by [Juliens](https://github.com/Juliens))
- **[websocket]** Use gorilla readMessage and writeMessage instead of just an io.Copy ([#2650](https://github.com/containous/traefik/pull/2650) by [Juliens](https://github.com/Juliens))
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2077](https://github.com/containous/traefik/pull/2077) by [Juliens](https://github.com/Juliens))
- **[zk]** Change Zookeeper default prefix. ([#2580](https://github.com/containous/traefik/pull/2580) by [ldez](https://github.com/ldez))
- Fix wrong default entry point and non-existing entry point issue ([#2501](https://github.com/containous/traefik/pull/2501) by [Juliens](https://github.com/Juliens))
- Fix goroutine leak in throttler logic. ([#2739](https://github.com/containous/traefik/pull/2739) by [timoreimann](https://github.com/timoreimann))
- Fix timeout integration test ([#2679](https://github.com/containous/traefik/pull/2679) by [ldez](https://github.com/ldez))
- Fix frontend redirect ([#2544](https://github.com/containous/traefik/pull/2544) by [ldez](https://github.com/ldez))
- Close ring buffer used in throttling function. ([#2532](https://github.com/containous/traefik/pull/2532) by [timoreimann](https://github.com/timoreimann))
**Documentation:**
- **[acme]** Improve documentation for Cloudflare API key ([#2558](https://github.com/containous/traefik/pull/2558) by [mmatur](https://github.com/mmatur))
- **[acme]** Update Let&#39;s Encrypt provider list ([#2347](https://github.com/containous/traefik/pull/2347) by [mmatur](https://github.com/mmatur))
- **[cluster]** Add a clustering example with Docker Swarm ([#2589](https://github.com/containous/traefik/pull/2589) by [jmaitrehenry](https://github.com/jmaitrehenry))
- **[consul,consulcatalog]** Split Consul and Consul Catalog documentation ([#2654](https://github.com/containous/traefik/pull/2654) by [ldez](https://github.com/ldez))
- **[consul]** Improve Consul documentation ([#2485](https://github.com/containous/traefik/pull/2485) by [mmatur](https://github.com/mmatur))
- **[docker/swarm]** Typo in docker.endpoint TCP port. ([#2626](https://github.com/containous/traefik/pull/2626) by [redhandpl](https://github.com/redhandpl))
- **[docker]** Fix Docker labels documentation render. ([#2505](https://github.com/containous/traefik/pull/2505) by [ldez](https://github.com/ldez))
- **[docker]** Add a note on how to add label to a docker compose file ([#2611](https://github.com/containous/traefik/pull/2611) by [jmaitrehenry](https://github.com/jmaitrehenry))
- **[etcd]** Fix typo in examples ([#2446](https://github.com/containous/traefik/pull/2446) by [dahefanteng](https://github.com/dahefanteng))
- **[k8s]** Add note to Kubernetes RBAC docs about RoleBindings and namespaces ([#2498](https://github.com/containous/traefik/pull/2498) by [jmara](https://github.com/jmara))
- **[k8s]** k8s guide: Leave note about assumed DaemonSet usage. ([#2634](https://github.com/containous/traefik/pull/2634) by [timoreimann](https://github.com/timoreimann))
- **[k8s]** Apply various contentual and stylish improvements to the k8s docs. ([#2677](https://github.com/containous/traefik/pull/2677) by [timoreimann](https://github.com/timoreimann))
- **[k8s]** Document rewrite-target annotation. ([#2676](https://github.com/containous/traefik/pull/2676) by [timoreimann](https://github.com/timoreimann))
- **[k8s]** Remove obsolete links in k8s docs ([#2465](https://github.com/containous/traefik/pull/2465) by [marco-jantke](https://github.com/marco-jantke))
- **[k8s]** Document filename parameter for Kubernetes. ([#2464](https://github.com/containous/traefik/pull/2464) by [timoreimann](https://github.com/timoreimann))
- **[marathon]** Improve Marathon service label documentation. ([#2635](https://github.com/containous/traefik/pull/2635) by [timoreimann](https://github.com/timoreimann))
- **[metrics]** Add entrypoint in Prometheus doc and remove web on Influxdb doc ([#2452](https://github.com/containous/traefik/pull/2452) by [Juliens](https://github.com/Juliens))
- **[provider,webui]** Fix redirect problem on dashboard + docs/tests on [web] ([#2686](https://github.com/containous/traefik/pull/2686) by [Juliens](https://github.com/Juliens))
- **[servicefabric]** Describe &#39;refreshSecond&#39; configuration. ([#2471](https://github.com/containous/traefik/pull/2471) by [ldez](https://github.com/ldez))
- **[tls]** Fix doc dynamic certificates ([#2737](https://github.com/containous/traefik/pull/2737) by [emilevauge](https://github.com/emilevauge))
- **[tls]** Add link to crypto/tls godoc. ([#2470](https://github.com/containous/traefik/pull/2470) by [ldez](https://github.com/ldez))
- Move rate limit documentation. ([#2588](https://github.com/containous/traefik/pull/2588) by [ldez](https://github.com/ldez))
- Grammar ([#2562](https://github.com/containous/traefik/pull/2562) by [geraldcroes](https://github.com/geraldcroes))
- Fix some doc links ([#2731](https://github.com/containous/traefik/pull/2731) by [eldondev](https://github.com/eldondev))
- Fix broken links and improve ResponseCodeRatio() description ([#2538](https://github.com/containous/traefik/pull/2538) by [mvasin](https://github.com/mvasin))
- Fix typo in anonymous usage log message. ([#2711](https://github.com/containous/traefik/pull/2711) by [Yggdrasil](https://github.com/Yggdrasil))
- Fix typos in changelog ([#2387](https://github.com/containous/traefik/pull/2387) by [ferhatelmas](https://github.com/ferhatelmas))
- Add mmatur to maintainers ([#2303](https://github.com/containous/traefik/pull/2303) by [emilevauge](https://github.com/emilevauge))
- Add a note about redirection rule to precise how regex/replacement work. ([#2243](https://github.com/containous/traefik/pull/2243) by [nmengin](https://github.com/nmengin))
- Add docker things for documentation ([#2020](https://github.com/containous/traefik/pull/2020) by [tcoupin](https://github.com/tcoupin))
- Prepare release v1.5.0-rc5 ([#2707](https://github.com/containous/traefik/pull/2707) by [mmatur](https://github.com/mmatur))
- Prepare release v1.5.0-rc4 ([#2656](https://github.com/containous/traefik/pull/2656) by [Juliens](https://github.com/Juliens))
- Prepare release v1.5.0-rc3 ([#2599](https://github.com/containous/traefik/pull/2599) by [ldez](https://github.com/ldez))
- Prepare release v1.5.0-rc2 ([#2533](https://github.com/containous/traefik/pull/2533) by [ldez](https://github.com/ldez))
- Prepare release v1.5.0-rc1 ([#2480](https://github.com/containous/traefik/pull/2480) by [ldez](https://github.com/ldez))
**Misc:**
- **[acme]** dumpcerts.sh: Fix call to &#34;base64&#34; for Alpine ([#2344](https://github.com/containous/traefik/pull/2344) by [nknapp](https://github.com/nknapp))
- **[acme]** dumpcerts.sh: fixed sed, extracted domain keys ([#2161](https://github.com/containous/traefik/pull/2161) by [sjawhar](https://github.com/sjawhar))
- **[etcd,kv,tls]** Add tests for TLS dynamic configuration in ETCD3 ([#2606](https://github.com/containous/traefik/pull/2606) by [dahefanteng](https://github.com/dahefanteng))
- Upgrade libkermit/compose version ([#2074](https://github.com/containous/traefik/pull/2074) by [nmengin](https://github.com/nmengin))
- Merge v1.4.6 into v1.5 ([#2642](https://github.com/containous/traefik/pull/2642) by [ldez](https://github.com/ldez))
- Merge v1.4.5 into v1.5 ([#2530](https://github.com/containous/traefik/pull/2530) by [mmatur](https://github.com/mmatur))
- Merge current v1.4 into master ([#2479](https://github.com/containous/traefik/pull/2479) by [ldez](https://github.com/ldez))
- Merge v1.4.3 into master ([#2415](https://github.com/containous/traefik/pull/2415) by [ldez](https://github.com/ldez))
- Merge v1.4.4 into master ([#2457](https://github.com/containous/traefik/pull/2457) by [ldez](https://github.com/ldez))
- Merge v1.4.3 into master ([#2406](https://github.com/containous/traefik/pull/2406) by [ldez](https://github.com/ldez))
- Revert &#34;Merge v1.4.2 into master&#34; ([#2414](https://github.com/containous/traefik/pull/2414) by [ldez](https://github.com/ldez))
- Merge v1.4.2 into master ([#2358](https://github.com/containous/traefik/pull/2358) by [ldez](https://github.com/ldez))
- Merge v1.4.1 into master ([#2318](https://github.com/containous/traefik/pull/2318) by [ldez](https://github.com/ldez))
- Merge v1.4.0 ([#2271](https://github.com/containous/traefik/pull/2271) by [ldez](https://github.com/ldez))
- Merge v1.4.0-rc5 into master ([#2242](https://github.com/containous/traefik/pull/2242) by [ldez](https://github.com/ldez))
- Merge v1.4.0-rc4 into master ([#2202](https://github.com/containous/traefik/pull/2202) by [ldez](https://github.com/ldez))
- Merge current v1.4 into master ([#2469](https://github.com/containous/traefik/pull/2469) by [ldez](https://github.com/ldez))
- Merge current v1.4 ([#2154](https://github.com/containous/traefik/pull/2154) by [ldez](https://github.com/ldez))
- Merge v1.4.0-rc3 into master ([#2140](https://github.com/containous/traefik/pull/2140) by [ldez](https://github.com/ldez))
- Merge v1.4.0-rc2 into master ([#2092](https://github.com/containous/traefik/pull/2092) by [ldez](https://github.com/ldez))
- Merge current 1.4 ([#2064](https://github.com/containous/traefik/pull/2064) by [ldez](https://github.com/ldez))
## [v1.5.0-rc5](https://github.com/containous/traefik/tree/v1.5.0-rc5) (2018-01-15)
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc4...v1.5.0-rc5)

View File

@@ -64,7 +64,7 @@ Once your environment is set up and the Træfik repository cloned you can build
cd ~/go/src/github.com/containous/traefik
# Get go-bindata. Please note, the ellipses are required
go get github.com/jteeuwen/go-bindata/...
go get github.com/containous/go-bindata/...
# Start build
@@ -87,9 +87,11 @@ If you happen to update the provider templates (in `/templates`), you need to ru
[dep](https://github.com/golang/dep) is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
You need to use [dep](https://github.com/golang/dep) >= O.4.1.
If you want to add a dependency, use `dep ensure -add` to have [dep](https://github.com/golang/dep) put it into the vendor folder and update the dep manifest/lock files (`Gopkg.toml` and `Gopkg.lock`, respectively).
A following `make prune-dep` run should be triggered to trim down the size of the vendor folder.
A following `make dep-prune` run should be triggered to trim down the size of the vendor folder.
The final result must be committed into VCS.
Here's a full example using dep to add a new dependency:

22
Gopkg.lock generated
View File

@@ -89,7 +89,7 @@
branch = "master"
name = "github.com/NYTimes/gziphandler"
packages = ["."]
revision = "47ca22a0aeea4c9ceddfb935d818d636d934c312"
revision = "289a3b81f5aedc99f8d6eb0f67827c142f1310d8"
[[projects]]
name = "github.com/Nvveen/Gotty"
@@ -210,9 +210,12 @@
[[projects]]
name = "github.com/containous/flaeg"
packages = ["."]
revision = "60c87a513a955ca7225e1b1c772581cea8420cb4"
version = "v1.0.1"
packages = [
".",
"parse"
]
revision = "963366c29a7acc2d6e02f4f9bcf260d5a1cf4968"
version = "v1.1.1"
[[projects]]
branch = "master"
@@ -223,8 +226,8 @@
[[projects]]
name = "github.com/containous/staert"
packages = ["."]
revision = "af517d5b70db9c4b0505e0144fcc62b054057d2a"
version = "v2.0.0"
revision = "68c67b32c3a986672d994d38127cd5c78d53eb26"
version = "v2.1.0"
[[projects]]
name = "github.com/containous/traefik-extra-service-fabric"
@@ -962,7 +965,8 @@
"mock",
"require"
]
revision = "4d4bfba8f1d1027c4fdbe371823030df51419987"
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
@@ -1027,7 +1031,7 @@
"roundrobin",
"utils"
]
revision = "fd0f370c961f6aa304379f4106e76ffe5ed7e97a"
revision = "af377749f48ff0ae9974b30ce12a816738b94558"
source = "https://github.com/containous/oxy.git"
[[projects]]
@@ -1386,6 +1390,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "bd1e7a1b07d95ff85c675468bbfc4bc7a91c39cf1feceeb58dfcdba9592180a5"
inputs-digest = "7122deb7b2056ecfa444c5fb64437c1b0723afae528a331c4b032b841039383b"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -64,7 +64,7 @@ ignored = ["github.com/sirupsen/logrus"]
[[constraint]]
name = "github.com/containous/staert"
version = "2.0.0"
version = "2.1.0"
[[constraint]]
name = "github.com/containous/traefik-extra-service-fabric"
@@ -190,3 +190,8 @@ ignored = ["github.com/sirupsen/logrus"]
# remove override on master
name = "github.com/coreos/bbolt"
revision = "32c383e75ce054674c53b5a07e55de85332aee14"
[prune]
non-go = true
go-tests = true
unused-packages = true

View File

@@ -127,7 +127,11 @@ fmt:
pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
prune-dep:
dep-ensure:
dep ensure -v
./script/prune-dep.sh
dep-prune:
./script/prune-dep.sh
help: ## this help

View File

@@ -12,7 +12,7 @@
[![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy)
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Træfik (pronounced like _traffic_) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
It supports several backends ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), and a lot more) to manage its configuration automatically and dynamically.
---

View File

@@ -222,6 +222,24 @@ func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate,
return nil, false
}
func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
domainsCertificatesMap := make(map[string]*tls.Certificate)
for _, domainCertificate := range dc.Certs {
certKey := domainCertificate.Domains.Main
if domainCertificate.Domains.SANs != nil {
sort.Strings(domainCertificate.Domains.SANs)
for _, dnsName := range domainCertificate.Domains.SANs {
if dnsName != domainCertificate.Domains.Main {
certKey += fmt.Sprintf(",%s", dnsName)
}
}
}
domainsCertificatesMap[certKey] = domainCertificate.tlsCert
}
return domainsCertificatesMap
}
// DomainsCertificate contains a certificate for multiple domains
type DomainsCertificate struct {
Domains Domain

View File

@@ -114,6 +114,20 @@ type Domain struct {
}
func (a *ACME) init() error {
// FIXME temporary fix, waiting for https://github.com/xenolf/lego/pull/478
acme.HTTPClient = http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 15 * time.Second,
ResponseHeaderTimeout: 15 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
if a.ACMELogging {
acme.Logger = fmtlog.New(os.Stderr, "legolog: ", fmtlog.LstdFlags)
} else {
@@ -281,6 +295,7 @@ func (a *ACME) leadershipListener(elected bool) error {
// CreateLocalConfig creates a tls.config using local ACME configuration
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
defer a.runJobs()
err := a.init()
if err != nil {
return err
@@ -319,7 +334,9 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
a.client, err = a.buildACMEClient(account)
if err != nil {
return err
log.Errorf(`Failed to build ACME client: %s
Let's Encrypt functionality will be limited until traefik is restarted.`, err)
return nil
}
if needRegister {
@@ -360,7 +377,6 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
a.retrieveCertificates()
a.renewCertificates()
a.runJobs()
ticker := time.NewTicker(24 * time.Hour)
safe.Go(func() {
@@ -375,7 +391,7 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
domain := types.CanonicalDomain(clientHello.ServerName)
account := a.store.Get().(*Account)
if providedCertificate := a.getProvidedCertificate([]string{domain}); providedCertificate != nil {
if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
return providedCertificate, nil
}
@@ -609,11 +625,6 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
// Check provided certificates
if a.getProvidedCertificate(domains) != nil {
return
}
operation := func() error {
if a.client == nil {
return errors.New("ACME client still not built")
@@ -631,32 +642,34 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
return
}
account := a.store.Get().(*Account)
var domain Domain
if len(domains) > 1 {
domain = Domain{Main: domains[0], SANs: domains[1:]}
} else {
domain = Domain{Main: domains[0]}
}
if _, exists := account.DomainsCertificate.exists(domain); exists {
// domain already exists
// Check provided certificates
uncheckedDomains := a.getUncheckedDomains(domains, account)
if len(uncheckedDomains) == 0 {
return
}
certificate, err := a.getDomainsCertificates(domains)
certificate, err := a.getDomainsCertificates(uncheckedDomains)
if err != nil {
log.Errorf("Error getting ACME certificates %+v : %v", domains, err)
log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
return
}
log.Debugf("Got certificate for domains %+v", domains)
log.Debugf("Got certificate for domains %+v", uncheckedDomains)
transaction, object, err := a.store.Begin()
if err != nil {
log.Errorf("Error creating transaction %+v : %v", domains, err)
log.Errorf("Error creating transaction %+v : %v", uncheckedDomains, err)
return
}
var domain Domain
if len(uncheckedDomains) > 1 {
domain = Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
} else {
domain = Domain{Main: uncheckedDomains[0]}
}
account = object.(*Account)
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
if err != nil {
log.Errorf("Error adding ACME certificates %+v : %v", domains, err)
log.Errorf("Error adding ACME certificates %+v : %v", uncheckedDomains, err)
return
}
if err = transaction.Commit(account); err != nil {
@@ -668,36 +681,95 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
log.Debugf("Looking for provided certificate to validate %s...", domains)
cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate)
if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate))
}
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
if cert == nil {
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
}
return cert
}
func searchProvidedCertificateForDomains(domains []string, certs map[string]*tls.Certificate) *tls.Certificate {
func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Certificate) *tls.Certificate {
// Use regex to test for provided certs that might have been added into TLSConfig
providedCertMatch := false
for k := range certs {
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$"
for _, domainToCheck := range domains {
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck)
if !providedCertMatch {
for certDomains := range certs {
domainCheck := false
for _, certDomain := range strings.Split(certDomains, ",") {
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
domainCheck, _ = regexp.MatchString(selector, domain)
if domainCheck {
break
}
}
if providedCertMatch {
log.Debugf("Got provided certificate for domains %s", domains)
return certs[k]
if domainCheck {
log.Debugf("Domain %q checked by provided certificate %q", domain, certDomains)
return certs[certDomains]
}
}
return nil
}
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string {
log.Debugf("Looking for provided certificate to validate %s...", domains)
allCerts := make(map[string]*tls.Certificate)
// Get static certificates
for domains, certificate := range a.TLSConfig.NameToCertificate {
allCerts[domains] = certificate
}
// Get dynamic certificates
if a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
for domains, certificate := range a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate) {
allCerts[domains] = certificate
}
}
// Get ACME certificates
if account != nil {
for domains, certificate := range account.DomainsCertificate.toDomainsMap() {
allCerts[domains] = certificate
}
}
return searchUncheckedDomains(domains, allCerts)
}
func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate) []string {
uncheckedDomains := []string{}
for _, domainToCheck := range domains {
domainCheck := false
for certDomains := range certs {
domainCheck = false
for _, certDomain := range strings.Split(certDomains, ",") {
// Use regex to test for provided certs that might have been added into TLSConfig
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
domainCheck, _ = regexp.MatchString(selector, domainToCheck)
if domainCheck {
break
}
}
if domainCheck {
break
}
}
if !domainCheck {
uncheckedDomains = append(uncheckedDomains, domainToCheck)
}
}
if len(uncheckedDomains) == 0 {
log.Debugf("No ACME certificate to generate for domains %q.", domains)
} else {
log.Debugf("Domains %q need ACME certificates generation for domains %q.", domains, strings.Join(uncheckedDomains, ","))
}
return uncheckedDomains
}
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
domains = fun.Map(types.CanonicalDomain, domains).([]string)
log.Debugf("Loading ACME certificates %s...", domains)

View File

@@ -281,7 +281,7 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
}
}
func TestAcme_getProvidedCertificate(t *testing.T) {
func TestAcme_getUncheckedCertificates(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
@@ -289,9 +289,36 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domains := []string{"traefik.containo.us", "trae.containo.us"}
certificate := a.getProvidedCertificate(domains)
assert.NotNil(t, certificate)
uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"}
certificate = a.getProvidedCertificate(domains)
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
{
tlsCert: &tls.Certificate{},
Domains: Domain{
Main: "*.acme.wtf",
SANs: []string{"trae.acme.io"},
},
},
}}
account := Account{DomainsCertificate: domainsCertificates}
uncheckedDomains = a.getUncheckedDomains(domains, &account)
assert.Empty(t, uncheckedDomains)
}
func TestAcme_getProvidedCertificate(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domain := "traefik.containo.us"
certificate := a.getProvidedCertificate(domain)
assert.NotNil(t, certificate)
domain = "trae.acme.io"
certificate = a.getProvidedCertificate(domain)
assert.Nil(t, certificate)
}

View File

@@ -441,9 +441,9 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
{{if $frontend.Redirect}}
[frontends."{{$frontendName}}".redirect]
entryPoint = "{{$frontend.RedirectEntryPoint}}"
regex = "{{$frontend.RedirectRegex}}"
replacement = "{{$frontend.RedirectReplacement}}"
entryPoint = "{{$frontend.Redirect.EntryPoint}}"
regex = "{{$frontend.Redirect.Regex}}"
replacement = "{{$frontend.Redirect.Replacement}}"
{{end}}
{{ if $frontend.Headers }}
@@ -522,7 +522,7 @@ func templatesKubernetesTmpl() (*asset, error) {
var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
{{$backends := List .Prefix "/backends/"}}
{{$tlsconfiguration := List .Prefix "/tlsconfiguration/"}}
{{$tls := List .Prefix "/tls/"}}
[backends]{{range $backends}}
{{$backend := .}}
@@ -587,13 +587,13 @@ var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
{{end}}
{{end}}
{{range $tlsconfiguration}}
{{range $tls}}
{{$entryPoints := SplitGet . "/entrypoints"}}
[[tlsConfiguration]]
[[tls]]
entryPoints = [{{range $entryPoints}}
"{{.}}",
{{end}}]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = """{{Get "" . "/certificate" "/certfile"}}"""
keyFile = """{{Get "" . "/certificate" "/keyfile"}}"""
{{end}}

View File

@@ -4,22 +4,20 @@ RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
&& rm -rf /var/cache/apk/*
RUN go get github.com/jteeuwen/go-bindata/... \
RUN go get github.com/containous/go-bindata/... \
&& go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck \
&& go get github.com/client9/misspell/cmd/misspell
# Which docker version to test on
ARG DOCKER_VERSION=17.03.2
ARG DEP_VERSION=0.3.2
ARG DEP_VERSION=0.4.1
# Download dep binary to bin folder in $GOPATH
RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \
&& chmod +x /usr/local/bin/dep
# Download docker
RUN mkdir -p /usr/local/bin \
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}-ce.tgz \

View File

@@ -61,7 +61,8 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
// TODO: Deprecated - default Metrics
defaultWeb.Metrics = &types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
EntryPoint: configuration.DefaultInternalEntryPointName,
},
Datadog: &types.Datadog{
Address: "localhost:8125",
@@ -220,7 +221,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
defaultMetrics := types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
EntryPoint: "traefik",
EntryPoint: configuration.DefaultInternalEntryPointName,
},
Datadog: &types.Datadog{
Address: "localhost:8125",
@@ -286,6 +287,9 @@ func NewTraefikConfiguration() *TraefikConfiguration {
HealthCheck: &configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
},
LifeCycle: &configuration.LifeCycle{
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
},
CheckNewVersion: true,
},
ConfigFile: "",

View File

@@ -29,11 +29,6 @@ func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
return func() error {
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
if traefikConfiguration.Ping == nil {
fmt.Println("Please enable `ping` to use healtcheck.")
os.Exit(1)
}
resp, errPing := healthCheck(traefikConfiguration.GlobalConfiguration)
if errPing != nil {
fmt.Printf("Error calling healthcheck: %s\n", errPing)
@@ -50,9 +45,13 @@ func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
}
func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
if globalConfiguration.Ping == nil {
return nil, errors.New("please enable `ping` to use health check")
}
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
if !ok {
return nil, errors.New("missing ping entrypoint")
return nil, errors.New("missing `ping` entrypoint")
}
client := &http.Client{Timeout: 5 * time.Second}

View File

@@ -28,6 +28,7 @@ import (
"github.com/containous/traefik/types"
"github.com/containous/traefik/version"
"github.com/coreos/go-systemd/daemon"
"github.com/ogier/pflag"
)
func main() {
@@ -75,6 +76,9 @@ Complete documentation is available at https://traefik.io`,
}
if _, err := f.Parse(usedCmd); err != nil {
if err == pflag.ErrHelp {
os.Exit(0)
}
fmtlog.Printf("Error parsing command: %s\n", err)
os.Exit(-1)
}
@@ -142,6 +146,7 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
globalConfiguration.SetEffectiveConfiguration(configFile)
globalConfiguration.ValidateConfiguration()
jsonConf, _ := json.Marshal(globalConfiguration)
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
@@ -261,14 +266,14 @@ func stats(globalConfiguration *configuration.GlobalConfiguration) {
Stats collection is enabled.
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
Help us improve Traefik by leaving this feature on :)
More details on: https://docs.traefik.io/basic/#collected-data
More details on: https://docs.traefik.io/basics/#collected-data
`)
collect(globalConfiguration)
} else {
log.Info(`
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://docs.traefik.io/basic/#collected-data
More details on: https://docs.traefik.io/basics/#collected-data
`)
}
}

View File

@@ -259,6 +259,19 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
}
}
// ValidateConfiguration validate that configuration is coherent
func (gc *GlobalConfiguration) ValidateConfiguration() {
if gc.ACME != nil {
if _, ok := gc.EntryPoints[gc.ACME.EntryPoint]; !ok {
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
} else {
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
log.Fatalf("Entrypoint without TLS %q for ACME configuration", gc.ACME.EntryPoint)
}
}
}
}
// DefaultEntryPoints holds default entry points
type DefaultEntryPoints []string

View File

@@ -1,23 +0,0 @@
## Current versions documentation
- [Latest stable](https://docs.traefik.io)
## Future version documentation
- [Experimental](https://master--traefik-docs.netlify.com/)
## Previous versions documentation
- [v1.5 aka Cancoillotte](http://v1-5.archive.docs.traefik.io/)
- [v1.4 aka Roquefort](http://v1-4.archive.docs.traefik.io/)
- [v1.3 aka Raclette](http://v1-3.archive.docs.traefik.io/)
- [v1.2 aka Morbier](http://v1-2.archive.docs.traefik.io/)
- [v1.1 aka Camembert](http://v1-1.archive.docs.traefik.io/)
## More
[Change log](https://github.com/containous/traefik/blob/master/CHANGELOG.md)

View File

@@ -236,7 +236,7 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
You can customize priority by frontend:
You can customize priority by frontend. The priority value is added to the rule length during sorting:
```toml
[frontends]
@@ -254,7 +254,7 @@ You can customize priority by frontend:
rule = "PathPrefix:/toto"
```
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
Here, `frontend1` will be matched before `frontend2` (`(3 + 10 == 13) > (4 + 5 == 9)`).
#### Custom headers
@@ -569,6 +569,11 @@ Each command is described at the beginning of the help section:
```bash
traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:1.5 --help
```
### Command: bug
@@ -612,6 +617,7 @@ Those data help us prioritize our developments and focus on what's more importan
### What ?
Once a day (the first call begins 10 minutes after the start of Træfik), we collect:
- the Træfik version
- a hash of the configuration
- an **anonymous version** of the static configuration:

View File

@@ -113,7 +113,7 @@ entryPoint = "https"
# Required
#
entryPoint = "http"
# Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge
#
# Optional
@@ -135,6 +135,7 @@ entryPoint = "https"
#
# delayBeforeCheck = 0
```
!!! note
Even if `TLS-SNI-01` challenge is [disabled](https://community.letsencrypt.org/t/2018-01-11-update-regarding-acme-tls-sni-and-shared-hosting-infrastructure/50188) for the moment, it stays the _by default_ ACME Challenge in Træfik.
If `TLS-SNI-01` challenge is not re-enabled in the future, it we will be removed from Træfik.
@@ -144,6 +145,19 @@ entryPoint = "https"
If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80.
These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
### Let's Encrypt downtime
Let's Encrypt functionality will be limited until Træfik is restarted.
If Let's Encrypt is not reachable, these certificates will be used :
- ACME certificates already generated before downtime
- Expired ACME certificates
- Provided certificates
!!! note
Default Træfik certificate will be used instead of ACME certificates for new (sub)domains (which need Let's Encrypt challenge).
### `storage`
```toml
@@ -153,27 +167,14 @@ storage = "acme.json"
# ...
```
File or key used for certificates storage.
The `storage` option sets where are stored your ACME certificates.
**WARNING:** If you use Træfik in Docker, you have 2 options:
There are two kind of `storage` :
- create a file on your host and mount it as a volume:
```toml
storage = "acme.json"
```
```bash
docker run -v "/my/host/acme.json:acme.json" traefik
```
- a JSON file,
- a KV store entry.
- mount the folder containing the file as a volume
```toml
storage = "/etc/traefik/acme/acme.json"
```
```bash
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
```
!!! note
!!! danger "DEPRECATED"
`storage` replaces `storageFile` which is deprecated.
!!! note
@@ -182,10 +183,53 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
- `storageFile` will contain the path to the `acme.json` file to migrate.
- `storage` will contain the key where the certificates will be stored.
#### Store data in a file
ACME certificates can be stored in a JSON file which with the `600` right mode.
There are two ways to store ACME certificates in a file from Docker:
- create a file on your host and mount it as a volume:
```toml
storage = "acme.json"
```
```bash
docker run -v "/my/host/acme.json:acme.json" traefik
```
- mount the folder containing the file as a volume
```toml
storage = "/etc/traefik/acme/acme.json"
```
```bash
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
```
!!! warning
This file cannot be shared per many instances of Træfik at the same time.
If you have to use Træfik cluster mode, please use [a KV Store entry](/configuration/acme/#storage-kv-entry).
#### Store data in a KV store entry
ACME certificates can be stored in a KV Store entry.
```toml
storage = "traefik/acme/account"
```
**This kind of storage is mandatory in cluster mode.**
Because KV stores (like Consul) have limited entries size, the certificates list is compressed before to be set in a KV store entry.
!!! note
It's possible to store up to approximately 100 ACME certificates in Consul.
### `acme.httpChallenge`
Use `HTTP-01` challenge to generate/renew ACME certificates.
The redirection is fully compatible with the HTTP-01 challenge.
You can use redirection with HTTP-01 challenge without problem.
```toml
[acme]
# ...
@@ -199,6 +243,8 @@ entryPoint = "https"
Specify the entryPoint to use during the challenges.
```toml
defaultEntryPoints = ["http", "http"]
[entryPoints]
[entryPoints.http]
address = ":80"
@@ -231,7 +277,7 @@ Use `DNS-01` challenge to generate/renew ACME certificates.
# ...
```
#### `provider`
#### `provider`
Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it:
@@ -273,7 +319,7 @@ Useful if internal networks block external DNS queries.
### `onDemand` (Deprecated)
!!! warning
!!! danger "DEPRECATED"
This option is deprecated.
```toml
@@ -285,11 +331,11 @@ onDemand = true
Enable on demand certificate.
This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate.
This will request a certificate from Let's Encrypt during the first TLS handshake for a host name that does not yet have a certificate.
!!! warning
TLS handshakes will be slow when requesting a hostname certificate for the first time, this can lead to DoS attacks.
TLS handshakes will be slow when requesting a host name certificate for the first time, this can lead to DoS attacks.
!!! warning
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
@@ -302,7 +348,7 @@ onHostRule = true
# ...
```
Enable certificate generation on frontends Host rules.
Enable certificate generation on frontends `Host` rules (for frontends wired on the `acme.entryPoint`).
This will request a certificate from Let's Encrypt for each frontend with a Host rule.
@@ -350,12 +396,12 @@ Each domain & SANs will lead to a certificate request.
### `dnsProvider` (Deprecated)
!!! warning
!!! danger "DEPRECATED"
This option is deprecated.
Please refer to [DNS challenge provider section](/configuration/acme/#provider)
### `delayDontCheckDNS` (Deprecated)
!!! warning
!!! danger "DEPRECATED"
This option is deprecated.
Please refer to [DNS challenge delayBeforeCheck section](/configuration/acme/#delaybeforecheck)

View File

@@ -1,5 +1,7 @@
# API Definition
## Configuration
```toml
# API definition
[api]
@@ -28,6 +30,8 @@
debug = true
```
For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check).
## Web UI
![Web UI Providers](/img/web.frontend.png)
@@ -42,7 +46,7 @@
| `/health` | `GET` | json health metrics |
| `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider (1) |
| `/api/providers/{provider}/backends` | `GET` | List backends |
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend |
| `/api/providers/{provider}/backends/{backend}/servers` | `GET` | List servers in backend |
@@ -52,6 +56,8 @@
| `/api/providers/{provider}/frontends/{frontend}/routes` | `GET` | List routes in a frontend |
| `/api/providers/{provider}/frontends/{frontend}/routes/{route}` | `GET` | Get a route in a frontend |
<1> See [Rest](/configuration/backends/rest/#api) for more information.
!!! warning
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
But be careful, in the configuration for all providers the key is still `web`.
@@ -185,6 +191,7 @@ curl -s "http://localhost:8080/health" | jq .
## Metrics
You can enable Traefik to export internal metrics to different monitoring systems.
```toml
[api]
# ...

View File

@@ -1,6 +1,140 @@
# File Backends
Like any other reverse proxy, Træfik can be configured with a file.
Træfik can be configured with a file.
## Reference
```toml
# Backends
[backends]
[backends.backend1]
[backends.backend1.servers]
[backends.backend1.servers.server0]
url = "http://10.10.10.1:80"
weight = 1
[backends.backend1.servers.server1]
url = "http://10.10.10.2:80"
weight = 2
# ...
[backends.backend1.circuitBreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.loadBalancer]
method = "drr"
[backends.backend1.loadBalancer.stickiness]
cookieName = "foobar"
[backends.backend1.maxConn]
amount = 10
extractorfunc = "request.host"
[backends.backend1.healthCheck]
path = "/health"
port = 88
interval = "30s"
[backends.backend2]
# ...
# Frontends
[frontends]
[frontends.frontend1]
entryPoints = ["http", "https"]
backend = "backend1"
passHostHeader = true
passTLSCert = true
priority = 42
basicAuth = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
[frontends.frontend1.routes]
[frontends.frontend1.routes.route0]
rule = "Host:test.localhost"
[frontends.frontend1.routes.Route1]
rule = "Method:GET"
# ...
[frontends.frontend1.headers]
allowedHosts = ["foobar", "foobar"]
hostsProxyHeaders = ["foobar", "foobar"]
SSLRedirect = true
SSLTemporaryRedirect = true
SSLHost = "foobar"
STSSeconds = 42
STSIncludeSubdomains = true
STSPreload = true
forceSTSHeader = true
frameDeny = true
customFrameOptionsValue = "foobar"
contentTypeNosniff = true
browserXSSFilter = true
contentSecurityPolicy = "foobar"
publicKey = "foobar"
referrerPolicy = "foobar"
isDevelopment = true
[frontends.frontend1.headers.customRequestHeaders]
X-Foo-Bar-01 = "foobar"
X-Foo-Bar-02 = "foobar"
# ...
[frontends.frontend1.headers.customResponseHeaders]
X-Foo-Bar-03 = "foobar"
X-Foo-Bar-04 = "foobar"
# ...
[frontends.frontend1.headers.SSLProxyHeaders]
X-Foo-Bar-05 = "foobar"
X-Foo-Bar-06 = "foobar"
# ...
[frontends.frontend1.errors]
[frontends.frontend1.errors.errorPage0]
status = ["500-599"]
backend = "error"
query = "/{status}.html"
[frontends.frontend1.errors.errorPage1]
status = ["404", "403"]
backend = "error"
query = "/{status}.html"
# ...
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
# ...
[frontends.frontend1.redirect]
entryPoint = "https"
regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1"
[frontends.frontend2]
# ...
# HTTPS certificates
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[tls]]
# ...
```
## Configuration mode
You have three choices:
@@ -12,7 +146,7 @@ To enable the file backend, you must either pass the `--file` option to the Træ
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik).
## Simple
### Simple
Add your configuration at the end of the global configuration file `traefik.toml`:
@@ -21,167 +155,93 @@ defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
# ...
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.org.cert"
keyFile = "integration/fixtures/https/snitest.org.key"
# ...
[file]
# rules
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
# ...
[backends.backend2]
[backends.backend2.maxconn]
amount = 10
extractorfunc = "request.host"
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
# ...
[frontends]
[frontends.frontend1]
backend = "backend2"
[frontends.frontend1.routes.test_1]
rule = "Host:test.localhost"
# ...
[frontends.frontend2]
backend = "backend1"
passHostHeader = true
priority = 10
# restrict access to this frontend to the specified list of IPv4/IPv6 CIDR Nets
# an unset or empty list allows all Source-IPs to access
# if one of the Net-Specifications are invalid, the whole list is invalid
# and allows all Source-IPs to access.
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost"
# ...
[frontends.frontend3]
entrypoints = ["http", "https"] # overrides defaultEntryPoints
backend = "backend2"
rule = "Path:/test"
# ...
# HTTPS certificate
[[tlsConfiguration]]
entryPoints = ["https"]
[tlsConfiguration.certificate]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
[[tls]]
# ...
[[tls]]
# ...
```
!!! note
adding certificates directly to the entrypoint is still maintained but certificates declared in this way cannot be managed dynamically.
It's recommended to use the file provider to declare certificates.
## Rules in a Separate File
### Rules in a Separate File
Put your rules in a separate file, for example `rules.toml`:
```toml
# traefik.toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
# ...
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
# ...
[file]
filename = "rules.toml"
filename = "rules.toml"
```
```toml
# rules.toml
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
# ...
[backends.backend2]
[backends.backend2.maxconn]
amount = 10
extractorfunc = "request.host"
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
# ...
[frontends]
[frontends.frontend1]
backend = "backend2"
[frontends.frontend1.routes.test_1]
rule = "Host:test.localhost"
# ...
[frontends.frontend2]
backend = "backend1"
passHostHeader = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost"
# ...
[frontends.frontend3]
entrypoints = ["http", "https"] # overrides defaultEntryPoints
backend = "backend2"
rule = "Path:/test"
# HTTPS certificate
[[tlsConfiguration]]
entryPoints = ["https"]
[tlsConfiguration.certificate]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
# ...
[[tlsConfiguration]]
entryPoints = ["https"]
[[tlsConfiguration.certificates]]
certFile = "integration/fixtures/https/snitest.org.cert"
keyFile = "integration/fixtures/https/snitest.org.key"
# HTTPS certificate
[[tls]]
# ...
[[tls]]
# ...
```
## Multiple `.toml` Files
### Multiple `.toml` Files
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
```toml
[file]
directory = "/path/to/config/"
directory = "/path/to/config/"
```
If you want Træfik to watch file changes automatically, just add:
```toml
[file]
watch = true
watch = true
```

View File

@@ -150,7 +150,7 @@ The following security annotations are applicable on the Ingress object:
| `ingress.kubernetes.io/ssl-host:HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. |
| `ngress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. |
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. |

View File

@@ -36,7 +36,6 @@ address = ":8080"
#
readOnly = true
# Set the root path for webui and API
#
# Deprecated
@@ -55,13 +54,13 @@ readOnly = true
### Authentication
!!! note
The `/ping` path of the api is excluded from authentication (since 1.4).
The `/ping` path of the API is excluded from authentication (since 1.4).
#### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
Users can be specified directly in the toml file, or indirectly by referencing an external file;
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence.
```toml
@@ -80,7 +79,7 @@ usersFile = "/path/to/.htpasswd"
You can use `htdigest` to generate those ones.
Users can be specified directly in the toml file, or indirectly by referencing an external file;
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence
```toml
@@ -98,7 +97,7 @@ usersFile = "/path/to/.htdigest"
## Metrics
You can enable Traefik to export internal metrics to different monitoring systems.
You can enable Træfik to export internal metrics to different monitoring systems.
### Prometheus
@@ -114,7 +113,7 @@ You can enable Traefik to export internal metrics to different monitoring system
# Optional
# Default: [0.1, 0.3, 1.2, 5]
buckets=[0.1,0.3,1.2,5.0]
# ...
```
@@ -221,7 +220,7 @@ recentErrors = 10
|-----------------------------------------------------------------|:-------------:|----------------------------------------------------------------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
| `/health` | `GET` | json health metrics |
| `/health` | `GET` | JSON health metrics |
| `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
@@ -244,7 +243,7 @@ curl -sv "http://localhost:8080/ping"
```
```shell
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
* Connected to localhost (::1) port 8080 (\#0)
> GET /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
@@ -255,7 +254,7 @@ curl -sv "http://localhost:8080/ping"
< Content-Length: 2
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
* Connection \#0 to host localhost left intact
OK
```
@@ -309,7 +308,7 @@ curl -s "http://localhost:8080/health" | jq .
"status": "Internal Server Error",
// request HTTP method
"method": "GET",
// request hostname
// request host name
"host": "localhost",
// request path
"path": "/path",
@@ -385,23 +384,28 @@ curl -s "http://localhost:8080/api" | jq .
}
```
## Path
### Deprecation compatibility
As web is deprecated, you can handle the `Path` option like this
#### Path
As the web provider is deprecated, you can handle the `Path` option like this:
```toml
[entrypoints.http]
address=":80"
defaultEntryPoints = ["http"]
[entrypoints.dashboard]
address=":8080"
[entryPoints]
[entryPoints.http]
address = ":80"
[entrypoints.api]
address=":8081"
[entryPoints.dashboard]
address = ":8080"
#Activate API and Dashboard
[entryPoints.api]
address = ":8081"
# Activate API and Dashboard
[api]
entrypoint="api"
entryPoint = "api"
[file]
[backends]
@@ -411,8 +415,67 @@ entrypoint="api"
[frontends]
[frontends.frontend1]
entrypoints=["dashboard"]
entryPoints = ["dashboard"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
```
```
#### Address
As the web provider is deprecated, you can handle the `Address` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.ping]
address = ":8082"
[entryPoints.api]
address = ":8083"
[ping]
entryPoint = "ping"
[api]
entryPoint = "api"
```
In the above example, you would access a regular path, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/foo`
* Admin Panel: `http://hostname:8083/`
* Ping URL: `http://hostname:8082/ping`
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via that entry point.
#### Authentication
As the web provider is deprecated, you can handle the `auth` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.api]
address=":8080"
[entryPoints.api.auth]
[entryPoints.api.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[api]
entrypoint="api"
```
For more information, see [entry points](/configuration/entrypoints/) .

View File

@@ -285,21 +285,17 @@ Multiple sets of rates can be added to each frontend, but the time periods must
```toml
[frontends]
[frontends.frontend1]
passHostHeader = true
entrypoints = ["http"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "Path:/"
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
# ...
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
```
In the above example, frontend1 is configured to limit requests by the client's ip address.

View File

@@ -1,5 +1,123 @@
# Entry Points Definition
## Reference
### TOML
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
compress = true
[entryPoints.http.tls]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
[[entryPoints.http.tls.certificates]]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[entryPoints.http.tls.certificates]]
certFile = "path/to/other.cert"
keyFile = "path/to/other.key"
# ...
[entryPoints.http.tls.clientCA]
files = ["path/to/ca1.crt", "path/to/ca2.crt"]
optional = false
[entryPoints.http.redirect]
entryPoint = "https"
regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1"
[entryPoints.http.auth]
headerField = "X-WebAuth-User"
[entryPoints.http.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
usersFile = "/path/to/.htpasswd"
[entryPoints.http.auth.digest]
users = [
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
]
usersFile = "/path/to/.htdigest"
[entryPoints.http.auth.forward]
address = "https://authserver.com/auth"
trustForwardHeader = true
[entryPoints.http.auth.forward.tls]
ca = [ "path/to/local.crt"]
caOptional = true
cert = "path/to/foo.cert"
key = "path/to/foo.key"
insecureSkipVerify = true
[entryPoints.http.proxyProtocol]
insecure = true
trustedIPs = ["10.10.10.1", "10.10.10.2"]
[entryPoints.http.forwardedHeaders]
trustedIPs = ["10.10.10.1", "10.10.10.2"]
[entryPoints.https]
# ...
```
### CLI
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
```shell
--entryPoints='Name:http Address::80'
--entryPoints='Name:https Address::443 TLS'
```
!!! note
Whitespace is used as option separator and `,` is used as value separator for the list.
The names of the options are case-insensitive.
In compose file the entrypoint syntax is different:
```yaml
traefik:
image: traefik
command:
- --defaultentrypoints=powpow
- "--entryPoints=Name:powpow Address::42 Compress:true"
```
or
```yaml
traefik:
image: traefik
command: --defaultentrypoints=powpow --entryPoints='Name:powpow Address::42 Compress:true'
```
#### All available options:
```ini
Name:foo
Address::80
TLS:goo,gii
TLS
CA:car
CA.Optional:true
Redirect.EntryPoint:https
Redirect.Regex:http://localhost/(.*)
Redirect.Replacement:http://mydomain/$1
Compress:true
WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
ProxyProtocol.TrustedIPs:192.168.0.1
ProxyProtocol.Insecure:tue
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
```
## Basic
```toml
# Entrypoints definition
#
@@ -51,10 +169,16 @@ To redirect an entrypoint rewriting the URL.
```
!!! note
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case).
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an `entrypoint` is defined for the redirection (they will not be used in this case).
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
## TLS
### Static Certificates
Define an entrypoint with SNI support.
```toml
@@ -70,6 +194,12 @@ Define an entrypoint with SNI support.
!!! note
If an empty TLS configuration is done, default self-signed certificates are generated.
### Dynamic Certificates
If you need to add or remove TLS certificates while Traefik is started, Dynamic TLS certificates are supported using the [file provider](/configuration/backends/file).
## TLS Mutual Authentication
TLS Mutual Authentication can be `optional` or not.
@@ -100,17 +230,16 @@ In the example below both `snitest.com` and `snitest.org` will require client ce
```
!!! note
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
If this parameter exists, the new ones are not checked.
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
If this parameter exists, the new ones are not checked.
## Authentication
### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate them.
Users can be specified directly in the toml file, or indirectly by referencing an external file;
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence.
```toml
@@ -125,9 +254,9 @@ Users can be specified directly in the toml file, or indirectly by referencing a
### Digest Authentication
You can use `htdigest` to generate those ones.
You can use `htdigest` to generate them.
Users can be specified directly in the toml file, or indirectly by referencing an external file;
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence
```toml
@@ -145,7 +274,7 @@ Users can be specified directly in the toml file, or indirectly by referencing a
This configuration will first forward the request to `http://authserver.com/auth`.
If the response code is 2XX, access is granted and the original request is performed.
Otherwise, the response from the auth server is returned.
Otherwise, the response from the authentication server is returned.
```toml
[entryPoints]
@@ -154,7 +283,7 @@ Otherwise, the response from the auth server is returned.
# To enable forward auth on an entrypoint
[entryPoints.http.auth.forward]
address = "https://authserver.com/auth"
# Trust existing X-Forwarded-* headers.
# Useful with another reverse proxy in front of Traefik.
#
@@ -162,7 +291,7 @@ Otherwise, the response from the auth server is returned.
# Default: false
#
trustForwardHeader = true
# Enable forward auth TLS connection.
#
# Optional
@@ -182,7 +311,10 @@ To specify an https entry point with a minimum TLS version, and specifying an ar
address = ":443"
[entryPoints.https.tls]
minVersion = "VersionTLS12"
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"]
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
@@ -226,7 +358,7 @@ Only IPs in `trustedIPs` will lead to remote client address replacement: you sho
!!! danger
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
Otherwise, it could introduce a security risk in your system by forging requests.
Otherwise, it could introduce a security risk in your system by forging requests.
```toml
[entryPoints]

View File

@@ -1,5 +1,7 @@
# Ping Definition
## Configuration
```toml
# Ping definition
[ping]
@@ -19,7 +21,7 @@
!!! warning
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
### Example
## Example
```shell
curl -sv "http://localhost:8080/ping"

View File

@@ -10,7 +10,7 @@
[![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy)
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Træfik (pronounced like _traffic_) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
It supports several backends ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), and a lot more) to manage its configuration automatically and dynamically.
## Overview

View File

@@ -35,14 +35,14 @@ TL;DR:
```shell
$ traefik \
--entrypoints=Name:http Address::80 Redirect.EntryPoint:https \
--entrypoints=Name:https Address::443 TLS \
--entrypoints='Name:http Address::80 Redirect.EntryPoint:https' \
--entrypoints='Name:https Address::443 TLS' \
--defaultentrypoints=http,https
```
To listen to different ports, we need to create an entry point for each.
The CLI syntax is `--entrypoints=Name:a_name Address:an_ip_or_empty:a_port options`.
The CLI syntax is `--entrypoints='Name:a_name Address:an_ip_or_empty:a_port options'`.
If you want to redirect traffic from one entry point to another, it's the option `Redirect.EntryPoint:entrypoint_name`.
By default, we don't want to configure all our services to listen on http and https, we add a default entry point configuration: `--defaultentrypoints=http,https`.

View File

@@ -23,3 +23,11 @@ A Træfik cluster is based on a manager/worker model.
When starting, Træfik will elect a manager.
If this instance fails, another manager will be automatically elected.
## Træfik cluster and Let's Encrypt
**In cluster mode, ACME certificates have to be stored in [a KV Store entry](/configuration/acme/#storage-kv-entry).**
Thanks to the Træfik cluster mode algorithm (based on [the Raft Consensus Algorithm](https://raft.github.io/)), only one instance will contact Let's encrypt to solve the challenges.
The others instances will get ACME certificate from the KV Store entry.

View File

@@ -69,7 +69,7 @@ networks:
```
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down).
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure its own internal configuration when containers are created (or shut down).
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
Finally, we're giving this container a static name called `traefik`.
@@ -110,7 +110,7 @@ entryPoint = "http"
This is the minimum configuration required to do the following:
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messagse
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messages
- Check for new versions of Træfik periodically
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
@@ -199,7 +199,7 @@ Since the `traefik` container we've created and started earlier is also attached
As mentioned earlier, we don't want containers exposed automatically by Træfik.
The reason behind this is simple: we want to have control over this process ourselves.
Thanks to Docker labels, we can tell Træfik how to create it's internal routing configuration.
Thanks to Docker labels, we can tell Træfik how to create its internal routing configuration.
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
@@ -222,7 +222,7 @@ We use both `container labels` and `service labels`.
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
With the `traefik.enable` label, we tell Træfik to include this container in it's internal configuration.
With the `traefik.enable` label, we tell Træfik to include this container in its internal configuration.
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
Essentially, this is the actual rule used for Layer-7 load balancing.

View File

@@ -91,7 +91,7 @@ entryPoint = "https"
This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com` with described SANs.
Traefik generates these certificates when it starts and it needs to be restart if new domains are added.
Træfik generates these certificates when it starts and it needs to be restart if new domains are added.
### OnHostRule option (with HTTP challenge)
@@ -126,9 +126,9 @@ entryPoint = "https"
This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com`.
Traefik generates these certificates when it starts.
Træfik generates these certificates when it starts.
If a backend is added with a `onHost` rule, Traefik will automatically generate the Let's Encrypt certificate for the new domain.
If a backend is added with a `onHost` rule, Træfik will automatically generate the Let's Encrypt certificate for the new domain (for frontends wired on the `acme.entryPoint`).
### OnDemand option (with HTTP challenge)
@@ -152,11 +152,10 @@ entryPoint = "https"
This configuration allows generating a Let's Encrypt certificate (thanks to `HTTP-01` challenge) during the first HTTPS request on a new domain.
!!! note
This option simplifies the configuration but :
* TLS handshakes will be slow when requesting a hostname certificate for the first time, this can leads to DDoS attacks.
* TLS handshakes will be slow when requesting a host name certificate for the first time, this can leads to DDoS attacks.
* Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
That's why, it's better to use the `onHostRule` option if possible.
@@ -191,7 +190,7 @@ entryPoint = "https"
```
DNS challenge needs environment variables to be executed.
This variables have to be set on the machine/container which host Traefik.
These variables have to be set on the machine/container which host Træfik.
These variables are described [in this section](/configuration/acme/#provider).
@@ -218,7 +217,7 @@ entryPoint = "https"
entryPoint = "http"
```
Traefik will only try to generate a Let's encrypt certificate (thanks to `HTTP-01` challenge) if the domain cannot be checked by the provided certificates.
Træfik will only try to generate a Let's encrypt certificate (thanks to `HTTP-01` challenge) if the domain cannot be checked by the provided certificates.
### Cluster mode
@@ -292,14 +291,14 @@ The `consul` provider contains the configuration.
rule = "Path:/test"
```
## Enable Basic authentication in an entrypoint
## Enable Basic authentication in an entry point
With two user/pass:
- `test`:`test`
- `test2`:`test2`
Passwords are encoded in MD5: you can use htpasswd to generate those ones.
Passwords are encoded in MD5: you can use `htpasswd` to generate them.
```toml
defaultEntryPoints = ["http"]
@@ -337,7 +336,7 @@ providersThrottleDuration = "5s"
idleTimeout = "360s"
```
## Securing Ping Health Check
## Ping Health Check
The `/ping` health-check URL is enabled with the command-line `--ping` or config file option `[ping]`.
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
@@ -346,40 +345,36 @@ Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you woul
* Admin panel: `http://hostname:8080/`
* Ping URL: `http://hostname:8080/ping`
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your admin panel's port.
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your administration panel's port.
In many environments, the security staff may not _allow_ you to expose it.
You have two options:
* Enable `/ping` on a regular entrypoint
* Enable `/ping` on a regular entry point
* Enable `/ping` on a dedicated port
### Enable ping health check on a regular entrypoint
### Enable ping health check on a regular entry point
To proxy `/ping` from a regular entrypoint to the admin one without exposing the panel, do the following:
To proxy `/ping` from a regular entry point to the administration one without exposing the panel, do the following:
```toml
[backends]
[backends.traefik]
[backends.traefik.servers.server1]
url = "http://localhost:8080"
weight = 10
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[ping]
entryPoint = "http"
[frontends]
[frontends.traefikadmin]
backend = "traefik"
[frontends.traefikadmin.routes.ping]
rule = "Path:/ping"
```
The above creates a new backend called `traefik`, listening on `http://localhost:8080`, i.e. the local admin port.
We only expose the admin panel via the `frontend` named `traefikadmin`, and only expose the `/ping` Path.
Be careful with the `traefikadmin` frontend. If you do _not_ specify a `Path:` rule, you would expose the entire dashboard.
The above link `ping` on the `http` entry point and then expose it on port `80`
### Enable ping health check on dedicated port
If you do not want to or cannot expose the health-check on a regular entrypoint - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entrypoint.
Use the following config:
If you do not want to or cannot expose the health-check on a regular entry point - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entry point.
Use the following configuration:
```toml
defaultEntryPoints = ["http"]
@@ -390,32 +385,18 @@ defaultEntryPoints = ["http"]
[entryPoints.ping]
address = ":8082"
[backends]
[backends.traefik]
[backends.traefik.servers.server1]
url = "http://localhost:8080"
weight = 10
[frontends]
[frontends.traefikadmin]
backend = "traefik"
entrypoints = ["ping"]
[frontends.traefikadmin.routes.ping]
rule = "Path:/ping"
[ping]
entryPoint = "ping"
```
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entrypoint, we enable it on a _dedicated_ entrypoint.
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entry point, we enable it on a _dedicated_ entry point.
In the above example, you would access a regular path, admin panel and health-check as follows:
In the above example, you would access a regular path and health-check as follows:
* Regular path: `http://hostname:80/foo`
* Admin panel: `http://hostname:8080/`
* Ping URL: `http://hostname:8082/ping`
Note the dedicated port `:8082` for `/ping`.
In the above example, it is _very_ important to create a named dedicated entrypoint, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via that entrypoint.
In the above example, we have two entrypoints, `http` and `ping`, but we only included `http` in `defaultEntryPoints`, while explicitly tying `frontend.traefikadmin` to the `ping` entrypoint.
This ensures that all the "normal" frontends will be exposed via entrypoint `http` and _not_ via entrypoint `ping`.
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via this entry point.

View File

@@ -1,6 +1,6 @@
# Key-value store configuration
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store.
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be stored in a Key-value store.
This section explains how to launch Træfik using a configuration loaded from a Key-value store.
@@ -274,14 +274,14 @@ Here is the toml configuration we would like to store in the store :
backend = "backend2"
rule = "Path:/test"
[[tlsConfiguration]]
[[tls]]
entryPoints = ["https"]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = "path/to/your.cert"
keyFile = "path/to/your.key"
[[tlsConfiguration]]
[[tls]]
entryPoints = ["https","other-https"]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = """-----BEGIN CERTIFICATE-----
<cert file content>
-----END CERTIFICATE-----"""
@@ -335,19 +335,19 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
- certificate 1
| Key | Value |
|----------------------------------------------------|--------------------|
| `/traefik/tlsconfiguration/1/entrypoints` | `https` |
| `/traefik/tlsconfiguration/1/certificate/certfile` | `path/to/your.cert`|
| `/traefik/tlsconfiguration/1/certificate/keyfile` | `path/to/your.key` |
| Key | Value |
|---------------------------------------|--------------------|
| `/traefik/tls/1/entrypoints` | `https` |
| `/traefik/tls/1/certificate/certfile` | `path/to/your.cert`|
| `/traefik/tls/1/certificate/keyfile` | `path/to/your.key` |
- certificate 2
| Key | Value |
|----------------------------------------------------|-----------------------|
| `/traefik/tlsconfiguration/2/entrypoints` | `https,other-https` |
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<cert file content>` |
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<key file content>` |
| Key | Value |
|---------------------------------------|-----------------------|
| `/traefik/tls/2/entrypoints` | `https,other-https` |
| `/traefik/tls/2/certificate/certfile` | `<cert file content>` |
| `/traefik/tls/2/certificate/certfile` | `<key file content>` |
### Atomic configuration changes
@@ -408,7 +408,7 @@ Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://1
## Store configuration in Key-value store
!!! note
Don't forget to [setup the connection between Træfik and Key-value store](/user-guide/kv-config/#launch-trfk).
Don't forget to [setup the connection between Træfik and Key-value store](/user-guide/kv-config/#launch-trfik).
The static Træfik configuration in a key-value store can be automatically created and updated, using the [`storeconfig` subcommand](/basics/#commands).
@@ -416,7 +416,7 @@ The static Træfik configuration in a key-value store can be automatically creat
traefik storeconfig [flags] ...
```
This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store).
Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store.
Træfik will not start but the [static configuration](/basics/#static-trfik-configuration) will be uploaded into the Key-value store.
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.

View File

@@ -29,6 +29,8 @@ services :
- bhsm
- bmysql
- brabbitmq
volumes:
- "./rate-limit-policies.yml:/go/src/github.com/letsencrypt/boulder/test/rate-limit-policies.yml:ro"
bhsm:
image: letsencrypt/boulder-tools:2016-11-02

View File

@@ -0,0 +1,42 @@
totalCertificates:
window: 1h
threshold: 100000
certificatesPerName:
window: 1h
threshold: 100000
overrides:
ratelimit.me: 1
lim.it: 0
# Hostnames used by the letsencrypt client integration test.
le.wtf: 10000
le1.wtf: 10000
le2.wtf: 10000
le3.wtf: 10000
nginx.wtf: 10000
good-caa-reserved.com: 10000
bad-caa-reserved.com: 10000
ecdsa.le.wtf: 10000
must-staple.le.wtf: 10000
registrationOverrides:
101: 1000
registrationsPerIP:
window: 1h
threshold: 100000
overrides:
127.0.0.1: 1000000
pendingAuthorizationsPerAccount:
window: 1h
threshold: 100000
certificatesPerFQDNSet:
window: 1h
threshold: 100000
overrides:
le.wtf: 10000
le1.wtf: 10000
le2.wtf: 10000
le3.wtf: 10000
le.wtf,le1.wtf: 10000
good-caa-reserved.com: 10000
nginx.wtf: 10000
ecdsa.le.wtf: 10000
must-staple.le.wtf: 10000

View File

@@ -73,6 +73,8 @@ services:
- bhsm
- bmysql
- brabbitmq
volumes:
- "./rate-limit-policies.yml:/go/src/github.com/letsencrypt/boulder/test/rate-limit-policies.yml:ro"
networks:
net:
ipv4_address: 10.0.1.3

View File

@@ -0,0 +1,42 @@
totalCertificates:
window: 1h
threshold: 100000
certificatesPerName:
window: 1h
threshold: 100000
overrides:
ratelimit.me: 1
lim.it: 0
# Hostnames used by the letsencrypt client integration test.
le.wtf: 10000
le1.wtf: 10000
le2.wtf: 10000
le3.wtf: 10000
nginx.wtf: 10000
good-caa-reserved.com: 10000
bad-caa-reserved.com: 10000
ecdsa.le.wtf: 10000
must-staple.le.wtf: 10000
registrationOverrides:
101: 1000
registrationsPerIP:
window: 1h
threshold: 100000
overrides:
127.0.0.1: 1000000
pendingAuthorizationsPerAccount:
window: 1h
threshold: 100000
certificatesPerFQDNSet:
window: 1h
threshold: 100000
overrides:
le.wtf: 10000
le1.wtf: 10000
le2.wtf: 10000
le3.wtf: 10000
le.wtf,le1.wtf: 10000
good-caa-reserved.com: 10000
nginx.wtf: 10000
ecdsa.le.wtf: 10000
must-staple.le.wtf: 10000

View File

@@ -19,8 +19,7 @@ caServer = "http://traefik.boulder.com:4000/directory"
entryPoint="http"
[web]
address = ":8080"
[api]
[docker]
endpoint = "unix:///var/run/docker.sock"

View File

@@ -26,11 +26,11 @@ curl -i -H "Accept: application/json" -X PUT -d "Path:/test" ht
# certificate 1
curl -i -H "Accept: application/json" -X PUT -d "https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/keyfile
curl -i -H "Accept: application/json" -X PUT -d "https" http://localhost:8500/v1/kv/traefik/tls/pair1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.crt" http://localhost:8500/v1/kv/traefik/tls/pair1/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.key" http://localhost:8500/v1/kv/traefik/tls/pair1/certificate/keyfile
# certificate 2
curl -i -H "Accept: application/json" -X PUT -d "http,https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/keyfile
curl -i -H "Accept: application/json" -X PUT -d "http,https" http://localhost:8500/v1/kv/traefik/tls/pair2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.crt" http://localhost:8500/v1/kv/traefik/tls/pair2/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.key" http://localhost:8500/v1/kv/traefik/tls/pair2/certificate/keyfile

View File

@@ -28,14 +28,14 @@ function insert_etcd2_data() {
curl -i -H "Accept: application/json" -X PUT -d value="Path:/test" http://localhost:2379/v2/keys/traefik/frontends/frontend2/routes/test_2/rule
# certificate 1
curl -i -H "Accept: application/json" -X PUT -d value="https" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.crt" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.key" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/certificate/keyfile
curl -i -H "Accept: application/json" -X PUT -d value="https" http://localhost:2379/v2/keys/traefik/tls/pair1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.crt" http://localhost:2379/v2/keys/traefik/tls/pair1/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.key" http://localhost:2379/v2/keys/traefik/tls/pair1/certificate/keyfile
# certificate 2
curl -i -H "Accept: application/json" -X PUT -d value="http,https" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.crt" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.key" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/certificate/keyfile
curl -i -H "Accept: application/json" -X PUT -d value="http,https" http://localhost:2379/v2/keys/traefik/tls/pair2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.crt" http://localhost:2379/v2/keys/traefik/tls/pair2/certificate/certfile
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.key" http://localhost:2379/v2/keys/traefik/tls/pair2/certificate/keyfile
}
#
@@ -71,14 +71,14 @@ function insert_etcd3_data() {
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend2/routes/test_2/rule" "Path:/test"
# certificate 1
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/entrypoints" "https"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/certificate/certfile" "/tmp/test1.crt"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/certificate/keyfile" "/tmp/test1.key"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair1/entrypoints" "https"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair1/certificate/certfile" "/tmp/test1.crt"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair1/certificate/keyfile" "/tmp/test1.key"
# certificate 2
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/entrypoints" "https"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/certificate/certfile" "/tmp/test2.crt"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/certificate/keyfile" "/tmp/test2.key"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair2/entrypoints" "https"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair2/certificate/certfile" "/tmp/test2.crt"
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tls/pair2/certificate/keyfile" "/tmp/test2.key"
}
function show_usage() {

View File

@@ -92,6 +92,16 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01(c *check.C) {
s.retrieveAcmeCertificate(c, testCase)
}
// Test OnHostRule option with none provided certificate and challenge HTTP-01 and web path
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01WithPath(c *check.C) {
testCase := AcmeTestCase{
traefikConfFilePath: "fixtures/acme/acme_http01_web.toml",
onDemand: false,
domainToCheck: acmeDomain}
s.retrieveAcmeCertificate(c, testCase)
}
// Test OnDemand option with a wildcard provided certificate
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithWildcard(c *check.C) {
testCase := AcmeTestCase{
@@ -132,6 +142,19 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *
s.retrieveAcmeCertificate(c, testCase)
}
// Test Let's encrypt down
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/acme/wrong_acme.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// Expected traefik works
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
// Doing an HTTPS request and test the response certificate
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
file := s.adaptFile(c, testCase.traefikConfFilePath, struct {

View File

@@ -336,3 +336,53 @@ func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
cmd, output := s.traefikCmd("--defaultEntryPoints=http", "--entryPoints=Name:http Address::8000", "--web", "--web.metrics.prometheus.buckets=0.1,0.3,1.2,5.0", "--docker", "--debug")
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/metrics", 1*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)
ipWhoami01 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
ipWhoami02 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{
IP: ipWhoami02,
})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.BodyContains(ipWhoami01))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/file", 1*time.Second, try.BodyContains(ipWhoami02))
c.Assert(err, checker.IsNil)
}

View File

@@ -91,11 +91,11 @@ func (s *ConstraintSuite) TestMatchConstraintGlobal(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -117,11 +117,11 @@ func (s *ConstraintSuite) TestDoesNotMatchConstraintGlobal(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -143,11 +143,11 @@ func (s *ConstraintSuite) TestMatchConstraintProvider(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -169,11 +169,11 @@ func (s *ConstraintSuite) TestDoesNotMatchConstraintProvider(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -196,11 +196,11 @@ func (s *ConstraintSuite) TestMatchMultipleConstraint(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api", "traefik.tags=eu-1"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api", "traefik.tags=eu-1"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -223,11 +223,11 @@ func (s *ConstraintSuite) TestDoesNotMatchMultipleConstraint(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx")
whoami := s.composeProject.Container(c, "whoami")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api", "traefik.tags=us-1"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"traefik.tags=api", "traefik.tags=us-1"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)

View File

@@ -145,9 +145,9 @@ func (s *ConsulCatalogSuite) TestSingleService(c *check.C) {
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
c.Assert(err, checker.IsNil)
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
@@ -157,7 +157,7 @@ func (s *ConsulCatalogSuite) TestSingleService(c *check.C) {
err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
s.deregisterService("test", nginx.NetworkSettings.IPAddress)
s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
c.Assert(err, checker.IsNil)
@@ -175,11 +175,11 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultFalseSingleService(c *check.C)
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -201,16 +201,16 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultFalseSimpleServiceMultipleNode(
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
nginx2 := s.composeProject.Container(c, "nginx2")
whoami := s.composeProject.Container(c, "whoami1")
whoami2 := s.composeProject.Container(c, "whoami2")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"traefik.enable=true"})
err = s.registerService("test", whoami2.NetworkSettings.IPAddress, 80, []string{"traefik.enable=true"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami2.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -232,16 +232,16 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultTrueSimpleServiceMultipleNode(c
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
nginx2 := s.composeProject.Container(c, "nginx2")
whoami := s.composeProject.Container(c, "whoami1")
whoami2 := s.composeProject.Container(c, "whoami2")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"})
err = s.registerService("test", whoami2.NetworkSettings.IPAddress, 80, []string{"name=whoami2"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami2.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -250,7 +250,7 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultTrueSimpleServiceMultipleNode(c
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1", "whoami2"))
c.Assert(err, checker.IsNil)
}
@@ -267,16 +267,16 @@ func (s *ConsulCatalogSuite) TestRefreshConfigWithMultipleNodeWithoutHealthCheck
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
nginx2 := s.composeProject.Container(c, "nginx2")
whoami := s.composeProject.Container(c, "whoami1")
whoami2 := s.composeProject.Container(c, "whoami2")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = s.registerAgentService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"})
err = s.registerAgentService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering agent service"))
defer s.deregisterAgentService(nginx.NetworkSettings.IPAddress)
defer s.deregisterAgentService(whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -285,25 +285,25 @@ func (s *ConsulCatalogSuite) TestRefreshConfigWithMultipleNodeWithoutHealthCheck
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.IsNil)
err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"})
err = s.registerService("test", whoami2.NetworkSettings.IPAddress, 80, []string{"name=whoami2"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1", "whoami2"))
c.Assert(err, checker.IsNil)
s.deregisterService("test", nginx2.NetworkSettings.IPAddress)
s.deregisterService("test", whoami2.NetworkSettings.IPAddress)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.IsNil)
err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"})
err = s.registerService("test", whoami2.NetworkSettings.IPAddress, 80, []string{"name=whoami2"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami2.NetworkSettings.IPAddress)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1", "whoami2"))
c.Assert(err, checker.IsNil)
}
@@ -320,13 +320,13 @@ func (s *ConsulCatalogSuite) TestBasicAuthSimpleService(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{
"traefik.frontend.auth.basic=test:$2a$06$O5NksJPAcgrC9MuANkSoE.Xe9DSg7KcLLFYNr1Lj6hPcMmvgwxhme,test2:$2y$10$xP1SZ70QbZ4K2bTGKJOhpujkpcLxQcB3kEPF6XAV19IdcqsZTyDEe",
})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -357,16 +357,16 @@ func (s *ConsulCatalogSuite) TestRefreshConfigTagChange(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=false", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1", "traefik.enable=false", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.NotNil)
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=true", "traefik.backend.circuitbreaker=ResponseCodeRatio(500, 600, 0, 600) > 0.5"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1", "traefik.enable=true", "traefik.backend.circuitbreaker=ResponseCodeRatio(500, 600, 0, 600) > 0.5"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
@@ -376,7 +376,7 @@ func (s *ConsulCatalogSuite) TestRefreshConfigTagChange(c *check.C) {
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.IsNil)
}
@@ -397,19 +397,19 @@ func (s *ConsulCatalogSuite) TestCircuitBreaker(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
nginx2 := s.composeProject.Container(c, "nginx2")
nginx3 := s.composeProject.Container(c, "nginx3")
whoami := s.composeProject.Container(c, "whoami1")
whoami2 := s.composeProject.Container(c, "whoami2")
whoami3 := s.composeProject.Container(c, "whoami3")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 42, []string{"name=nginx2", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = s.registerService("test", whoami2.NetworkSettings.IPAddress, 42, []string{"name=whoami2", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress)
err = s.registerService("test", nginx3.NetworkSettings.IPAddress, 42, []string{"name=nginx3", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
defer s.deregisterService("test", whoami2.NetworkSettings.IPAddress)
err = s.registerService("test", whoami3.NetworkSettings.IPAddress, 42, []string{"name=whoami3", "traefik.enable=true", "traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx3.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami3.NetworkSettings.IPAddress)
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
@@ -432,9 +432,9 @@ func (s *ConsulCatalogSuite) TestRefreshConfigPortChange(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 81, []string{"name=nginx1", "traefik.enable=true"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 81, []string{"name=whoami1", "traefik.enable=true"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
@@ -444,15 +444,15 @@ func (s *ConsulCatalogSuite) TestRefreshConfigPortChange(c *check.C) {
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusBadGateway))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.IsNil)
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=true"})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{"name=whoami1", "traefik.enable=true"})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
defer s.deregisterService("test", whoami.NetworkSettings.IPAddress)
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("whoami1"))
c.Assert(err, checker.IsNil)
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
@@ -491,9 +491,9 @@ func (s *ConsulCatalogSuite) TestRetryWithConsulServer(c *check.C) {
s.composeProject.Scale(c, "consul", 1)
s.waitToElectConsulLeader()
nginx := s.composeProject.Container(c, "nginx1")
whoami := s.composeProject.Container(c, "whoami1")
// Register service
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80, []string{})
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
// Provider consul catalog should be present

View File

@@ -564,15 +564,15 @@ func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
}
tlsconfigure1 := map[string]string{
"traefik/tlsconfiguration/snitestcom/entrypoints": "https",
"traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
"traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
"traefik/tls/snitestcom/entrypoints": "https",
"traefik/tls/snitestcom/certificate/keyfile": string(snitestComKey),
"traefik/tls/snitestcom/certificate/certfile": string(snitestComCert),
}
tlsconfigure2 := map[string]string{
"traefik/tlsconfiguration/snitestorg/entrypoints": "https",
"traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
"traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
"traefik/tls/snitestorg/entrypoints": "https",
"traefik/tls/snitestorg/certificate/keyfile": string(snitestOrgKey),
"traefik/tls/snitestorg/certificate/certfile": string(snitestOrgCert),
}
// config backends,frontends and first tls keypair
@@ -613,7 +613,7 @@ func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for consul
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
_, err := s.kv.Get("traefik/tls/snitestcom/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)
@@ -642,7 +642,7 @@ func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for consul
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
_, err := s.kv.Get("traefik/tls/snitestorg/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)

View File

@@ -14,7 +14,7 @@ import (
"github.com/docker/docker/pkg/namesgenerator"
"github.com/go-check/check"
d "github.com/libkermit/docker"
docker "github.com/libkermit/docker-check"
"github.com/libkermit/docker-check"
checker "github.com/vdemeester/shakers"
)
@@ -25,8 +25,8 @@ var (
// Images to have or pull before the build in order to make it work
// FIXME handle this offline but loading them before build
RequiredImages = map[string]string{
"swarm": "1.0.0",
"nginx": "1",
"swarm": "1.0.0",
"emilevauge/whoami": "latest",
}
)

View File

@@ -474,15 +474,15 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
}
tlsconfigure1 := map[string]string{
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
"/traefik/tls/snitestcom/entrypoints": "https",
"/traefik/tls/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tls/snitestcom/certificate/certfile": string(snitestComCert),
}
tlsconfigure2 := map[string]string{
"/traefik/tlsconfiguration/snitestorg/entrypoints": "https",
"/traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
"/traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
"/traefik/tls/snitestorg/entrypoints": "https",
"/traefik/tls/snitestorg/certificate/keyfile": string(snitestOrgKey),
"/traefik/tls/snitestorg/certificate/certfile": string(snitestOrgCert),
}
// config backends,frontends and first tls keypair
@@ -523,7 +523,7 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
_, err := s.kv.Get("/traefik/tls/snitestcom/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)
@@ -557,7 +557,7 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
_, err := s.kv.Get("/traefik/tls/snitestorg/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)
@@ -609,9 +609,9 @@ func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
}
tlsconfigure1 := map[string]string{
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
"/traefik/tls/snitestcom/entrypoints": "https",
"/traefik/tls/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tls/snitestcom/certificate/certfile": string(snitestComCert),
}
// config backends,frontends and first tls keypair
@@ -637,7 +637,7 @@ func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
_, err := s.kv.Get("/traefik/tls/snitestcom/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)

View File

@@ -490,15 +490,15 @@ func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
}
tlsconfigure1 := map[string]string{
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
"/traefik/tls/snitestcom/entrypoints": "https",
"/traefik/tls/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tls/snitestcom/certificate/certfile": string(snitestComCert),
}
tlsconfigure2 := map[string]string{
"/traefik/tlsconfiguration/snitestorg/entrypoints": "https",
"/traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
"/traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
"/traefik/tls/snitestorg/entrypoints": "https",
"/traefik/tls/snitestorg/certificate/keyfile": string(snitestOrgKey),
"/traefik/tls/snitestorg/certificate/certfile": string(snitestOrgCert),
}
// config backends,frontends and first tls keypair
@@ -539,7 +539,7 @@ func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
_, err := s.kv.Get("/traefik/tls/snitestcom/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)
@@ -573,7 +573,7 @@ func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
_, err := s.kv.Get("/traefik/tls/snitestorg/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)

View File

@@ -0,0 +1,38 @@
logLevel = "DEBUG"
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":5002"
[entryPoints.https]
address = ":5001"
[entryPoints.https.tls]
[web]
path="/traefik"
[acme]
email = "test@traefik.io"
storage = "/dev/null"
entryPoint = "https"
onDemand = {{.OnDemand}}
OnHostRule = {{.OnHostRule}}
caServer = "http://{{.BoulderHost}}:4000/directory"
[acme.httpchallenge]
entrypoint="http"
[file]
[backends]
[backends.backend]
[backends.backend.servers.server1]
url = "http://127.0.0.1:9010"
[frontends]
[frontends.frontend]
backend = "backend"
[frontends.frontend.routes.test]
rule = "Host:traefik.acme.wtf"

View File

@@ -9,8 +9,8 @@
[frontends.frontend.routes.test]
rule = "Host:traefik.acme.wtf"
[[tlsConfiguration]]
[[tls]]
entryPoints = ["https"]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = "fixtures/acme/ssl/wildcard.crt"
keyFile = "fixtures/acme/ssl/wildcard.key"

View File

@@ -0,0 +1,34 @@
logLevel = "DEBUG"
defaultEntryPoints = ["http", "https"]
[api]
[entryPoints]
[entryPoints.http]
address = ":8081"
[entryPoints.https]
address = ":5001"
[entryPoints.https.tls]
[acme]
email = "test@traefik.io"
storage = "/dev/null"
entryPoint = "https"
OnHostRule = true
caServer = "http://wrongurl:4000/directory"
[file]
[backends]
[backends.backend]
[backends.backend.servers.server1]
url = "http://127.0.0.1:9010"
[frontends]
[frontends.frontend]
backend = "backend"
[frontends.frontend.routes.test]
rule = "Host:traefik.acme.wtf"

View File

@@ -16,9 +16,9 @@
[frontends.frontend2.routes.test_2]
rule = "Host:snitest.org"
[[tlsConfiguration]]
[[tls]]
entryPoints = ["https"]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = """-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIJALAYHG/vGqWEMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
BAMMC3NuaXRlc3Qub3JnMB4XDTE1MTEyMzIyMDU0NFoXDTI1MTEyMDIyMDU0NFow

View File

@@ -0,0 +1,25 @@
defaultEntryPoints = ["http"]
debug=true
[entryPoints]
[entryPoints.http]
address = ":8000"
[api]
[docker]
endpoint = "unix:///var/run/docker.sock"
watch = true
exposedbydefault = false
[file]
[frontends]
[frontends.frontend-1]
backend = "backend-test"
[frontends.frontend-1.routes.test_1]
rule = "PathPrefix:/file"
[backends]
[backends.backend-test]
[backends.backend-test.servers.website]
url = "http://{{ .IP }}"

View File

@@ -624,7 +624,7 @@ func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName, en
// If certificate file is not provided, just truncate the configuration file
if len(certFileName) > 0 {
tlsConf := types.Configuration{
TLSConfiguration: []*traefikTls.Configuration{
TLS: []*traefikTls.Configuration{
{
Certificate: &traefikTls.Certificate{
CertFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".cert"),

View File

@@ -19,7 +19,7 @@ func (s *RateLimitSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "ratelimit")
s.composeProject.Start(c)
s.ServerIP = s.composeProject.Container(c, "nginx1").NetworkSettings.IPAddress
s.ServerIP = s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
}
func (s *RateLimitSuite) TestSimpleConfiguration(c *check.C) {

View File

@@ -3,3 +3,9 @@ whoami1:
labels:
- traefik.enable=true
- traefik.frontend.rule=PathPrefix:/whoami
- traefik.backend="test"
whoami2:
image: emilevauge/whoami
labels:
- traefik.enable=false

View File

@@ -11,7 +11,7 @@ consul:
- "8301/udp"
- "8302"
- "8302/udp"
nginx:
image: nginx:alpine
whoami:
image: emilevauge/whoami
ports:
- "8881:80"

View File

@@ -11,9 +11,9 @@ consul:
- "8301/udp"
- "8302"
- "8302/udp"
nginx1:
image: nginx:alpine
nginx2:
image: nginx:alpine
nginx3:
image: nginx:alpine
whoami1:
image: emilevauge/whoami
whoami2:
image: emilevauge/whoami
whoami3:
image: emilevauge/whoami

View File

@@ -1,4 +1,4 @@
nginx1:
image: nginx:alpine
image: nginx:1.13.8-alpine
nginx2:
image: nginx:alpine
image: nginx:1.13.8-alpine

View File

@@ -1,20 +1,20 @@
nginx1:
image: nginx:alpine
whoami1:
image: emilevauge/whoami
ports:
- "8881:80"
nginx2:
image: nginx:alpine
whoami2:
image: emilevauge/whoami
ports:
- "8882:80"
nginx3:
image: nginx:alpine
whoami3:
image: emilevauge/whoami
ports:
- "8883:80"
nginx4:
image: nginx:alpine
whoami4:
image: emilevauge/whoami
ports:
- "8884:80"
nginx5:
image: nginx:alpine
whoami5:
image: emilevauge/whoami
ports:
- "8885:80"

View File

@@ -1,2 +1,2 @@
nginx1:
image: nginx:alpine
whoami1:
image: emilevauge/whoami

View File

@@ -100,4 +100,3 @@ pages:
- 'gRPC Example': 'user-guide/grpc.md'
- 'Traefik cluster example with Swarm': 'user-guide/cluster-docker-consul.md'
- Benchmarks: benchmarks.md
- 'Archive': 'archive.md'

View File

@@ -95,6 +95,25 @@ func taskSlot(slot int) func(*swarm.Task) {
}
}
func taskNetworkAttachment(id string, name string, driver string, addresses []string) func(*swarm.Task) {
return func(task *swarm.Task) {
task.NetworksAttachments = append(task.NetworksAttachments, swarm.NetworkAttachment{
Network: swarm.Network{
ID: id,
Spec: swarm.NetworkSpec{
Annotations: swarm.Annotations{
Name: name,
},
DriverConfiguration: &swarm.Driver{
Name: driver,
},
},
},
Addresses: addresses,
})
}
}
func taskStatus(ops ...func(*swarm.TaskStatus)) func(*swarm.Task) {
return func(task *swarm.Task) {
status := &swarm.TaskStatus{}
@@ -113,6 +132,14 @@ func taskState(state swarm.TaskState) func(*swarm.TaskStatus) {
}
}
func taskContainerStatus(id string) func(*swarm.TaskStatus) {
return func(status *swarm.TaskStatus) {
status.ContainerStatus = swarm.ContainerStatus{
ContainerID: id,
}
}
}
func swarmService(ops ...func(*swarm.Service)) swarm.Service {
service := &swarm.Service{
ID: "serviceID",

View File

@@ -777,19 +777,21 @@ func listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerD
for _, service := range serviceList {
dockerData := parseService(service, networkMap)
if len(dockerData.NetworkSettings.Networks) > 0 {
useSwarmLB, _ := strconv.ParseBool(getIsBackendLBSwarm(dockerData))
if useSwarmLB {
useSwarmLB, _ := strconv.ParseBool(getIsBackendLBSwarm(dockerData))
if useSwarmLB {
if len(dockerData.NetworkSettings.Networks) > 0 {
dockerDataList = append(dockerDataList, dockerData)
} else {
isGlobalSvc := service.Spec.Mode.Global != nil
dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap, isGlobalSvc)
for _, dockerDataTask := range dockerDataListTasks {
dockerDataList = append(dockerDataList, dockerDataTask)
}
}
} else {
isGlobalSvc := service.Spec.Mode.Global != nil
dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap, isGlobalSvc)
for _, dockerDataTask := range dockerDataListTasks {
dockerDataList = append(dockerDataList, dockerDataTask)
}
}
}
return dockerDataList, err
@@ -805,7 +807,10 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes
if service.Spec.EndpointSpec != nil {
if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeDNSRR {
log.Warnf("Ignored endpoint-mode not supported, service name: %s", service.Spec.Annotations.Name)
useSwarmLB, _ := strconv.ParseBool(getIsBackendLBSwarm(dockerData))
if useSwarmLB {
log.Warnf("Ignored %s endpoint-mode not supported, service name: %s. Fallback to Træfik load balancing", swarmtypes.ResolutionModeDNSRR, service.Spec.Annotations.Name)
}
} else if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeVIP {
dockerData.NetworkSettings.Networks = make(map[string]*networkData)
for _, virtualIP := range service.Endpoint.VirtualIPs {
@@ -844,7 +849,9 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str
continue
}
dockerData := parseTasks(task, serviceDockerData, networkMap, isGlobalSvc)
dockerDataList = append(dockerDataList, dockerData)
if len(dockerData.NetworkSettings.Networks) > 0 {
dockerDataList = append(dockerDataList, dockerData)
}
}
return dockerDataList, err
}

View File

@@ -709,14 +709,19 @@ func TestSwarmTaskParsing(t *testing.T) {
type fakeTasksClient struct {
dockerclient.APIClient
tasks []swarm.Task
err error
tasks []swarm.Task
container dockertypes.ContainerJSON
err error
}
func (c *fakeTasksClient) TaskList(ctx context.Context, options dockertypes.TaskListOptions) ([]swarm.Task, error) {
return c.tasks, c.err
}
func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string) (dockertypes.ContainerJSON, error) {
return c.container, c.err
}
func TestListTasks(t *testing.T) {
testCases := []struct {
service swarm.Service
@@ -728,11 +733,30 @@ func TestListTasks(t *testing.T) {
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1", taskSlot(1), taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id2", taskSlot(2), taskStatus(taskState(swarm.TaskStatePending))),
swarmTask("id3", taskSlot(3)),
swarmTask("id4", taskSlot(4), taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id5", taskSlot(5), taskStatus(taskState(swarm.TaskStateFailed))),
swarmTask("id1",
taskSlot(1),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
swarmTask("id2",
taskSlot(2),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.2"}),
taskStatus(taskState(swarm.TaskStatePending)),
),
swarmTask("id3",
taskSlot(3),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.3"}),
),
swarmTask("id4",
taskSlot(4),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.4"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
swarmTask("id5",
taskSlot(5),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.5"}),
taskStatus(taskState(swarm.TaskStateFailed)),
),
},
isGlobalSVC: false,
expectedTasks: []string{
@@ -753,7 +777,7 @@ func TestListTasks(t *testing.T) {
t.Parallel()
dockerData := parseService(test.service, test.networks)
dockerClient := &fakeTasksClient{tasks: test.tasks}
taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, map[string]*docker.NetworkResource{}, test.isGlobalSVC)
taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, test.networks, test.isGlobalSVC)
if len(test.expectedTasks) != len(taskDockerData) {
t.Errorf("expected tasks %v, got %v", spew.Sdump(test.expectedTasks), spew.Sdump(taskDockerData))
@@ -773,6 +797,7 @@ type fakeServicesClient struct {
dockerVersion string
networks []dockertypes.NetworkResource
services []swarm.Service
tasks []swarm.Task
err error
}
@@ -788,10 +813,15 @@ func (c *fakeServicesClient) NetworkList(ctx context.Context, options dockertype
return c.networks, c.err
}
func (c *fakeServicesClient) TaskList(ctx context.Context, options dockertypes.TaskListOptions) ([]swarm.Task, error) {
return c.tasks, c.err
}
func TestListServices(t *testing.T) {
testCases := []struct {
desc string
services []swarm.Service
tasks []swarm.Task
dockerVersion string
networks []dockertypes.NetworkResource
expectedServices []string
@@ -813,7 +843,8 @@ func TestListServices(t *testing.T) {
swarmService(
serviceName("service2"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
labelDockerNetwork: "barnet",
labelBackendLoadBalancerSwarm: "true",
}),
withEndpointSpec(modeDNSSR)),
},
@@ -838,7 +869,8 @@ func TestListServices(t *testing.T) {
swarmService(
serviceName("service2"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
labelDockerNetwork: "barnet",
labelBackendLoadBalancerSwarm: "true",
}),
withEndpointSpec(modeDNSSR)),
},
@@ -867,14 +899,74 @@ func TestListServices(t *testing.T) {
"service1",
},
},
{
desc: "Should return service1 and service2",
services: []swarm.Service{
swarmService(
serviceName("service1"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
}),
withEndpointSpec(modeVIP),
withEndpoint(
virtualIP("yk6l57rfwizjzxxzftn4amaot", "10.11.12.13/24"),
virtualIP("2", "10.11.12.99/24"),
)),
swarmService(
serviceName("service2"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
}),
withEndpointSpec(modeDNSSR)),
},
tasks: []swarm.Task{
swarmTask("id1",
taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
swarmTask("id2",
taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
},
dockerVersion: "1.30",
networks: []dockertypes.NetworkResource{
{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.network.driver.overlay.vxlanid_list": "4098",
"com.docker.network.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
},
},
},
expectedServices: []string{
"service1.0",
"service1.0",
"service2.0",
"service2.0",
},
},
}
for caseID, test := range testCases {
test := test
t.Run(strconv.Itoa(caseID), func(t *testing.T) {
t.Parallel()
dockerClient := &fakeServicesClient{services: test.services, dockerVersion: test.dockerVersion, networks: test.networks}
serviceDockerData, _ := listServices(context.Background(), dockerClient)
dockerClient := &fakeServicesClient{services: test.services, tasks: test.tasks, dockerVersion: test.dockerVersion, networks: test.networks}
serviceDockerData, err := listServices(context.Background(), dockerClient)
assert.NoError(t, err)
assert.Equal(t, len(test.expectedServices), len(serviceDockerData))
for i, serviceName := range test.expectedServices {

View File

@@ -55,12 +55,12 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
safe.Go(func() {
for t := range ticker.C {
log.Debug("Refreshing Provider " + t.String())
log.Debugf("Refreshing Provider %s", t.String())
configuration, err := p.buildConfiguration()
if err != nil {
log.Errorf("Failed to refresh Provider configuration, error: %s", err)
return
continue
}
configurationChan <- types.ConfigMessage{

View File

@@ -186,7 +186,7 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
}
}
for _, conf := range c.TLSConfiguration {
for _, conf := range c.TLS {
if _, exists := configTLSMaps[conf]; exists {
log.Warnf("TLS Configuration %v already configured, skipping", conf)
} else {
@@ -196,7 +196,7 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
}
for conf := range configTLSMaps {
configuration.TLSConfiguration = append(configuration.TLSConfiguration, conf)
configuration.TLS = append(configuration.TLS, conf)
}
return configuration, nil
}

View File

@@ -26,7 +26,7 @@ func TestProvideSingleFileAndWatch(t *testing.T) {
tempDir, "simple.toml",
createFrontendConfiguration(expectedNumFrontends),
createBackendConfiguration(expectedNumBackends),
createTLSConfiguration(expectedNumTLSConf))
createTLS(expectedNumTLSConf))
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
@@ -45,7 +45,7 @@ func TestProvideSingleFileAndWatch(t *testing.T) {
tempDir, "simple.toml",
createFrontendConfiguration(expectedNumFrontends),
createBackendConfiguration(expectedNumBackends),
createTLSConfiguration(expectedNumTLSConf))
createTLS(expectedNumTLSConf))
err = waitForSignal(signal, 2*time.Second, "single frontend, backend, TLS configuration")
assert.NoError(t, err)
@@ -63,7 +63,7 @@ func TestProvideSingleFileAndNotWatch(t *testing.T) {
tempDir, "simple.toml",
createFrontendConfiguration(expectedNumFrontends),
createBackendConfiguration(expectedNumBackends),
createTLSConfiguration(expectedNumTLSConf))
createTLS(expectedNumTLSConf))
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
@@ -82,7 +82,7 @@ func TestProvideSingleFileAndNotWatch(t *testing.T) {
tempDir, "simple.toml",
createFrontendConfiguration(expectedNumFrontends),
createBackendConfiguration(expectedNumBackends),
createTLSConfiguration(expectedNumTLSConf))
createTLS(expectedNumTLSConf))
// Must fail because we don't watch the changes
err = waitForSignal(signal, 2*time.Second, "single frontend, backend and TLS configuration")
@@ -99,7 +99,7 @@ func TestProvideDirectoryAndWatch(t *testing.T) {
tempFile1 := createRandomFile(t, tempDir, createFrontendConfiguration(expectedNumFrontends))
tempFile2 := createRandomFile(t, tempDir, createBackendConfiguration(expectedNumBackends))
tempFile3 := createRandomFile(t, tempDir, createTLSConfiguration(expectedNumTLSConf))
tempFile3 := createRandomFile(t, tempDir, createTLS(expectedNumTLSConf))
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
@@ -145,7 +145,7 @@ func TestProvideDirectoryAndNotWatch(t *testing.T) {
createRandomFile(t, tempDir, createFrontendConfiguration(expectedNumFrontends))
tempFile2 := createRandomFile(t, tempDir, createBackendConfiguration(expectedNumBackends))
createRandomFile(t, tempTLSDir, createTLSConfiguration(expectedNumTLSConf))
createRandomFile(t, tempTLSDir, createTLS(expectedNumTLSConf))
configurationChan, signal := createConfigurationRoutine(t, &expectedNumFrontends, &expectedNumBackends, &expectedNumTLSConf)
@@ -167,7 +167,7 @@ func TestProvideDirectoryAndNotWatch(t *testing.T) {
}
func createConfigurationRoutine(t *testing.T, expectedNumFrontends *int, expectedNumBackends *int, expectedNumTLSConfigurations *int) (chan types.ConfigMessage, chan interface{}) {
func createConfigurationRoutine(t *testing.T, expectedNumFrontends *int, expectedNumBackends *int, expectedNumTLSes *int) (chan types.ConfigMessage, chan interface{}) {
configurationChan := make(chan types.ConfigMessage)
signal := make(chan interface{})
@@ -177,7 +177,7 @@ func createConfigurationRoutine(t *testing.T, expectedNumFrontends *int, expecte
assert.Equal(t, "file", data.ProviderName)
assert.Len(t, data.Configuration.Frontends, *expectedNumFrontends)
assert.Len(t, data.Configuration.Backends, *expectedNumBackends)
assert.Len(t, data.Configuration.TLSConfiguration, *expectedNumTLSConfigurations)
assert.Len(t, data.Configuration.TLS, *expectedNumTLSes)
signal <- nil
}
})
@@ -297,13 +297,13 @@ func createBackendConfiguration(n int) string {
return conf
}
// createTLSConfiguration Helper
func createTLSConfiguration(n int) string {
// createTLS Helper
func createTLS(n int) string {
var conf string
for i := 1; i <= n; i++ {
conf += fmt.Sprintf(`[[TLSConfiguration]]
conf += fmt.Sprintf(`[[TLS]]
EntryPoints = ["https"]
[TLSConfiguration.Certificate]
[TLS.Certificate]
CertFile = "integration/fixtures/https/snitest%[1]d.com.cert"
KeyFile = "integration/fixtures/https/snitest%[1]d.com.key"
`, i)

View File

@@ -91,19 +91,24 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool
for {
select {
case <-ticker.C:
checkAPI, errAPI := rancherClient.ApiKey.List(withoutPagination)
log.Debugf("Refreshing new Data from Provider API")
var stacks = listRancherStacks(rancherClient)
var services = listRancherServices(rancherClient)
var container = listRancherContainer(rancherClient)
if errAPI != nil {
log.Errorf("Cannot establish connection: %+v, Rancher API return: %+v; Skipping refresh Data from Rancher API.", errAPI, checkAPI)
} else {
log.Debugf("Refreshing new Data from Rancher API")
stacks := listRancherStacks(rancherClient)
services := listRancherServices(rancherClient)
container := listRancherContainer(rancherClient)
rancherData := parseAPISourcedRancherData(stacks, services, container)
rancherData := parseAPISourcedRancherData(stacks, services, container)
configuration := p.loadRancherConfig(rancherData)
if configuration != nil {
configurationChan <- types.ConfigMessage{
ProviderName: "rancher",
Configuration: configuration,
configuration := p.loadRancherConfig(rancherData)
if configuration != nil {
configurationChan <- types.ConfigMessage{
ProviderName: "rancher",
Configuration: configuration,
}
}
}
case <-stop:

View File

@@ -28,14 +28,6 @@ echo $VERSION | git commit --file -
echo $VERSION | git tag -a $VERSION --file -
git push -q --follow-tags -u origin master > /dev/null 2>&1
# create docker image emilevauge/traefik (compatibility)
echo "Updating docker emilevauge/traefik image..."
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker tag containous/traefik emilevauge/traefik:latest
docker push emilevauge/traefik:latest
docker tag emilevauge/traefik:latest emilevauge/traefik:${VERSION}
docker push emilevauge/traefik:${VERSION}
cd ..
rm -Rf traefik-library-image/

View File

@@ -6,8 +6,6 @@ set -o nounset
echo "Prune dependencies"
dep prune
find vendor -name '*_test.go' -exec rm {} \;
find vendor -type f \( ! -iname 'licen[cs]e*' \
@@ -32,3 +30,28 @@ find vendor -type f \( ! -iname 'licen[cs]e*' \
-a ! -iname '*.hpp' \
-a ! -iname '*.hxx' \
-a ! -iname '*.s' \) -exec rm -f {} +
find -type d \( -iname '*Godeps*' \) -exec rm -rf {} +
find vendor -type l \( ! -iname 'licen[cs]e*' \
-a ! -iname '*notice*' \
-a ! -iname '*patent*' \
-a ! -iname '*copying*' \
-a ! -iname '*unlicense*' \
-a ! -iname '*copyright*' \
-a ! -iname '*copyleft*' \
-a ! -iname '*legal*' \
-a ! -iname 'disclaimer*' \
-a ! -iname 'third-party*' \
-a ! -iname 'thirdparty*' \
-a ! -iname '*.go' \
-a ! -iname '*.c' \
-a ! -iname '*.S' \
-a ! -iname '*.cc' \
-a ! -iname '*.cpp' \
-a ! -iname '*.cxx' \
-a ! -iname '*.h' \
-a ! -iname '*.hh' \
-a ! -iname '*.hpp' \
-a ! -iname '*.hxx' \
-a ! -iname '*.s' \) -exec rm -f {} +

View File

@@ -4,7 +4,7 @@ set -e
export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export DEST=.
TESTFLAGS="${TESTFLAGS} -test.timeout=9m -check.v"
TESTFLAGS="${TESTFLAGS} -test.timeout=20m -check.v"
if [ -n "$VERBOSE" ]; then
TESTFLAGS="${TESTFLAGS} -v"

View File

@@ -66,6 +66,7 @@ type Server struct {
stopChan chan bool
providers []provider.Provider
currentConfigurations safe.Safe
providerConfigUpdateMap map[string]chan types.ConfigMessage
globalConfiguration configuration.GlobalConfiguration
accessLoggerMiddleware *accesslog.LogHandler
routinesPool *safe.Pool
@@ -105,6 +106,7 @@ func NewServer(globalConfiguration configuration.GlobalConfiguration) *Server {
server.configureSignals()
currentConfigurations := make(types.Configurations)
server.currentConfigurations.Set(currentConfigurations)
server.providerConfigUpdateMap = make(map[string]chan types.ConfigMessage)
server.globalConfiguration = globalConfiguration
if server.globalConfiguration.API != nil {
server.globalConfiguration.API.CurrentConfigurations = &server.currentConfigurations
@@ -350,25 +352,25 @@ func (s *Server) listenProviders(stop chan bool) {
}
func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) {
providerConfigUpdateMap := map[string]chan types.ConfigMessage{}
providersThrottleDuration := time.Duration(s.globalConfiguration.ProvidersThrottleDuration)
s.defaultConfigurationValues(configMsg.Configuration)
currentConfigurations := s.currentConfigurations.Get().(types.Configurations)
jsonConf, _ := json.Marshal(configMsg.Configuration)
log.Debugf("Configuration received from provider %s: %s", configMsg.ProviderName, string(jsonConf))
if configMsg.Configuration == nil || configMsg.Configuration.Backends == nil && configMsg.Configuration.Frontends == nil && configMsg.Configuration.TLSConfiguration == nil {
if configMsg.Configuration == nil || configMsg.Configuration.Backends == nil && configMsg.Configuration.Frontends == nil && configMsg.Configuration.TLS == nil {
log.Infof("Skipping empty Configuration for provider %s", configMsg.ProviderName)
} else if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) {
log.Infof("Skipping same configuration for provider %s", configMsg.ProviderName)
} else {
if _, ok := providerConfigUpdateMap[configMsg.ProviderName]; !ok {
providerConfigUpdate := make(chan types.ConfigMessage)
providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdate
providerConfigUpdateCh, ok := s.providerConfigUpdateMap[configMsg.ProviderName]
if !ok {
providerConfigUpdateCh = make(chan types.ConfigMessage)
s.providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdateCh
s.routinesPool.Go(func(stop chan bool) {
throttleProviderConfigReload(providersThrottleDuration, s.configurationValidatedChan, providerConfigUpdate, stop)
throttleProviderConfigReload(providersThrottleDuration, s.configurationValidatedChan, providerConfigUpdateCh, stop)
})
}
providerConfigUpdateMap[configMsg.ProviderName] <- configMsg
providerConfigUpdateCh <- configMsg
}
}
@@ -460,8 +462,8 @@ func (s *Server) loadHTTPSConfiguration(configurations types.Configurations) (ma
newEPCertificates := make(map[string]*traefikTls.DomainsCertificates)
// Get all certificates
for _, configuration := range configurations {
if configuration.TLSConfiguration != nil && len(configuration.TLSConfiguration) > 0 {
if err := traefikTls.SortTLSConfigurationPerEntryPoints(configuration.TLSConfiguration, newEPCertificates); err != nil {
if configuration.TLS != nil && len(configuration.TLS) > 0 {
if err := traefikTls.SortTLSPerEntryPoints(configuration.TLS, newEPCertificates); err != nil {
return nil, err
}
}
@@ -482,6 +484,7 @@ func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tl
}
}
}
log.Debugf("No certificate provided dynamically can check the domain %q, a per default certificate will be used.", domainToCheck)
}
return nil, nil
}
@@ -671,31 +674,27 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefikTls.TL
}
if s.globalConfiguration.ACME != nil {
if _, ok := s.serverEntryPoints[s.globalConfiguration.ACME.EntryPoint]; ok {
if entryPointName == s.globalConfiguration.ACME.EntryPoint {
checkOnDemandDomain := func(domain string) bool {
routeMatch := &mux.RouteMatch{}
router := router.GetHandler()
match := router.Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
if match && routeMatch.Route != nil {
return true
}
return false
if entryPointName == s.globalConfiguration.ACME.EntryPoint {
checkOnDemandDomain := func(domain string) bool {
routeMatch := &mux.RouteMatch{}
router := router.GetHandler()
match := router.Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
if match && routeMatch.Route != nil {
return true
}
if s.leadership == nil {
err := s.globalConfiguration.ACME.CreateLocalConfig(config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
if err != nil {
return nil, err
}
} else {
err := s.globalConfiguration.ACME.CreateClusterConfig(s.leadership, config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
if err != nil {
return nil, err
}
return false
}
if s.leadership == nil {
err := s.globalConfiguration.ACME.CreateLocalConfig(config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
if err != nil {
return nil, err
}
} else {
err := s.globalConfiguration.ACME.CreateClusterConfig(s.leadership, config, &s.serverEntryPoints[entryPointName].certs, checkOnDemandDomain)
if err != nil {
return nil, err
}
}
} else {
return nil, errors.New("Unknown entrypoint " + s.globalConfiguration.ACME.EntryPoint + " for ACME configuration")
}
} else {
config.GetCertificate = s.serverEntryPoints[entryPointName].getCertificate
@@ -758,7 +757,9 @@ func (s *Server) addInternalPublicRoutes(entryPointName string, router *mux.Rout
if s.globalConfiguration.Ping != nil && s.globalConfiguration.Ping.EntryPoint != "" && s.globalConfiguration.Ping.EntryPoint == entryPointName {
s.globalConfiguration.Ping.AddRoutes(router)
}
}
func (s *Server) addACMERoutes(entryPointName string, router *mux.Router) {
if s.globalConfiguration.ACME != nil && s.globalConfiguration.ACME.HTTPChallenge != nil && s.globalConfiguration.ACME.HTTPChallenge.EntryPoint == entryPointName {
s.globalConfiguration.ACME.AddRoutes(router)
}
@@ -839,6 +840,9 @@ func (s *Server) buildInternalRouter(entryPointName, path string, internalMiddle
internalMuxRouter.Walk(wrapRoute(internalMiddlewares))
s.addInternalPublicRoutes(entryPointName, internalMuxSubrouter)
s.addACMERoutes(entryPointName, internalMuxRouter)
return internalMuxRouter
}
@@ -910,7 +914,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
backendsHealthCheck := map[string]*healthcheck.BackendHealthCheck{}
errorHandler := NewRecordingErrorHandler(middlewares.DefaultNetErrorRecorder{})
for _, config := range configurations {
for providerName, config := range configurations {
frontendNames := sortedFrontendNamesForConfig(config)
frontend:
for _, frontendName := range frontendNames {
@@ -918,23 +922,23 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
log.Debugf("Creating frontend %s", frontendName)
var frontendEntryPoints []string
for _, entryPointName := range frontend.EntryPoints {
if _, ok := serverEntryPoints[entryPointName]; !ok {
log.Errorf("Undefined entrypoint '%s' for frontend %s", entryPointName, frontendName)
} else {
frontendEntryPoints = append(frontendEntryPoints, entryPointName)
}
}
frontend.EntryPoints = frontendEntryPoints
if len(frontend.EntryPoints) == 0 {
log.Errorf("No entrypoint defined for frontend %s, defaultEntryPoints:%s", frontendName, globalConfiguration.DefaultEntryPoints)
log.Errorf("No entrypoint defined for frontend %s", frontendName)
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
var failedEntrypoints int
for _, entryPointName := range frontend.EntryPoints {
log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName)
if _, ok := serverEntryPoints[entryPointName]; !ok {
log.Errorf("Undefined entrypoint '%s' for frontend %s", entryPointName, frontendName)
failedEntrypoints++
if failedEntrypoints == len(frontend.EntryPoints) {
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
continue
}
newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)}
for routeName, route := range frontend.Routes {
@@ -949,7 +953,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
entryPoint := globalConfiguration.EntryPoints[entryPointName]
n := negroni.New()
if entryPoint.Redirect != nil {
if entryPoint.Redirect != nil && entryPointName != entryPoint.Redirect.EntryPoint {
if redirectHandlers[entryPointName] != nil {
n.Use(redirectHandlers[entryPointName])
} else if handler, err := s.buildRedirectHandler(entryPointName, entryPoint.Redirect); err != nil {
@@ -967,7 +971,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
}
}
}
if backends[entryPointName+frontend.Backend] == nil {
if backends[entryPointName+providerName+frontend.Backend] == nil {
log.Debugf("Creating backend %s", frontend.Backend)
roundTripper, err := s.getRoundTripper(entryPointName, globalConfiguration, frontend.PassTLSCert, entryPoint.TLS)
@@ -1138,7 +1142,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
log.Infof("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
}
if frontend.Redirect != nil {
if frontend.Redirect != nil && entryPointName != frontend.Redirect.EntryPoint {
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
if err != nil {
log.Errorf("Error creating Frontend Redirect: %v", err)
@@ -1188,14 +1192,14 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
} else {
n.UseHandler(lb)
}
backends[entryPointName+frontend.Backend] = n
backends[entryPointName+providerName+frontend.Backend] = n
} else {
log.Debugf("Reusing backend %s", frontend.Backend)
}
if frontend.Priority > 0 {
newServerRoute.route.Priority(frontend.Priority)
}
s.wireFrontendBackend(newServerRoute, backends[entryPointName+frontend.Backend])
s.wireFrontendBackend(newServerRoute, backends[entryPointName+providerName+frontend.Backend])
err := newServerRoute.route.GetError()
if err != nil {
@@ -1344,7 +1348,7 @@ func (s *Server) buildRedirect(entryPointName string) (string, string, error) {
protocol = "https"
}
replacement := protocol + "://$1" + match[0] + "$2"
replacement := protocol + "://${1}" + match[0] + "${2}"
return defaultRedirectRegex, replacement, nil
}

View File

@@ -463,7 +463,7 @@ func TestServerLoadConfigHealthCheckOptions(t *testing.T) {
HealthCheck: healthCheck,
},
},
TLSConfiguration: []*tls.Configuration{
TLS: []*tls.Configuration{
{
Certificate: &tls.Certificate{
CertFile: localhostCert,
@@ -644,7 +644,7 @@ func TestServerLoadConfigEmptyBasicAuth(t *testing.T) {
},
},
},
TLSConfiguration: []*tls.Configuration{
TLS: []*tls.Configuration{
{
Certificate: &tls.Certificate{
CertFile: localhostCert,
@@ -1071,7 +1071,7 @@ func TestServerBuildRedirect(t *testing.T) {
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
},
},
expectedReplacement: "https://$1:443$2",
expectedReplacement: "https://${1}:443${2}",
},
{
desc: "Redirect endpoint http to http02 with HTTP protocol",
@@ -1082,7 +1082,7 @@ func TestServerBuildRedirect(t *testing.T) {
"http02": &configuration.EntryPoint{Address: ":88"},
},
},
expectedReplacement: "http://$1:88$2",
expectedReplacement: "http://${1}:88${2}",
},
{
desc: "Redirect endpoint to non-existent entry point",

View File

@@ -37,9 +37,9 @@
{{if $frontend.Redirect}}
[frontends."{{$frontendName}}".redirect]
entryPoint = "{{$frontend.RedirectEntryPoint}}"
regex = "{{$frontend.RedirectRegex}}"
replacement = "{{$frontend.RedirectReplacement}}"
entryPoint = "{{$frontend.Redirect.EntryPoint}}"
regex = "{{$frontend.Redirect.Regex}}"
replacement = "{{$frontend.Redirect.Replacement}}"
{{end}}
{{ if $frontend.Headers }}

View File

@@ -1,6 +1,6 @@
{{$frontends := List .Prefix "/frontends/" }}
{{$backends := List .Prefix "/backends/"}}
{{$tlsconfiguration := List .Prefix "/tlsconfiguration/"}}
{{$tls := List .Prefix "/tls/"}}
[backends]{{range $backends}}
{{$backend := .}}
@@ -65,13 +65,13 @@
{{end}}
{{end}}
{{range $tlsconfiguration}}
{{range $tls}}
{{$entryPoints := SplitGet . "/entrypoints"}}
[[tlsConfiguration]]
[[tls]]
entryPoints = [{{range $entryPoints}}
"{{.}}",
{{end}}]
[tlsConfiguration.certificate]
[tls.certificate]
certFile = """{{Get "" . "/certificate" "/certfile"}}"""
keyFile = """{{Get "" . "/certificate" "/keyfile"}}"""
{{end}}

View File

@@ -95,7 +95,8 @@ func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, map[
for _, certificate := range *c {
err := certificate.AppendCertificates(domainsCertificates, entryPointName)
if err != nil {
return nil, nil, err
log.Errorf("Unable to add a certificate to the entryPoint %q : %v", entryPointName, err)
continue
}
for _, certDom := range domainsCertificates {
for _, cert := range certDom.Get().(map[string]*tls.Certificate) {
@@ -127,16 +128,16 @@ func (c *Certificate) AppendCertificates(certs map[string]*DomainsCertificates,
certContent, err := c.CertFile.Read()
if err != nil {
return err
return fmt.Errorf("unable to read CertFile : %v", err)
}
keyContent, err := c.KeyFile.Read()
if err != nil {
return err
return fmt.Errorf("uUnable to read KeyFile : %v", err)
}
tlsCert, err := tls.X509KeyPair(certContent, keyContent)
if err != nil {
return err
return fmt.Errorf("unable to generate TLS certificate : %v", err)
}
parsedCert, _ := x509.ParseCertificate(tlsCert.Certificate[0])
@@ -144,7 +145,12 @@ func (c *Certificate) AppendCertificates(certs map[string]*DomainsCertificates,
certKey := parsedCert.Subject.CommonName
if parsedCert.DNSNames != nil {
sort.Strings(parsedCert.DNSNames)
certKey += fmt.Sprintf("%s,%s", parsedCert.Subject.CommonName, strings.Join(parsedCert.DNSNames, ","))
for _, dnsName := range parsedCert.DNSNames {
if dnsName != parsedCert.Subject.CommonName {
certKey += fmt.Sprintf(",%s", dnsName)
}
}
}
certExists := false

View File

@@ -86,8 +86,8 @@ func (r *RootCAs) Type() string {
return "rootcas"
}
// SortTLSConfigurationPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
func SortTLSConfigurationPerEntryPoints(configurations []*Configuration, epConfiguration map[string]*DomainsCertificates) error {
// SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
func SortTLSPerEntryPoints(configurations []*Configuration, epConfiguration map[string]*DomainsCertificates) error {
if epConfiguration == nil {
epConfiguration = make(map[string]*DomainsCertificates)
}

View File

@@ -197,9 +197,9 @@ type Configurations map[string]*Configuration
// Configuration of a provider.
type Configuration struct {
Backends map[string]*Backend `json:"backends,omitempty"`
Frontends map[string]*Frontend `json:"frontends,omitempty"`
TLSConfiguration []*traefikTls.Configuration `json:"tlsConfiguration,omitempty"`
Backends map[string]*Backend `json:"backends,omitempty"`
Frontends map[string]*Frontend `json:"frontends,omitempty"`
TLS []*traefikTls.Configuration `json:"tls,omitempty"`
}
// ConfigMessage hold configuration information exchanged between parts of traefik.

20
vendor/cloud.google.com/go/cloud.go generated vendored
View File

@@ -1,20 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package cloud is the root of the packages used to access Google Cloud
// Services. See https://godoc.org/cloud.google.com/go for a full list
// of sub-packages.
//
// This package documents how to authorize and authenticate the sub packages.
package cloud // import "cloud.google.com/go"

View File

@@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

14
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING generated vendored Normal file
View File

@@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@@ -1,13 +0,0 @@
package main
import (
"os"
"github.com/JamesClonk/vultr/cmd"
)
func main() {
cli := cmd.NewCLI()
cli.RegisterCommands()
cli.Run(os.Args)
}

View File

@@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -88,7 +88,7 @@ type GzipResponseWriterWithCloseNotify struct {
*GzipResponseWriter
}
func (w *GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

View File

@@ -1,7 +0,0 @@
// Package sdk is the official AWS SDK for the Go programming language.
//
// See our Developer Guide for information for on getting started and using
// the SDK.
//
// https://github.com/aws/aws-sdk-go/wiki
package sdk

View File

@@ -1,5 +0,0 @@
// Package service contains automatically generated AWS clients.
package service
//go:generate go run -tags codegen ../private/model/cli/gen-api/main.go -path=../service ../models/apis/*/*/api-2.json
//go:generate gofmt -s -w ../service

View File

@@ -12,64 +12,63 @@ import (
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/containous/flaeg/parse"
flag "github.com/ogier/pflag"
)
// ErrParserNotFound is thrown when a field is flaged but not parser match its type
var ErrParserNotFound = errors.New("Parser not found or custom parser missing")
var ErrParserNotFound = errors.New("parser not found or custom parser missing")
// GetTypesRecursive links in flagmap a flag with its reflect.StructField
// GetTypesRecursive links in flagMap a flag with its reflect.StructField
// You can whether provide objValue on a structure or a pointer to structure as first argument
// Flags are genereted from field name or from StructTag
func getTypesRecursive(objValue reflect.Value, flagmap map[string]reflect.StructField, key string) error {
// Flags are generated from field name or from StructTag
func getTypesRecursive(objValue reflect.Value, flagMap map[string]reflect.StructField, key string) error {
name := key
switch objValue.Kind() {
case reflect.Struct:
for i := 0; i < objValue.NumField(); i++ {
if objValue.Type().Field(i).Anonymous {
if err := getTypesRecursive(objValue.Field(i), flagmap, name); err != nil {
if err := getTypesRecursive(objValue.Field(i), flagMap, name); err != nil {
return err
}
} else if len(objValue.Type().Field(i).Tag.Get("description")) > 0 {
fieldName := objValue.Type().Field(i).Name
if !isExported(fieldName) {
return fmt.Errorf("Field %s is an unexported field", fieldName)
return fmt.Errorf("field %s is an unexported field", fieldName)
}
name += objValue.Type().Name()
if tag := objValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
//Lower Camel Case
//name = strings.ToLower(string(fieldName[0])) + fieldName[1:]
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
if _, ok := flagmap[name]; ok {
return errors.New("Tag already exists: " + name)
}
flagmap[name] = objValue.Type().Field(i)
if err := getTypesRecursive(objValue.Field(i), flagmap, name); err != nil {
if _, ok := flagMap[name]; ok {
return fmt.Errorf("tag already exists: %s", name)
}
flagMap[name] = objValue.Type().Field(i)
if err := getTypesRecursive(objValue.Field(i), flagMap, name); err != nil {
return err
}
}
}
case reflect.Ptr:
if len(key) > 0 {
field := flagmap[name]
field := flagMap[name]
field.Type = reflect.TypeOf(false)
flagmap[name] = field
flagMap[name] = field
}
typ := objValue.Type().Elem()
inst := reflect.New(typ).Elem()
if err := getTypesRecursive(inst, flagmap, name); err != nil {
if err := getTypesRecursive(inst, flagMap, name); err != nil {
return err
}
default:
@@ -78,14 +77,15 @@ func getTypesRecursive(objValue reflect.Value, flagmap map[string]reflect.Struct
return nil
}
//GetPointerFlags returns flags on pointers
// GetBoolFlags returns flags on pointers
func GetBoolFlags(config interface{}) ([]string, error) {
flagmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagmap, ""); err != nil {
flagMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagMap, ""); err != nil {
return []string{}, err
}
flags := make([]string, 0, len(flagmap))
for f, structField := range flagmap {
flags := make([]string, 0, len(flagMap))
for f, structField := range flagMap {
if structField.Type.Kind() == reflect.Bool {
flags = append(flags, f)
}
@@ -93,86 +93,42 @@ func GetBoolFlags(config interface{}) ([]string, error) {
return flags, nil
}
//GetFlags returns flags
// GetFlags returns flags
func GetFlags(config interface{}) ([]string, error) {
flagmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagmap, ""); err != nil {
flagMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(config), flagMap, ""); err != nil {
return []string{}, err
}
flags := make([]string, 0, len(flagmap))
for f := range flagmap {
flags := make([]string, 0, len(flagMap))
for f := range flagMap {
flags = append(flags, f)
}
return flags, nil
}
//loadParsers loads default parsers and custom parsers given as parameter. Return a map [reflect.Type]parsers
// bool, int, int64, uint, uint64, float64,
func loadParsers(customParsers map[reflect.Type]Parser) (map[reflect.Type]Parser, error) {
parsers := map[reflect.Type]Parser{}
var boolParser boolValue
parsers[reflect.TypeOf(true)] = &boolParser
var intParser intValue
parsers[reflect.TypeOf(1)] = &intParser
var int64Parser int64Value
parsers[reflect.TypeOf(int64(1))] = &int64Parser
var uintParser uintValue
parsers[reflect.TypeOf(uint(1))] = &uintParser
var uint64Parser uint64Value
parsers[reflect.TypeOf(uint64(1))] = &uint64Parser
var stringParser stringValue
parsers[reflect.TypeOf("")] = &stringParser
var float64Parser float64Value
parsers[reflect.TypeOf(float64(1.5))] = &float64Parser
var durationParser Duration
parsers[reflect.TypeOf(Duration(time.Second))] = &durationParser
var timeParser timeValue
parsers[reflect.TypeOf(time.Now())] = &timeParser
for rType, parser := range customParsers {
parsers[rType] = parser
}
return parsers, nil
}
//ParseArgs : parses args return valmap map[flag]Getter, using parsers map[type]Getter
//args must be formated as like as flag documentation. See https://golang.org/pkg/flag
func parseArgs(args []string, flagmap map[string]reflect.StructField, parsers map[reflect.Type]Parser) (map[string]Parser, error) {
//Return var
valmap := make(map[string]Parser)
//Visitor in flag.Parse
flagList := []*flag.Flag{}
visitor := func(fl *flag.Flag) {
flagList = append(flagList, fl)
}
newParsers := map[string]Parser{}
// ParseArgs : parses args return a map[flag]Getter, using parsers map[type]Getter
// args must be formatted as like as flag documentation. See https://golang.org/pkg/flag
func parseArgs(args []string, flagMap map[string]reflect.StructField, parsers map[reflect.Type]parse.Parser) (map[string]parse.Parser, error) {
newParsers := map[string]parse.Parser{}
flagSet := flag.NewFlagSet("flaeg.Load", flag.ContinueOnError)
//Disable output
// Disable output
flagSet.SetOutput(ioutil.Discard)
var err error
for flag, structField := range flagmap {
//for _, flag := range flags {
//structField := flagmap[flag]
for flg, structField := range flagMap {
if parser, ok := parsers[structField.Type]; ok {
newparserValue := reflect.New(reflect.TypeOf(parser).Elem())
newparserValue.Elem().Set(reflect.ValueOf(parser).Elem())
newparser := newparserValue.Interface().(Parser)
newParserValue := reflect.New(reflect.TypeOf(parser).Elem())
newParserValue.Elem().Set(reflect.ValueOf(parser).Elem())
newParser := newParserValue.Interface().(parse.Parser)
if short := structField.Tag.Get("short"); len(short) == 1 {
// fmt.Printf("short : %s long : %s\n", short, flag)
flagSet.VarP(newparser, flag, short, structField.Tag.Get("description"))
flagSet.VarP(newParser, flg, short, structField.Tag.Get("description"))
} else {
flagSet.Var(newparser, flag, structField.Tag.Get("description"))
flagSet.Var(newParser, flg, structField.Tag.Get("description"))
}
newParsers[flag] = newparser
newParsers[flg] = newParser
} else {
err = ErrParserNotFound
}
@@ -180,24 +136,35 @@ func parseArgs(args []string, flagmap map[string]reflect.StructField, parsers ma
// prevents case sensitivity issue
args = argsToLower(args)
if err := flagSet.Parse(args); err != nil {
return nil, err
if errParse := flagSet.Parse(args); errParse != nil {
return nil, errParse
}
//Fill flagList with parsed flags
// Visitor in flag.Parse
var flagList []*flag.Flag
visitor := func(fl *flag.Flag) {
flagList = append(flagList, fl)
}
// Fill flagList with parsed flags
flagSet.Visit(visitor)
//Return parsers on parsed flag
for _, flag := range flagList {
valmap[flag.Name] = newParsers[flag.Name]
// Return var
valMap := make(map[string]parse.Parser)
// Return parsers on parsed flag
for _, flg := range flagList {
valMap[flg.Name] = newParsers[flg.Name]
}
return valmap, err
return valMap, err
}
func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Value, defaultValmap map[string]reflect.Value, key string) error {
if defaultValue.Type() != defaultPointersValue.Type() {
return fmt.Errorf("Parameters defaultValue and defaultPointersValue must be the same struct. defaultValue type : %s is not defaultPointersValue type : %s", defaultValue.Type().String(), defaultPointersValue.Type().String())
return fmt.Errorf("parameters defaultValue and defaultPointersValue must be the same struct. defaultValue type: %s is not defaultPointersValue type: %s", defaultValue.Type().String(), defaultPointersValue.Type().String())
}
name := key
switch defaultValue.Kind() {
case reflect.Struct:
@@ -207,22 +174,19 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
return err
}
} else if len(defaultValue.Type().Field(i).Tag.Get("description")) > 0 {
name += defaultValue.Type().Name()
fieldName := defaultValue.Type().Field(i).Name
if tag := defaultValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
if defaultValue.Field(i).Kind() != reflect.Ptr {
// if _, ok := defaultValmap[name]; ok {
// return errors.New("Tag already exists: " + name)
// }
defaultValmap[name] = defaultValue.Field(i)
// fmt.Printf("%s: got default value %+v\n", name, defaultValue.Field(i))
}
if err := getDefaultValue(defaultValue.Field(i), defaultPointersValue.Field(i), defaultValmap, name); err != nil {
return err
@@ -232,14 +196,14 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
case reflect.Ptr:
if !defaultPointersValue.IsNil() {
if len(key) != 0 {
//turn ptr fields to nil
// turn ptr fields to nil
defaultPointersNilValue, err := setPointersNil(defaultPointersValue)
if err != nil {
return err
}
defaultValmap[name] = defaultPointersNilValue
// fmt.Printf("%s: got default value %+v\n", name, defaultPointersNilValue)
}
if !defaultValue.IsNil() {
if err := getDefaultValue(defaultValue.Elem(), defaultPointersValue.Elem(), defaultValmap, name); err != nil {
return err
@@ -253,8 +217,8 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
instValue := reflect.New(defaultPointersValue.Type().Elem())
if len(key) != 0 {
defaultValmap[name] = instValue
// fmt.Printf("%s: got default value %+v\n", name, instValue)
}
if !defaultValue.IsNil() {
if err := getDefaultValue(defaultValue.Elem(), instValue.Elem(), defaultValmap, name); err != nil {
return err
@@ -271,17 +235,18 @@ func getDefaultValue(defaultValue reflect.Value, defaultPointersValue reflect.Va
return nil
}
//objValue a reflect.Value of a not-nil pointer on a struct
// objValue a reflect.Value of a not-nil pointer on a struct
func setPointersNil(objValue reflect.Value) (reflect.Value, error) {
if objValue.Kind() != reflect.Ptr {
return objValue, fmt.Errorf("Parameters objValue must be a not-nil pointer on a struct, not a %s", objValue.Kind().String())
return objValue, fmt.Errorf("parameters objValue must be a not-nil pointer on a struct, not a %s", objValue.Kind())
} else if objValue.IsNil() {
return objValue, fmt.Errorf("Parameters objValue must be a not-nil pointer")
return objValue, errors.New("parameters objValue must be a not-nil pointer")
} else if objValue.Elem().Kind() != reflect.Struct {
// fmt.Printf("Parameters objValue must be a not-nil pointer on a struct, not a pointer on a %s\n", objValue.Elem().Kind().String())
return objValue, nil
}
//Clone
// Clone
starObjValue := objValue.Elem()
nilPointersObjVal := reflect.New(starObjValue.Type())
starNilPointersObjVal := nilPointersObjVal.Elem()
@@ -295,39 +260,38 @@ func setPointersNil(objValue reflect.Value) (reflect.Value, error) {
return nilPointersObjVal, nil
}
//FillStructRecursive initialize a value of any taged Struct given by reference
func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string]reflect.Value, valmap map[string]Parser, key string) error {
// FillStructRecursive initialize a value of any tagged Struct given by reference
func fillStructRecursive(objValue reflect.Value, defaultPointerValMap map[string]reflect.Value, valMap map[string]parse.Parser, key string) error {
name := key
switch objValue.Kind() {
case reflect.Struct:
for i := 0; i < objValue.Type().NumField(); i++ {
if objValue.Type().Field(i).Anonymous {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValMap, valMap, name); err != nil {
return err
}
} else if len(objValue.Type().Field(i).Tag.Get("description")) > 0 {
name += objValue.Type().Name()
fieldName := objValue.Type().Field(i).Name
if tag := objValue.Type().Field(i).Tag.Get("long"); len(tag) > 0 {
fieldName = tag
}
if len(key) == 0 {
name = strings.ToLower(fieldName)
} else {
name = key + "." + strings.ToLower(fieldName)
}
// fmt.Println(name)
if objValue.Field(i).Kind() != reflect.Ptr {
if val, ok := valmap[name]; ok {
// fmt.Printf("%s : set def val\n", name)
if objValue.Field(i).Kind() != reflect.Ptr {
if val, ok := valMap[name]; ok {
if err := setFields(objValue.Field(i), val); err != nil {
return err
}
}
}
if err := fillStructRecursive(objValue.Field(i), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Field(i), defaultPointerValMap, valMap, name); err != nil {
return err
}
}
@@ -335,39 +299,38 @@ func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string
case reflect.Ptr:
if len(key) == 0 && !objValue.IsNil() {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValmap, valmap, name); err != nil {
return err
}
return nil
return fillStructRecursive(objValue.Elem(), defaultPointerValMap, valMap, name)
}
contains := false
for flag := range valmap {
for flg := range valMap {
// TODO replace by regexp
if strings.HasPrefix(flag, name+".") {
if strings.HasPrefix(flg, name+".") {
contains = true
break
}
}
needDefault := false
if _, ok := valmap[name]; ok {
needDefault = valmap[name].Get().(bool)
if _, ok := valMap[name]; ok {
needDefault = valMap[name].Get().(bool)
}
if contains && objValue.IsNil() {
needDefault = true
}
if needDefault {
if defVal, ok := defaultPointerValmap[name]; ok {
//set default pointer value
// fmt.Printf("%s : set default value %+v\n", name, defVal)
if defVal, ok := defaultPointerValMap[name]; ok {
// set default pointer value
objValue.Set(defVal)
} else {
return fmt.Errorf("flag %s default value not provided", name)
}
}
if !objValue.IsNil() && contains {
if objValue.Type().Elem().Kind() == reflect.Struct {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValmap, valmap, name); err != nil {
if err := fillStructRecursive(objValue.Elem(), defaultPointerValMap, valMap, name); err != nil {
return err
}
}
@@ -378,35 +341,35 @@ func fillStructRecursive(objValue reflect.Value, defaultPointerValmap map[string
return nil
}
// SetFields sets value to fieldValue using tag as key in valmap
func setFields(fieldValue reflect.Value, val Parser) error {
// SetFields sets value to fieldValue using tag as key in valMap
func setFields(fieldValue reflect.Value, val parse.Parser) error {
if fieldValue.CanSet() {
fieldValue.Set(reflect.ValueOf(val).Elem().Convert(fieldValue.Type()))
} else {
return errors.New(fieldValue.Type().String() + " is not settable.")
return fmt.Errorf("%s is not settable", fieldValue.Type().String())
}
return nil
}
//PrintHelp generates and prints command line help
func PrintHelp(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser) error {
return PrintHelpWithCommand(flagmap, defaultValmap, parsers, nil, nil)
// PrintHelp generates and prints command line help
func PrintHelp(flagMap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser) error {
return PrintHelpWithCommand(flagMap, defaultValmap, parsers, nil, nil)
}
//PrintError takes a not nil error and prints command line help
func PrintError(err error, flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser) error {
// PrintError takes a not nil error and prints command line help
func PrintError(err error, flagMap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser) error {
if err != flag.ErrHelp {
fmt.Printf("Error : %s\n", err)
fmt.Printf("Error: %s\n", err)
}
if !strings.Contains(err.Error(), ":No parser for type") {
PrintHelp(flagmap, defaultValmap, parsers)
PrintHelp(flagMap, defaultValmap, parsers)
}
return err
}
//LoadWithParsers initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers may be given.
func LoadWithParsers(config interface{}, defaultValue interface{}, args []string, customParsers map[reflect.Type]Parser) error {
// LoadWithParsers initializes config : struct fields given by reference, with args : arguments.
// Some custom parsers may be given.
func LoadWithParsers(config interface{}, defaultValue interface{}, args []string, customParsers map[reflect.Type]parse.Parser) error {
cmd := &Command{
Config: config,
DefaultPointersConfig: defaultValue,
@@ -415,8 +378,8 @@ func LoadWithParsers(config interface{}, defaultValue interface{}, args []string
return LoadWithCommand(cmd, args, customParsers, nil)
}
//Load initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers may be given.
// Load initializes config : struct fields given by reference, with args : arguments.
// Some custom parsers may be given.
func Load(config interface{}, defaultValue interface{}, args []string) error {
return LoadWithParsers(config, defaultValue, args, nil)
}
@@ -430,35 +393,34 @@ type Command struct {
Name string
Description string
Config interface{}
DefaultPointersConfig interface{} //TODO:case DefaultPointersConfig is nil
DefaultPointersConfig interface{} // TODO: case DefaultPointersConfig is nil
Run func() error
Metadata map[string]string
}
//LoadWithCommand initializes config : struct fields given by reference, with args : arguments.
//Some custom parsers and some subCommand may be given.
func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.Type]Parser, subCommand []*Command) error {
parsers, err := loadParsers(customParsers)
// LoadWithCommand initializes config : struct fields given by reference, with args : arguments.
// Some custom parsers and some subCommand may be given.
func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.Type]parse.Parser, subCommand []*Command) error {
parsers, err := parse.LoadParsers(customParsers)
if err != nil {
return err
}
tagsmap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(cmd.Config), tagsmap, ""); err != nil {
tagsMap := make(map[string]reflect.StructField)
if err := getTypesRecursive(reflect.ValueOf(cmd.Config), tagsMap, ""); err != nil {
return err
}
defaultValmap := make(map[string]reflect.Value)
if err := getDefaultValue(reflect.ValueOf(cmd.Config), reflect.ValueOf(cmd.DefaultPointersConfig), defaultValmap, ""); err != nil {
defaultValMap := make(map[string]reflect.Value)
if err := getDefaultValue(reflect.ValueOf(cmd.Config), reflect.ValueOf(cmd.DefaultPointersConfig), defaultValMap, ""); err != nil {
return err
}
valmap, errParseArgs := parseArgs(cmdArgs, tagsmap, parsers)
valMap, errParseArgs := parseArgs(cmdArgs, tagsMap, parsers)
if errParseArgs != nil && errParseArgs != ErrParserNotFound {
return PrintErrorWithCommand(errParseArgs, tagsmap, defaultValmap, parsers, cmd, subCommand)
return PrintErrorWithCommand(errParseArgs, tagsMap, defaultValMap, parsers, cmd, subCommand)
}
if err := fillStructRecursive(reflect.ValueOf(cmd.Config), defaultValmap, valmap, ""); err != nil {
if err := fillStructRecursive(reflect.ValueOf(cmd.Config), defaultValMap, valMap, ""); err != nil {
return err
}
@@ -469,8 +431,8 @@ func LoadWithCommand(cmd *Command, cmdArgs []string, customParsers map[reflect.T
return nil
}
//PrintHelpWithCommand generates and prints command line help for a Command
func PrintHelpWithCommand(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, cmd *Command, subCmd []*Command) error {
// PrintHelpWithCommand generates and prints command line help for a Command
func PrintHelpWithCommand(flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, cmd *Command, subCmd []*Command) error {
// Define a templates
// Using POSXE STD : http://pubs.opengroup.org/onlinepubs/9699919799/
const helper = `{{if .ProgDescription}}{{.ProgDescription}}
@@ -504,7 +466,7 @@ Flags:
_, tempStruct.ProgName = path.Split(os.Args[0])
}
//Run Template
// Run Template
tmplHelper, err := template.New("helper").Parse(helper)
if err != nil {
return err
@@ -514,38 +476,38 @@ Flags:
return err
}
return printFlagsDescriptionsDefaultValues(flagmap, defaultValmap, parsers, os.Stdout)
return printFlagsDescriptionsDefaultValues(flagMap, defaultValMap, parsers, os.Stdout)
}
func printFlagsDescriptionsDefaultValues(flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, output io.Writer) error {
func printFlagsDescriptionsDefaultValues(flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, output io.Writer) error {
// Sort alphabetically & Delete unparsable flags in a slice
flags := []string{}
for flag, field := range flagmap {
var flags []string
for flg, field := range flagMap {
if _, ok := parsers[field.Type]; ok {
flags = append(flags, flag)
flags = append(flags, flg)
}
}
sort.Strings(flags)
// Process data
descriptions := []string{}
defaultValues := []string{}
flagsWithDashs := []string{}
shortFlagsWithDash := []string{}
for _, flag := range flags {
field := flagmap[flag]
var descriptions []string
var defaultValues []string
var flagsWithDash []string
var shortFlagsWithDash []string
for _, flg := range flags {
field := flagMap[flg]
if short := field.Tag.Get("short"); len(short) == 1 {
shortFlagsWithDash = append(shortFlagsWithDash, "-"+short+",")
} else {
shortFlagsWithDash = append(shortFlagsWithDash, "")
}
flagsWithDashs = append(flagsWithDashs, "--"+flag)
flagsWithDash = append(flagsWithDash, "--"+flg)
//flag on pointer ?
if defVal, ok := defaultValmap[flag]; ok {
// flag on pointer ?
if defVal, ok := defaultValMap[flg]; ok {
if defVal.Kind() != reflect.Ptr {
// Set defaultValue on parsers
parsers[field.Type].SetValue(defaultValmap[flag].Interface())
parsers[field.Type].SetValue(defaultValMap[flg].Interface())
}
if defVal := parsers[field.Type].String(); len(defVal) > 0 {
@@ -560,17 +522,19 @@ func printFlagsDescriptionsDefaultValues(flagmap map[string]reflect.StructField,
descriptions = append(descriptions, description)
if i != 0 {
defaultValues = append(defaultValues, "")
flagsWithDashs = append(flagsWithDashs, "")
flagsWithDash = append(flagsWithDash, "")
shortFlagsWithDash = append(shortFlagsWithDash, "")
}
}
}
//add help flag
shortFlagsWithDash = append(shortFlagsWithDash, "-h,")
flagsWithDashs = append(flagsWithDashs, "--help")
flagsWithDash = append(flagsWithDash, "--help")
descriptions = append(descriptions, "Print Help (this message) and exit")
defaultValues = append(defaultValues, "")
return displayTab(output, shortFlagsWithDash, flagsWithDashs, descriptions, defaultValues)
return displayTab(output, shortFlagsWithDash, flagsWithDash, descriptions, defaultValues)
}
func split(str string, width int) []string {
if len(str) > width {
@@ -578,16 +542,19 @@ func split(str string, width int) []string {
if index == -1 {
index = width
}
return append([]string{strings.TrimSpace(str[:index])}, split(strings.TrimSpace(str[index:]), width)...)
}
return []string{str}
}
func displayTab(output io.Writer, columns ...[]string) error {
nbRow := len(columns[0])
nbCol := len(columns)
w := new(tabwriter.Writer)
w.Init(output, 0, 4, 1, ' ', 0)
nbRow := len(columns[0])
nbCol := len(columns)
for i := 0; i < nbRow; i++ {
row := ""
for j, col := range columns {
@@ -598,56 +565,58 @@ func displayTab(output io.Writer, columns ...[]string) error {
}
fmt.Fprintln(w, row)
}
w.Flush()
return nil
return w.Flush()
}
//PrintErrorWithCommand takes a not nil error and prints command line help
func PrintErrorWithCommand(err error, flagmap map[string]reflect.StructField, defaultValmap map[string]reflect.Value, parsers map[reflect.Type]Parser, cmd *Command, subCmd []*Command) error {
// PrintErrorWithCommand takes a not nil error and prints command line help
func PrintErrorWithCommand(err error, flagMap map[string]reflect.StructField, defaultValMap map[string]reflect.Value, parsers map[reflect.Type]parse.Parser, cmd *Command, subCmd []*Command) error {
if err != flag.ErrHelp {
fmt.Printf("Error here : %s\n", err)
}
PrintHelpWithCommand(flagmap, defaultValmap, parsers, cmd, subCmd)
PrintHelpWithCommand(flagMap, defaultValMap, parsers, cmd, subCmd)
return err
}
//Flaeg struct contains commands (at least the root one)
//and row arguments (command and/or flags)
//a map of custom parsers could be use
// Flaeg struct contains commands (at least the root one)
// and row arguments (command and/or flags)
// a map of custom parsers could be use
type Flaeg struct {
calledCommand *Command
commands []*Command ///rootCommand is th fist one in this slice
args []string
commmandArgs []string
customParsers map[reflect.Type]Parser
commandArgs []string
customParsers map[reflect.Type]parse.Parser
}
//New creats and initialize a pointer on Flaeg
// New creates and initialize a pointer on Flaeg
func New(rootCommand *Command, args []string) *Flaeg {
var f Flaeg
f.commands = []*Command{rootCommand}
f.args = args
f.customParsers = map[reflect.Type]Parser{}
f.customParsers = map[reflect.Type]parse.Parser{}
return &f
}
//AddCommand adds sub-command to the root command
// AddCommand adds sub-command to the root command
func (f *Flaeg) AddCommand(command *Command) {
f.commands = append(f.commands, command)
}
//AddParser adds custom parser for a type to the map of custom parsers
func (f *Flaeg) AddParser(typ reflect.Type, parser Parser) {
// AddParser adds custom parser for a type to the map of custom parsers
func (f *Flaeg) AddParser(typ reflect.Type, parser parse.Parser) {
f.customParsers[typ] = parser
}
// Run calls the command with flags given as agruments
// Run calls the command with flags given as arguments
func (f *Flaeg) Run() error {
if f.calledCommand == nil {
if _, _, err := f.findCommandWithCommandArgs(); err != nil {
return err
}
}
if _, err := f.Parse(f.calledCommand); err != nil {
return err
}
@@ -658,15 +627,16 @@ func (f *Flaeg) Run() error {
// It returns nil and a not nil error if it fails
func (f *Flaeg) Parse(cmd *Command) (*Command, error) {
if f.calledCommand == nil {
f.commmandArgs = f.args
f.commandArgs = f.args
}
if err := LoadWithCommand(cmd, f.commmandArgs, f.customParsers, f.commands); err != nil {
if err := LoadWithCommand(cmd, f.commandArgs, f.customParsers, f.commands); err != nil {
return cmd, err
}
return cmd, nil
}
//splitArgs takes args (type []string) and return command ("" if rootCommand) and command's args
// splitArgs takes args (type []string) and return command ("" if rootCommand) and command's args
func splitArgs(args []string) (string, []string) {
if len(args) >= 1 && len(args[0]) >= 1 && string(args[0][0]) != "-" {
if len(args) == 1 {
@@ -680,20 +650,20 @@ func splitArgs(args []string) (string, []string) {
// findCommandWithCommandArgs returns the called command (by reference) and command's args
// the error returned is not nil if it fails
func (f *Flaeg) findCommandWithCommandArgs() (*Command, []string, error) {
commandName := ""
commandName, f.commmandArgs = splitArgs(f.args)
var commandName string
commandName, f.commandArgs = splitArgs(f.args)
if len(commandName) > 0 {
for _, command := range f.commands {
if commandName == command.Name {
f.calledCommand = command
return f.calledCommand, f.commmandArgs, nil
return f.calledCommand, f.commandArgs, nil
}
}
return nil, []string{}, fmt.Errorf("Command %s not found", commandName)
return nil, []string{}, fmt.Errorf("command %s not found", commandName)
}
f.calledCommand = f.commands[0]
return f.calledCommand, f.commmandArgs, nil
return f.calledCommand, f.commandArgs, nil
}
// GetCommand splits args and returns the called command (by reference)
@@ -706,15 +676,17 @@ func (f *Flaeg) GetCommand() (*Command, error) {
return f.calledCommand, nil
}
//isExported return true is the field (from fieldName) is exported,
//else false
// isExported return true is the field (from fieldName) is exported,
// else false
func isExported(fieldName string) bool {
if len(fieldName) < 1 {
return false
}
if string(fieldName[0]) == strings.ToUpper(string(fieldName[0])) {
return true
}
return false
}
@@ -722,22 +694,24 @@ func argToLower(inArg string) string {
if len(inArg) < 2 {
return strings.ToLower(inArg)
}
var outArg string
dashIndex := strings.Index(inArg, "--")
if dashIndex == -1 {
if dashIndex = strings.Index(inArg, "-"); dashIndex == -1 {
return inArg
}
//-fValue
// -fValue
outArg = strings.ToLower(inArg[dashIndex:dashIndex+2]) + inArg[dashIndex+2:]
return outArg
}
//--flag
// --flag
if equalIndex := strings.Index(inArg, "="); equalIndex != -1 {
//--flag=value
// --flag=value
outArg = strings.ToLower(inArg[dashIndex:equalIndex]) + inArg[equalIndex:]
} else {
//--boolflag
// --boolflag
outArg = strings.ToLower(inArg[dashIndex:])
}
@@ -745,7 +719,7 @@ func argToLower(inArg string) string {
}
func argsToLower(inArgs []string) []string {
outArgs := make([]string, len(inArgs), len(inArgs))
outArgs := make([]string, len(inArgs))
for i, inArg := range inArgs {
outArgs[i] = argToLower(inArg)
}

7
vendor/github.com/containous/flaeg/flaeg_types.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
package flaeg
import "github.com/containous/flaeg/parse"
// Duration is deprecated use parse.Duration instead
// Deprecated
type Duration = parse.Duration

301
vendor/github.com/containous/flaeg/parse/parse.go generated vendored Normal file
View File

@@ -0,0 +1,301 @@
package parse
import (
"flag"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
// Parser is an interface that allows the contents of a flag.Getter to be set.
type Parser interface {
flag.Getter
SetValue(interface{})
}
// BoolValue bool Value type
type BoolValue bool
// Set sets bool value from the given string value.
func (b *BoolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
*b = BoolValue(v)
return err
}
// Get returns the bool value.
func (b *BoolValue) Get() interface{} { return bool(*b) }
func (b *BoolValue) String() string { return fmt.Sprintf("%v", *b) }
// IsBoolFlag return true
func (b *BoolValue) IsBoolFlag() bool { return true }
// SetValue sets the duration from the given bool-asserted value.
func (b *BoolValue) SetValue(val interface{}) {
*b = BoolValue(val.(bool))
}
// BoolFlag optional interface to indicate boolean flags that can be
// supplied without "=value" text
type BoolFlag interface {
flag.Value
IsBoolFlag() bool
}
// IntValue int Value
type IntValue int
// Set sets int value from the given string value.
func (i *IntValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = IntValue(v)
return err
}
// Get returns the int value.
func (i *IntValue) Get() interface{} { return int(*i) }
func (i *IntValue) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the IntValue from the given int-asserted value.
func (i *IntValue) SetValue(val interface{}) {
*i = IntValue(val.(int))
}
// Int64Value int64 Value
type Int64Value int64
// Set sets int64 value from the given string value.
func (i *Int64Value) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = Int64Value(v)
return err
}
// Get returns the int64 value.
func (i *Int64Value) Get() interface{} { return int64(*i) }
func (i *Int64Value) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the Int64Value from the given int64-asserted value.
func (i *Int64Value) SetValue(val interface{}) {
*i = Int64Value(val.(int64))
}
// UintValue uint Value
type UintValue uint
// Set sets uint value from the given string value.
func (i *UintValue) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = UintValue(v)
return err
}
// Get returns the uint value.
func (i *UintValue) Get() interface{} { return uint(*i) }
func (i *UintValue) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the UintValue from the given uint-asserted value.
func (i *UintValue) SetValue(val interface{}) {
*i = UintValue(val.(uint))
}
// Uint64Value uint64 Value
type Uint64Value uint64
// Set sets uint64 value from the given string value.
func (i *Uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = Uint64Value(v)
return err
}
// Get returns the uint64 value.
func (i *Uint64Value) Get() interface{} { return uint64(*i) }
func (i *Uint64Value) String() string { return fmt.Sprintf("%v", *i) }
// SetValue sets the Uint64Value from the given uint64-asserted value.
func (i *Uint64Value) SetValue(val interface{}) {
*i = Uint64Value(val.(uint64))
}
// StringValue string Value
type StringValue string
// Set sets string value from the given string value.
func (s *StringValue) Set(val string) error {
*s = StringValue(val)
return nil
}
// Get returns the string value.
func (s *StringValue) Get() interface{} { return string(*s) }
func (s *StringValue) String() string { return string(*s) }
// SetValue sets the StringValue from the given string-asserted value.
func (s *StringValue) SetValue(val interface{}) {
*s = StringValue(val.(string))
}
// Float64Value float64 Value
type Float64Value float64
// Set sets float64 value from the given string value.
func (f *Float64Value) Set(s string) error {
v, err := strconv.ParseFloat(s, 64)
*f = Float64Value(v)
return err
}
// Get returns the float64 value.
func (f *Float64Value) Get() interface{} { return float64(*f) }
func (f *Float64Value) String() string { return fmt.Sprintf("%v", *f) }
// SetValue sets the Float64Value from the given float64-asserted value.
func (f *Float64Value) SetValue(val interface{}) {
*f = Float64Value(val.(float64))
}
// Duration is a custom type suitable for parsing duration values.
// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
// the latter case, seconds are assumed.
type Duration time.Duration
// Set sets the duration from the given string value.
func (d *Duration) Set(s string) error {
if v, err := strconv.Atoi(s); err == nil {
*d = Duration(time.Duration(v) * time.Second)
return nil
}
v, err := time.ParseDuration(s)
*d = Duration(v)
return err
}
// Get returns the duration value.
func (d *Duration) Get() interface{} { return time.Duration(*d) }
// String returns a string representation of the duration value.
func (d *Duration) String() string { return (*time.Duration)(d).String() }
// SetValue sets the duration from the given Duration-asserted value.
func (d *Duration) SetValue(val interface{}) {
*d = val.(Duration)
}
// MarshalText serialize the given duration value into a text.
func (d *Duration) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// UnmarshalText deserializes the given text into a duration value.
// It is meant to support TOML decoding of durations.
func (d *Duration) UnmarshalText(text []byte) error {
return d.Set(string(text))
}
// UnmarshalJSON deserializes the given text into a duration value.
func (d *Duration) UnmarshalJSON(text []byte) error {
if v, err := strconv.Atoi(string(text)); err == nil {
*d = Duration(time.Duration(v))
return nil
}
v, err := time.ParseDuration(string(text))
*d = Duration(v)
return err
}
// TimeValue time.Time Value
type TimeValue time.Time
// Set sets time.Time value from the given string value.
func (t *TimeValue) Set(s string) error {
v, err := time.Parse(time.RFC3339, s)
*t = TimeValue(v)
return err
}
// Get returns the time.Time value.
func (t *TimeValue) Get() interface{} { return time.Time(*t) }
func (t *TimeValue) String() string { return (*time.Time)(t).String() }
// SetValue sets the TimeValue from the given time.Time-asserted value.
func (t *TimeValue) SetValue(val interface{}) {
*t = TimeValue(val.(time.Time))
}
// SliceStrings parse slice of strings
type SliceStrings []string
// Set adds strings elem into the the parser.
// It splits str on , and ;
func (s *SliceStrings) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*s = append(*s, slice...)
return nil
}
// Get []string
func (s *SliceStrings) Get() interface{} { return []string(*s) }
// String return slice in a string
func (s *SliceStrings) String() string { return fmt.Sprintf("%v", *s) }
// SetValue sets []string into the parser
func (s *SliceStrings) SetValue(val interface{}) {
*s = SliceStrings(val.([]string))
}
// LoadParsers loads default parsers and custom parsers given as parameter.
// Return a map [reflect.Type]parsers
// bool, int, int64, uint, uint64, float64,
func LoadParsers(customParsers map[reflect.Type]Parser) (map[reflect.Type]Parser, error) {
parsers := map[reflect.Type]Parser{}
var boolParser BoolValue
parsers[reflect.TypeOf(true)] = &boolParser
var intParser IntValue
parsers[reflect.TypeOf(1)] = &intParser
var int64Parser Int64Value
parsers[reflect.TypeOf(int64(1))] = &int64Parser
var uintParser UintValue
parsers[reflect.TypeOf(uint(1))] = &uintParser
var uint64Parser Uint64Value
parsers[reflect.TypeOf(uint64(1))] = &uint64Parser
var stringParser StringValue
parsers[reflect.TypeOf("")] = &stringParser
var float64Parser Float64Value
parsers[reflect.TypeOf(float64(1.5))] = &float64Parser
var durationParser Duration
parsers[reflect.TypeOf(Duration(time.Second))] = &durationParser
var timeParser TimeValue
parsers[reflect.TypeOf(time.Now())] = &timeParser
for rType, parser := range customParsers {
parsers[rType] = parser
}
return parsers, nil
}

View File

@@ -1,221 +0,0 @@
package flaeg
import (
"flag"
"fmt"
"strconv"
"strings"
"time"
)
//TODO : add parsers on all types in https://golang.org/pkg/builtin/
// Parser is an interface that allows the contents of a flag.Getter to be set.
type Parser interface {
flag.Getter
SetValue(interface{})
}
// -- bool Value
type boolValue bool
func (b *boolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
*b = boolValue(v)
return err
}
func (b *boolValue) Get() interface{} { return bool(*b) }
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
func (b *boolValue) IsBoolFlag() bool { return true }
func (b *boolValue) SetValue(val interface{}) {
*b = boolValue(val.(bool))
}
// optional interface to indicate boolean flags that can be
// supplied without "=value" text
type boolFlag interface {
flag.Value
IsBoolFlag() bool
}
// -- int Value
type intValue int
func (i *intValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = intValue(v)
return err
}
func (i *intValue) Get() interface{} { return int(*i) }
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
func (i *intValue) SetValue(val interface{}) {
*i = intValue(val.(int))
}
// -- int64 Value
type int64Value int64
func (i *int64Value) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
*i = int64Value(v)
return err
}
func (i *int64Value) Get() interface{} { return int64(*i) }
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
func (i *int64Value) SetValue(val interface{}) {
*i = int64Value(val.(int64))
}
// -- uint Value
type uintValue uint
func (i *uintValue) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = uintValue(v)
return err
}
func (i *uintValue) Get() interface{} { return uint(*i) }
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
func (i *uintValue) SetValue(val interface{}) {
*i = uintValue(val.(uint))
}
// -- uint64 Value
type uint64Value uint64
func (i *uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
*i = uint64Value(v)
return err
}
func (i *uint64Value) Get() interface{} { return uint64(*i) }
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
func (i *uint64Value) SetValue(val interface{}) {
*i = uint64Value(val.(uint64))
}
// -- string Value
type stringValue string
func (s *stringValue) Set(val string) error {
*s = stringValue(val)
return nil
}
func (s *stringValue) Get() interface{} { return string(*s) }
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
func (s *stringValue) SetValue(val interface{}) {
*s = stringValue(val.(string))
}
// -- float64 Value
type float64Value float64
func (f *float64Value) Set(s string) error {
v, err := strconv.ParseFloat(s, 64)
*f = float64Value(v)
return err
}
func (f *float64Value) Get() interface{} { return float64(*f) }
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
func (f *float64Value) SetValue(val interface{}) {
*f = float64Value(val.(float64))
}
// Duration is a custom type suitable for parsing duration values.
// It supports `time.ParseDuration`-compatible values and suffix-less digits; in
// the latter case, seconds are assumed.
type Duration time.Duration
// Set sets the duration from the given string value.
func (d *Duration) Set(s string) error {
if v, err := strconv.Atoi(s); err == nil {
*d = Duration(time.Duration(v) * time.Second)
return nil
}
v, err := time.ParseDuration(s)
*d = Duration(v)
return err
}
// Get returns the duration value.
func (d *Duration) Get() interface{} { return time.Duration(*d) }
// String returns a string representation of the duration value.
func (d *Duration) String() string { return (*time.Duration)(d).String() }
// SetValue sets the duration from the given Duration-asserted value.
func (d *Duration) SetValue(val interface{}) {
*d = Duration(val.(Duration))
}
// UnmarshalText deserializes the given text into a duration value.
// It is meant to support TOML decoding of durations.
func (d *Duration) UnmarshalText(text []byte) error {
return d.Set(string(text))
}
// -- time.Time Value
type timeValue time.Time
func (t *timeValue) Set(s string) error {
v, err := time.Parse(time.RFC3339, s)
*t = timeValue(v)
return err
}
func (t *timeValue) Get() interface{} { return time.Time(*t) }
func (t *timeValue) String() string { return (*time.Time)(t).String() }
func (t *timeValue) SetValue(val interface{}) {
*t = timeValue(val.(time.Time))
}
//SliceStrings parse slice of strings
type SliceStrings []string
//Set adds strings elem into the the parser
//it splits str on , and ;
func (s *SliceStrings) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*s = append(*s, slice...)
return nil
}
//Get []string
func (s *SliceStrings) Get() interface{} { return []string(*s) }
//String return slice in a string
func (s *SliceStrings) String() string { return fmt.Sprintf("%v", *s) }
//SetValue sets []string into the parser
func (s *SliceStrings) SetValue(val interface{}) {
*s = SliceStrings(val.([]string))
}

View File

@@ -1,10 +1,14 @@
package staert
import (
"bytes"
"compress/gzip"
"encoding"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"reflect"
"sort"
"strconv"
@@ -155,16 +159,32 @@ func decodeHook(fromType reflect.Type, toType reflect.Type, data interface{}) (i
return dataOutput, nil
} else if fromType.Kind() == reflect.String {
b, err := base64.StdEncoding.DecodeString(data.(string))
if err != nil {
return nil, err
}
return b, nil
return readCompressedData(data.(string), gzipReader, base64Reader)
}
}
return data, nil
}
func readCompressedData(data string, fs ...func(io.Reader) (io.Reader, error)) ([]byte, error) {
var err error
for _, f := range fs {
var reader io.Reader
reader, err = f(bytes.NewBufferString(data))
if err == nil {
return ioutil.ReadAll(reader)
}
}
return nil, err
}
func base64Reader(r io.Reader) (io.Reader, error) {
return base64.NewDecoder(base64.StdEncoding, r), nil
}
func gzipReader(r io.Reader) (io.Reader, error) {
return gzip.NewReader(r)
}
// StoreConfig stores the config into the KV Store
func (kv *KvSource) StoreConfig(config interface{}) error {
kvMap := map[string]string{}
@@ -263,7 +283,11 @@ func collateKvRecursive(objValue reflect.Value, kv map[string]string, key string
case reflect.Array, reflect.Slice:
// Byte slices get special treatment
if objValue.Type().Elem().Kind() == reflect.Uint8 {
kv[name] = base64.StdEncoding.EncodeToString(objValue.Bytes())
compressedData, err := writeCompressedData(objValue.Bytes())
if err != nil {
return err
}
kv[name] = compressedData
} else {
for i := 0; i < objValue.Len(); i++ {
name = key + "/" + strconv.Itoa(i)
@@ -286,6 +310,17 @@ func collateKvRecursive(objValue reflect.Value, kv map[string]string, key string
return nil
}
func writeCompressedData(data []byte) (string, error) {
var buffer bytes.Buffer
gzipWriter := gzip.NewWriter(&buffer)
_, err := gzipWriter.Write(data)
if err != nil {
return "", err
}
gzipWriter.Close()
return buffer.String(), nil
}
// ListRecursive lists all key value children under key
func (kv *KvSource) ListRecursive(key string, pairs map[string][]byte) error {
pairsN1, err := kv.List(key, nil)

View File

@@ -1,16 +0,0 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package auth provides client role authentication for accessing keys in etcd.
package auth

View File

@@ -1,137 +0,0 @@
// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"crypto/rsa"
"io/ioutil"
jwt "github.com/dgrijalva/jwt-go"
"golang.org/x/net/context"
)
type tokenJWT struct {
signMethod string
signKey *rsa.PrivateKey
verifyKey *rsa.PublicKey
}
func (t *tokenJWT) enable() {}
func (t *tokenJWT) disable() {}
func (t *tokenJWT) invalidateUser(string) {}
func (t *tokenJWT) genTokenPrefix() (string, error) { return "", nil }
func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInfo, bool) {
// rev isn't used in JWT, it is only used in simple token
var (
username string
revision uint64
)
parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return t.verifyKey, nil
})
switch err.(type) {
case nil:
if !parsed.Valid {
plog.Warningf("invalid jwt token: %s", token)
return nil, false
}
claims := parsed.Claims.(jwt.MapClaims)
username = claims["username"].(string)
revision = uint64(claims["revision"].(float64))
default:
plog.Warningf("failed to parse jwt token: %s", err)
return nil, false
}
return &AuthInfo{Username: username, Revision: revision}, true
}
func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
// Future work: let a jwt token include permission information would be useful for
// permission checking in proxy side.
tk := jwt.NewWithClaims(jwt.GetSigningMethod(t.signMethod),
jwt.MapClaims{
"username": username,
"revision": revision,
})
token, err := tk.SignedString(t.signKey)
if err != nil {
plog.Debugf("failed to sign jwt token: %s", err)
return "", err
}
plog.Debugf("jwt token: %s", token)
return token, err
}
func prepareOpts(opts map[string]string) (jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath string, err error) {
for k, v := range opts {
switch k {
case "sign-method":
jwtSignMethod = v
case "pub-key":
jwtPubKeyPath = v
case "priv-key":
jwtPrivKeyPath = v
default:
plog.Errorf("unknown token specific option: %s", k)
return "", "", "", ErrInvalidAuthOpts
}
}
return jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, nil
}
func newTokenProviderJWT(opts map[string]string) (*tokenJWT, error) {
jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, err := prepareOpts(opts)
if err != nil {
return nil, ErrInvalidAuthOpts
}
t := &tokenJWT{}
t.signMethod = jwtSignMethod
verifyBytes, err := ioutil.ReadFile(jwtPubKeyPath)
if err != nil {
plog.Errorf("failed to read public key (%s) for jwt: %s", jwtPubKeyPath, err)
return nil, err
}
t.verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
if err != nil {
plog.Errorf("failed to parse public key (%s): %s", jwtPubKeyPath, err)
return nil, err
}
signBytes, err := ioutil.ReadFile(jwtPrivKeyPath)
if err != nil {
plog.Errorf("failed to read private key (%s) for jwt: %s", jwtPrivKeyPath, err)
return nil, err
}
t.signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
if err != nil {
plog.Errorf("failed to parse private key (%s): %s", jwtPrivKeyPath, err)
return nil, err
}
return t, nil
}

View File

@@ -1,133 +0,0 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"github.com/coreos/etcd/auth/authpb"
"github.com/coreos/etcd/mvcc/backend"
"github.com/coreos/etcd/pkg/adt"
)
func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermissions {
user := getUser(tx, userName)
if user == nil {
plog.Errorf("invalid user name %s", userName)
return nil
}
readPerms := &adt.IntervalTree{}
writePerms := &adt.IntervalTree{}
for _, roleName := range user.Roles {
role := getRole(tx, roleName)
if role == nil {
continue
}
for _, perm := range role.KeyPermission {
var ivl adt.Interval
var rangeEnd []byte
if len(perm.RangeEnd) != 1 || perm.RangeEnd[0] != 0 {
rangeEnd = perm.RangeEnd
}
if len(perm.RangeEnd) != 0 {
ivl = adt.NewBytesAffineInterval(perm.Key, rangeEnd)
} else {
ivl = adt.NewBytesAffinePoint(perm.Key)
}
switch perm.PermType {
case authpb.READWRITE:
readPerms.Insert(ivl, struct{}{})
writePerms.Insert(ivl, struct{}{})
case authpb.READ:
readPerms.Insert(ivl, struct{}{})
case authpb.WRITE:
writePerms.Insert(ivl, struct{}{})
}
}
}
return &unifiedRangePermissions{
readPerms: readPerms,
writePerms: writePerms,
}
}
func checkKeyInterval(cachedPerms *unifiedRangePermissions, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
rangeEnd = nil
}
ivl := adt.NewBytesAffineInterval(key, rangeEnd)
switch permtyp {
case authpb.READ:
return cachedPerms.readPerms.Contains(ivl)
case authpb.WRITE:
return cachedPerms.writePerms.Contains(ivl)
default:
plog.Panicf("unknown auth type: %v", permtyp)
}
return false
}
func checkKeyPoint(cachedPerms *unifiedRangePermissions, key []byte, permtyp authpb.Permission_Type) bool {
pt := adt.NewBytesAffinePoint(key)
switch permtyp {
case authpb.READ:
return cachedPerms.readPerms.Intersects(pt)
case authpb.WRITE:
return cachedPerms.writePerms.Intersects(pt)
default:
plog.Panicf("unknown auth type: %v", permtyp)
}
return false
}
func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
// assumption: tx is Lock()ed
_, ok := as.rangePermCache[userName]
if !ok {
perms := getMergedPerms(tx, userName)
if perms == nil {
plog.Errorf("failed to create a unified permission of user %s", userName)
return false
}
as.rangePermCache[userName] = perms
}
if len(rangeEnd) == 0 {
return checkKeyPoint(as.rangePermCache[userName], key, permtyp)
}
return checkKeyInterval(as.rangePermCache[userName], key, rangeEnd, permtyp)
}
func (as *authStore) clearCachedPerm() {
as.rangePermCache = make(map[string]*unifiedRangePermissions)
}
func (as *authStore) invalidateCachedPerm(userName string) {
delete(as.rangePermCache, userName)
}
type unifiedRangePermissions struct {
readPerms *adt.IntervalTree
writePerms *adt.IntervalTree
}

Some files were not shown because too many files have changed in this diff Show More