Compare commits

...

83 Commits
v1.5.0 ... v1.5

Author SHA1 Message Date
Fernandez Ludovic
36c0e63120 fix: mkdocs.yml 2023-01-23 11:05:07 +01:00
Fernandez Ludovic
76465727d9 fix: doc requirements 2022-07-18 12:18:47 +02:00
Fernandez Ludovic
41c64ea81b fix: alpine version 2022-05-24 21:47:15 +02:00
Ludovic Fernandez
de9eec1c92 Freeze mkdocs version. 2018-08-06 15:58:03 +02:00
Jean-Baptiste Doumenjou
482afed4a6 Fix multiple frontends with docker-compose --scale 2018-04-17 14:18:04 +02:00
Ludovic Fernandez
29e1e9eef2 fix: backend name for stateful service. 2018-04-13 14:38:03 +02:00
Manuel Zapf
2641832304 Default certificate expiry 2018-04-11 10:36:03 +02:00
Emile Vauge
ccd919aba3 Fix Azure brand 2018-04-10 17:26:04 +02:00
jakeprem
1b93551572 Update docker-and-lets-encrypt example to show traefik:1.5.4 2018-04-03 11:14:04 +02:00
Gérald Croës
b9af55fc49 Introduction update 2018-03-22 12:34:03 +01:00
Denis Shatilov
e0d92aed6d Miss-leading Docker backend documentation 2018-03-22 10:22:04 +01:00
Michael
a3372acb6d Dependency fsnotify organization has been renamed 2018-03-21 17:04:08 +01:00
yutopp
43a510c046 Fix goroutine leak in consulcatalog when consul is down 2018-03-20 14:36:03 +01:00
Timo Reimann
7afa33dfa1 Fix link to InsecureSkipVerify option. 2018-03-20 09:12:03 +01:00
Timo Reimann
73c6007730 Set INFO log level in Kubernetes guide and examples. 2018-03-19 10:38:04 +01:00
Michael
79cd306ac2 Prepare release v1.5.4 2018-03-15 14:26:03 +01:00
Daniel Tomcej
35b83678bd Add TLS Docs 2018-03-15 12:24:03 +01:00
Jérôme Mirc
eacb6ea15a Fix Rancher Healthcheck when upgrading a service 2018-03-15 12:06:04 +01:00
SALLEYRON Julien
d88263dbf9 Use goroutine pool in throttleProvider 2018-03-15 10:54:03 +01:00
Ludovic Fernandez
b1e3444798 Add lower-case passHostHeader key support. 2018-03-15 10:14:03 +01:00
Ludovic Fernandez
f6c6d2bcd0 Add [file] in syntax reference 2018-03-15 09:02:03 +01:00
Gérald Croës
593c0e7ce2 Updated the test-it example according to the latest docker version 2018-03-13 08:42:03 +01:00
SALLEYRON Julien
e2b42ca57b Handle quoted strings in UnmarshalJSON 2018-03-12 22:00:04 +01:00
Jean-Baptiste Doumenjou
7860534f0c Clarify how setting a frontend priority works 2018-03-12 11:12:05 +01:00
Ludovic Fernandez
fc81d92c88 Enhance Traefik TOML sample. 2018-03-12 10:40:04 +01:00
molsson
8fbac2e39e Fix typo in docs 2018-03-12 10:04:03 +01:00
Ludovic Fernandez
59f7b2ea98 Propagate insecure in white list. 2018-03-08 15:08:03 +01:00
Ludovic Fernandez
862957c30c Safe access to ECS API pointer values. 2018-03-08 10:08:03 +01:00
SALLEYRON Julien
546f0173ab Don't failed traefik start if register and subscribe failed on acme 2018-03-08 00:54:02 +01:00
Michael
04e3f2f401 Unable to generate documentation 2018-03-07 21:50:04 +01:00
SALLEYRON Julien
acc432b5a8 capitalize Sec-WebSocket-Protocol Header 2018-03-07 08:08:03 +01:00
Ludovic Fernandez
13e2358815 Clarify some deprecations. 2018-03-02 14:46:03 +01:00
Ludovic Fernandez
716eca5976 fix: gh-pages drop edge. 2018-03-02 14:20:04 +01:00
Ludovic Fernandez
9ae808aac4 Fix panic when parsing resolv.conf 2018-03-02 10:46:04 +01:00
Ludovic Fernandez
f149b56063 Enhance API, REST, ping documentation. 2018-03-01 08:42:03 +01:00
Gerben Welter
49a9e2a9e0 Second defaultEntryPoint should be https, not http. 2018-02-28 14:24:03 +01:00
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
679 changed files with 42905 additions and 49905 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

@@ -24,13 +24,14 @@ 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;
make image;
if [ "$TRAVIS_TAG" ]; then
make -j${N_MAKE_JOBS} crossbinary-parallel;
make image-dirty;
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;
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" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --exp-branch=master --debug;
fi
deploy:
- provider: releases
@@ -53,7 +54,7 @@ deploy:
on:
repo: containous/traefik
- provider: pages
edge: true
edge: false
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true

View File

@@ -1,5 +1,86 @@
# Change Log
## [v1.5.4](https://github.com/containous/traefik/tree/v1.5.4) (2018-03-15)
[All Commits](https://github.com/containous/traefik/compare/v1.5.3...v1.5.4)
**Bug fixes:**
- **[acme]** Fix panic when parsing resolv.conf ([#2955](https://github.com/containous/traefik/pull/2955) by [ldez](https://github.com/ldez))
- **[acme]** Don&#39;t failed traefik start if register and subscribe failed on acme ([#2977](https://github.com/containous/traefik/pull/2977) by [Juliens](https://github.com/Juliens))
- **[ecs]** Safe access to ECS API pointer values. ([#2983](https://github.com/containous/traefik/pull/2983) by [ldez](https://github.com/ldez))
- **[kv]** Add lower-case passHostHeader key support. ([#3015](https://github.com/containous/traefik/pull/3015) by [ldez](https://github.com/ldez))
- **[middleware]** Propagate insecure in white list. ([#2981](https://github.com/containous/traefik/pull/2981) by [ldez](https://github.com/ldez))
- **[rancher]** Fix Rancher Healthcheck when upgrading a service ([#2962](https://github.com/containous/traefik/pull/2962) by [jmirc](https://github.com/jmirc))
- **[websocket]** Capitalize Sec-WebSocket-Protocol Header ([#2975](https://github.com/containous/traefik/pull/2975) by [Juliens](https://github.com/Juliens))
- Use goroutine pool in throttleProvider ([#3013](https://github.com/containous/traefik/pull/3013) by [Juliens](https://github.com/Juliens))
- Handle quoted strings in UnmarshalJSON ([#3004](https://github.com/containous/traefik/pull/3004) by [Juliens](https://github.com/Juliens))
**Documentation:**
- **[acme]** Clarify some deprecations. ([#2959](https://github.com/containous/traefik/pull/2959) by [ldez](https://github.com/ldez))
- **[acme]** Second defaultEntryPoint should be https, not http. ([#2948](https://github.com/containous/traefik/pull/2948) by [GerbenWelter](https://github.com/GerbenWelter))
- **[api]** Enhance API, REST, ping documentation. ([#2950](https://github.com/containous/traefik/pull/2950) by [ldez](https://github.com/ldez))
- **[k8s]** Add TLS Docs ([#3012](https://github.com/containous/traefik/pull/3012) by [dtomcej](https://github.com/dtomcej))
- Enhance Traefik TOML sample. ([#2996](https://github.com/containous/traefik/pull/2996) by [ldez](https://github.com/ldez))
- Fix typo in docs ([#2990](https://github.com/containous/traefik/pull/2990) by [mo](https://github.com/mo))
- Clarify how setting a frontend priority works ([#2984](https://github.com/containous/traefik/pull/2984) by [jbdoumenjou](https://github.com/jbdoumenjou))
- Add [file] in syntax reference ([#3016](https://github.com/containous/traefik/pull/3016) by [ldez](https://github.com/ldez))
- Updated the test-it example according to the latest docker version ([#3000](https://github.com/containous/traefik/pull/3000) by [geraldcroes](https://github.com/geraldcroes))
## [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)

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:

44
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 = "b4c2f060875361c070ed2bc300c5929b82f5fa2e"
version = "v1.1.2"
[[projects]]
branch = "master"
@@ -223,14 +226,14 @@
[[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"
packages = ["."]
revision = "ca1fb57108293caad285b1c366b763f6c6ab71c9"
version = "v1.0.5"
revision = "dd5326f23d6e529aa327c10ce1f996079f5d7262"
version = "v1.0.6"
[[projects]]
name = "github.com/coreos/bbolt"
@@ -642,9 +645,10 @@
revision = "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
[[projects]]
branch = "master"
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "a69d9f6de432e2c6b296a947d8a5ee88f68522cf"
revision = "eb925808374e5ca90c83401a40d711dc08c0c0f6"
[[projects]]
name = "github.com/hashicorp/consul"
@@ -807,9 +811,10 @@
source = "https://github.com/containous/mesos-dns.git"
[[projects]]
branch = "master"
name = "github.com/miekg/dns"
packages = ["."]
revision = "8060d9f51305bbe024b99679454e62f552cd0b0b"
revision = "906238edc6eb0ddface4a1923f6d41ef2a5ca59b"
[[projects]]
branch = "master"
@@ -962,7 +967,8 @@
"mock",
"require"
]
revision = "4d4bfba8f1d1027c4fdbe371823030df51419987"
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
@@ -1027,7 +1033,7 @@
"roundrobin",
"utils"
]
revision = "fd6f71c694e2ab8b584c50b98ab4825027feb315"
revision = "af377749f48ff0ae9974b30ce12a816738b94558"
source = "https://github.com/containous/oxy.git"
[[projects]]
@@ -1102,28 +1108,35 @@
packages = [
"bcrypt",
"blowfish",
"ed25519",
"ed25519/internal/edwards25519",
"ocsp",
"pbkdf2",
"scrypt"
]
revision = "4ed45ec682102c643324fae5dff8dab085b6c300"
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
[[projects]]
name = "golang.org/x/net"
packages = [
"bpf",
"context",
"context/ctxhttp",
"http2",
"http2/hpack",
"idna",
"internal/iana",
"internal/socket",
"internal/timeseries",
"ipv4",
"ipv6",
"lex/httplex",
"proxy",
"publicsuffix",
"trace",
"websocket"
]
revision = "c8c74377599bd978aee1cf3b9b63a8634051cec2"
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
[[projects]]
name = "golang.org/x/oauth2"
@@ -1228,6 +1241,7 @@
name = "gopkg.in/fsnotify.v1"
packages = ["."]
revision = "629574ca2a5df945712d3079857300b5e4da0236"
source = "github.com/fsnotify/fsnotify"
version = "v1.4.2"
[[projects]]
@@ -1386,6 +1400,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "bd1e7a1b07d95ff85c675468bbfc4bc7a91c39cf1feceeb58dfcdba9592180a5"
inputs-digest = "2211d5f1d7b7137b83976ba0a92441ef7a80c8a858eabb7d9a5102d7b3dfbe4f"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -64,11 +64,11 @@ 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"
version = "1.0.5"
version = "1.0.6"
[[constraint]]
name = "github.com/coreos/go-systemd"
@@ -162,6 +162,7 @@ ignored = ["github.com/sirupsen/logrus"]
[[constraint]]
name = "gopkg.in/fsnotify.v1"
source = "github.com/fsnotify/fsnotify"
version = "1.4.2"
[[constraint]]
@@ -173,10 +174,6 @@ ignored = ["github.com/sirupsen/logrus"]
revision = "6018b68f96b839edfbe3fb48668853f5dbad88a3"
source = "github.com/ijc25/Gotty"
[[override]]
name = "github.com/gorilla/websocket"
revision = "a69d9f6de432e2c6b296a947d8a5ee88f68522cf"
[[override]]
# always keep this override
name = "github.com/mailgun/timetools"
@@ -190,3 +187,20 @@ ignored = ["github.com/sirupsen/logrus"]
# remove override on master
name = "github.com/coreos/bbolt"
revision = "32c383e75ce054674c53b5a07e55de85332aee14"
[[override]]
branch = "master"
name = "github.com/miekg/dns"
[[override]]
name = "golang.org/x/crypto"
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
[[override]]
name = "golang.org/x/net"
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
[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

215
README.md
View File

@@ -12,8 +12,9 @@
[![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.
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.
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
Træfik integrates with your existing infrastructure components ([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 configures itself automatically and dynamically.
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
---
@@ -36,60 +37,101 @@ It supports several backends ([Docker](https://www.docker.com/), [Swarm mode](ht
## Overview
Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
If you want your users to access some of your microservices from the Internet, you will have to use a reverse proxy and configure it using virtual hosts or prefix paths:
Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul).
Now you want users to access these microservices, and you need a reverse proxy.
- domain `api.domain.com` will point the microservice `api` in your private network
- path `domain.com/web` will point the microservice `web` in your private network
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice.
In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
**This is when Træfik can help you!**
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
Træfik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
Here enters Træfik.
**Run Træfik and let it do the work for you!**
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
![Architecture](docs/img/architecture.png)
Træfik can listen to your service registry/orchestrator API, and knows each time a microservice is added, removed, killed or upgraded, and can generate its configuration automatically.
Routes to your services will be created instantly.
Run it and forget it!
## Features
- [It's fast](https://docs.traefik.io/benchmarks)
- No dependency hell, single binary made with go
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
- Rest API
- Hot-reloading of configuration. No need to restart the process
- Continuously updates its configuration (No restarts!)
- Supports multiple load balancing algorithms
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org)
- Circuit breakers, retry
- Round Robin, rebalancer load-balancers
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Clean AngularJS Web UI
- Websocket, HTTP/2, GRPC ready
- Access Logs (JSON, CLF)
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
- [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support
- High Availability with cluster mode (beta)
- See the magic through its clean web UI
- Websocket, HTTP/2, GRPC ready
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Keeps access logs (JSON, CLF)
- [Fast](https://docs.traefik.io/benchmarks) ... which is nice
- Exposes a Rest API
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
## Supported backends
- [Docker](https://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/)
- [Kubernetes](https://kubernetes.io)
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/)
- [Rancher](https://rancher.com) (API, Metadata)
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt)
- [Eureka](https://github.com/Netflix/eureka)
- [Amazon ECS](https://aws.amazon.com/ecs)
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb)
- File
- Rest API
## Supported Backends
- [Docker](docs/configuration/backends/docker/) / [Swarm mode](docs/configuration/backends/docker/#docker-swarm-mode)
- [Kubernetes](docs/configuration/backends/kubernetes/)
- [Mesos](docs/configuration/backends/mesos/) / [Marathon](docs/configuration/backends/marathon/)
- [Rancher](docs/configuration/backends/rancher/) (API, Metadata)
- [Azure Service Fabric](docs/configuration/backends/servicefabric/)
- [Consul Catalog](docs/configuration/backends/consulcatalog/)
- [Consul](docs/configuration/backends/consul/) / [Etcd](docs/configuration/backends/etcd/) / [Zookeeper](docs/configuration/backends/zookeeper/) / [BoltDB](docs/configuration/backends/boltdb/)
- [Eureka](docs/configuration/backends/eureka/)
- [Amazon ECS](docs/configuration/backends/ecs/)
- [Amazon DynamoDB](docs/configuration/backends/dynamodb/)
- [File](docs/configuration/backends/file/)
- [Rest](docs/configuration/backends/rest/)
## Quickstart
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers. If you are looking for a more comprehensive and real use-case example, you can also check [Play-With-Docker](http://training.play-with-docker.com/traefik-load-balancing/) to see how to load balance between multiple nodes.
To get your hands on Træfik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-trfik-quickstart-using-docker) in our documentation (you will need Docker).
Alternatively, if you don't want to install anything on your computer, you can try Træfik online in this great [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
If you are looking for a more comprehensive and real use-case example, you can also check [Play-With-Docker](http://training.play-with-docker.com/traefik-load-balancing/) to see how to load balance between multiple nodes.
## Web UI
You can access the simple HTML frontend of Træfik.
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Documentation
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Support
To get community support, you can:
- join the Træfik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
## Download
- Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
./traefik --configFile=traefik.toml
```
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
- Or get the sources:
```shell
git clone https://github.com/containous/traefik
```
## Introductory Videos
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/).
You will learn Træfik basics in less than 10 minutes.
@@ -101,81 +143,26 @@ You will learn fundamental Træfik features and see some demos with Kubernetes.
[![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
## Web UI
You can access the simple HTML frontend of Træfik.
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Test it
- The simple way: grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
./traefik --configFile=traefik.toml
```
- Use the tiny Docker image and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
- From sources:
```shell
git clone https://github.com/containous/traefik
```
## Documentation
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Support
To get basic support, you can:
- join the Træfik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
## Release cycle
- Release: We try to release a new version every 2 months
- i.e.: 1.3.0, 1.4.0, 1.5.0
- Release candidate: we do RC (1.**x**.0-rc**y**) before the final release (1.**x**.0)
- i.e.: 1.1.0-rc1 -> 1.1.0-rc2 -> 1.1.0-rc3 -> 1.1.0-rc4 -> 1.1.0
- Bug-fixes: For each version we release bug fixes
- i.e.: 1.1.1, 1.1.2, 1.1.3
- those versions contain only bug-fixes
- no additional features are delivered in those versions
- Each version is supported until the next one is released
- i.e.: 1.1.x will be supported until 1.2.0 is out
- We use [Semantic Versioning](http://semver.org/)
## Contributing
Please refer to [contributing documentation](CONTRIBUTING.md).
### Code of Conduct
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
By participating in this project you agree to abide by its terms.
## Maintainers
[Information about process and maintainers](MAINTAINER.md)
## Contributing
If you'd like to contribute to the project, refer to the [contributing documentation](CONTRIBUTING.md).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
By participating in this project, you agree to abide by its terms.
## Release Cycle
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0)
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only)
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out)
We use [Semantic Versioning](http://semver.org/)
## Plumbing
@@ -184,11 +171,11 @@ By participating in this project you agree to abide by its terms.
- [Negroni](https://github.com/urfave/negroni): web middlewares made simple
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
## Credits
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo ![logo](docs/img/traefik.icon.png).
Traefik's logo licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).

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

@@ -295,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
@@ -333,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 {
@@ -341,7 +344,9 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
log.Info("Register...")
reg, err := a.client.Register()
if err != nil {
return err
log.Errorf(`Failed to register user: %s
Let's Encrypt functionality will be limited until Traefik is restarted.`, err)
return nil
}
account.Registration = reg
}
@@ -354,7 +359,9 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
// Let's Encrypt Subscriber Agreement renew ?
reg, err := a.client.QueryRegistration()
if err != nil {
return err
log.Errorf(`Failed to renew subscriber agreement: %s
Let's Encrypt functionality will be limited until Traefik is restarted.`, err)
return nil
}
account.Registration = reg
err = a.client.AgreeToTOS()
@@ -374,7 +381,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() {
@@ -389,7 +395,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
}
@@ -623,11 +629,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")
@@ -645,32 +646,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 {
@@ -682,36 +685,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 }}
@@ -575,7 +575,13 @@ var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
{{$entryPoints := GetList . "/entrypoints"}}
[frontends."{{$frontend}}"]
backend = "{{Get "" . "/backend"}}"
{{ $passHostHeader := Get "" . "/passhostheader"}}
{{if $passHostHeader}}
passHostHeader = {{ $passHostHeader }}
{{else}}
# keep for compatibility reason
passHostHeader = {{Get "true" . "/passHostHeader"}}
{{end}}
priority = {{Get "0" . "/priority"}}
entryPoints = [{{range $entryPoints}}
"{{.}}",

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

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

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,4 +1,4 @@
FROM alpine
FROM alpine:3.14
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin

View File

@@ -234,27 +234,26 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
#### Priorities
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`.
`PathPrefix:/foo;Host:foo.com` (length == 28) will be matched before `PathPrefixStrip:/foobar` (length == 23) will be matched before `PathPrefix:/foo,/bar` (length == 20).
You can customize priority by frontend:
You can customize priority by frontend. The priority value override the rule length during sorting:
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
priority = 10
priority = 20
passHostHeader = true
[frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to"
[frontends.frontend2]
priority = 5
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto"
```
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
Here, `frontend1` will be matched before `frontend2` (`20 > 16`).
#### Custom headers
@@ -569,6 +568,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 +616,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", "https"]
[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,10 @@ Each domain & SANs will lead to a certificate request.
### `dnsProvider` (Deprecated)
!!! warning
This option is deprecated.
Please refer to [DNS challenge provider section](/configuration/acme/#provider)
!!! danger "DEPRECATED"
This option is deprecated, use [dnsChallenge.provider](/configuration/acme/#acmednschallenge) instead.
### `delayDontCheckDNS` (Deprecated)
!!! warning
This option is deprecated.
Please refer to [DNS challenge delayBeforeCheck section](/configuration/acme/#delaybeforecheck)
!!! danger "DEPRECATED"
This option is deprecated, use [dnsChallenge.delayBeforeCheck](/configuration/acme/#acmednschallenge) instead.

View File

@@ -1,5 +1,7 @@
# API Definition
## Configuration
```toml
# API definition
[api]
@@ -9,14 +11,14 @@
# Default: "traefik"
#
entryPoint = "traefik"
# Enabled Dashboard
#
# Optional
# Default: true
#
dashboard = true
# Enable debug mode.
# This will install HTTP handlers to expose Go expvars under /debug/vars and
# pprof profiling data under /debug/pprof.
@@ -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)
@@ -39,10 +43,10 @@
| Path | Method | Description |
|-----------------------------------------------------------------|------------------|-------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
| `/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 |
| `/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,11 +56,108 @@
| `/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`.
### Provider configurations
### Address / Port
You can define a custom address/port like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
[api]
entryPoint = "bar"
```
In the above example, you would access a regular path, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/path`
* 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.
### Custom Path
You can define a custom path like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8080"
[entryPoints.bar]
address = ":8081"
# Activate API and Dashboard
[api]
entryPoint = "bar"
dashboard = true
[file]
[backends]
[backends.backend1]
[backends.backend1.servers.server1]
url = "http://127.0.0.1:8081"
[frontends]
[frontends.frontend1]
entryPoints = ["foo"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
```
### Authentication
You can define the authentication like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address=":8080"
[entryPoints.foo.auth]
[entryPoints.foo.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[api]
entrypoint="foo"
```
For more information, see [entry points](/configuration/entrypoints/) .
### Provider call example
```shell
curl -s "http://localhost:8080/api" | jq .
@@ -185,6 +286,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

@@ -145,10 +145,10 @@ To enable constraints see [backend-specific constraints section](/configuration/
## Labels: overriding default behaviour
!!! note
If you use a compose file, labels should be defined in the `deploy` part of your service.
#### Using Docker with Swarm Mode
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
If you use a compose file with the Swarm mode, labels should be defined in the `deploy` part of your service.
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
```yaml
version: "3"
@@ -159,37 +159,52 @@ services:
traefik.docker.network: traefik
```
#### Using Docker Compose
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
```yaml
version: "3"
services:
whoami:
labels:
traefik.docker.network: traefik
```
### On Containers
Labels can be used on containers to override default behaviour.
| Label | Description |
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.weight=10` | Assign this weight to the container |
| `traefik.enable=false` | Disable this container in Træfik |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
| `traefik.frontend.priority=10` | Override default frontend priority |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. 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. |
| `traefik.docker.network` | Set the docker network to use for connections to this container. If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them). For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. |
| Label | Description |
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.weight=10` | Assign this weight to the container |
| `traefik.enable=false` | Disable this container in Træfik |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
| `traefik.frontend.priority=10` | Override default frontend priority |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. 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. |
| `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. |
[1] `traefik.docker.network`:
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
Or if your service references external network use it's name instead.
#### Security Headers

View File

@@ -1,6 +1,142 @@
# 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
[file]
# 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 +148,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,172 +157,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
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
# ...
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my/other.cert"
keyFile = "path/to/my/other.key"
# ...
```
!!! 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
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my/other.cert"
keyFile = "path/to/my/other.key"
# ...
## Multiple `.toml` Files
[[tls]]
# ...
```
### 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

@@ -94,6 +94,17 @@ A label selector can be defined to filter on specific Ingress objects only.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
### TLS communication between Traefik and backend pods
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
Although traefik will connect directly to the endpoints (pods), it still checks the service port to see if TLS communication is required.
If the service port defined in the ingress spec is 443, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
!!! note
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
If this is not an option, you may need to skip TLS certificate verification.
See the [InsecureSkipVerify](/configuration/commons/#main-section) setting for more details.
## Annotations
### General annotations
@@ -150,7 +161,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

@@ -29,9 +29,10 @@ Træfik can be configured:
```shell
curl -XPUT @file "http://localhost:8080/api"
curl -XPUT @file "http://localhost:8080/api/providers/rest"
```
with `@file`
with `@file`:
```json
{
"frontends": {
@@ -88,4 +89,4 @@ with `@file`
}
}
}
```
```

View File

@@ -1,33 +1,33 @@
# Service Fabric Backend
# Azure Service Fabric Backend
Træfik can be configured to use Service Fabric as a backend configuration.
Træfik can be configured to use Azure Service Fabric as a backend configuration.
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
## Service Fabric
## Azure Service Fabric
```toml
################################################################
# Service Fabric provider
# Azure Service Fabric provider
################################################################
# Enable Service Fabric configuration backend
# Enable Azure Service Fabric configuration backend
[serviceFabric]
# Service Fabric Management Endpoint
# Azure Service Fabric Management Endpoint
#
# Required
#
clusterManagementUrl = "https://localhost:19080"
# Service Fabric Management Endpoint API Version
# Azure Service Fabric Management Endpoint API Version
#
# Required
# Default: "3.0"
#
apiVersion = "3.0"
# Service Fabric Polling Interval (in seconds)
# Azure Service Fabric Polling Interval (in seconds)
#
# Required
# Default: 10

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,62 @@ curl -s "http://localhost:8080/api" | jq .
}
```
## Path
### Deprecation compatibility
As web is deprecated, you can handle the `Path` option like this
#### Address
As the web provider is deprecated, you can handle the `Address` option like this:
```toml
[entrypoints.http]
address=":80"
defaultEntryPoints = ["http"]
[entrypoints.dashboard]
address=":8080"
[entryPoints]
[entryPoints.http]
address = ":80"
[entrypoints.api]
address=":8081"
[entryPoints.foo]
address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
#Activate API and Dashboard
[api]
entrypoint="api"
entryPoint = "bar"
```
In the above example, you would access a regular path, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/path`
* 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.
#### Path
As the web provider is deprecated, you can handle the `Path` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8080"
[entryPoints.bar]
address = ":8081"
# Activate API and Dashboard
[api]
entryPoint = "bar"
dashboard = true
[file]
[backends]
@@ -411,8 +449,34 @@ entrypoint="api"
[frontends]
[frontends.frontend1]
entrypoints=["dashboard"]
entryPoints = ["foo"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
```
```
#### Authentication
As the web provider is deprecated, you can handle the `auth` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address=":8080"
[entryPoints.foo.auth]
[entryPoints.foo.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[api]
entrypoint="foo"
```
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,7 +169,11 @@ 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
@@ -71,7 +193,7 @@ Define an entrypoint with SNI support.
!!! note
If an empty TLS configuration is done, default self-signed certificates are generated.
### Dynamic Certificates
@@ -108,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
@@ -133,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
@@ -153,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]
@@ -162,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.
#
@@ -170,7 +291,7 @@ Otherwise, the response from the auth server is returned.
# Default: false
#
trustForwardHeader = true
# Enable forward auth TLS connection.
#
# Optional
@@ -190,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"
@@ -234,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,24 +21,67 @@
!!! warning
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
### Example
## Examples
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:
* Regular path: `http://hostname:80/foo`
* 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 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 entry point
* Enable `/ping` on a dedicated port
### Ping health check on a regular entry point
To proxy `/ping` from a regular entry point to the administration one without exposing the panel, do the following:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[ping]
entryPoint = "http"
```shell
curl -sv "http://localhost:8080/ping"
```
```shell
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 25 Aug 2016 01:35:36 GMT
< Content-Length: 2
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
OK
```
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 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"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.ping]
address = ":8082"
[ping]
entryPoint = "ping"
```
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 and health-check as follows:
* Regular path: `http://hostname:80/foo`
* 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 entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via this entry point.

View File

@@ -10,65 +10,165 @@
[![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.
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.
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
Træfik integrates with your existing infrastructure components ([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 configures itself automatically and dynamically.
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
## Overview
Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
If you want your users to access some of your microservices from the Internet, you will have to use a reverse proxy and configure it using virtual hosts or prefix paths:
Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul).
Now you want users to access these microservices, and you need a reverse proxy.
- domain `api.domain.com` will point the microservice `api` in your private network
- path `domain.com/web` will point the microservice `web` in your private network
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice. In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
**This is when Træfik can help you!**
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
Træfik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
Here enters Træfik.
**Run Træfik and let it do the work for you!**
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
![Architecture](img/architecture.png)
Træfik can listen to your service registry/orchestrator API, and knows each time a microservice is added, removed, killed or upgraded, and can generate its configuration automatically.
Routes to your services will be created instantly.
Run it and forget it!
## Features
- [It's fast](/benchmarks)
- No dependency hell, single binary made with go
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
- Rest API
- Hot-reloading of configuration. No need to restart the process
- Continuously updates its configuration (No restarts!)
- Supports multiple load balancing algorithms
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org)
- Circuit breakers, retry
- Round Robin, rebalancer load-balancers
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Clean AngularJS Web UI
- High Availability with cluster mode (beta)
- See the magic through its clean web UI
- Websocket, HTTP/2, GRPC ready
- Access Logs (JSON, CLF)
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
- High Availability with cluster mode
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Keeps access logs (JSON, CLF)
- [Fast](/benchmarks) ... which is nice
- Exposes a Rest API
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
## Supported backends
- [Docker](https://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/)
- [Kubernetes](https://kubernetes.io)
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/)
- [Rancher](https://rancher.com) (API, Metadata)
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt)
- [Eureka](https://github.com/Netflix/eureka)
- [Amazon ECS](https://aws.amazon.com/ecs)
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb)
- File
- Rest API
- [Docker](/configuration/backends/docker/) / [Swarm mode](/configuration/backends/docker/#docker-swarm-mode)
- [Kubernetes](/configuration/backends/kubernetes/)
- [Mesos](/configuration/backends/mesos/) / [Marathon](/configuration/backends/marathon/)
- [Rancher](/configuration/backends/rancher/) (API, Metadata)
- [Azure Service Fabric](/configuration/backends/servicefabric/)
- [Consul Catalog](/configuration/backends/consulcatalog/)
- [Consul](/configuration/backends/consul/) / [Etcd](/configuration/backends/etcd/) / [Zookeeper](/configuration/backends/zookeeper/) / [BoltDB](/configuration/backends/boltdb/)
- [Eureka](/configuration/backends/eureka/)
- [Amazon ECS](/configuration/backends/ecs/)
- [Amazon DynamoDB](/configuration/backends/dynamodb/)
- [File](/configuration/backends/file/)
- [Rest](/configuration/backends/rest/)
## The Træfik Quickstart (Using Docker)
## Quickstart
In this quickstart, we'll use [Docker compose](https://docs.docker.com/compose) to create our demo infrastructure.
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
To save some time, you can clone [Træfik's repository](https://github.com/containous/traefik) and use the quickstart files located in the [examples/quickstart](https://github.com/containous/traefik/tree/master/examples/quickstart/) directory.
### 1 — Launch Træfik — Tell It to Listen to Docker
Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Træfik image:
```yaml
version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
```
**That's it. Now you can launch Træfik!**
Start your `reverse-proxy` with the following command:
```shell
docker-compose up -d reverse-proxy
```
You can open a browser and go to [http://localhost:8080](http://localhost:8080) to see Træfik's dashboard (we'll go back there once we have launched a service in step 2).
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
Now that we have a Træfik instance up and running, we will deploy new services.
Edit your `docker-compose.yml` file and add the following at the end of your file.
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```
The above defines `whoami`: a simple web service that outputs information about the machine it is deployed on (its IP address, host, and so on).
Start the `whoami` service with the following command:
```shell
docker-compose up -d whoami
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new container and updated its own configuration.
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
_Shows the following output:_
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
### 3 — Launch More Instances — Traefik Load Balances Them
Run more instances of your `whoami` service with the following command:
```shell
docker-compose up -d --scale whoami=2
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new instance of the container.
Finally, see that Træfik load-balances between the two instances of your services by running twice the following command:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
The output will show alternatively one of the followings:
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
```yaml
Hostname: 8458f154e1f1
IP: 172.27.0.4
# ...
```
### 4 — Enjoy Træfik's Magic
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you! Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/configuration/backends/available) that will do the job.
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).
## Resources
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com).
You will learn Træfik basics in less than 10 minutes.
@@ -80,9 +180,9 @@ You will learn fundamental Træfik features and see some demos with Kubernetes.
[![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
## Get it
## Downloads
### Binary
### The Official Binary File
You can grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
@@ -90,114 +190,10 @@ You can grab the latest binary from the [releases](https://github.com/containous
./traefik -c traefik.toml
```
### Docker
### The Official Docker Image
Using the tiny Docker image:
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
## Test it
You can test Træfik easily using [Docker compose](https://docs.docker.com/compose), with this `docker-compose.yml` file in a folder named `traefik`:
```yaml
version: '2'
services:
proxy:
image: traefik
command: --api --docker --docker.domain=docker.localhost --logLevel=DEBUG
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /dev/null:/traefik.toml
networks:
webgateway:
driver: bridge
```
Start it from within the `traefik` folder:
```shell
docker-compose up -d
```
In a browser, you may open [http://localhost:8080](http://localhost:8080) to access Træfik's dashboard and observe the following magic.
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
```yaml
version: '2'
services:
whoami:
image: emilevauge/whoami
networks:
- web
labels:
- "traefik.backend=whoami"
- "traefik.frontend.rule=Host:whoami.docker.localhost"
networks:
web:
external:
name: traefik_webgateway
```
Then, start and scale it in the `test` folder:
```shell
docker-compose up -d
docker-compose scale whoami=2
```
Finally, test load-balancing between the two services `test_whoami_1` and `test_whoami_2`:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
```yaml
Hostname: ef194d07634a
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.4
IP: fe80::42:acff:fe11:4
GET / HTTP/1.1
Host: 172.17.0.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1
X-Forwarded-Host: 172.17.0.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: dbb60406010d
```
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
```yaml
Hostname: 6c3c5df0c79a
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.3
IP: fe80::42:acff:fe11:3
GET / HTTP/1.1
Host: 172.17.0.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1
X-Forwarded-Host: 172.17.0.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: dbb60406010d
```
```

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

@@ -50,7 +50,7 @@ version: '2'
services:
traefik:
image: traefik:1.3.5
image: traefik:1.5.4
restart: always
ports:
- 80:80
@@ -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 hostname certificate for the first time, which can lead 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"]
@@ -336,86 +335,3 @@ providersThrottleDuration = "5s"
[respondingTimeouts]
idleTimeout = "360s"
```
## Securing 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:
* Regular path: `http://hostname:80/foo`
* 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.
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 dedicated port
### Enable ping health check on a regular entrypoint
To proxy `/ping` from a regular entrypoint to the admin one without exposing the panel, do the following:
```toml
[backends]
[backends.traefik]
[backends.traefik.servers.server1]
url = "http://localhost:8080"
weight = 10
[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.
### 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:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[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"
```
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entrypoint, we enable it on a _dedicated_ entrypoint.
In the above example, you would access a regular path, admin panel 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`.

View File

@@ -184,9 +184,9 @@ spec:
securityContext:
privileged: true
args:
- -d
- --api
- --kubernetes
- --logLevel=INFO
---
kind: Service
apiVersion: v1

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.
@@ -328,7 +328,7 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
| Key | Value |
|----------------------------------------------------|--------------------|
| `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/passHostHeader` | `true` |
| `/traefik/frontends/frontend2/passhostheader` | `true` |
| `/traefik/frontends/frontend2/priority` | `10` |
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |

View File

@@ -5,11 +5,15 @@ traefikLogsFile = "log/traefik.log"
accessLogsFile = "log/access.log"
logLevel = "DEBUG"
[entryPoints]
[entryPoints.api]
address = ":7888"
################################################################
# Web configuration backend
# API configuration
################################################################
[web]
address = ":7888"
[api]
entryPoint = "api"
################################################################
# File configuration backend

View File

@@ -5,11 +5,15 @@ traefikLogsFile = "log/traefik.log"
accessLogsFile = "log/access.log"
logLevel = "DEBUG"
[entryPoints]
[entryPoints.api]
address = ":7888"
################################################################
# Web configuration backend
# API configuration
################################################################
[web]
address = ":7888"
[api]
entryPoint = "api"
################################################################
# File configuration backend

View File

@@ -11,7 +11,6 @@ defaultEntryPoints = ["http", "https"]
address = ":443"
[entryPoints.https.tls]
[acme]
email = "test@traefik.io"
storage = "/etc/traefik/conf/acme.json"
@@ -19,12 +18,10 @@ entryPoint = "https"
onDemand = false
OnHostRule = true
caServer = "http://traefik.localhost.com:4000/directory"
[acme.httpChallenge]
entryPoint="http"
[acme.httpChallenge]
entryPoint="http"
[web]
address = ":8080"
[api]
[docker]
endpoint = "unix:///var/run/docker.sock"

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

@@ -1,6 +1,6 @@
traefik:
image: traefik
command: --web --rancher --rancher.domain=rancher.localhost --rancher.endpoint=http://example.com --rancher.accesskey=XXXXXXX --rancher.secretkey=YYYYYY --logLevel=DEBUG
command: --api --rancher --rancher.domain=rancher.localhost --rancher.endpoint=http://example.com --rancher.accesskey=XXXXXXX --rancher.secretkey=YYYYYY --logLevel=DEBUG
ports:
- "80:80"
- "443:443"

View File

@@ -1,6 +1,6 @@
traefik:
image: traefik
command: -c /dev/null --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
command: -c /dev/null --api --docker --docker.domain=docker.localhost --logLevel=DEBUG
ports:
- "80:80"
- "8080:8080"

View File

@@ -29,8 +29,9 @@ spec:
- image: traefik
name: traefik-ingress-lb
args:
- --web
- --api
- --kubernetes
- --logLevel=INFO
---
kind: Service
apiVersion: v1

View File

@@ -34,9 +34,9 @@ spec:
securityContext:
privileged: true
args:
- -d
- --web
- --api
- --kubernetes
- --logLevel=INFO
---
kind: Service
apiVersion: v1

View File

@@ -0,0 +1,106 @@
## The Træfik Quickstart (Using Docker)
In this quickstart, we'll use [Docker compose](https://docs.docker.com/compose) to create our demo infrastructure.
To save some time, you can clone [Træfik's repository](https://github.com/containous/traefik) and use the quickstart files located in the [examples/quickstart](https://github.com/containous/traefik/tree/master/examples/quickstart/) directory.
### 1 — Launch Træfik — Tell It to Listen to Docker
Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Træfik image:
```yaml
version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
```
**That's it. Now you can launch Træfik!**
Start your `reverse-proxy` with the following command:
```shell
docker-compose up -d reverse-proxy
```
You can open a browser and go to [http://localhost:8080](http://localhost:8080) to see Træfik's dashboard (we'll go back there once we have launched a service in step 2).
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
Now that we have a Træfik instance up and running, we will deploy new services.
Edit your `docker-compose.yml` file and add the following at the end of your file.
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```
The above defines `whoami`: a simple web service that outputs information about the machine it is deployed on (its IP address, host, and so on).
Start the `whoami` service with the following command:
```shell
docker-compose up -d whoami
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new container and updated its own configuration.
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
_Shows the following output:_
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
### 3 — Launch More Instances — Traefik Load Balances Them
Run more instances of your `whoami` service with the following command:
```shell
docker-compose up -d --scale whoami=2
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new instance of the container.
Finally, see that Træfik load-balances between the two instances of your services by running twice the following command:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
The output will show alternatively one of the followings:
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
```yaml
Hostname: 8458f154e1f1
IP: 172.27.0.4
# ...
```
### 4 — Enjoy Træfik's Magic
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you! Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/configuration/backends/available) that will do the job.
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).

View File

@@ -0,0 +1,18 @@
version: '3'
services:
#The reverse proxy service (Træfik)
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
#A container that exposes a simple API
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"

View File

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

@@ -358,3 +358,31 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
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

@@ -0,0 +1,78 @@
package integration
import (
"encoding/json"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/containous/traefik/integration/try"
"github.com/containous/traefik/testhelpers"
"github.com/containous/traefik/types"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
)
const (
composeProject = "minimal"
)
// Docker test suites
type DockerComposeSuite struct {
BaseSuite
}
func (s *DockerComposeSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, composeProject)
s.composeProject.Start(c)
}
func (s *DockerComposeSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Stop(c)
}
}
func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
var serviceCount = 2
var composeService = "whoami1"
s.composeProject.Scale(c, composeService, serviceCount)
file := s.adaptFileForHost(c, "fixtures/docker/simple.toml")
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
req.Host = "my.super.host"
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
c.Assert(err, checker.IsNil)
resp, err := http.Get("http://127.0.0.1:8080/api/providers/docker")
c.Assert(err, checker.IsNil)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
c.Assert(err, checker.IsNil)
var provider types.Configuration
c.Assert(json.Unmarshal(body, &provider), checker.IsNil)
// check that we have only one backend with n servers
c.Assert(provider.Backends, checker.HasLen, 1)
myBackend := provider.Backends["backend-"+composeService+"-integrationtest"+composeProject]
c.Assert(myBackend, checker.NotNil)
c.Assert(myBackend.Servers, checker.HasLen, serviceCount)
// check that we have only one frontend
c.Assert(provider.Frontends, checker.HasLen, 1)
}

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

@@ -17,8 +17,8 @@ entryPoint = "https"
onDemand = {{.OnDemand}}
OnHostRule = {{.OnHostRule}}
caServer = "http://{{.BoulderHost}}:4000/directory"
[acme.httpchallenge]
entrypoint="http"
[acme.httpchallenge]
entrypoint="http"
[file]

View File

@@ -20,8 +20,8 @@ entryPoint = "https"
onDemand = {{.OnDemand}}
OnHostRule = {{.OnHostRule}}
caServer = "http://{{.BoulderHost}}:4000/directory"
[acme.httpchallenge]
entrypoint="http"
[acme.httpchallenge]
entrypoint="http"
[file]

View File

@@ -11,6 +11,6 @@
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "fixtures/acme/ssl/wildcard.crt"
keyFile = "fixtures/acme/ssl/wildcard.key"
[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

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

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

@@ -0,0 +1,4 @@
whoami1:
image: emilevauge/whoami
labels:
- traefik.frontend.rule=PathPrefix:/whoami

View File

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

View File

@@ -16,9 +16,6 @@ theme:
include_sidebar: true
favicon: img/traefik.icon.png
logo: img/traefik.logo.png
palette:
primary: 'blue'
accent: 'light blue'
feature:
tabs: false
palette:
@@ -83,7 +80,7 @@ pages:
- 'Backend: Mesos': 'configuration/backends/mesos.md'
- 'Backend: Rancher': 'configuration/backends/rancher.md'
- 'Backend: Rest': 'configuration/backends/rest.md'
- 'Backend: Service Fabric': 'configuration/backends/servicefabric.md'
- 'Backend: Azure Service Fabric': 'configuration/backends/servicefabric.md'
- 'Backend: Zookeeper': 'configuration/backends/zookeeper.md'
- 'API / Dashboard': 'configuration/api.md'
- 'Ping': 'configuration/ping.md'

View File

@@ -6,6 +6,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"text/template"
"time"
@@ -135,7 +136,7 @@ func getChangedIntKeys(currState []int, prevState []int) ([]int, []int) {
return fun.Keys(addedKeys).([]int), fun.Keys(removedKeys).([]int)
}
func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan<- map[string][]string, errorCh chan<- error) {
func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan<- map[string][]string, notifyError func(error)) {
health := p.client.Health()
catalog := p.client.Catalog()
@@ -156,7 +157,7 @@ func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan<
healthyState, meta, err := health.State("passing", options)
if err != nil {
log.WithError(err).Error("Failed to retrieve health checks")
errorCh <- err
notifyError(err)
return
}
@@ -180,7 +181,7 @@ func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan<
data, _, err := catalog.Services(&api.QueryOptions{})
if err != nil {
log.Errorf("Failed to list services: %s", err)
errorCh <- err
notifyError(err)
return
}
@@ -214,7 +215,7 @@ type Service struct {
Ports []int
}
func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh chan<- map[string][]string, errorCh chan<- error) {
func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh chan<- map[string][]string, notifyError func(error)) {
catalog := p.client.Catalog()
safe.Go(func() {
@@ -233,7 +234,7 @@ func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh c
data, meta, err := catalog.Services(options)
if err != nil {
log.Errorf("Failed to list services: %s", err)
errorCh <- err
notifyError(err)
return
}
@@ -249,7 +250,7 @@ func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh c
nodes, _, err := catalog.Service(key, "", &api.QueryOptions{})
if err != nil {
log.Errorf("Failed to get detail of service %s: %s", key, err)
errorCh <- err
notifyError(err)
return
}
nodesID := getServiceIds(nodes)
@@ -572,8 +573,15 @@ func (p *CatalogProvider) watch(configurationChan chan<- types.ConfigMessage, st
watchCh := make(chan map[string][]string)
errorCh := make(chan error)
p.watchHealthState(stopCh, watchCh, errorCh)
p.watchCatalogServices(stopCh, watchCh, errorCh)
var errorOnce sync.Once
notifyError := func(err error) {
errorOnce.Do(func() {
errorCh <- err
})
}
p.watchHealthState(stopCh, watchCh, notifyError)
p.watchCatalogServices(stopCh, watchCh, notifyError)
defer close(stopCh)
defer close(watchCh)

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

@@ -369,11 +369,12 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
servers := map[string][]dockerData{}
serviceNames := make(map[string]struct{})
for idx, container := range filteredContainers {
if _, exists := serviceNames[container.ServiceName]; !exists {
serviceNameKey := getServiceNameKey(container, p.SwarmMode)
if _, exists := serviceNames[serviceNameKey]; !exists {
frontendName := p.getFrontendName(container, idx)
frontends[frontendName] = append(frontends[frontendName], container)
if len(container.ServiceName) > 0 {
serviceNames[container.ServiceName] = struct{}{}
if len(serviceNameKey) > 0 {
serviceNames[serviceNameKey] = struct{}{}
}
}
backendName := getBackend(container)
@@ -403,6 +404,16 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
return configuration
}
func getServiceNameKey(container dockerData, swarmMode bool) string {
serviceNameKey := container.ServiceName
if len(container.Labels[labelDockerComposeProject]) > 0 && len(container.Labels[labelDockerComposeService]) > 0 && !swarmMode {
serviceNameKey = container.Labels[labelDockerComposeService] + container.Labels[labelDockerComposeProject]
}
return serviceNameKey
}
// Regexp used to extract the name of the service and the name of the property for this service
// All properties are under the format traefik.<servicename>.frontent.*= except the port/weight/protocol directly after traefik.<servicename>.
var servicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.(?P<property_name>port|weight|protocol|frontend\.(.*))$`)
@@ -777,19 +788,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 +818,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 +860,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

@@ -852,6 +852,94 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
},
},
{
containers: []docker.ContainerJSON{
containerJSON(
name("test_0"),
labels(map[string]string{
labelDockerComposeProject: "myProject",
labelDockerComposeService: "myService",
}),
ports(nat.PortMap{
"80/tcp": {},
}),
withNetwork("bridge", ipv4("127.0.0.1")),
),
containerJSON(
name("test_1"),
labels(map[string]string{
labelDockerComposeProject: "myProject",
labelDockerComposeService: "myService",
}),
ports(nat.PortMap{
"80/tcp": {},
}),
withNetwork("bridge", ipv4("127.0.0.2")),
),
containerJSON(
name("test_2"),
labels(map[string]string{
labelDockerComposeProject: "myProject",
labelDockerComposeService: "myService2",
}),
ports(nat.PortMap{
"80/tcp": {},
}),
withNetwork("bridge", ipv4("127.0.0.3")),
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-myService-myProject-docker-localhost-0": {
Backend: "backend-myService-myProject",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-myService-myProject-docker-localhost-0": {
Rule: "Host:myService.myProject.docker.localhost",
},
},
},
"frontend-Host-myService2-myProject-docker-localhost-2": {
Backend: "backend-myService2-myProject",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-myService2-myProject-docker-localhost-2": {
Rule: "Host:myService2.myProject.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-myService2-myProject": {
Servers: map[string]types.Server{
"server-test_2": {
URL: "http://127.0.0.3:80",
Weight: 0,
},
},
CircuitBreaker: nil,
},
"backend-myService-myProject": {
Servers: map[string]types.Server{
"server-test_0": {
URL: "http://127.0.0.1:80",
Weight: 0,
}, "server-test_1": {
URL: "http://127.0.0.2:80",
Weight: 0,
},
},
CircuitBreaker: nil,
},
},
},
}
for caseID, test := range testCases {

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

@@ -308,12 +308,12 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
byTaskDefinition := make(map[string]int)
for _, task := range tasks {
if _, found := byContainerInstance[*task.ContainerInstanceArn]; !found {
byContainerInstance[*task.ContainerInstanceArn] = len(containerInstanceArns)
if _, found := byContainerInstance[aws.StringValue(task.ContainerInstanceArn)]; !found {
byContainerInstance[aws.StringValue(task.ContainerInstanceArn)] = len(containerInstanceArns)
containerInstanceArns = append(containerInstanceArns, task.ContainerInstanceArn)
}
if _, found := byTaskDefinition[*task.TaskDefinitionArn]; !found {
byTaskDefinition[*task.TaskDefinitionArn] = len(taskDefinitionArns)
if _, found := byTaskDefinition[aws.StringValue(task.TaskDefinitionArn)]; !found {
byTaskDefinition[aws.StringValue(task.TaskDefinitionArn)] = len(taskDefinitionArns)
taskDefinitionArns = append(taskDefinitionArns, task.TaskDefinitionArn)
}
}
@@ -327,11 +327,10 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
if err != nil {
return nil, err
}
for _, task := range tasks {
machineIdx := byContainerInstance[*task.ContainerInstanceArn]
taskDefIdx := byTaskDefinition[*task.TaskDefinitionArn]
machineIdx := byContainerInstance[aws.StringValue(task.ContainerInstanceArn)]
taskDefIdx := byTaskDefinition[aws.StringValue(task.TaskDefinitionArn)]
for _, container := range task.Containers {
@@ -345,8 +344,8 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
}
instances = append(instances, ecsInstance{
fmt.Sprintf("%s-%s", strings.Replace(*task.Group, ":", "-", 1), *container.Name),
(*task.TaskArn)[len(*task.TaskArn)-12:],
fmt.Sprintf("%s-%s", strings.Replace(aws.StringValue(task.Group), ":", "-", 1), *container.Name),
(aws.StringValue(task.TaskArn))[len(aws.StringValue(task.TaskArn))-12:],
task,
taskDefinition,
container,
@@ -381,7 +380,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
containerResp := req.Data.(*ecs.DescribeContainerInstancesOutput)
for i, container := range containerResp.ContainerInstances {
order[*container.Ec2InstanceId] = order[*container.ContainerInstanceArn]
order[aws.StringValue(container.Ec2InstanceId)] = order[aws.StringValue(container.ContainerInstanceArn)]
instanceIds[i] = container.Ec2InstanceId
}
}
@@ -399,7 +398,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
for _, r := range instancesResp.Reservations {
for _, i := range r.Instances {
if i.InstanceId != nil {
instances[order[*i.InstanceId]] = i
instances[order[aws.StringValue(i.InstanceId)]] = i
}
}
}
@@ -426,7 +425,7 @@ func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient,
func (p *Provider) label(i ecsInstance, k string) string {
if v, found := i.containerDefinition.DockerLabels[k]; found {
return *v
return aws.StringValue(v)
}
return ""
}
@@ -565,14 +564,14 @@ func (p *Provider) getProtocol(i ecsInstance) string {
}
func (p *Provider) getHost(i ecsInstance) string {
return *i.machine.PrivateIpAddress
return aws.StringValue(i.machine.PrivateIpAddress)
}
func (p *Provider) getPort(i ecsInstance) string {
if port := p.label(i, types.LabelPort); port != "" {
return port
}
return strconv.FormatInt(*i.container.NetworkBindings[0].HostPort, 10)
return strconv.FormatInt(aws.Int64Value(i.container.NetworkBindings[0].HostPort), 10)
}
func (p *Provider) getWeight(i ecsInstance) string {

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

@@ -283,7 +283,7 @@ func containerFilter(name, healthState, state string) bool {
return false
}
if state != "" && state != "running" && state != "updating-running" {
if state != "" && state != "running" && state != "updating-running" && state != "upgraded" {
log.Debugf("Filtering container %s with state of %s", name, state)
return false
}
@@ -319,7 +319,7 @@ func (p *Provider) serviceFilter(service rancherData) bool {
return false
}
if service.State != "" && service.State != "active" && service.State != "updating-active" && service.State != "upgraded" {
if service.State != "" && service.State != "active" && service.State != "updating-active" && service.State != "upgraded" && service.State != "upgrading" {
log.Debugf("Filtering service %s with state of %s", service.Name, service.State)
return false
}

View File

@@ -1,4 +1,39 @@
mkdocs>=0.17.2
pymdown-extensions>=1.4
mkdocs-bootswatch>=0.4.0
mkdocs-material>=2.2.6
mkdocs==0.17.5
pymdown-extensions==4.12
mkdocs-bootswatch==0.5.0
mkdocs-material==2.9.4
appdirs==1.4.4
CacheControl==0.12.6
certifi==2020.12.5
chardet==4.0.0
click==8.1.3
colorama==0.4.4
contextlib2==0.6.0
distlib==0.3.1
distro==1.5.0
html5lib==1.1
idna==3.2
importlib-metadata==4.12.0
Jinja2==3.1.2
livereload==2.6.3
lockfile==0.12.2
Markdown==3.3.7
MarkupSafe==2.1.1
msgpack==1.0.2
ordered-set==4.0.2
packaging==20.9
pep517==0.10.0
progress==1.5
Pygments==2.12.0
pymdown-extensions==4.12
pyparsing==2.4.7
PyYAML==6.0
requests==2.25.1
retrying==1.3.3
six==1.15.0
toml==0.10.2
tornado==4.5.3
urllib3==1.26.5
webencodings==0.5.1
zipp==3.8.1

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

@@ -367,7 +367,7 @@ func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) {
providerConfigUpdateCh = make(chan types.ConfigMessage)
s.providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdateCh
s.routinesPool.Go(func(stop chan bool) {
throttleProviderConfigReload(providersThrottleDuration, s.configurationValidatedChan, providerConfigUpdateCh, stop)
s.throttleProviderConfigReload(providersThrottleDuration, s.configurationValidatedChan, providerConfigUpdateCh, stop)
})
}
providerConfigUpdateCh <- configMsg
@@ -378,11 +378,11 @@ func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) {
// It will immediately publish a new configuration and then only publish the next configuration after the throttle duration.
// Note that in the case it receives N new configs in the timeframe of the throttle duration after publishing,
// it will publish the last of the newly received configurations.
func throttleProviderConfigReload(throttle time.Duration, publish chan<- types.ConfigMessage, in <-chan types.ConfigMessage, stop chan bool) {
func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish chan<- types.ConfigMessage, in <-chan types.ConfigMessage, stop chan bool) {
ring := channels.NewRingChannel(1)
defer ring.Close()
safe.Go(func() {
s.routinesPool.Go(func(stop chan bool) {
for {
select {
case <-stop:
@@ -484,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
}
@@ -673,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
@@ -917,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 {
@@ -925,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 {
@@ -956,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 {
@@ -974,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)
@@ -1145,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)
@@ -1195,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 {
@@ -1351,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

@@ -297,7 +297,10 @@ func TestThrottleProviderConfigReload(t *testing.T) {
stop <- true
}()
go throttleProviderConfigReload(throttleDuration, publishConfig, providerConfig, stop)
globalConfig := configuration.GlobalConfiguration{}
server := NewServer(globalConfig)
go server.throttleProviderConfigReload(throttleDuration, publishConfig, providerConfig, stop)
publishedConfigCount := 0
stopConsumeConfigs := make(chan bool)
@@ -1071,7 +1074,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 +1085,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

@@ -53,7 +53,13 @@
{{$entryPoints := GetList . "/entrypoints"}}
[frontends."{{$frontend}}"]
backend = "{{Get "" . "/backend"}}"
{{ $passHostHeader := Get "" . "/passhostheader"}}
{{if $passHostHeader}}
passHostHeader = {{ $passHostHeader }}
{{else}}
# keep for compatibility reason
passHostHeader = {{Get "true" . "/passHostHeader"}}
{{end}}
priority = {{Get "0" . "/priority"}}
entryPoints = [{{range $entryPoints}}
"{{.}}",

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

@@ -71,7 +71,7 @@ func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]by
}
if expiration.IsZero() {
expiration = time.Now().Add(365)
expiration = time.Now().Add(365 * (24 * time.Hour))
}
template := x509.Certificate{

View File

@@ -14,7 +14,7 @@
# Optional
# Default: "ERROR"
#
# logLevel = "ERROR"
# logLevel = "DEBUG"
# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
@@ -24,6 +24,10 @@
#
# defaultEntryPoints = ["http", "https"]
################################################################
# Entrypoints configuration
################################################################
# Entrypoints definition
#
# Optional
@@ -32,6 +36,10 @@
[entryPoints.http]
address = ":80"
################################################################
# Traefik logs configuration
################################################################
# Traefik logs
# Enabled by default and log to stdout
#
@@ -54,6 +62,10 @@
#
# format = "common"
################################################################
# Access logs configuration
################################################################
# Enable access logs
# By default it will write to stdout and produce logs in the textual
# Common Log Format (CLF), extended with additional fields.
@@ -78,17 +90,39 @@
# format = "common"
################################################################
# Web configuration backend
# API and dashboard configuration
################################################################
# Enable web configuration backend
[web]
# Enable API and dashboard
[api]
# Web administration port
#
# Required
#
address = ":8080"
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
# entryPoint = "traefik"
# Enabled Dashboard
#
# Optional
# Default: true
#
# dashboard = false
################################################################
# Ping configuration
################################################################
# Enable ping
[ping]
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
# entryPoint = "traefik"
################################################################
# Docker configuration backend

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.

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