forked from SW/traefik
Compare commits
206 Commits
v1.4.0-rc5
...
v1.5.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22bdbd2498 | ||
|
|
287fb78654 | ||
|
|
5b24403c8e | ||
|
|
e83599dd08 | ||
|
|
f30ad20c9b | ||
|
|
01e17b6c3e | ||
|
|
3e13ebec93 | ||
|
|
23c1a9ca8e | ||
|
|
741c739ef1 | ||
|
|
52f16e11a8 | ||
|
|
0ee6973e2f | ||
|
|
4819974a1c | ||
|
|
e8e8b41eed | ||
|
|
7d23d3c0a4 | ||
|
|
718fc7a79d | ||
|
|
bfd142b13b | ||
|
|
75533b2beb | ||
|
|
9a7821b8fa | ||
|
|
e8333883df | ||
|
|
1e44e339ad | ||
|
|
89a79d0f1b | ||
|
|
9e41485ff1 | ||
|
|
3c7c6c4d9f | ||
|
|
cd1b3904da | ||
|
|
b23b2611b3 | ||
|
|
877770f7cf | ||
|
|
3142a4f4b3 | ||
|
|
b4dc96527d | ||
|
|
35b5ca4c63 | ||
|
|
daf3023b02 | ||
|
|
b17d5b80b8 | ||
|
|
48b4eb5c0d | ||
|
|
7ecd6d20ba | ||
|
|
bddad57a7b | ||
|
|
799136a714 | ||
|
|
350d61b4a6 | ||
|
|
b6f5a66fab | ||
|
|
b0c12e2422 | ||
|
|
623a7dc7e6 | ||
|
|
709c7e5707 | ||
|
|
ee04f52a16 | ||
|
|
7d98c1c4e0 | ||
|
|
4387cf38d7 | ||
|
|
a9d38570ab | ||
|
|
0e619369fd | ||
|
|
cda09c843a | ||
|
|
6333bfe6e8 | ||
|
|
41d8863d2f | ||
|
|
523b7f96f8 | ||
|
|
ab1a930705 | ||
|
|
3a99c86cb3 | ||
|
|
d6ad7e2e64 | ||
|
|
aaf120f263 | ||
|
|
c228e73b26 | ||
|
|
e27e65eb76 | ||
|
|
1c8acf3929 | ||
|
|
40b3c17703 | ||
|
|
313357a6b3 | ||
|
|
37a1aaad64 | ||
|
|
f084d2a28b | ||
|
|
077b39d7c6 | ||
|
|
7081f3df58 | ||
|
|
9fe6a0a894 | ||
|
|
3d452fd5b9 | ||
|
|
47a5cfbd3e | ||
|
|
4cb6241e93 | ||
|
|
b572879691 | ||
|
|
ad07a6ab2b | ||
|
|
4bdeb33ac1 | ||
|
|
101a4d0d8d | ||
|
|
89e07d0c55 | ||
|
|
39c1cc1b3c | ||
|
|
9f6f637527 | ||
|
|
0f09551a76 | ||
|
|
8cd72cfc1b | ||
|
|
7a141c8616 | ||
|
|
0ca65f955d | ||
|
|
011b748a55 | ||
|
|
f6181ef3e2 | ||
|
|
24368747ab | ||
|
|
66591cf216 | ||
|
|
1feeeb2eec | ||
|
|
419d46c958 | ||
|
|
7063da1c7d | ||
|
|
bee8ebb00b | ||
|
|
da5e4a13bf | ||
|
|
5dc1ec68a3 | ||
|
|
3d2e5ebe39 | ||
|
|
f5130db6b0 | ||
|
|
676b79db42 | ||
|
|
6d2f4a0813 | ||
|
|
4b91204686 | ||
|
|
7ddefcef72 | ||
|
|
0f3e42d463 | ||
|
|
c9129b8ecf | ||
|
|
a6955ecf59 | ||
|
|
6619a787a3 | ||
|
|
aae17c817b | ||
|
|
ab87bad952 | ||
|
|
be306d651e | ||
|
|
8fe5c22075 | ||
|
|
05a9350e57 | ||
|
|
7ed4ae2f8c | ||
|
|
5d6384e101 | ||
|
|
1a4564d998 | ||
|
|
66e489addb | ||
|
|
cdab6b1796 | ||
|
|
722f299306 | ||
|
|
66be04f39e | ||
|
|
8719f2836e | ||
|
|
0c702b0b6b | ||
|
|
6fcab72ec7 | ||
|
|
77b111702b | ||
|
|
96a7cc483f | ||
|
|
1e3506848a | ||
|
|
5ee2cae85c | ||
|
|
5c119fe2d6 | ||
|
|
d55115844a | ||
|
|
4f4491c247 | ||
|
|
1691f586d7 | ||
|
|
04dfe0de84 | ||
|
|
27d1b46835 | ||
|
|
2f62ec3632 | ||
|
|
384488ac02 | ||
|
|
c469e669fd | ||
|
|
56affb90ae | ||
|
|
f6aa147c78 | ||
|
|
9bd0fff319 | ||
|
|
00d7c5972f | ||
|
|
58a438167b | ||
|
|
e3131481e9 | ||
|
|
bc8d68bd31 | ||
|
|
07c6e33598 | ||
|
|
70812c70fc | ||
|
|
d89b234cad | ||
|
|
2070aa9443 | ||
|
|
91ff94ea56 | ||
|
|
0347537f43 | ||
|
|
db9b18f121 | ||
|
|
ee70001be3 | ||
|
|
972eea97fe | ||
|
|
2b4d33e919 | ||
|
|
fc4d670c88 | ||
|
|
02035d4942 | ||
|
|
93a46089ce | ||
|
|
e8d63b2a3b | ||
|
|
d3c7681bc5 | ||
|
|
dc66db4abe | ||
|
|
a0e1cf8376 | ||
|
|
5292b84f4f | ||
|
|
b27455a36f | ||
|
|
5042c5bf40 | ||
|
|
da7b6f0baf | ||
|
|
9b5845f1cb | ||
|
|
e8633d17e8 | ||
|
|
d1d8b01dfb | ||
|
|
7c4353a0ac | ||
|
|
1b2cb53d4f | ||
|
|
3158e51c62 | ||
|
|
a0c72cdf00 | ||
|
|
f0371da838 | ||
|
|
44b82e6231 | ||
|
|
04f0bf3070 | ||
|
|
7400c39511 | ||
|
|
008a5af6d6 | ||
|
|
35ca40c3de | ||
|
|
de821fc305 | ||
|
|
e3cac7d0e5 | ||
|
|
81f7aa9df2 | ||
|
|
6bce298d90 | ||
|
|
afbad56012 | ||
|
|
d973096464 | ||
|
|
7192aa86b5 | ||
|
|
9c8df8b9ce | ||
|
|
ff4c7b82bc | ||
|
|
47ff51e640 | ||
|
|
08503655d9 | ||
|
|
3afd6024b5 | ||
|
|
aa308b7a3a | ||
|
|
9598f646f5 | ||
|
|
8af39bdaf7 | ||
|
|
914f3d1fa3 | ||
|
|
8cb3f0835a | ||
|
|
cba0898e4f | ||
|
|
8d158402f3 | ||
|
|
7f2582e3b6 | ||
|
|
dbc796359f | ||
|
|
4d1285d8e5 | ||
|
|
871d097b30 | ||
|
|
1532033a7f | ||
|
|
9faae7387e | ||
|
|
a5c644e719 | ||
|
|
cf508b6d48 | ||
|
|
1c98a9ad3e | ||
|
|
fc550ac1fc | ||
|
|
d6ef8ec3d1 | ||
|
|
837db9a2d9 | ||
|
|
2cbf9cae71 | ||
|
|
808ffb0491 | ||
|
|
d54417acfe | ||
|
|
9fba37b409 | ||
|
|
03eb5139a2 | ||
|
|
5c4931e235 | ||
|
|
7fd1eb3780 | ||
|
|
2b863d9bc2 | ||
|
|
9ce4f94818 |
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -16,8 +16,21 @@ HOW TO WRITE A GOOD PULL REQUEST?
|
||||
|
||||
-->
|
||||
|
||||
### Description
|
||||
### What does this PR do?
|
||||
|
||||
<!--
|
||||
Briefly describe the pull request in a few paragraphs.
|
||||
-->
|
||||
<!-- A brief description of the change being made with this pull request. -->
|
||||
|
||||
|
||||
### Motivation
|
||||
|
||||
<!-- What inspired you to submit this pull request? -->
|
||||
|
||||
|
||||
### More
|
||||
|
||||
- [ ] Added/updated tests
|
||||
- [ ] Added/updated documentation
|
||||
|
||||
### Additional Notes
|
||||
|
||||
<!-- Anything else we should know when reviewing? -->
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
/dist
|
||||
/autogen/gen.go
|
||||
.idea
|
||||
.intellij
|
||||
/autogen/genstatic/gen.go
|
||||
.idea/
|
||||
.intellij/
|
||||
*.iml
|
||||
/traefik
|
||||
/traefik.toml
|
||||
|
||||
@@ -10,7 +10,7 @@ else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=roquefort
|
||||
export CODENAME=cancoillotte
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ env:
|
||||
global:
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: $TRAVIS_TAG
|
||||
- CODENAME: roquefort
|
||||
- CODENAME: cancoillotte
|
||||
- N_MAKE_JOBS: 2
|
||||
|
||||
script:
|
||||
@@ -36,6 +36,7 @@ deploy:
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
condition: ${TRAVIS_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$
|
||||
- provider: releases
|
||||
api_key: ${GITHUB_TOKEN}
|
||||
file: dist/traefik*
|
||||
|
||||
470
CHANGELOG.md
470
CHANGELOG.md
@@ -1,5 +1,467 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.5.0-rc4](https://github.com/containous/traefik/tree/v1.5.0-rc4) (2018-01-04)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc3...v1.5.0-rc4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consulcatalog]** Use prefix for sticky and stickiness tags. ([#2624](https://github.com/containous/traefik/pull/2624) by [ldez](https://github.com/ldez))
|
||||
- **[file,tls]** Send empty configuration from file provider ([#2609](https://github.com/containous/traefik/pull/2609) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,docker,k8s]** Fix custom headers template ([#2621](https://github.com/containous/traefik/pull/2621) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Don't panic if ResponseWriter does not implement CloseNotify ([#2651](https://github.com/containous/traefik/pull/2651) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** We need to flush the end of the body when retry is streamed ([#2644](https://github.com/containous/traefik/pull/2644) by [Juliens](https://github.com/Juliens))
|
||||
- **[tls]** Allow deleting dynamically all TLS certificates from an entryPoint ([#2603](https://github.com/containous/traefik/pull/2603) by [nmengin](https://github.com/nmengin))
|
||||
- **[websocket]** Use gorilla readMessage and writeMessage instead of just an io.Copy ([#2650](https://github.com/containous/traefik/pull/2650) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[consul,consulcatalog]** Split Consul and Consul Catalog documentation ([#2654](https://github.com/containous/traefik/pull/2654) by [ldez](https://github.com/ldez))
|
||||
- **[docker/swarm]** Typo in docker.endpoint TCP port. ([#2626](https://github.com/containous/traefik/pull/2626) by [redhandpl](https://github.com/redhandpl))
|
||||
- **[docker]** Add a note on how to add label to a docker compose file ([#2611](https://github.com/containous/traefik/pull/2611) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||
- **[k8s]** k8s guide: Leave note about assumed DaemonSet usage. ([#2634](https://github.com/containous/traefik/pull/2634) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Improve Marathon service label documentation. ([#2635](https://github.com/containous/traefik/pull/2635) by [timoreimann](https://github.com/timoreimann))
|
||||
|
||||
**Misc:**
|
||||
- **[etcd,kv,tls]** Add tests for TLS dynamic configuration in ETCD3 ([#2606](https://github.com/containous/traefik/pull/2606) by [dahefanteng](https://github.com/dahefanteng))
|
||||
- Merge v1.4.6 into v1.5 ([#2642](https://github.com/containous/traefik/pull/2642) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.4.6](https://github.com/containous/traefik/tree/v1.4.6) (2018-01-02)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.5...v1.4.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Normalize serviceName added to the service backend names ([#2631](https://github.com/containous/traefik/pull/2631) by [mmatur](https://github.com/mmatur))
|
||||
- **[websocket]** Use gorilla readMessage and writeMessage instead of just an io.Copy ([#2640](https://github.com/containous/traefik/pull/2640) by [Juliens](https://github.com/Juliens))
|
||||
- Fix bug report command ([#2638](https://github.com/containous/traefik/pull/2638) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.5.0-rc3](https://github.com/containous/traefik/tree/v1.5.0-rc3) (2017-12-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc2...v1.5.0-rc3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[docker,k8s,rancher]** Support regex redirect by frontend ([#2570](https://github.com/containous/traefik/pull/2570) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,docker]** Modify ACME configuration migration into KV store ([#2598](https://github.com/containous/traefik/pull/2598) by [nmengin](https://github.com/nmengin))
|
||||
- **[consulcatalog]** Reload configuration when port change for one service ([#2574](https://github.com/containous/traefik/pull/2574) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Fix bad Træfik update on Consul Catalog ([#2573](https://github.com/containous/traefik/pull/2573) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Add missing entrypoints template. ([#2594](https://github.com/containous/traefik/pull/2594) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Fix stickiness bug due to template syntax error ([#2591](https://github.com/containous/traefik/pull/2591) by [dahefanteng](https://github.com/dahefanteng))
|
||||
- **[marathon]** Update go-marathon ([#2585](https://github.com/containous/traefik/pull/2585) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[mesos]** Mesos: Use slave.PID.Host as task SlaveIP. ([#2590](https://github.com/containous/traefik/pull/2590) by [nemosupremo](https://github.com/nemosupremo))
|
||||
- **[middleware]** Fix RawPath handling in addPrefix ([#2560](https://github.com/containous/traefik/pull/2560) by [risdenk](https://github.com/risdenk))
|
||||
- **[rules]** Add non regex pathPrefix ([#2592](https://github.com/containous/traefik/pull/2592) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[servicefabric]** Fix backend name for Stateful services. (Service Fabric) ([#2559](https://github.com/containous/traefik/pull/2559) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix isHealthy logic. ([#2577](https://github.com/containous/traefik/pull/2577) by [ldez](https://github.com/ldez))
|
||||
- **[zk]** Change Zookeeper default prefix. ([#2580](https://github.com/containous/traefik/pull/2580) by [ldez](https://github.com/ldez))
|
||||
- Fix frontend redirect ([#2544](https://github.com/containous/traefik/pull/2544) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Improve documentation for Cloudflare API key ([#2558](https://github.com/containous/traefik/pull/2558) by [mmatur](https://github.com/mmatur))
|
||||
- Move rate limit documentation. ([#2588](https://github.com/containous/traefik/pull/2588) by [ldez](https://github.com/ldez))
|
||||
- Grammar ([#2562](https://github.com/containous/traefik/pull/2562) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- Fix broken links and improve ResponseCodeRatio() description ([#2538](https://github.com/containous/traefik/pull/2538) by [mvasin](https://github.com/mvasin))
|
||||
|
||||
## [v1.5.0-rc2](https://github.com/containous/traefik/tree/v1.5.0-rc2) (2017-12-06)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.5.0-rc2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Modify the ACME renewing logs level ([#2520](https://github.com/containous/traefik/pull/2520) by [nmengin](https://github.com/nmengin))
|
||||
- **[api]** Fix pprof route order. ([#2523](https://github.com/containous/traefik/pull/2523) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[docker,k8s]** Change custom headers separator ([#2509](https://github.com/containous/traefik/pull/2509) by [ldez](https://github.com/ldez))
|
||||
- **[docker,k8s]** Fix Labels/annotation logs and values. ([#2488](https://github.com/containous/traefik/pull/2488) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Quote template strings ([#2496](https://github.com/containous/traefik/pull/2496) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[docker]** Fix empty IP for backend when dnsrr in Docker swarm mode ([#2490](https://github.com/containous/traefik/pull/2490) by [mmatur](https://github.com/mmatur))
|
||||
- **[healthcheck]** Fix healthcheck when web is not specified ([#2529](https://github.com/containous/traefik/pull/2529) by [Juliens](https://github.com/Juliens))
|
||||
- **[k8s]** Reduce logs with new Kubernetes security annotations ([#2506](https://github.com/containous/traefik/pull/2506) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Do not ignore web params when web.metrics.prometheus is set ([#2499](https://github.com/containous/traefik/pull/2499) by [Juliens](https://github.com/Juliens))
|
||||
- **[metrics]** Fix metrics problem on multiple entrypoints ([#2492](https://github.com/containous/traefik/pull/2492) by [Juliens](https://github.com/Juliens))
|
||||
- Close ring buffer used in throttling function. ([#2532](https://github.com/containous/traefik/pull/2532) by [timoreimann](https://github.com/timoreimann))
|
||||
- Fix wrong default entrypoint and non-existing entrypoint issue ([#2501](https://github.com/containous/traefik/pull/2501) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[consul]** Improve Consul documentation ([#2485](https://github.com/containous/traefik/pull/2485) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker]** Fix Docker labels documentation render. ([#2505](https://github.com/containous/traefik/pull/2505) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add note to Kubernetes RBAC docs about RoleBindings and namespaces ([#2498](https://github.com/containous/traefik/pull/2498) by [jmara](https://github.com/jmara))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.4.5 into v1.5 ([#2530](https://github.com/containous/traefik/pull/2530) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
## [v1.4.5](https://github.com/containous/traefik/tree/v1.4.5) (2017-12-05)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.4...v1.4.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Fix empty ip when container is stopped ([#2478](https://github.com/containous/traefik/pull/2478) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Fix kubernetes path prefix rule with rewrite-target ([#2461](https://github.com/containous/traefik/pull/2461) by [cheungpat](https://github.com/cheungpat))
|
||||
|
||||
**Documentation:**
|
||||
- **[file]** Emphasize the necessity of enabling file backend ([#2483](https://github.com/containous/traefik/pull/2483) by [mvasin](https://github.com/mvasin))
|
||||
- Add link to future 1.5 documentation. ([#2477](https://github.com/containous/traefik/pull/2477) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.5.0-rc1](https://github.com/containous/traefik/tree/v1.5.0-rc1) (2017-11-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme,provider,docker,tls]** Make the TLS certificates management dynamic. ([#2233](https://github.com/containous/traefik/pull/2233) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update github.com/xenolf/lego to 0.4.1 ([#2304](https://github.com/containous/traefik/pull/2304) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
- **[api,healthcheck,metrics,provider,webui]** Split Web into API/Dashboard, ping, metric and Rest Provider ([#2335](https://github.com/containous/traefik/pull/2335) by [Juliens](https://github.com/Juliens))
|
||||
- **[authentication]** Pass through certain forward auth negative response headers ([#2127](https://github.com/containous/traefik/pull/2127) by [wheresmysocks](https://github.com/wheresmysocks))
|
||||
- **[cluster,consul,file]** Add file to storeconfig ([#2419](https://github.com/containous/traefik/pull/2419) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[cluster,provider]** Support Etcd v3, enhance KV support ([#2407](https://github.com/containous/traefik/pull/2407) by [nmengin](https://github.com/nmengin))
|
||||
- **[docker,k8s,rancher,webui]** redirect to another entryPoint per frontend ([#2133](https://github.com/containous/traefik/pull/2133) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[docker]** Add Custom header parsing to Docker Provider ([#2030](https://github.com/containous/traefik/pull/2030) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[docker]** Docker labels ([#2473](https://github.com/containous/traefik/pull/2473) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Add docker security headers via labels ([#2334](https://github.com/containous/traefik/pull/2334) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[docker]** Use Node IP in Swarm Standalone with "host" NetworkMode ([#2274](https://github.com/containous/traefik/pull/2274) by [BlakeMesdag](https://github.com/BlakeMesdag))
|
||||
- **[ecs]** ECS provider refactoring ([#2050](https://github.com/containous/traefik/pull/2050) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add health check label to ECS ([#2421](https://github.com/containous/traefik/pull/2421) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
- **[ecs]** Support Host NetworkMode for ECS provider ([#2320](https://github.com/containous/traefik/pull/2320) by [FriggaHel](https://github.com/FriggaHel))
|
||||
- **[etcd]** Manage certificates dynamically in kv store ([#2411](https://github.com/containous/traefik/pull/2411) by [dahefanteng](https://github.com/dahefanteng))
|
||||
- **[healthcheck]** Use healthcheck for systemd watchdog ([#2283](https://github.com/containous/traefik/pull/2283) by [guilhem](https://github.com/guilhem))
|
||||
- **[k8s]** Kubernetes security header annotations ([#2460](https://github.com/containous/traefik/pull/2460) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add labels for `traefik.frontend.entryPoints` & `PassTLSCert` to Kubernetes ([#2324](https://github.com/containous/traefik/pull/2324) by [ryarnyah](https://github.com/ryarnyah))
|
||||
- **[k8s]** Only listen to configured k8s namespaces. ([#1895](https://github.com/containous/traefik/pull/1895) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[logs,middleware,consul,docker]** Use constants from http package. ([#2425](https://github.com/containous/traefik/pull/2425) by [ldez](https://github.com/ldez))
|
||||
- **[logs]** Add json format support for Traefik logs ([#2056](https://github.com/containous/traefik/pull/2056) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[marathon]** Marathon constraints filtering ([#2388](https://github.com/containous/traefik/pull/2388) by [aantono](https://github.com/aantono))
|
||||
- **[marathon]** Remove unused lightMarathonClient. ([#2383](https://github.com/containous/traefik/pull/2383) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[metrics]** Add InfluxDB support for traefik metrics ([#2289](https://github.com/containous/traefik/pull/2289) by [adityacs](https://github.com/adityacs))
|
||||
- **[middleware]** Added ReplacePathRegex middleware ([#2033](https://github.com/containous/traefik/pull/2033) by [Tiscs](https://github.com/Tiscs))
|
||||
- **[middleware]** Fix custom headers replacement ([#2455](https://github.com/containous/traefik/pull/2455) by [mmatur](https://github.com/mmatur))
|
||||
- **[oxy]** Resync oxy with original repository ([#2451](https://github.com/containous/traefik/pull/2451) by [Juliens](https://github.com/Juliens))
|
||||
- **[provider]** Support template as raw string. ([#2413](https://github.com/containous/traefik/pull/2413) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Run Rancher tests cases in parallel. ([#2424](https://github.com/containous/traefik/pull/2424) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Update Rancher API integration to go-rancher client v2. ([#2291](https://github.com/containous/traefik/pull/2291) by [rawmind0](https://github.com/rawmind0))
|
||||
- **[servicefabric]** Add Service Fabric Provider ([#2117](https://github.com/containous/traefik/pull/2117) by [lawrencegripper](https://github.com/lawrencegripper))
|
||||
- **[tls]** Allow adding optional Client CA files ([#2306](https://github.com/containous/traefik/pull/2306) by [nmengin](https://github.com/nmengin))
|
||||
- **[websocket]** Add tests for websocket headers ([#2379](https://github.com/containous/traefik/pull/2379) by [Juliens](https://github.com/Juliens))
|
||||
- Upgrade libkermit/compose version ([#2071](https://github.com/containous/traefik/pull/2071) by [nmengin](https://github.com/nmengin))
|
||||
- Add proxy protocol tests ([#2325](https://github.com/containous/traefik/pull/2325) by [emilevauge](https://github.com/emilevauge))
|
||||
- Register pprof handlers. ([#2428](https://github.com/containous/traefik/pull/2428) by [timoreimann](https://github.com/timoreimann))
|
||||
- Rate limiting for frontends ([#2034](https://github.com/containous/traefik/pull/2034) by [bparli](https://github.com/bparli))
|
||||
- Stats collection. ([#2447](https://github.com/containous/traefik/pull/2447) by [ldez](https://github.com/ldez))
|
||||
- Add request accepting grace period delaying graceful shutdown. ([#1971](https://github.com/containous/traefik/pull/1971) by [timoreimann](https://github.com/timoreimann))
|
||||
- Put subcommand in dedicated files. ([#2265](https://github.com/containous/traefik/pull/2265) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[ecs]** Add missing functions for ECS template ([#2312](https://github.com/containous/traefik/pull/2312) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
- **[logs]** Fix traefik logs to behave like configured ([#2176](https://github.com/containous/traefik/pull/2176) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Flaky test Influxdb. ([#2386](https://github.com/containous/traefik/pull/2386) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Fix typo in frontend.headers.customresponseheaders label ([#2356](https://github.com/containous/traefik/pull/2356) by [nmandery](https://github.com/nmandery))
|
||||
- **[provider]** fix concurrent provider config reloads ([#2276](https://github.com/containous/traefik/pull/2276) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[servicefabric]** Service Fabric 'expose' as boolean. ([#2476](https://github.com/containous/traefik/pull/2476) by [ldez](https://github.com/ldez))
|
||||
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2077](https://github.com/containous/traefik/pull/2077) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update Let's Encrypt provider list ([#2347](https://github.com/containous/traefik/pull/2347) by [mmatur](https://github.com/mmatur))
|
||||
- **[etcd]** Fix typo in examples ([#2446](https://github.com/containous/traefik/pull/2446) by [dahefanteng](https://github.com/dahefanteng))
|
||||
- **[k8s]** Remove obsolete links in k8s docs ([#2465](https://github.com/containous/traefik/pull/2465) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s]** Document filename parameter for Kubernetes. ([#2464](https://github.com/containous/traefik/pull/2464) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[metrics]** Add entrypoint in Prometheus doc and remove web on Influxdb doc ([#2452](https://github.com/containous/traefik/pull/2452) by [Juliens](https://github.com/Juliens))
|
||||
- **[servicefabric]** Describe 'refreshSecond' configuration. ([#2471](https://github.com/containous/traefik/pull/2471) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Add link to crypto/tls godoc. ([#2470](https://github.com/containous/traefik/pull/2470) by [ldez](https://github.com/ldez))
|
||||
- Fix typos in changelog ([#2387](https://github.com/containous/traefik/pull/2387) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Add mmatur to maintainers ([#2303](https://github.com/containous/traefik/pull/2303) by [emilevauge](https://github.com/emilevauge))
|
||||
- Add a note about redirection rule to precise how regex/replacement work. ([#2243](https://github.com/containous/traefik/pull/2243) by [nmengin](https://github.com/nmengin))
|
||||
- Add docker things for documentation ([#2020](https://github.com/containous/traefik/pull/2020) by [tcoupin](https://github.com/tcoupin))
|
||||
|
||||
**Misc:**
|
||||
- **[acme]** dumpcerts.sh: Fix call to "base64" for Alpine ([#2344](https://github.com/containous/traefik/pull/2344) by [nknapp](https://github.com/nknapp))
|
||||
- **[acme]** Dumpcerts.sh: fixed sed, extracted domain keys ([#2161](https://github.com/containous/traefik/pull/2161) by [sjawhar](https://github.com/sjawhar))
|
||||
- Merge current v1.4 into master ([#2469](https://github.com/containous/traefik/pull/2469) by [ldez](https://github.com/ldez))
|
||||
- Revert "Merge v1.4.2 into master" ([#2414](https://github.com/containous/traefik/pull/2414) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.3 into master ([#2406](https://github.com/containous/traefik/pull/2406) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.2 into master ([#2358](https://github.com/containous/traefik/pull/2358) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.3 into master ([#2415](https://github.com/containous/traefik/pull/2415) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.1 into master ([#2318](https://github.com/containous/traefik/pull/2318) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.0 ([#2271](https://github.com/containous/traefik/pull/2271) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.0-rc5 into master ([#2242](https://github.com/containous/traefik/pull/2242) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.0-rc4 into master ([#2202](https://github.com/containous/traefik/pull/2202) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.4 into master ([#2457](https://github.com/containous/traefik/pull/2457) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.4 ([#2154](https://github.com/containous/traefik/pull/2154) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.0-rc3 into master ([#2140](https://github.com/containous/traefik/pull/2140) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.4.0-rc2 into master ([#2092](https://github.com/containous/traefik/pull/2092) by [ldez](https://github.com/ldez))
|
||||
- Upgrade libkermit/compose version ([#2074](https://github.com/containous/traefik/pull/2074) by [nmengin](https://github.com/nmengin))
|
||||
- Merge current 1.4 ([#2064](https://github.com/containous/traefik/pull/2064) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.4.4](https://github.com/containous/traefik/tree/v1.4.4) (2017-11-21)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.3...v1.4.4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[middleware]** Remove GzipHandler Fork ([#2436](https://github.com/containous/traefik/pull/2436) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Fix problems about duplicated and missing Docker backends/frontends. ([#2434](https://github.com/containous/traefik/pull/2434) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware]** Fix raw path handling in strip prefix ([#2382](https://github.com/containous/traefik/pull/2382) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[rancher]** Fix issue with label traefik.backend.loadbalancer.stickiness.cookieName ([#2423](https://github.com/containous/traefik/pull/2423) by [rawmind0](https://github.com/rawmind0))
|
||||
- http.Server log goes to Debug level. ([#2420](https://github.com/containous/traefik/pull/2420) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- Documentation archive ([#2405](https://github.com/containous/traefik/pull/2405) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.4.3](https://github.com/containous/traefik/tree/v1.4.3) (2017-11-14)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.2...v1.4.3)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consulcatalog]** Fix Traefik reload if Consul Catalog tags change ([#2389](https://github.com/containous/traefik/pull/2389) by [mmatur](https://github.com/mmatur))
|
||||
- **[kv]** Add Traefik prefix to the KV key ([#2400](https://github.com/containous/traefik/pull/2400) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware]** Flush and Status code ([#2403](https://github.com/containous/traefik/pull/2403) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Exclude GRPC from compress ([#2391](https://github.com/containous/traefik/pull/2391) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Keep status when stream mode and compress ([#2380](https://github.com/containous/traefik/pull/2380) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix some typos ([#2363](https://github.com/containous/traefik/pull/2363) by [tomsaleeba](https://github.com/tomsaleeba))
|
||||
- **[docker]** Minor fix for docker volume vs created directory ([#2372](https://github.com/containous/traefik/pull/2372) by [visibilityspots](https://github.com/visibilityspots))
|
||||
- **[k8s]** Link corrected ([#2385](https://github.com/containous/traefik/pull/2385) by [xlazex](https://github.com/xlazex))
|
||||
|
||||
**Misc:**
|
||||
- **[k8s]** Add secret creation to docs for kubernetes backend ([#2374](https://github.com/containous/traefik/pull/2374) by [shadycuz](https://github.com/shadycuz))
|
||||
|
||||
## [v1.4.2](https://github.com/containous/traefik/tree/v1.4.2) (2017-11-02)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.1...v1.4.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[cluster]** Fix datastore corruption on reload due to shrinking config size ([#2340](https://github.com/containous/traefik/pull/2340) by [else](https://github.com/else))
|
||||
- **[docker,docker/swarm]** Make frontend names differents for similar routes ([#2338](https://github.com/containous/traefik/pull/2338) by [nmengin](https://github.com/nmengin))
|
||||
- **[docker]** Fix IP address when Docker container network mode is container ([#2331](https://github.com/containous/traefik/pull/2331) by [nmengin](https://github.com/nmengin))
|
||||
- **[docker]** Make the traefik.port label optional when using service labels in Docker containers. ([#2330](https://github.com/containous/traefik/pull/2330) by [nmengin](https://github.com/nmengin))
|
||||
- **[docker]** Add unique ID to Docker services replicas ([#2314](https://github.com/containous/traefik/pull/2314) by [nmengin](https://github.com/nmengin))
|
||||
- **[marathon]** Missing Backend key in configuration when application has no tasks ([#2333](https://github.com/containous/traefik/pull/2333) by [aantono](https://github.com/aantono))
|
||||
- Remove hardcoded runtime.GOMAXPROCS. ([#2317](https://github.com/containous/traefik/pull/2317) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** fixed dead link in kubernetes backend config docs ([#2337](https://github.com/containous/traefik/pull/2337) by [perplexa](https://github.com/perplexa))
|
||||
- **[k8s]** Fix the k8s docs example deployment yaml ([#2308](https://github.com/containous/traefik/pull/2308) by [gnur](https://github.com/gnur))
|
||||
- Minor grammar change ([#2350](https://github.com/containous/traefik/pull/2350) by [haxorjim](https://github.com/haxorjim))
|
||||
- Minor typo ([#2343](https://github.com/containous/traefik/pull/2343) by [burningTyger](https://github.com/burningTyger))
|
||||
|
||||
## [v1.4.1](https://github.com/containous/traefik/tree/v1.4.1) (2017-10-24)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.0...v1.4.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Network filter ([#2301](https://github.com/containous/traefik/pull/2301) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Fix healthcheck path ([#2295](https://github.com/containous/traefik/pull/2295) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[rules]** Regex capturing group. ([#2296](https://github.com/containous/traefik/pull/2296) by [ldez](https://github.com/ldez))
|
||||
- **[websocket]** Force http/1.1 for websocket ([#2292](https://github.com/containous/traefik/pull/2292) by [Juliens](https://github.com/Juliens))
|
||||
- Stream mode when http2 ([#2309](https://github.com/containous/traefik/pull/2309) by [Juliens](https://github.com/Juliens))
|
||||
- Enhance Trust Forwarded Headers ([#2302](https://github.com/containous/traefik/pull/2302) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.4.0](https://github.com/containous/traefik/tree/v1.4.0) (2017-10-16)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Display Traefik logs in integration tests ([#2114](https://github.com/containous/traefik/pull/2114) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Make the ACME developments testing easier ([#1769](https://github.com/containous/traefik/pull/1769) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** contrib: Dump keys/certs from acme.json to files ([#1484](https://github.com/containous/traefik/pull/1484) by [brianredbeard](https://github.com/brianredbeard))
|
||||
- **[api]** Add HTTP HEAD handling to /ping endpoint ([#1768](https://github.com/containous/traefik/pull/1768) by [martinbaillie](https://github.com/martinbaillie))
|
||||
- **[authentication,consulcatalog]** Add Basic auth for consul catalog ([#2027](https://github.com/containous/traefik/pull/2027) by [mmatur](https://github.com/mmatur))
|
||||
- **[authentication,marathon]** Add marathon label to configure basic auth ([#1799](https://github.com/containous/traefik/pull/1799) by [nikore](https://github.com/nikore))
|
||||
- **[authentication,ecs]** Add basic auth for ecs ([#2026](https://github.com/containous/traefik/pull/2026) by [mmatur](https://github.com/mmatur))
|
||||
- **[authentication,middleware]** Add forward authentication option ([#1972](https://github.com/containous/traefik/pull/1972) by [drampelt](https://github.com/drampelt))
|
||||
- **[authentication]** Manage Headers for the Authentication forwarding. ([#2132](https://github.com/containous/traefik/pull/2132) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,sticky-session]** Enable loadbalancer.sticky for Consul Catalog ([#1917](https://github.com/containous/traefik/pull/1917) by [nbonneval](https://github.com/nbonneval))
|
||||
- **[consulcatalog]** Exposed by default feature in Consul Catalog ([#2006](https://github.com/containous/traefik/pull/2006) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Speeding up consul catalog health change detection ([#1694](https://github.com/containous/traefik/pull/1694) by [vholovko](https://github.com/vholovko))
|
||||
- **[consulcatalog]** Enhanced flexibility in Consul Catalog configuration ([#1565](https://github.com/containous/traefik/pull/1565) by [aantono](https://github.com/aantono))
|
||||
- **[docker,k8s]** IP Whitelists for Frontend (with Docker- & Kubernetes-Provider Support) ([#1332](https://github.com/containous/traefik/pull/1332) by [MaZderMind](https://github.com/MaZderMind))
|
||||
- **[ecs,sticky-session]** Enable loadbalancer.sticky for ECS ([#1925](https://github.com/containous/traefik/pull/1925) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add support for several ECS backends ([#1913](https://github.com/containous/traefik/pull/1913) by [mmatur](https://github.com/mmatur))
|
||||
- **[file]** Allow file provider to load service config from files in a directory. ([#1672](https://github.com/containous/traefik/pull/1672) by [rjshep](https://github.com/rjshep))
|
||||
- **[healthcheck]** Add healthcheck command ([#1982](https://github.com/containous/traefik/pull/1982) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[healthcheck]** Allow overriding the port used for healthchecks ([#1567](https://github.com/containous/traefik/pull/1567) by [bakins](https://github.com/bakins))
|
||||
- **[k8s,rules]** kubernetes ingress rewrite-target implementation ([#1723](https://github.com/containous/traefik/pull/1723) by [mlaccetti](https://github.com/mlaccetti))
|
||||
- **[k8s]** Added ability to override frontend priority for k8s ingress router ([#1874](https://github.com/containous/traefik/pull/1874) by [DiverOfDark](https://github.com/DiverOfDark))
|
||||
- **[kv]** Adds definitions to backend kv template for health checking ([#1644](https://github.com/containous/traefik/pull/1644) by [zachomedia](https://github.com/zachomedia))
|
||||
- **[logs,dynamodb,ecs,marathon]** Link some providers logs to Traefik ([#1746](https://github.com/containous/traefik/pull/1746) by [ldez](https://github.com/ldez))
|
||||
- **[logs,marathon]** remove confusing go-marathon log message ([#1810](https://github.com/containous/traefik/pull/1810) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs]** Send traefik logs to stdout instead stderr ([#2054](https://github.com/containous/traefik/pull/2054) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs]** enable logging to stdout for access logs ([#1683](https://github.com/containous/traefik/pull/1683) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs]** Logs & errors review ([#1673](https://github.com/containous/traefik/pull/1673) by [ldez](https://github.com/ldez))
|
||||
- **[logs]** Switch access logging to logrus ([#1647](https://github.com/containous/traefik/pull/1647) by [rjshep](https://github.com/rjshep))
|
||||
- **[logs]** log X-Forwarded-For as ClientHost if present ([#1946](https://github.com/containous/traefik/pull/1946) by [mildis](https://github.com/mildis))
|
||||
- **[logs]** Restore: First stage of access logging middleware. ([#1571](https://github.com/containous/traefik/pull/1571) by [ldez](https://github.com/ldez))
|
||||
- **[logs]** Add log file close and reopen on receipt of SIGUSR1 ([#1761](https://github.com/containous/traefik/pull/1761) by [rjshep](https://github.com/rjshep))
|
||||
- **[logs]** add RetryAttempts to AccessLog in JSON format ([#1793](https://github.com/containous/traefik/pull/1793) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs]** Add JSON as access logging format ([#1669](https://github.com/containous/traefik/pull/1669) by [rjshep](https://github.com/rjshep))
|
||||
- **[marathon]** Support multi-port service routing for containers running on Marathon ([#1742](https://github.com/containous/traefik/pull/1742) by [aantono](https://github.com/aantono))
|
||||
- **[marathon]** Improve Marathon integration tests. ([#1406](https://github.com/containous/traefik/pull/1406) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Exported getSubDomain function from Marathon provider ([#1693](https://github.com/containous/traefik/pull/1693) by [aantono](https://github.com/aantono))
|
||||
- **[marathon]** Use test builder. ([#1871](https://github.com/containous/traefik/pull/1871) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Add support for readiness checks. ([#1883](https://github.com/containous/traefik/pull/1883) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Move marathon mock ([#1732](https://github.com/containous/traefik/pull/1732) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Use single API call to fetch Marathon resources. ([#1815](https://github.com/containous/traefik/pull/1815) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[metrics]** Added RetryMetrics to DataDog and StatsD providers ([#1884](https://github.com/containous/traefik/pull/1884) by [aantono](https://github.com/aantono))
|
||||
- **[metrics]** Extract metrics to own package and refactor implementations ([#1968](https://github.com/containous/traefik/pull/1968) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Add metrics for backend_retries_total ([#1504](https://github.com/containous/traefik/pull/1504) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Add status code to request duration metric ([#1755](https://github.com/containous/traefik/pull/1755) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Add trusted whitelist proxy protocol ([#2234](https://github.com/containous/traefik/pull/2234) by [emilevauge](https://github.com/emilevauge)))
|
||||
- **[metrics]** DataDog and StatsD Metrics Support ([#1701](https://github.com/containous/traefik/pull/1701) by [aantono](https://github.com/aantono))
|
||||
- **[middleware]** Create Header Middleware ([#1236](https://github.com/containous/traefik/pull/1236) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Add configurable timeouts and curate default timeout settings ([#1873](https://github.com/containous/traefik/pull/1873) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Fix command bug content. ([#2002](https://github.com/containous/traefik/pull/2002) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Retry only on real network errors ([#1549](https://github.com/containous/traefik/pull/1549) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Return 503 on empty backend ([#1748](https://github.com/containous/traefik/pull/1748) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Custom Error Pages ([#1675](https://github.com/containous/traefik/pull/1675) by [bparli](https://github.com/bparli))
|
||||
- **[oxy]** Support X-Forwarded-Port. ([#1960](https://github.com/containous/traefik/pull/1960) by [ldez](https://github.com/ldez))
|
||||
- **[provider,tls]** Added a check to ensure clientTLS configuration contains either a cert or a key ([#1932](https://github.com/containous/traefik/pull/1932) by [aantono](https://github.com/aantono))
|
||||
- **[provider]** Deflake integration tests ([#1599](https://github.com/containous/traefik/pull/1599) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Factorize labels ([#1843](https://github.com/containous/traefik/pull/1843) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Replace go routine by Safe.Go ([#1879](https://github.com/containous/traefik/pull/1879) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Refactor into dual Rancher API/Metadata providers ([#1563](https://github.com/containous/traefik/pull/1563) by [martinbaillie](https://github.com/martinbaillie))
|
||||
- **[rules]** Add support for Query String filtering ([#1934](https://github.com/containous/traefik/pull/1934) by [driverpt](https://github.com/driverpt))
|
||||
- **[rules]** Simplify stripPrefix and stripPrefixRegex tests ([#1699](https://github.com/containous/traefik/pull/1699) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Enhance rules tests. ([#1679](https://github.com/containous/traefik/pull/1679) by [ldez](https://github.com/ldez))
|
||||
- **[sticky-session]** make the cookie name unique to the backend being served ([#1716](https://github.com/containous/traefik/pull/1716) by [richardjq](https://github.com/richardjq))
|
||||
- **[tls]** Handle RootCAs certificate ([#1789](https://github.com/containous/traefik/pull/1789) by [Juliens](https://github.com/Juliens))
|
||||
- **[tls]** enable TLS client forwarding ([#1446](https://github.com/containous/traefik/pull/1446) by [drewwells](https://github.com/drewwells))
|
||||
- **[websocket]** Add tests for urlencoded part in url ([#2199](https://github.com/containous/traefik/pull/2199) by [Juliens](https://github.com/Juliens))
|
||||
- **[websocket]** Add test for SSL TERMINATION in Websocket IT ([#2063](https://github.com/containous/traefik/pull/2063) by [Juliens](https://github.com/Juliens)
|
||||
- **[webui]** Proxy in dev mode ([#1544](https://github.com/containous/traefik/pull/1544) by [maxwo](https://github.com/maxwo))
|
||||
- **[webui]** Minor Health UI fixes ([#1651](https://github.com/containous/traefik/pull/1651) by [mihaitodor](https://github.com/mihaitodor))
|
||||
- Fail fast in IT and fix some flaky tests ([#2126](https://github.com/containous/traefik/pull/2126) by [ldez](https://github.com/ldez))
|
||||
- extract lb configuration steps into method ([#1841](https://github.com/containous/traefik/pull/1841) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- Add whitelist configuration option for entrypoints ([#1702](https://github.com/containous/traefik/pull/1702) by [christopherobin](https://github.com/christopherobin))
|
||||
- Enhance integration tests ([#1842](https://github.com/containous/traefik/pull/1842) by [ldez](https://github.com/ldez))
|
||||
- Add helloworld tests with gRPC ([#1845](https://github.com/containous/traefik/pull/1845) by [Juliens](https://github.com/Juliens))
|
||||
- Add the sprig functions in the template engine ([#1891](https://github.com/containous/traefik/pull/1891) by [thomasbach76](https://github.com/thomasbach76))
|
||||
- Refactor globalConfiguration / WebProvider ([#1938](https://github.com/containous/traefik/pull/1938) by [Juliens](https://github.com/Juliens))
|
||||
- Code cleaning. ([#1956](https://github.com/containous/traefik/pull/1956) by [ldez](https://github.com/ldez))
|
||||
- Add proxy protocol ([#2004](https://github.com/containous/traefik/pull/2004) by [emilevauge](https://github.com/emilevauge))
|
||||
- Bump gorilla/mux version. ([#1954](https://github.com/containous/traefik/pull/1954) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[cluster,kv]** Be certain to clear our marshalled representation before reloading it ([#2165](https://github.com/containous/traefik/pull/2165) by [gozer](https://github.com/gozer))
|
||||
- **[consulcatalog,docker,ecs,k8s,kv,marathon,rancher,sticky-session]** Backward compatibility for sticky ([#2266](https://github.com/containous/traefik/pull/2266) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness cookie name ([#2232](https://github.com/containous/traefik/pull/2232) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness cookie name. ([#2251](https://github.com/containous/traefik/pull/2251) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Fix consul catalog retry ([#2263](https://github.com/containous/traefik/pull/2263) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Flaky tests and refresh problem in consul catalog ([#2148](https://github.com/containous/traefik/pull/2148) by [Juliens](https://github.com/Juliens))
|
||||
- **[consulcatalog]** Consul catalog failed to remove service ([#2157](https://github.com/containous/traefik/pull/2157) by [Juliens](https://github.com/Juliens))
|
||||
- **[consulcatalog]** Fix Consul Catalog refresh ([#2089](https://github.com/containous/traefik/pull/2089) by [Juliens](https://github.com/Juliens))
|
||||
- **[docker]** Changed Docker network filter to allow any swarm network ([#2244](https://github.com/containous/traefik/pull/2244) by [pistolero](https://github.com/pistolero))
|
||||
- **[docker]** Error handling for docker swarm mode ([#1533](https://github.com/containous/traefik/pull/1533) by [tanyadegurechaff](https://github.com/tanyadegurechaff))
|
||||
- **[ecs]** Handle empty ECS Clusters properly ([#2170](https://github.com/containous/traefik/pull/2170) by [jeffreykoetsier](https://github.com/jeffreykoetsier))
|
||||
- **[healthcheck]** Fix healthcheck port ([#2131](https://github.com/containous/traefik/pull/2131) by [fredix](https://github.com/fredix))
|
||||
- **[healthcheck]** Bind healthcheck to backend by entryPointName ([#1868](https://github.com/containous/traefik/pull/1868) by [chrigl](https://github.com/chrigl))
|
||||
- **[k8s]** Continue processing on invalid auth-realm annotation. ([#2252](https://github.com/containous/traefik/pull/2252) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Use default frontend priority of zero. ([#1906](https://github.com/containous/traefik/pull/1906) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[kv]** add retry backoff to staert config loading ([#2268](https://github.com/containous/traefik/pull/2268) by [emilevauge](https://github.com/emilevauge))
|
||||
- **[logs,middleware]** Enable loss less rotation of log files ([#2062](https://github.com/containous/traefik/pull/2062) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs,middleware]** Access log default values ([#2061](https://github.com/containous/traefik/pull/2061) by [ldez](https://github.com/ldez))
|
||||
- **[logs]** Fix flakiness in log rotation test ([#2213](https://github.com/containous/traefik/pull/2213) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[marathon]** Assign filtered tasks to apps contained in slice. ([#1881](https://github.com/containous/traefik/pull/1881) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Fix fallback to other nodes for Marathon ([#1740](https://github.com/containous/traefik/pull/1740) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** prometheus, HTTP method and utf8 ([#2081](https://github.com/containous/traefik/pull/2081) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Enable prefix matching within slash boundaries ([#2214](https://github.com/containous/traefik/pull/2214) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Fix SSE subscriptions when retries are enabled ([#2145](https://github.com/containous/traefik/pull/2145) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** compress: preserve status code ([#1948](https://github.com/containous/traefik/pull/1948) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Add stack name to backend name generation to fix rancher metadata backend ([#2107](https://github.com/containous/traefik/pull/2107) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[rancher]** Rancher host IP address ([#2101](https://github.com/containous/traefik/pull/2101) by [matq007](https://github.com/matq007))
|
||||
- **[rancher]** fix seconds to really be seconds ([#2259](https://github.com/containous/traefik/pull/2259) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[rancher]** fix rancher api environment get ([#2053](https://github.com/containous/traefik/pull/2053) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[sticky-session]** Sanitize cookie names. ([#2216](https://github.com/containous/traefik/pull/2216) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[sticky-session]** Setting the Cookie Path explicitly to root ([#1950](https://github.com/containous/traefik/pull/1950) by [marcopaga](https://github.com/marcopaga))
|
||||
- **[websocket]** Forward upgrade error from backend ([#2187](https://github.com/containous/traefik/pull/2187) by [Juliens](https://github.com/Juliens))
|
||||
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2088](https://github.com/containous/traefik/pull/2088) by [Juliens](https://github.com/Juliens))
|
||||
- Nil body retries ([#2258](https://github.com/containous/traefik/pull/2258) by [Juliens](https://github.com/Juliens))
|
||||
- Fix deprecated IdleTimeout config ([#2143](https://github.com/containous/traefik/pull/2143) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- Fixes entry points configuration. ([#2120](https://github.com/containous/traefik/pull/2120) by [ldez](https://github.com/ldez))
|
||||
- Delay first version check ([#2215](https://github.com/containous/traefik/pull/2215) by [emilevauge](https://github.com/emilevauge))
|
||||
- Move http2 configure transport ([#2231](https://github.com/containous/traefik/pull/2231) by [Juliens](https://github.com/Juliens))
|
||||
- Fix error in prepareServer ([#2076](https://github.com/containous/traefik/pull/2076) by [emilevauge](https://github.com/emilevauge))
|
||||
- New entry point parser. ([#2248](https://github.com/containous/traefik/pull/2248) by [ldez](https://github.com/ldez))
|
||||
- Add TrustForwardHeader options. ([#2262](https://github.com/containous/traefik/pull/2262) by [ldez](https://github.com/ldez))
|
||||
- `bug` command. ([#2178](https://github.com/containous/traefik/pull/2178) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme,provider]** Enhance documentation readability. ([#2095](https://github.com/containous/traefik/pull/2095) by [ldez](https://github.com/ldez))
|
||||
- **[acme,provider]** Fix whitespaces ([#2075](https://github.com/containous/traefik/pull/2075) by [chulkilee](https://github.com/chulkilee))
|
||||
- **[acme,provider]** Re-organize documentation ([#2012](https://github.com/containous/traefik/pull/2012) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||
- **[acme]** Fix grammar ([#2208](https://github.com/containous/traefik/pull/2208) by [mvasin](https://github.com/mvasin))
|
||||
- **[acme]** Add guide for Docker, Traefik & Letsencrypt ([#1923](https://github.com/containous/traefik/pull/1923) by [mvdstam](https://github.com/mvdstam))
|
||||
- **[acme]** Improve Let's Encrypt documentation ([#1885](https://github.com/containous/traefik/pull/1885) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update docs for dnsimple env vars. ([#1872](https://github.com/containous/traefik/pull/1872) by [untalpierre](https://github.com/untalpierre))
|
||||
- **[api]** Add examples of proxying ping ([#2102](https://github.com/containous/traefik/pull/2102) by [deitch](https://github.com/deitch))
|
||||
- **[authentication,k8s]** traefik controller access to secrets ([#1707](https://github.com/containous/traefik/pull/1707) by [spinto](https://github.com/spinto))
|
||||
- **[consul,tls]** doc change regarding consul SSL ([#1774](https://github.com/containous/traefik/pull/1774) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness documentation ([#2238](https://github.com/containous/traefik/pull/2238) by [ldez](https://github.com/ldez))
|
||||
- **[consul]** added consul acl token note ([#1720](https://github.com/containous/traefik/pull/1720) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||
- **[docker]** Updating Docker output and curl for sticky sessions ([#2150](https://github.com/containous/traefik/pull/2150) by [jtyr](https://github.com/jtyr))
|
||||
- **[docker]** Add more visibility to docker stack deploy label issue ([#1984](https://github.com/containous/traefik/pull/1984) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||
- **[ecs]** Fix IAM policy sid. ([#2066](https://github.com/containous/traefik/pull/2066) by [charlieoleary](https://github.com/charlieoleary))
|
||||
- **[k8s,marathon]** Mark Marathon and Kubernetes as constraint-supporting. ([#1964](https://github.com/containous/traefik/pull/1964) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Add guide section on production advice, esp. CPU. ([#2113](https://github.com/containous/traefik/pull/2113) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Document ways to partition Ingresses in the k8s guide. ([#2223](https://github.com/containous/traefik/pull/2223) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Remove pod from RBAC rules. ([#2229](https://github.com/containous/traefik/pull/2229) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Quote priority values in annotation examples. ([#2230](https://github.com/containous/traefik/pull/2230) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Fix invalid service yaml example ([#2059](https://github.com/containous/traefik/pull/2059) by [kairen](https://github.com/kairen))
|
||||
- **[k8s]** Update usage of `.local` with `.minikube` in k8s docs ([#1551](https://github.com/containous/traefik/pull/1551) by [errm](https://github.com/errm))
|
||||
- **[k8s]** Update the documentation to use DaemonSet or Deployment ([#1735](https://github.com/containous/traefik/pull/1735) by [saschagrunert](https://github.com/saschagrunert))
|
||||
- **[k8s]** Fix docs about default namespaces. ([#1961](https://github.com/containous/traefik/pull/1961) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Moved namespace to correct place ([#1911](https://github.com/containous/traefik/pull/1911) by [markround](https://github.com/markround))
|
||||
- **[k8s]** examples/k8s: fix ui ingress port out of sync with deployment ([#1943](https://github.com/containous/traefik/pull/1943) by [borancar](https://github.com/borancar))
|
||||
- **[k8s]** Add secrets resource to in-line RBAC spec. ([#1890](https://github.com/containous/traefik/pull/1890) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Improve documentation. ([#1831](https://github.com/containous/traefik/pull/1831) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Fix documentation glitches. ([#1996](https://github.com/containous/traefik/pull/1996) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[metrics]** Enhance web backend documentation ([#2122](https://github.com/containous/traefik/pull/2122) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** fix: documentation Mesos. ([#2029](https://github.com/containous/traefik/pull/2029) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Improve compression documentation ([#2184](https://github.com/containous/traefik/pull/2184) by [errm](https://github.com/errm))
|
||||
- **[provider]** Clarify that provider-enabling argument parameters set all defaults. ([#1830](https://github.com/containous/traefik/pull/1830) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[rancher]** Update Rancher documentation. ([#1776](https://github.com/containous/traefik/pull/1776) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Document yarnpkg. ([#1558](https://github.com/containous/traefik/pull/1558) by [Stibbons](https://github.com/Stibbons))
|
||||
- Add forward auth documentation. ([#2110](https://github.com/containous/traefik/pull/2110) by [ldez](https://github.com/ldez))
|
||||
- User guide gRPC ([#2108](https://github.com/containous/traefik/pull/2108) by [Juliens](https://github.com/Juliens))
|
||||
- Document custom error page restrictions. ([#2104](https://github.com/containous/traefik/pull/2104) by [timoreimann](https://github.com/timoreimann))
|
||||
- Prepare release v1.4.0-rc3 ([#2135](https://github.com/containous/traefik/pull/2135) by [Juliens](https://github.com/Juliens))
|
||||
- Update gRPC example ([#2191](https://github.com/containous/traefik/pull/2191) by [jsenon](https://github.com/jsenon))
|
||||
- Prepare release v1.4.0-rc2 ([#2091](https://github.com/containous/traefik/pull/2091) by [ldez](https://github.com/ldez))
|
||||
- Fix grammar mistake in the kv-config docs ([#2197](https://github.com/containous/traefik/pull/2197) by [chr4](https://github.com/chr4))
|
||||
- Update cluster.md ([#2073](https://github.com/containous/traefik/pull/2073) by [kmbremner](https://github.com/kmbremner))
|
||||
- Prepare release v1.4.0-rc4 ([#2201](https://github.com/containous/traefik/pull/2201) by [nmengin](https://github.com/nmengin))
|
||||
- Prepare release v1.4.0-rc5 ([#2241](https://github.com/containous/traefik/pull/2241) by [ldez](https://github.com/ldez))
|
||||
- Enhance documentation. ([#2048](https://github.com/containous/traefik/pull/2048) by [ldez](https://github.com/ldez))
|
||||
- doc: add notes on server urls with path ([#2045](https://github.com/containous/traefik/pull/2045) by [chulkilee](https://github.com/chulkilee))
|
||||
- Enhance security headers doc. ([#2042](https://github.com/containous/traefik/pull/2042) by [ldez](https://github.com/ldez))
|
||||
- HTTPS for images, video and links in docs. ([#2041](https://github.com/containous/traefik/pull/2041) by [ldez](https://github.com/ldez))
|
||||
- Fix error pages configuration. ([#2038](https://github.com/containous/traefik/pull/2038) by [ldez](https://github.com/ldez))
|
||||
- Fix Proxy Protocol documentation ([#2253](https://github.com/containous/traefik/pull/2253) by [emilevauge](https://github.com/emilevauge))
|
||||
- Update GraceTimeOut documentation ([#1875](https://github.com/containous/traefik/pull/1875) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- Release cycle. ([#1812](https://github.com/containous/traefik/pull/1812) by [ldez](https://github.com/ldez))
|
||||
- Update contributing guide build steps ([#1801](https://github.com/containous/traefik/pull/1801) by [jsturtevant](https://github.com/jsturtevant))
|
||||
- Add Nicolas Mengin to maintainers ([#1792](https://github.com/containous/traefik/pull/1792) by [emilevauge](https://github.com/emilevauge))
|
||||
- Add Julien Salleyron to maintainers ([#1790](https://github.com/containous/traefik/pull/1790) by [emilevauge](https://github.com/emilevauge))
|
||||
- Change to a more flexible PR review process ([#1781](https://github.com/containous/traefik/pull/1781) by [emilevauge](https://github.com/emilevauge))
|
||||
- Traefik "bug" command documentation ([#1811](https://github.com/containous/traefik/pull/1811) by [ldez](https://github.com/ldez))
|
||||
- Change Traefik intro video ([#1893](https://github.com/containous/traefik/pull/1893) by [emilevauge](https://github.com/emilevauge))
|
||||
- Prepare release v1.4.0-rc1 ([#2021](https://github.com/containous/traefik/pull/2021) by [ldez](https://github.com/ldez))
|
||||
- Add play-with-docker example ([#1726](https://github.com/containous/traefik/pull/1726) by [marcosnils](https://github.com/marcosnils))
|
||||
- Add Marco Jantke to maintainers ([#1980](https://github.com/containous/traefik/pull/1980) by [emilevauge](https://github.com/emilevauge))
|
||||
- Remove Russel from maintainers ([#1614](https://github.com/containous/traefik/pull/1614) by [emilevauge](https://github.com/emilevauge))
|
||||
- Update CONTRIBUTING.md. ([#1667](https://github.com/containous/traefik/pull/1667) by [timoreimann](https://github.com/timoreimann))
|
||||
- drop "slave" wording for "worker" ([#1645](https://github.com/containous/traefik/pull/1645) by [djalal](https://github.com/djalal))
|
||||
- Use more inclusive language in README.md {guys => folks} ([#1640](https://github.com/containous/traefik/pull/1640) by [igorwwwwwwwwwwwwwwwwwwww](https://github.com/igorwwwwwwwwwwwwwwwwwwww))
|
||||
- Remove Thomas Recloux from maintainers ([#1616](https://github.com/containous/traefik/pull/1616) by [emilevauge](https://github.com/emilevauge))
|
||||
- Update documentation for 1.4 release ([#2011](https://github.com/containous/traefik/pull/2011) by [emilevauge](https://github.com/emilevauge))
|
||||
- Small toml documentation update ([#1603](https://github.com/containous/traefik/pull/1603) by [antoine-aumjaud](https://github.com/antoine-aumjaud))
|
||||
- Add @ldez to maintainers ([#1589](https://github.com/containous/traefik/pull/1589) by [emilevauge](https://github.com/emilevauge))
|
||||
- doc: add labels documentation. ([#1582](https://github.com/containous/traefik/pull/1582) by [ldez](https://github.com/ldez))
|
||||
- Update golang version in contributing guide ([#2018](https://github.com/containous/traefik/pull/2018) by [ArikaChen](https://github.com/ArikaChen))
|
||||
- toml page - replace li by table ([#1995](https://github.com/containous/traefik/pull/1995) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.3.7 ([#2013](https://github.com/containous/traefik/pull/2013) by [ldez](https://github.com/ldez))
|
||||
- Merge 1.3.6 ([#1992](https://github.com/containous/traefik/pull/1992) by [ldez](https://github.com/ldez))
|
||||
- Merge 1.3.5 ([#1909](https://github.com/containous/traefik/pull/1909) by [ldez](https://github.com/ldez))
|
||||
- Merge 1.3.3 ([#1836](https://github.com/containous/traefik/pull/1836) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.3.2 to master ([#1809](https://github.com/containous/traefik/pull/1809) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 ([#1797](https://github.com/containous/traefik/pull/1797) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 ([#1786](https://github.com/containous/traefik/pull/1786) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.3.1 to master ([#1763](https://github.com/containous/traefik/pull/1763) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 ([#1753](https://github.com/containous/traefik/pull/1753) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 ([#1705](https://github.com/containous/traefik/pull/1705) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 to master ([#1697](https://github.com/containous/traefik/pull/1697) by [ldez](https://github.com/ldez))
|
||||
- Merge v1 3 0 ([#1692](https://github.com/containous/traefik/pull/1692) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 to master (rc3) ([#1666](https://github.com/containous/traefik/pull/1666) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.3 to master ([#1643](https://github.com/containous/traefik/pull/1643) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.3.0-rc2 master ([#1613](https://github.com/containous/traefik/pull/1613) by [emilevauge](https://github.com/emilevauge))
|
||||
- Merge v1.3 branch into master [2017-05-11] ([#1548](https://github.com/containous/traefik/pull/1548) by [timoreimann](https://github.com/timoreimann))
|
||||
|
||||
## [v1.4.0-rc5](https://github.com/containous/traefik/tree/v1.4.0-rc5) (2017-10-10)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc4...v1.4.0-rc5)
|
||||
|
||||
@@ -581,7 +1043,7 @@
|
||||
- Chunk taskArns into groups of 100 [\#1209](https://github.com/containous/traefik/pull/1209) ([owen](https://github.com/owen))
|
||||
- Prepare release v1.2.0 rc2 [\#1204](https://github.com/containous/traefik/pull/1204) ([emilevauge](https://github.com/emilevauge))
|
||||
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
||||
- Small fixes and improvments [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||
- Small fixes and improvements [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
||||
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
||||
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
||||
@@ -681,7 +1143,7 @@
|
||||
**Merged pull requests:**
|
||||
|
||||
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
||||
- Small fixes and improvments [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||
- Small fixes and improvements [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
||||
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
||||
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
||||
@@ -1044,7 +1506,7 @@
|
||||
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
||||
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
||||
- Name servers after thier pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||
- Name servers after their pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
||||
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
||||
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
||||
@@ -1267,7 +1729,7 @@
|
||||
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
||||
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
||||
- Name servers after thier pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||
- Name servers after their pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
||||
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
||||
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
||||
|
||||
@@ -16,7 +16,7 @@ Step 0 : FROM golang:1.9-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/Masterminds/glide
|
||||
[...]
|
||||
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
||||
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'gen.go'
|
||||
|
||||
@@ -66,6 +66,9 @@ cd ~/go/src/github.com/containous/traefik
|
||||
go get github.com/jteeuwen/go-bindata/...
|
||||
|
||||
# Start build
|
||||
|
||||
# generate
|
||||
# (required to merge non-code components into the final binary, such as the web dashboard and provider's Go templates)
|
||||
go generate
|
||||
|
||||
# Standard go build
|
||||
@@ -75,6 +78,10 @@ go build ./cmd/traefik
|
||||
|
||||
You will find the Træfik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`.
|
||||
|
||||
### Updating the templates
|
||||
|
||||
If you happen to update the provider templates (in `/templates`), you need to run `go generate` to update the `autogen` package.
|
||||
|
||||
### Setting up `glide` and `glide-vc` for dependency management
|
||||
|
||||
- Glide is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
|
||||
@@ -108,7 +115,7 @@ integration test using the `test-integration` target.
|
||||
$ make test-unit
|
||||
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
||||
# […]
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/containous/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'gen.go'
|
||||
|
||||
@@ -138,15 +145,36 @@ More: https://labix.org/gocheck
|
||||
|
||||
#### Method 2: `go`
|
||||
|
||||
- Tests can be run from the cloned directory, by `$ go test ./...` which should return `ok` similar to:
|
||||
Unit tests can be run from the cloned directory by `$ go test ./...` which should return `ok` similar to:
|
||||
```
|
||||
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
|
||||
ok _/home/user/go/src/github/containous/traefik 0.004s
|
||||
```
|
||||
|
||||
Integration tests must be run from the `integration/` directory and require the `-integration` switch to be passed like this: `$ cd integration && go test -integration ./...`.
|
||||
|
||||
## Documentation
|
||||
|
||||
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
You can test documentation using the `docs` target.
|
||||
|
||||
```bash
|
||||
$ make docs
|
||||
docker build -t traefik-docs -f docs.Dockerfile .
|
||||
# […]
|
||||
docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000 traefik-docs mkdocs serve
|
||||
# […]
|
||||
[I 170828 20:47:48 server:283] Serving on http://0.0.0.0:8000
|
||||
[I 170828 20:47:48 handlers:60] Start watching changes
|
||||
[I 170828 20:47:48 handlers:62] Start detecting changes
|
||||
```
|
||||
|
||||
And go to [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
||||
|
||||
### Method 2: `mkdocs`
|
||||
|
||||
First make sure you have python and pip installed
|
||||
|
||||
```shell
|
||||
@@ -159,7 +187,7 @@ pip 1.5.2
|
||||
Then install mkdocs with pip
|
||||
|
||||
```shell
|
||||
$ pip install mkdocs
|
||||
pip install --user -r requirements.txt
|
||||
```
|
||||
|
||||
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2017 Containous SAS
|
||||
Copyright (c) 2016-2018 Containous SAS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* Julien Salleyron [@juliens](https://github.com/juliens)
|
||||
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
|
||||
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
|
||||
* Michaël Matur [@mmatur](https://github.com/mmatur)
|
||||
|
||||
|
||||
## PR review process:
|
||||
@@ -40,6 +41,14 @@ The status `status/4-merge-in-progress` is only for the bot.
|
||||
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
|
||||
In this case you must solve conflicts/CI/... and after you only need to remove `bot/need-human-merge`.
|
||||
|
||||
A maintainer can add `bot/no-merge` on a PR if he want (temporarily) prevent a merge by the bot.
|
||||
|
||||
`bot/light-review` can be used to decrease required LGTM from 3 to 1 when:
|
||||
|
||||
- vendor updates from previously reviewed PRs
|
||||
- merges branches into master
|
||||
- prepare release
|
||||
|
||||
|
||||
### [Myrmica Bibikoffi](https://github.com/containous/bibikoffi/)
|
||||
|
||||
@@ -52,19 +61,20 @@ In this case you must solve conflicts/CI/... and after you only need to remove `
|
||||
**Manage GitHub labels**
|
||||
|
||||
* Add labels on new PR [GitHub WebHook]
|
||||
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
|
||||
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
|
||||
* Weekly report of PR status on Slack (CaptainPR) [cron]
|
||||
|
||||
|
||||
## Labels
|
||||
|
||||
If we open/look an issue/PR, we must add a `kind/*` and an `area/*`.
|
||||
If we open/look an issue/PR, we must add a `kind/*`, an `area/*` and a `status/*`.
|
||||
|
||||
### Contributor
|
||||
|
||||
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
|
||||
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
|
||||
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)**
|
||||
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
|
||||
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
|
||||
|
||||
### Kind
|
||||
@@ -75,7 +85,7 @@ If we open/look an issue/PR, we must add a `kind/*` and an `area/*`.
|
||||
* _Proposal issues_ are design proposal that need to be refined with multiple contributors.
|
||||
* _Proposal PRs_ are technical prototypes that need to be refined with multiple contributors.
|
||||
|
||||
* `kind/bug/possible`: if we need to analyze to understand if it's a bug or not. **(only for issues)** _[bot only]_
|
||||
* `kind/bug/possible`: if we need to analyze to understand if it's a bug or not. **(only for issues)**
|
||||
* `kind/bug/confirmed`: we are sure, it's a bug. **(only for issues)**
|
||||
* `kind/bug/fix`: it's a bug fix. **(only for PR)**
|
||||
|
||||
|
||||
15
Makefile
15
Makefile
@@ -20,12 +20,16 @@ GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/nul
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
TRAEFIK_DOC_IMAGE := traefik-docs
|
||||
|
||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) -i $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_DOC_PORT := 8000
|
||||
DOCKER_RUN_DOC_MOUNT := -v $(CURDIR):/mkdocs
|
||||
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNT) -p $(DOCKER_RUN_DOC_PORT):8000
|
||||
|
||||
|
||||
print-%: ; @echo $*=$($*)
|
||||
@@ -67,9 +71,10 @@ test-unit: build ## run the unit tests
|
||||
|
||||
test-integration: build ## run the integration tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
validate: build ## validate gofmt, golint and go vet
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
|
||||
|
||||
build: dist
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
@@ -89,6 +94,12 @@ image-dirty: binary ## build a docker traefik image
|
||||
image: clear-static binary ## clean up static directory and build a docker traefik image
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
docs: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve
|
||||
|
||||
docs-image:
|
||||
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
|
||||
|
||||
clear-static:
|
||||
rm -rf static
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ If you want your users to access some of your microservices from the Internet, y
|
||||
- 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
|
||||
|
||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
||||
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||
|
||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||
|
||||
@@ -66,7 +66,7 @@ Run it and forget it!
|
||||
- Hot-reloading of configuration. No need to restart the process
|
||||
- Circuit breakers, retry
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Metrics (Rest, Prometheus, Datadog, Statd)
|
||||
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Clean AngularJS Web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Access Logs (JSON, CLF)
|
||||
|
||||
123
acme/acme.go
123
acme/acme.go
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/eapache/channels"
|
||||
"github.com/xenolf/lego/acme"
|
||||
@@ -49,6 +51,7 @@ type ACME struct {
|
||||
checkOnDemandDomain func(domain string) bool
|
||||
jobs *channels.InfiniteChannel
|
||||
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
||||
dynamicCerts *safe.Safe
|
||||
}
|
||||
|
||||
//Domains parse []Domain
|
||||
@@ -99,7 +102,7 @@ func (a *ACME) init() error {
|
||||
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
}
|
||||
// no certificates in TLS config, so we add a default one
|
||||
cert, err := generateDefaultCertificate()
|
||||
cert, err := generate.DefaultCertificate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,7 +117,7 @@ func (a *ACME) init() error {
|
||||
}
|
||||
|
||||
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
|
||||
func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error {
|
||||
func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -123,6 +126,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
||||
return errors.New("Empty Store, please provide a key for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
a.dynamicCerts = certs
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
@@ -234,7 +238,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
||||
}
|
||||
|
||||
// CreateLocalConfig creates a tls.config using local ACME configuration
|
||||
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error {
|
||||
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -243,6 +247,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
||||
return errors.New("Empty Store, please provide a filename for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
a.dynamicCerts = certs
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
@@ -389,44 +394,27 @@ func (a *ACME) retrieveCertificates() {
|
||||
|
||||
func (a *ACME) renewCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Debug("Testing certificate renew...")
|
||||
log.Info("Testing certificate renew...")
|
||||
account := a.store.Get().(*Account)
|
||||
for _, certificateResource := range account.DomainsCertificate.Certs {
|
||||
if certificateResource.needRenew() {
|
||||
log.Debugf("Renewing certificate %+v", certificateResource.Domains)
|
||||
renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{
|
||||
Domain: certificateResource.Certificate.Domain,
|
||||
CertURL: certificateResource.Certificate.CertURL,
|
||||
CertStableURL: certificateResource.Certificate.CertStableURL,
|
||||
PrivateKey: certificateResource.Certificate.PrivateKey,
|
||||
Certificate: certificateResource.Certificate.Certificate,
|
||||
}, true, OSCPMustStaple)
|
||||
log.Infof("Renewing certificate from LE : %+v", certificateResource.Domains)
|
||||
renewedACMECert, err := a.renewACMECertificate(certificateResource)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %v", err)
|
||||
log.Errorf("Error renewing certificate from LE: %v", err)
|
||||
continue
|
||||
}
|
||||
log.Debugf("Renewed certificate %+v", certificateResource.Domains)
|
||||
renewedACMECert := &Certificate{
|
||||
Domain: renewedCert.Domain,
|
||||
CertURL: renewedCert.CertURL,
|
||||
CertStableURL: renewedCert.CertStableURL,
|
||||
PrivateKey: renewedCert.PrivateKey,
|
||||
Certificate: renewedCert.Certificate,
|
||||
operation := func() error {
|
||||
return a.storeRenewedCertificate(account, certificateResource, renewedACMECert)
|
||||
}
|
||||
transaction, object, err := a.store.Begin()
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Warnf("Renewed certificate storage error: %v, retrying in %s", err, time)
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 60 * time.Second
|
||||
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %v", err)
|
||||
continue
|
||||
}
|
||||
account = object.(*Account)
|
||||
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error())
|
||||
log.Errorf("Datastore cannot sync: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -434,6 +422,56 @@ func (a *ACME) renewCertificates() {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*Certificate, error) {
|
||||
renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{
|
||||
Domain: certificateResource.Certificate.Domain,
|
||||
CertURL: certificateResource.Certificate.CertURL,
|
||||
CertStableURL: certificateResource.Certificate.CertStableURL,
|
||||
PrivateKey: certificateResource.Certificate.PrivateKey,
|
||||
Certificate: certificateResource.Certificate.Certificate,
|
||||
}, true, OSCPMustStaple)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("Renewed certificate from LE: %+v", certificateResource.Domains)
|
||||
return &Certificate{
|
||||
Domain: renewedCert.Domain,
|
||||
CertURL: renewedCert.CertURL,
|
||||
CertStableURL: renewedCert.CertStableURL,
|
||||
PrivateKey: renewedCert.PrivateKey,
|
||||
Certificate: renewedCert.Certificate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ACME) storeRenewedCertificate(account *Account, certificateResource *DomainsCertificate, renewedACMECert *Certificate) error {
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error during transaction initialization for renewing certificate: %v", err)
|
||||
}
|
||||
|
||||
log.Infof("Renewing certificate in data store : %+v ", certificateResource.Domains)
|
||||
account = object.(*Account)
|
||||
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error renewing certificate in datastore: %v ", err)
|
||||
}
|
||||
|
||||
log.Infof("Commit certificate renewed in data store : %+v", certificateResource.Domains)
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
return fmt.Errorf("error saving ACME account %+v: %v", account, err)
|
||||
}
|
||||
|
||||
oldAccount := a.store.Get().(*Account)
|
||||
for _, oldCertificateResource := range oldAccount.DomainsCertificate.Certs {
|
||||
if oldCertificateResource.Domains.Main == certificateResource.Domains.Main && strings.Join(oldCertificateResource.Domains.SANs, ",") == strings.Join(certificateResource.Domains.SANs, ",") && certificateResource.Certificate != renewedACMECert {
|
||||
return fmt.Errorf("renewed certificate not stored: %+v", certificateResource.Domains)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Certificate successfully renewed in data store: %+v", certificateResource.Domains)
|
||||
return nil
|
||||
}
|
||||
|
||||
func dnsOverrideDelay(delay int) error {
|
||||
var err error
|
||||
if delay > 0 {
|
||||
@@ -443,7 +481,7 @@ func dnsOverrideDelay(delay int) error {
|
||||
return true, nil
|
||||
}
|
||||
} else if delay < 0 {
|
||||
err = fmt.Errorf("Invalid negative DelayDontCheckDNS: %d", delay)
|
||||
err = fmt.Errorf("invalid negative DelayDontCheckDNS: %d", delay)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -583,11 +621,21 @@ 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 {
|
||||
log.Debugf("Look 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)
|
||||
return cert
|
||||
}
|
||||
|
||||
func searchProvidedCertificateForDomains(domains []string, certs map[string]*tls.Certificate) *tls.Certificate {
|
||||
// Use regex to test for provided certs that might have been added into TLSConfig
|
||||
providedCertMatch := false
|
||||
log.Debugf("Look for provided certificate to validate %s...", domains)
|
||||
for k := range a.TLSConfig.NameToCertificate {
|
||||
for k := range certs {
|
||||
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$"
|
||||
for _, domainToCheck := range domains {
|
||||
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck)
|
||||
@@ -597,11 +645,10 @@ func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
|
||||
}
|
||||
if providedCertMatch {
|
||||
log.Debugf("Got provided certificate for domains %s", domains)
|
||||
return a.TLSConfig.NameToCertificate[k]
|
||||
return certs[k]
|
||||
|
||||
}
|
||||
}
|
||||
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
@@ -70,8 +71,8 @@ func TestDomainsSetAppend(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCertificatesRenew(t *testing.T) {
|
||||
foo1Cert, foo1Key, _ := generateKeyPair("foo1.com", time.Now())
|
||||
foo2Cert, foo2Key, _ := generateKeyPair("foo2.com", time.Now())
|
||||
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
|
||||
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
@@ -101,7 +102,7 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
foo1Cert, foo1Key, _ = generateKeyPair("foo1.com", time.Now())
|
||||
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
|
||||
newCertificate := &Certificate{
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
@@ -128,10 +129,10 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
|
||||
func TestRemoveDuplicates(t *testing.T) {
|
||||
now := time.Now()
|
||||
fooCert, fooKey, _ := generateKeyPair("foo.com", now)
|
||||
foo24Cert, foo24Key, _ := generateKeyPair("foo.com", now.Add(24*time.Hour))
|
||||
foo48Cert, foo48Key, _ := generateKeyPair("foo.com", now.Add(48*time.Hour))
|
||||
barCert, barKey, _ := generateKeyPair("bar.com", now)
|
||||
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
|
||||
foo24Cert, foo24Key, _ := generate.KeyPair("foo.com", now.Add(24*time.Hour))
|
||||
foo48Cert, foo48Key, _ := generate.KeyPair("foo.com", now.Add(48*time.Hour))
|
||||
barCert, barKey, _ := generate.KeyPair("bar.com", now)
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -11,6 +19,7 @@ import (
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
@@ -60,7 +69,7 @@ func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate
|
||||
|
||||
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge Present %s", domain)
|
||||
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
|
||||
cert, _, err := tlsSNI01ChallengeCert(keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -95,3 +104,47 @@ func (c *challengeProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (c *challengeProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return 60 * time.Second, 5 * time.Second
|
||||
}
|
||||
|
||||
// tlsSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
||||
func tlsSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
|
||||
// generate a new RSA key for the certificates
|
||||
var tempPrivKey crypto.PrivateKey
|
||||
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||
|
||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||
tempCertPEM, err := generate.PemCert(rsaPrivKey, domain, time.Time{})
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
|
||||
}
|
||||
|
||||
func pemEncode(data interface{}) []byte {
|
||||
var pemBlock *pem.Block
|
||||
switch key := data.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
||||
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
||||
case *rsa.PrivateKey:
|
||||
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
||||
case *x509.CertificateRequest:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
||||
case []byte:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
|
||||
133
acme/crypto.go
133
acme/crypto.go
@@ -1,133 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func generateDefaultCertificate() (*tls.Certificate, error) {
|
||||
randomBytes := make([]byte, 100)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zBytes := sha256.Sum256(randomBytes)
|
||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||
domain := fmt.Sprintf("%s.%s.traefik.default", z[:32], z[32:])
|
||||
|
||||
certPEM, keyPEM, err := generateKeyPair(domain, time.Time{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &certificate, nil
|
||||
}
|
||||
|
||||
func generateKeyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
|
||||
|
||||
certPEM, err := generatePemCert(rsaPrivKey, domain, expiration)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return certPEM, keyPEM, nil
|
||||
}
|
||||
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, expiration, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||
}
|
||||
|
||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if expiration.IsZero() {
|
||||
expiration = time.Now().Add(365)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "TRAEFIK DEFAULT CERT",
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: expiration,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
}
|
||||
|
||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||
}
|
||||
|
||||
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
||||
func TLSSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
|
||||
// generate a new RSA key for the certificates
|
||||
var tempPrivKey crypto.PrivateKey
|
||||
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||
|
||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||
tempCertPEM, err := generatePemCert(rsaPrivKey, domain, time.Time{})
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
|
||||
}
|
||||
func pemEncode(data interface{}) []byte {
|
||||
var pemBlock *pem.Block
|
||||
switch key := data.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
||||
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
||||
case *rsa.PrivateKey:
|
||||
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
||||
case *x509.CertificateRequest:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
||||
case []byte:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
22
api/dashboard.go
Normal file
22
api/dashboard.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/autogen/genstatic"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
)
|
||||
|
||||
// DashboardHandler expose dashboard routes
|
||||
type DashboardHandler struct{}
|
||||
|
||||
// AddRoutes add dashboard routes on a router
|
||||
func (g DashboardHandler) AddRoutes(router *mux.Router) {
|
||||
// Expose dashboard
|
||||
router.Methods(http.MethodGet).Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, "/dashboard/", 302)
|
||||
})
|
||||
router.Methods(http.MethodGet).PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"})))
|
||||
}
|
||||
46
api/debug.go
Normal file
46
api/debug.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"runtime"
|
||||
|
||||
"github.com/containous/mux"
|
||||
)
|
||||
|
||||
func init() {
|
||||
expvar.Publish("Goroutines", expvar.Func(goroutines))
|
||||
}
|
||||
|
||||
func goroutines() interface{} {
|
||||
return runtime.NumGoroutine()
|
||||
}
|
||||
|
||||
// DebugHandler expose debug routes
|
||||
type DebugHandler struct{}
|
||||
|
||||
// AddRoutes add debug routes on a router
|
||||
func (g DebugHandler) AddRoutes(router *mux.Router) {
|
||||
router.Methods(http.MethodGet).Path("/debug/vars").
|
||||
HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
fmt.Fprint(w, "{\n")
|
||||
first := true
|
||||
expvar.Do(func(kv expvar.KeyValue) {
|
||||
if !first {
|
||||
fmt.Fprint(w, ",\n")
|
||||
}
|
||||
first = false
|
||||
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
|
||||
})
|
||||
fmt.Fprint(w, "\n}\n")
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline)
|
||||
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/profile").HandlerFunc(pprof.Profile)
|
||||
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/symbol").HandlerFunc(pprof.Symbol)
|
||||
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/trace").HandlerFunc(pprof.Trace)
|
||||
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
||||
}
|
||||
250
api/handler.go
Normal file
250
api/handler.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
thoas_stats "github.com/thoas/stats"
|
||||
"github.com/unrolled/render"
|
||||
)
|
||||
|
||||
// Handler expose api routes
|
||||
type Handler struct {
|
||||
EntryPoint string `description:"EntryPoint" export:"true"`
|
||||
Dashboard bool `description:"Activate dashboard" export:"true"`
|
||||
Debug bool `export:"true"`
|
||||
CurrentConfigurations *safe.Safe
|
||||
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||
Stats *thoas_stats.Stats
|
||||
StatsRecorder *middlewares.StatsRecorder
|
||||
}
|
||||
|
||||
var (
|
||||
templatesRenderer = render.New(render.Options{
|
||||
Directory: "nowhere",
|
||||
})
|
||||
)
|
||||
|
||||
// AddRoutes add api routes on a router
|
||||
func (p Handler) AddRoutes(router *mux.Router) {
|
||||
if p.Debug {
|
||||
DebugHandler{}.AddRoutes(router)
|
||||
}
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api").HandlerFunc(p.getConfigHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers").HandlerFunc(p.getConfigHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}").HandlerFunc(p.getProviderHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends").HandlerFunc(p.getBackendsHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}").HandlerFunc(p.getBackendHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers").HandlerFunc(p.getServersHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers/{server}").HandlerFunc(p.getServerHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends").HandlerFunc(p.getFrontendsHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}").HandlerFunc(p.getFrontendHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes").HandlerFunc(p.getRoutesHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes/{route}").HandlerFunc(p.getRouteHandler)
|
||||
|
||||
// health route
|
||||
router.Methods(http.MethodGet).Path("/health").HandlerFunc(p.getHealthHandler)
|
||||
|
||||
version.Handler{}.AddRoutes(router)
|
||||
|
||||
if p.Dashboard {
|
||||
DashboardHandler{}.AddRoutes(router)
|
||||
}
|
||||
}
|
||||
|
||||
func getProviderIDFromVars(vars map[string]string) string {
|
||||
providerID := vars["provider"]
|
||||
// TODO: Deprecated
|
||||
if providerID == "rest" {
|
||||
providerID = "web"
|
||||
}
|
||||
return providerID
|
||||
}
|
||||
|
||||
func (p Handler) getConfigHandler(response http.ResponseWriter, request *http.Request) {
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, currentConfigurations)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Handler) getProviderHandler(response http.ResponseWriter, request *http.Request) {
|
||||
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, provider)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Handler) getBackendsHandler(response http.ResponseWriter, request *http.Request) {
|
||||
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, provider.Backends)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Handler) getBackendHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
backendID := vars["backend"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if backend, ok := provider.Backends[backendID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, backend)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
func (p Handler) getServersHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
backendID := vars["backend"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if backend, ok := provider.Backends[backendID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, backend.Servers)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
func (p Handler) getServerHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
backendID := vars["backend"]
|
||||
serverID := vars["server"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if backend, ok := provider.Backends[backendID]; ok {
|
||||
if server, ok := backend.Servers[serverID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, server)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
func (p Handler) getFrontendsHandler(response http.ResponseWriter, request *http.Request) {
|
||||
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, provider.Frontends)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Handler) getFrontendHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
frontendID := vars["frontend"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, frontend)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
func (p Handler) getRoutesHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
frontendID := vars["frontend"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, frontend.Routes)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
func (p Handler) getRouteHandler(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
providerID := getProviderIDFromVars(vars)
|
||||
frontendID := vars["frontend"]
|
||||
routeID := vars["route"]
|
||||
|
||||
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||
if provider, ok := currentConfigurations[providerID]; ok {
|
||||
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||
if route, ok := frontend.Routes[routeID]; ok {
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, route)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
http.NotFound(response, request)
|
||||
}
|
||||
|
||||
// healthResponse combines data returned by thoas/stats with statistics (if
|
||||
// they are enabled).
|
||||
type healthResponse struct {
|
||||
*thoas_stats.Data
|
||||
*middlewares.Stats
|
||||
}
|
||||
|
||||
func (p *Handler) getHealthHandler(response http.ResponseWriter, request *http.Request) {
|
||||
health := &healthResponse{Data: p.Stats.Data()}
|
||||
if p.StatsRecorder != nil {
|
||||
health.Stats = p.StatsRecorder.Data()
|
||||
}
|
||||
err := templatesRenderer.JSON(response, http.StatusOK, health)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
984
autogen/gentemplates/gen.go
Normal file
984
autogen/gentemplates/gen.go
Normal file
@@ -0,0 +1,984 @@
|
||||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// templates/consul_catalog.tmpl
|
||||
// templates/docker.tmpl
|
||||
// templates/ecs.tmpl
|
||||
// templates/eureka.tmpl
|
||||
// templates/kubernetes.tmpl
|
||||
// templates/kv.tmpl
|
||||
// templates/marathon.tmpl
|
||||
// templates/mesos.tmpl
|
||||
// templates/notFound.tmpl
|
||||
// templates/rancher.tmpl
|
||||
// DO NOT EDIT!
|
||||
|
||||
package gentemplates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||
{{range $index, $node := .Nodes}}
|
||||
[backends."backend-{{getBackend $node}}".servers."{{getBackendName $node $index}}"]
|
||||
url = "{{getAttribute "protocol" $node.Service.Tags "http"}}://{{getBackendAddress $node}}:{{$node.Service.Port}}"
|
||||
{{$weight := getAttribute "backend.weight" $node.Service.Tags "0"}}
|
||||
{{with $weight}}
|
||||
weight = {{$weight}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range .Services}}
|
||||
{{$service := .ServiceName}}
|
||||
{{$circuitBreaker := getAttribute "backend.circuitbreaker" .Attributes ""}}
|
||||
{{with $circuitBreaker}}
|
||||
[backends."backend-{{$service}}".circuitbreaker]
|
||||
expression = "{{$circuitBreaker}}"
|
||||
{{end}}
|
||||
|
||||
[backends."backend-{{$service}}".loadbalancer]
|
||||
method = "{{getAttribute "backend.loadbalancer" .Attributes "wrr"}}"
|
||||
sticky = {{getSticky .Attributes}}
|
||||
{{if hasStickinessLabel .Attributes}}
|
||||
[backends."backend-{{$service}}".loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName .Attributes}}"
|
||||
{{end}}
|
||||
|
||||
{{if hasMaxconnAttributes .Attributes}}
|
||||
[backends."backend-{{$service}}".maxconn]
|
||||
amount = {{getAttribute "backend.maxconn.amount" .Attributes "" }}
|
||||
extractorfunc = "{{getAttribute "backend.maxconn.extractorfunc" .Attributes "" }}"
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]
|
||||
{{range .Services}}
|
||||
[frontends."frontend-{{.ServiceName}}"]
|
||||
backend = "backend-{{.ServiceName}}"
|
||||
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}}
|
||||
priority = {{getAttribute "frontend.priority" .Attributes "0"}}
|
||||
{{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}}
|
||||
{{with $entryPoints}}
|
||||
entrypoints = [{{range getEntryPoints $entryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
basicAuth = [{{range getBasicAuth .Attributes}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
[frontends."frontend-{{.ServiceName}}".routes."route-host-{{.ServiceName}}"]
|
||||
rule = "{{getFrontendRule .}}"
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesConsul_catalogTmplBytes() ([]byte, error) {
|
||||
return _templatesConsul_catalogTmpl, nil
|
||||
}
|
||||
|
||||
func templatesConsul_catalogTmpl() (*asset, error) {
|
||||
bytes, err := templatesConsul_catalogTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/consul_catalog.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||
[backends]{{range $backendName, $backend := .Backends}}
|
||||
{{if hasCircuitBreakerLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.circuitbreaker]
|
||||
expression = "{{getCircuitBreakerExpression $backend}}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.loadbalancer]
|
||||
method = "{{getLoadBalancerMethod $backend}}"
|
||||
sticky = {{getSticky $backend}}
|
||||
{{if hasStickinessLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.loadBalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $backend}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasMaxConnLabels $backend}}
|
||||
[backends.backend-{{$backendName}}.maxconn]
|
||||
amount = {{getMaxConnAmount $backend}}
|
||||
extractorfunc = "{{getMaxConnExtractorFunc $backend}}"
|
||||
{{end}}
|
||||
|
||||
{{$servers := index $backendServers $backendName}}
|
||||
{{range $serverName, $server := $servers}}
|
||||
{{if hasServices $server}}
|
||||
{{$services := getServiceNames $server}}
|
||||
{{range $serviceIndex, $serviceName := $services}}
|
||||
[backends.backend-{{getServiceBackend $server $serviceName}}.servers.service-{{$serverName}}]
|
||||
url = "{{getServiceProtocol $server $serviceName}}://{{getIPAddress $server}}:{{getServicePort $server $serviceName}}"
|
||||
weight = {{getServiceWeight $server $serviceName}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
[backends.backend-{{$backendName}}.servers.server-{{$server.Name | replace "/" "" | replace "." "-"}}]
|
||||
url = "{{getProtocol $server}}://{{getIPAddress $server}}:{{getPort $server}}"
|
||||
weight = {{getWeight $server}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $frontend, $containers := .Frontends}}
|
||||
{{$container := index $containers 0}}
|
||||
{{if hasServices $container}}
|
||||
{{$services := getServiceNames $container}}
|
||||
{{range $serviceIndex, $serviceName := $services}}
|
||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
||||
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
||||
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
||||
{{if getWhitelistSourceRange $container}}
|
||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
priority = {{getServicePriority $container $serviceName}}
|
||||
entryPoints = [{{range getServiceEntryPoints $container $serviceName}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{if hasServiceRedirect $container $serviceName}}
|
||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}".redirect]
|
||||
entryPoint = "{{getServiceRedirectEntryPoint $container $serviceName}}"
|
||||
regex = "{{getServiceRedirectRegex $container $serviceName}}"
|
||||
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||
{{end}}
|
||||
{{else}}
|
||||
[frontends."frontend-{{$frontend}}"]
|
||||
backend = "backend-{{getBackend $container}}"
|
||||
passHostHeader = {{getPassHostHeader $container}}
|
||||
{{if getWhitelistSourceRange $container}}
|
||||
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
priority = {{getPriority $container}}
|
||||
entryPoints = [{{range getEntryPoints $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range getBasicAuth $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{if hasRedirect $container}}
|
||||
[frontends."frontend-{{$frontend}}".redirect]
|
||||
entryPoint = "{{getRedirectEntryPoint $container}}"
|
||||
regex = "{{getRedirectRegex $container}}"
|
||||
replacement = "{{getRedirectReplacement $container}}"
|
||||
{{end}}
|
||||
|
||||
{{ if hasHeaders $container}}
|
||||
[frontends."frontend-{{$frontend}}".headers]
|
||||
{{if hasSSLRedirectHeaders $container}}
|
||||
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasSSLTemporaryRedirectHeaders $container}}
|
||||
SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasSSLHostHeaders $container}}
|
||||
SSLHost = "{{getSSLHostHeaders $container}}"
|
||||
{{end}}
|
||||
{{if hasSTSSecondsHeaders $container}}
|
||||
STSSeconds = {{getSTSSecondsHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasSTSIncludeSubdomainsHeaders $container}}
|
||||
STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasSTSPreloadHeaders $container}}
|
||||
STSPreload = {{getSTSPreloadHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasForceSTSHeaderHeaders $container}}
|
||||
ForceSTSHeader = {{getForceSTSHeaderHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasFrameDenyHeaders $container}}
|
||||
FrameDeny = {{getFrameDenyHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasCustomFrameOptionsValueHeaders $container}}
|
||||
CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $container}}"
|
||||
{{end}}
|
||||
{{if hasContentTypeNosniffHeaders $container}}
|
||||
ContentTypeNosniff = {{getContentTypeNosniffHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasBrowserXSSFilterHeaders $container}}
|
||||
BrowserXSSFilter = {{getBrowserXSSFilterHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasContentSecurityPolicyHeaders $container}}
|
||||
ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $container}}"
|
||||
{{end}}
|
||||
{{if hasPublicKeyHeaders $container}}
|
||||
PublicKey = "{{getPublicKeyHeaders $container}}"
|
||||
{{end}}
|
||||
{{if hasReferrerPolicyHeaders $container}}
|
||||
ReferrerPolicy = "{{getReferrerPolicyHeaders $container}}"
|
||||
{{end}}
|
||||
{{if hasIsDevelopmentHeaders $container}}
|
||||
IsDevelopment = {{getIsDevelopmentHeaders $container}}
|
||||
{{end}}
|
||||
{{if hasAllowedHostsHeaders $container}}
|
||||
AllowedHosts = [{{range getAllowedHostsHeaders $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
{{if hasHostsProxyHeaders $container}}
|
||||
HostsProxyHeaders = [{{range getHostsProxyHeaders $container}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
{{if hasRequestHeaders $container}}
|
||||
[frontends."frontend-{{$frontend}}".headers.customrequestheaders]
|
||||
{{range $k, $v := getRequestHeaders $container}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if hasResponseHeaders $container}}
|
||||
[frontends."frontend-{{$frontend}}".headers.customresponseheaders]
|
||||
{{range $k, $v := getResponseHeaders $container}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if hasSSLProxyHeaders $container}}
|
||||
[frontends."frontend-{{$frontend}}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := getSSLProxyHeaders $container}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
|
||||
rule = "{{getFrontendRule $container}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesDockerTmplBytes() ([]byte, error) {
|
||||
return _templatesDockerTmpl, nil
|
||||
}
|
||||
|
||||
func templatesDockerTmpl() (*asset, error) {
|
||||
bytes, err := templatesDockerTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/docker.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesEcsTmpl = []byte(`[backends]{{range $serviceName, $instances := .Services}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer]
|
||||
method = "{{ getLoadBalancerMethod $instances}}"
|
||||
sticky = {{ getLoadBalancerSticky $instances}}
|
||||
{{if hasStickinessLabel $instances}}
|
||||
[backends.backend-{{ $serviceName }}.loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $instances}}"
|
||||
{{end}}
|
||||
{{ if hasHealthCheckLabels $instances }}
|
||||
[backends.backend-{{ $serviceName }}.healthcheck]
|
||||
path = "{{getHealthCheckPath $instances }}"
|
||||
interval = "{{getHealthCheckInterval $instances }}"
|
||||
{{end}}
|
||||
|
||||
{{range $index, $i := $instances}}
|
||||
[backends.backend-{{ $i.Name }}.servers.server-{{ $i.Name }}{{ $i.ID }}]
|
||||
url = "{{ getProtocol $i }}://{{ getHost $i }}:{{ getPort $i }}"
|
||||
weight = {{ getWeight $i}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $serviceName, $instances := .Services}}
|
||||
{{range filterFrontends $instances}}
|
||||
[frontends.frontend-{{ $serviceName }}]
|
||||
backend = "backend-{{ $serviceName }}"
|
||||
passHostHeader = {{ getPassHostHeader .}}
|
||||
priority = {{ getPriority .}}
|
||||
entryPoints = [{{range getEntryPoints .}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range getBasicAuth .}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
[frontends.frontend-{{ $serviceName }}.routes.route-frontend-{{ $serviceName }}]
|
||||
rule = "{{getFrontendRule .}}"
|
||||
{{end}}
|
||||
{{end}}`)
|
||||
|
||||
func templatesEcsTmplBytes() ([]byte, error) {
|
||||
return _templatesEcsTmpl, nil
|
||||
}
|
||||
|
||||
func templatesEcsTmpl() (*asset, error) {
|
||||
bytes, err := templatesEcsTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/ecs.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesEurekaTmpl = []byte(`[backends]{{range .Applications}}
|
||||
{{ $app := .}}
|
||||
{{range .Instances}}
|
||||
[backends.backend{{$app.Name}}.servers.server-{{ getInstanceID . }}]
|
||||
url = "{{ getProtocol . }}://{{ .IpAddr }}:{{ getPort . }}"
|
||||
weight = {{ getWeight . }}
|
||||
{{end}}{{end}}
|
||||
|
||||
[frontends]{{range .Applications}}
|
||||
[frontends.frontend{{.Name}}]
|
||||
backend = "backend{{.Name}}"
|
||||
entryPoints = ["http"]
|
||||
[frontends.frontend{{.Name }}.routes.route-host{{.Name}}]
|
||||
rule = "Host:{{ .Name | tolower }}"
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesEurekaTmplBytes() ([]byte, error) {
|
||||
return _templatesEurekaTmpl, nil
|
||||
}
|
||||
|
||||
func templatesEurekaTmpl() (*asset, error) {
|
||||
bytes, err := templatesEurekaTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/eureka.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend := .Backends}}
|
||||
[backends."{{$backendName}}"]
|
||||
{{if $backend.CircuitBreaker}}
|
||||
[backends."{{$backendName}}".circuitbreaker]
|
||||
expression = "{{$backend.CircuitBreaker.Expression}}"
|
||||
{{end}}
|
||||
[backends."{{$backendName}}".loadbalancer]
|
||||
method = "{{$backend.LoadBalancer.Method}}"
|
||||
{{if $backend.LoadBalancer.Sticky}}
|
||||
sticky = true
|
||||
{{end}}
|
||||
{{if $backend.LoadBalancer.Stickiness}}
|
||||
[backends."{{$backendName}}".loadbalancer.stickiness]
|
||||
cookieName = "{{$backend.LoadBalancer.Stickiness.CookieName}}"
|
||||
{{end}}
|
||||
{{range $serverName, $server := $backend.Servers}}
|
||||
[backends."{{$backendName}}".servers."{{$serverName}}"]
|
||||
url = "{{$server.URL}}"
|
||||
weight = {{$server.Weight}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $frontendName, $frontend := .Frontends}}
|
||||
[frontends."{{$frontendName}}"]
|
||||
backend = "{{$frontend.Backend}}"
|
||||
priority = {{$frontend.Priority}}
|
||||
passHostHeader = {{$frontend.PassHostHeader}}
|
||||
entryPoints = [{{range $frontend.EntryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range $frontend.BasicAuth}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{if $frontend.Redirect}}
|
||||
[frontends."{{$frontendName}}".redirect]
|
||||
entryPoint = "{{$frontend.RedirectEntryPoint}}"
|
||||
regex = "{{$frontend.RedirectRegex}}"
|
||||
replacement = "{{$frontend.RedirectReplacement}}"
|
||||
{{end}}
|
||||
|
||||
{{ if $frontend.Headers }}
|
||||
[frontends."{{$frontendName}}".headers]
|
||||
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
||||
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
||||
SSLHost = "{{$frontend.Headers.SSLHost}}"
|
||||
STSSeconds = {{$frontend.Headers.STSSeconds}}
|
||||
STSIncludeSubdomains = {{$frontend.Headers.STSIncludeSubdomains}}
|
||||
STSPreload = {{$frontend.Headers.STSPreload}}
|
||||
ForceSTSHeader = {{$frontend.Headers.ForceSTSHeader}}
|
||||
FrameDeny = {{$frontend.Headers.FrameDeny}}
|
||||
CustomFrameOptionsValue = "{{$frontend.Headers.CustomFrameOptionsValue}}"
|
||||
ContentTypeNosniff = {{$frontend.Headers.ContentTypeNosniff}}
|
||||
BrowserXSSFilter = {{$frontend.Headers.BrowserXSSFilter}}
|
||||
ContentSecurityPolicy = "{{$frontend.Headers.ContentSecurityPolicy}}"
|
||||
PublicKey = "{{$frontend.Headers.PublicKey}}"
|
||||
ReferrerPolicy = "{{$frontend.Headers.ReferrerPolicy}}"
|
||||
IsDevelopment = {{$frontend.Headers.IsDevelopment}}
|
||||
|
||||
{{if $frontend.Headers.AllowedHosts}}
|
||||
AllowedHosts = [{{range $frontend.Headers.AllowedHosts}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Headers.HostsProxyHeaders}}
|
||||
HostsProxyHeaders = [{{range $frontend.Headers.HostsProxyHeaders}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Headers.CustomRequestHeaders}}
|
||||
[frontends."{{$frontendName}}".headers.customrequestheaders]
|
||||
{{range $k, $v := $frontend.Headers.CustomRequestHeaders}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Headers.CustomResponseHeaders}}
|
||||
[frontends."{{$frontendName}}".headers.customresponseheaders]
|
||||
{{range $k, $v := $frontend.Headers.CustomResponseHeaders}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $frontend.Headers.SSLProxyHeaders}}
|
||||
[frontends."{{$frontendName}}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := $frontend.Headers.SSLProxyHeaders}}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $routeName, $route := $frontend.Routes}}
|
||||
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
|
||||
rule = "{{$route.Rule}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesKubernetesTmplBytes() ([]byte, error) {
|
||||
return _templatesKubernetesTmpl, nil
|
||||
}
|
||||
|
||||
func templatesKubernetesTmpl() (*asset, error) {
|
||||
bytes, err := templatesKubernetesTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/kubernetes.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
|
||||
{{$backends := List .Prefix "/backends/"}}
|
||||
{{$tlsconfiguration := List .Prefix "/tlsconfiguration/"}}
|
||||
|
||||
[backends]{{range $backends}}
|
||||
{{$backend := .}}
|
||||
{{$backendName := Last $backend}}
|
||||
{{$servers := ListServers $backend }}
|
||||
|
||||
{{$circuitBreaker := Get "" . "/circuitbreaker/" "expression"}}
|
||||
{{with $circuitBreaker}}
|
||||
[backends."{{$backendName}}".circuitBreaker]
|
||||
expression = "{{$circuitBreaker}}"
|
||||
{{end}}
|
||||
|
||||
{{$loadBalancer := Get "" . "/loadbalancer/" "method"}}
|
||||
{{with $loadBalancer}}
|
||||
[backends."{{$backendName}}".loadBalancer]
|
||||
method = "{{$loadBalancer}}"
|
||||
sticky = {{ getSticky . }}
|
||||
{{if hasStickinessLabel $backend}}
|
||||
[backends."{{$backendName}}".loadBalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $backend}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{$healthCheck := Get "" . "/healthcheck/" "path"}}
|
||||
{{with $healthCheck}}
|
||||
[backends."{{$backendName}}".healthCheck]
|
||||
path = "{{$healthCheck}}"
|
||||
interval = "{{ Get "30s" $backend "/healthcheck/" "interval" }}"
|
||||
{{end}}
|
||||
|
||||
{{$maxConnAmt := Get "" . "/maxconn/" "amount"}}
|
||||
{{$maxConnExtractorFunc := Get "" . "/maxconn/" "extractorfunc"}}
|
||||
{{with $maxConnAmt}}
|
||||
{{with $maxConnExtractorFunc}}
|
||||
[backends."{{$backendName}}".maxConn]
|
||||
amount = {{$maxConnAmt}}
|
||||
extractorFunc = "{{$maxConnExtractorFunc}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $servers}}
|
||||
[backends."{{$backendName}}".servers."{{Last .}}"]
|
||||
url = "{{Get "" . "/url"}}"
|
||||
weight = {{Get "0" . "/weight"}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $frontends}}
|
||||
{{$frontend := Last .}}
|
||||
{{$entryPoints := SplitGet . "/entrypoints"}}
|
||||
[frontends."{{$frontend}}"]
|
||||
backend = "{{Get "" . "/backend"}}"
|
||||
passHostHeader = {{Get "true" . "/passHostHeader"}}
|
||||
priority = {{Get "0" . "/priority"}}
|
||||
entryPoints = [{{range $entryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{$routes := List . "/routes/"}}
|
||||
{{range $routes}}
|
||||
[frontends."{{$frontend}}".routes."{{Last .}}"]
|
||||
rule = "{{Get "" . "/rule"}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $tlsconfiguration}}
|
||||
{{$entryPoints := SplitGet . "/entrypoints"}}
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = [{{range $entryPoints}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = """{{Get "" . "/certificate" "/certfile"}}"""
|
||||
keyFile = """{{Get "" . "/certificate" "/keyfile"}}"""
|
||||
{{end}}
|
||||
|
||||
`)
|
||||
|
||||
func templatesKvTmplBytes() ([]byte, error) {
|
||||
return _templatesKvTmpl, nil
|
||||
}
|
||||
|
||||
func templatesKvTmpl() (*asset, error) {
|
||||
bytes, err := templatesKvTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/kv.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesMarathonTmpl = []byte(`{{$apps := .Applications}}
|
||||
|
||||
{{range $app := $apps}}
|
||||
{{range $task := $app.Tasks}}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
||||
[backends."backend{{getBackend $app $serviceName}}".servers."server-{{$task.ID | replace "." "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||
url = "{{getProtocol $app $serviceName}}://{{getBackendServer $task $app}}:{{getPort $task $app $serviceName}}"
|
||||
weight = {{getWeight $app $serviceName}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{range $app := $apps}}
|
||||
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
||||
[backends."backend{{getBackend $app $serviceName }}"]
|
||||
{{ if hasMaxConnLabels $app }}
|
||||
[backends."backend{{getBackend $app $serviceName }}".maxconn]
|
||||
amount = {{getMaxConnAmount $app }}
|
||||
extractorfunc = "{{getMaxConnExtractorFunc $app }}"
|
||||
{{end}}
|
||||
{{ if hasLoadBalancerLabels $app }}
|
||||
[backends."backend{{getBackend $app $serviceName }}".loadbalancer]
|
||||
method = "{{getLoadBalancerMethod $app }}"
|
||||
sticky = {{getSticky $app}}
|
||||
{{if hasStickinessLabel $app}}
|
||||
[backends."backend{{getBackend $app $serviceName }}".loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $app}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{ if hasCircuitBreakerLabels $app }}
|
||||
[backends."backend{{getBackend $app $serviceName }}".circuitbreaker]
|
||||
expression = "{{getCircuitBreakerExpression $app }}"
|
||||
{{end}}
|
||||
{{ if hasHealthCheckLabels $app }}
|
||||
[backends."backend{{getBackend $app $serviceName }}".healthcheck]
|
||||
path = "{{getHealthCheckPath $app }}"
|
||||
interval = "{{getHealthCheckInterval $app }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $app := $apps}}{{range $serviceIndex, $serviceName := getServiceNames .}}
|
||||
[frontends."{{ getFrontendName $app $serviceName }}"]
|
||||
backend = "backend{{getBackend $app $serviceName}}"
|
||||
passHostHeader = {{getPassHostHeader $app $serviceName}}
|
||||
priority = {{getPriority $app $serviceName}}
|
||||
entryPoints = [{{range getEntryPoints $app $serviceName}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range getBasicAuth $app $serviceName}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
[frontends."{{ getFrontendName $app $serviceName }}".routes."route-host{{$app.ID | replace "/" "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||
rule = "{{getFrontendRule $app $serviceName}}"
|
||||
{{end}}{{end}}
|
||||
`)
|
||||
|
||||
func templatesMarathonTmplBytes() ([]byte, error) {
|
||||
return _templatesMarathonTmpl, nil
|
||||
}
|
||||
|
||||
func templatesMarathonTmpl() (*asset, error) {
|
||||
bytes, err := templatesMarathonTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/marathon.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesMesosTmpl = []byte(`{{$apps := .Applications}}
|
||||
[backends]{{range .Tasks}}
|
||||
[backends.backend{{getBackend . $apps}}.servers.server-{{getID .}}]
|
||||
url = "{{getProtocol . $apps}}://{{getHost .}}:{{getPort . $apps}}"
|
||||
weight = {{getWeight . $apps}}
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range .Applications}}
|
||||
[frontends.frontend-{{getFrontEndName .}}]
|
||||
backend = "backend{{getFrontendBackend .}}"
|
||||
passHostHeader = {{getPassHostHeader .}}
|
||||
priority = {{getPriority .}}
|
||||
entryPoints = [{{range getEntryPoints .}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
[frontends.frontend-{{getFrontEndName .}}.routes.route-host{{getFrontEndName .}}]
|
||||
rule = "{{getFrontendRule .}}"
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesMesosTmplBytes() ([]byte, error) {
|
||||
return _templatesMesosTmpl, nil
|
||||
}
|
||||
|
||||
func templatesMesosTmpl() (*asset, error) {
|
||||
bytes, err := templatesMesosTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/mesos.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesNotfoundTmpl = []byte(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Traefik</title>
|
||||
</head>
|
||||
<body>
|
||||
Ohhhh man, this is bad...
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
func templatesNotfoundTmplBytes() ([]byte, error) {
|
||||
return _templatesNotfoundTmpl, nil
|
||||
}
|
||||
|
||||
func templatesNotfoundTmpl() (*asset, error) {
|
||||
bytes, err := templatesNotfoundTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/notFound.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _templatesRancherTmpl = []byte(`{{$backendServers := .Backends}}
|
||||
[backends]{{range $backendName, $backend := .Backends}}
|
||||
{{if hasCircuitBreakerLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.circuitbreaker]
|
||||
expression = "{{getCircuitBreakerExpression $backend}}"
|
||||
{{end}}
|
||||
|
||||
{{if hasLoadBalancerLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.loadbalancer]
|
||||
method = "{{getLoadBalancerMethod $backend}}"
|
||||
sticky = {{getSticky $backend}}
|
||||
{{if hasStickinessLabel $backend}}
|
||||
[backends.backend-{{$backendName}}.loadbalancer.stickiness]
|
||||
cookieName = "{{getStickinessCookieName $backend}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if hasMaxConnLabels $backend}}
|
||||
[backends.backend-{{$backendName}}.maxconn]
|
||||
amount = {{getMaxConnAmount $backend}}
|
||||
extractorfunc = "{{getMaxConnExtractorFunc $backend}}"
|
||||
{{end}}
|
||||
|
||||
{{range $index, $ip := $backend.Containers}}
|
||||
[backends.backend-{{$backendName}}.servers.server-{{$index}}]
|
||||
url = "{{getProtocol $backend}}://{{$ip}}:{{getPort $backend}}"
|
||||
weight = {{getWeight $backend}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
[frontends]{{range $frontendName, $service := .Frontends}}
|
||||
[frontends."frontend-{{$frontendName}}"]
|
||||
backend = "backend-{{getBackend $service}}"
|
||||
passHostHeader = {{getPassHostHeader $service}}
|
||||
priority = {{getPriority $service}}
|
||||
entryPoints = [{{range getEntryPoints $service}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
basicAuth = [{{range getBasicAuth $service}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{if hasRedirect $service}}
|
||||
[frontends."frontend-{{$frontendName}}".redirect]
|
||||
entryPoint = "{{getRedirectEntryPoint $service}}"
|
||||
regex = "{{getRedirectRegex $service}}"
|
||||
replacement = "{{getRedirectReplacement $service}}"
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
||||
rule = "{{getFrontendRule $service}}"
|
||||
{{end}}
|
||||
`)
|
||||
|
||||
func templatesRancherTmplBytes() ([]byte, error) {
|
||||
return _templatesRancherTmpl, nil
|
||||
}
|
||||
|
||||
func templatesRancherTmpl() (*asset, error) {
|
||||
bytes, err := templatesRancherTmplBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "templates/rancher.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"templates/consul_catalog.tmpl": templatesConsul_catalogTmpl,
|
||||
"templates/docker.tmpl": templatesDockerTmpl,
|
||||
"templates/ecs.tmpl": templatesEcsTmpl,
|
||||
"templates/eureka.tmpl": templatesEurekaTmpl,
|
||||
"templates/kubernetes.tmpl": templatesKubernetesTmpl,
|
||||
"templates/kv.tmpl": templatesKvTmpl,
|
||||
"templates/marathon.tmpl": templatesMarathonTmpl,
|
||||
"templates/mesos.tmpl": templatesMesosTmpl,
|
||||
"templates/notFound.tmpl": templatesNotfoundTmpl,
|
||||
"templates/rancher.tmpl": templatesRancherTmpl,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"templates": {nil, map[string]*bintree{
|
||||
"consul_catalog.tmpl": {templatesConsul_catalogTmpl, map[string]*bintree{}},
|
||||
"docker.tmpl": {templatesDockerTmpl, map[string]*bintree{}},
|
||||
"ecs.tmpl": {templatesEcsTmpl, map[string]*bintree{}},
|
||||
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
||||
"kubernetes.tmpl": {templatesKubernetesTmpl, map[string]*bintree{}},
|
||||
"kv.tmpl": {templatesKvTmpl, map[string]*bintree{}},
|
||||
"marathon.tmpl": {templatesMarathonTmpl, map[string]*bintree{}},
|
||||
"mesos.tmpl": {templatesMesosTmpl, map[string]*bintree{}},
|
||||
"notFound.tmpl": {templatesNotfoundTmpl, map[string]*bintree{}},
|
||||
"rancher.tmpl": {templatesRancherTmpl, map[string]*bintree{}},
|
||||
}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object,
|
||||
|
||||
func (d *Datastore) watchChanges() error {
|
||||
stopCh := make(chan struct{})
|
||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh)
|
||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,19 +119,8 @@ func (d *Datastore) watchChanges() error {
|
||||
|
||||
func (d *Datastore) reload() error {
|
||||
log.Debug("Datastore reload")
|
||||
d.localLock.Lock()
|
||||
err := d.kv.LoadConfig(d.meta)
|
||||
if err != nil {
|
||||
d.localLock.Unlock()
|
||||
return err
|
||||
}
|
||||
err = d.meta.unmarshall()
|
||||
if err != nil {
|
||||
d.localLock.Unlock()
|
||||
return err
|
||||
}
|
||||
d.localLock.Unlock()
|
||||
return nil
|
||||
_, err := d.Load()
|
||||
return err
|
||||
}
|
||||
|
||||
// Begin creates a transaction with the KV store.
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
@@ -23,11 +22,9 @@ import (
|
||||
"github.com/containous/traefik/provider/marathon"
|
||||
"github.com/containous/traefik/provider/mesos"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/provider/web"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/safe"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
thoas_stats "github.com/thoas/stats"
|
||||
)
|
||||
|
||||
func TestDo_globalConfiguration(t *testing.T) {
|
||||
@@ -48,16 +45,19 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
"foo": {
|
||||
Network: "foo Network",
|
||||
Address: "foo Address",
|
||||
TLS: &configuration.TLS{
|
||||
TLS: &traefikTls.TLS{
|
||||
MinVersion: "foo MinVersion",
|
||||
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
|
||||
Certificates: configuration.Certificates{
|
||||
Certificates: traefikTls.Certificates{
|
||||
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCAFiles: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
ClientCA: traefikTls.ClientCA{
|
||||
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
Redirect: &configuration.Redirect{
|
||||
Redirect: &types.Redirect{
|
||||
Replacement: "foo Replacement",
|
||||
Regex: "foo Regex",
|
||||
EntryPoint: "foo EntryPoint",
|
||||
@@ -91,16 +91,19 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
"fii": {
|
||||
Network: "fii Network",
|
||||
Address: "fii Address",
|
||||
TLS: &configuration.TLS{
|
||||
TLS: &traefikTls.TLS{
|
||||
MinVersion: "fii MinVersion",
|
||||
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
|
||||
Certificates: configuration.Certificates{
|
||||
Certificates: traefikTls.Certificates{
|
||||
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCAFiles: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
ClientCA: traefikTls.ClientCA{
|
||||
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
Redirect: &configuration.Redirect{
|
||||
Redirect: &types.Redirect{
|
||||
Replacement: "fii Replacement",
|
||||
Regex: "fii Regex",
|
||||
EntryPoint: "fii EntryPoint",
|
||||
@@ -178,7 +181,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
config.MaxIdleConnsPerHost = 666
|
||||
config.IdleTimeout = flaeg.Duration(666 * time.Second)
|
||||
config.InsecureSkipVerify = true
|
||||
config.RootCAs = configuration.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.RootCAs = traefikTls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.Retry = &configuration.Retry{
|
||||
Attempts: 666,
|
||||
}
|
||||
@@ -246,7 +249,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
},
|
||||
Directory: "file Directory",
|
||||
}
|
||||
config.Web = &web.Provider{
|
||||
config.Web = &configuration.WebCompatibility{
|
||||
Address: "web Address",
|
||||
CertFile: "web CertFile",
|
||||
KeyFile: "web KeyFile",
|
||||
@@ -289,15 +292,6 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Debug: true,
|
||||
CurrentConfigurations: &safe.Safe{},
|
||||
Stats: &thoas_stats.Stats{
|
||||
Uptime: time.Now(),
|
||||
Pid: 666,
|
||||
ResponseCounts: map[string]int{"foo": 1, "fii": 2, "fuu": 3},
|
||||
TotalResponseCounts: map[string]int{"foo": 1, "fii": 2, "fuu": 3},
|
||||
TotalResponseTime: time.Now(),
|
||||
},
|
||||
StatsRecorder: &middlewares.StatsRecorder{},
|
||||
}
|
||||
config.Marathon = &marathon.Provider{
|
||||
BaseProvider: provider.BaseProvider{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package anonymize
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_doOnJSON(t *testing.T) {
|
||||
|
||||
@@ -84,7 +84,7 @@ Add more configuration information here.
|
||||
)
|
||||
|
||||
// newBugCmd builds a new Bug command
|
||||
func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration interface{}) *flaeg.Command {
|
||||
func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
return &flaeg.Command{
|
||||
@@ -99,7 +99,7 @@ func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration in
|
||||
}
|
||||
}
|
||||
|
||||
func runBugCmd(traefikConfiguration interface{}) func() error {
|
||||
func runBugCmd(traefikConfiguration *TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
|
||||
body, err := createBugReport(traefikConfiguration)
|
||||
@@ -113,7 +113,7 @@ func runBugCmd(traefikConfiguration interface{}) func() error {
|
||||
}
|
||||
}
|
||||
|
||||
func createBugReport(traefikConfiguration interface{}) (string, error) {
|
||||
func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error) {
|
||||
var version bytes.Buffer
|
||||
if err := getVersionPrint(&version); err != nil {
|
||||
return "", err
|
||||
@@ -124,7 +124,7 @@ func createBugReport(traefikConfiguration interface{}) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
config, err := anonymize.Do(&traefikConfiguration, true)
|
||||
config, err := anonymize.Do(traefikConfiguration, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -6,27 +6,44 @@ import (
|
||||
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_createBugReport(t *testing.T) {
|
||||
traefikConfiguration := TraefikConfiguration{
|
||||
traefikConfiguration := &TraefikConfiguration{
|
||||
ConfigFile: "FOO",
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
EntryPoints: configuration.EntryPoints{
|
||||
"goo": &configuration.EntryPoint{
|
||||
Address: "hoo.bar",
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
UsersFile: "foo Basic UsersFile",
|
||||
Users: types.Users{"foo Basic Users 1", "foo Basic Users 2", "foo Basic Users 3"},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
UsersFile: "foo Digest UsersFile",
|
||||
Users: types.Users{"foo Digest Users 1", "foo Digest Users 2", "foo Digest Users 3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
File: &file.Provider{
|
||||
Directory: "BAR",
|
||||
},
|
||||
RootCAs: configuration.RootCAs{"fllf"},
|
||||
RootCAs: tls.RootCAs{"fllf"},
|
||||
},
|
||||
}
|
||||
|
||||
report, err := createBugReport(traefikConfiguration)
|
||||
assert.NoError(t, err, report)
|
||||
|
||||
// exported anonymous configuration
|
||||
assert.NotContains(t, "web Basic Users ", report)
|
||||
assert.NotContains(t, "foo Digest Users ", report)
|
||||
assert.NotContains(t, "hoo.bar", report)
|
||||
}
|
||||
|
||||
func Test_anonymize_traefikConfiguration(t *testing.T) {
|
||||
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik-extra-service-fabric"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares/accesslog"
|
||||
"github.com/containous/traefik/ping"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
@@ -18,9 +21,10 @@ import (
|
||||
"github.com/containous/traefik/provider/marathon"
|
||||
"github.com/containous/traefik/provider/mesos"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/provider/web"
|
||||
"github.com/containous/traefik/provider/rest"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/types"
|
||||
sf "github.com/jjcollinge/servicefabric"
|
||||
)
|
||||
|
||||
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
||||
@@ -43,14 +47,18 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultFile.Watch = true
|
||||
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
|
||||
|
||||
// default Web
|
||||
var defaultWeb web.Provider
|
||||
// default Rest
|
||||
var defaultRest rest.Provider
|
||||
defaultRest.EntryPoint = configuration.DefaultInternalEntryPointName
|
||||
|
||||
// TODO: Deprecated - Web provider, use REST provider instead
|
||||
var defaultWeb configuration.WebCompatibility
|
||||
defaultWeb.Address = ":8080"
|
||||
defaultWeb.Statistics = &types.Statistics{
|
||||
RecentErrors: 10,
|
||||
}
|
||||
|
||||
// default Metrics
|
||||
// TODO: Deprecated - default Metrics
|
||||
defaultWeb.Metrics = &types.Metrics{
|
||||
Prometheus: &types.Prometheus{
|
||||
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
|
||||
@@ -63,6 +71,10 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
Address: "localhost:8125",
|
||||
PushInterval: "10s",
|
||||
},
|
||||
InfluxDB: &types.InfluxDB{
|
||||
Address: "localhost:8089",
|
||||
PushInterval: "10s",
|
||||
},
|
||||
}
|
||||
|
||||
// default Marathon
|
||||
@@ -100,7 +112,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
var defaultZookeeper zk.Provider
|
||||
defaultZookeeper.Watch = true
|
||||
defaultZookeeper.Endpoint = "127.0.0.1:2181"
|
||||
defaultZookeeper.Prefix = "/traefik"
|
||||
defaultZookeeper.Prefix = "traefik"
|
||||
defaultZookeeper.Constraints = types.Constraints{}
|
||||
|
||||
//default Boltdb
|
||||
@@ -153,6 +165,22 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
var defaultEureka eureka.Provider
|
||||
defaultEureka.Delay = "30s"
|
||||
|
||||
// default ServiceFabric
|
||||
var defaultServiceFabric servicefabric.Provider
|
||||
defaultServiceFabric.APIVersion = sf.DefaultAPIVersion
|
||||
defaultServiceFabric.RefreshSeconds = 10
|
||||
|
||||
// default Ping
|
||||
var defaultPing = ping.Handler{
|
||||
EntryPoint: "traefik",
|
||||
}
|
||||
|
||||
// default TraefikLog
|
||||
defaultTraefikLog := types.TraefikLog{
|
||||
Format: "common",
|
||||
FilePath: "",
|
||||
}
|
||||
|
||||
// default AccessLog
|
||||
defaultAccessLog := types.AccessLog{
|
||||
Format: accesslog.CommonFormat,
|
||||
@@ -174,10 +202,45 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
DialTimeout: flaeg.Duration(configuration.DefaultDialTimeout),
|
||||
}
|
||||
|
||||
// default LifeCycle
|
||||
defaultLifeCycle := configuration.LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||
}
|
||||
|
||||
// default ApiConfiguration
|
||||
defaultAPI := api.Handler{
|
||||
EntryPoint: "traefik",
|
||||
Dashboard: true,
|
||||
}
|
||||
defaultAPI.Statistics = &types.Statistics{
|
||||
RecentErrors: 10,
|
||||
}
|
||||
|
||||
// default Metrics
|
||||
defaultMetrics := types.Metrics{
|
||||
Prometheus: &types.Prometheus{
|
||||
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
|
||||
EntryPoint: "traefik",
|
||||
},
|
||||
Datadog: &types.Datadog{
|
||||
Address: "localhost:8125",
|
||||
PushInterval: "10s",
|
||||
},
|
||||
StatsD: &types.Statsd{
|
||||
Address: "localhost:8125",
|
||||
PushInterval: "10s",
|
||||
},
|
||||
InfluxDB: &types.InfluxDB{
|
||||
Address: "localhost:8089",
|
||||
PushInterval: "10s",
|
||||
},
|
||||
}
|
||||
|
||||
defaultConfiguration := configuration.GlobalConfiguration{
|
||||
Docker: &defaultDocker,
|
||||
File: &defaultFile,
|
||||
Web: &defaultWeb,
|
||||
Rest: &defaultRest,
|
||||
Marathon: &defaultMarathon,
|
||||
Consul: &defaultConsul,
|
||||
ConsulCatalog: &defaultConsulCatalog,
|
||||
@@ -192,9 +255,14 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
DynamoDB: &defaultDynamoDB,
|
||||
Retry: &configuration.Retry{},
|
||||
HealthCheck: &healthCheck,
|
||||
AccessLog: &defaultAccessLog,
|
||||
RespondingTimeouts: &respondingTimeouts,
|
||||
ForwardingTimeouts: &forwardingTimeouts,
|
||||
TraefikLog: &defaultTraefikLog,
|
||||
AccessLog: &defaultAccessLog,
|
||||
LifeCycle: &defaultLifeCycle,
|
||||
Ping: &defaultPing,
|
||||
API: &defaultAPI,
|
||||
Metrics: &defaultMetrics,
|
||||
}
|
||||
|
||||
return &TraefikConfiguration{
|
||||
@@ -206,13 +274,12 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
func NewTraefikConfiguration() *TraefikConfiguration {
|
||||
return &TraefikConfiguration{
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
GraceTimeOut: flaeg.Duration(10 * time.Second),
|
||||
AccessLogsFile: "",
|
||||
TraefikLogsFile: "",
|
||||
LogLevel: "ERROR",
|
||||
EntryPoints: map[string]*configuration.EntryPoint{},
|
||||
Constraints: types.Constraints{},
|
||||
DefaultEntryPoints: []string{},
|
||||
DefaultEntryPoints: []string{"http"},
|
||||
ProvidersThrottleDuration: flaeg.Duration(2 * time.Second),
|
||||
MaxIdleConnsPerHost: 200,
|
||||
IdleTimeout: flaeg.Duration(0),
|
||||
|
||||
72
cmd/traefik/healthcheck.go
Normal file
72
cmd/traefik/healthcheck.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/configuration"
|
||||
)
|
||||
|
||||
func newHealthCheckCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "healthcheck",
|
||||
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runHealthCheck(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
os.Exit(1)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Printf("Bad healthcheck status: %s\n", resp.Status)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("OK: %s\n", resp.Request.URL)
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
|
||||
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
|
||||
if !ok {
|
||||
return nil, errors.New("missing ping entrypoint")
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
protocol := "http"
|
||||
if pingEntryPoint.TLS != nil {
|
||||
protocol = "https"
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client.Transport = tr
|
||||
}
|
||||
path := "/"
|
||||
if globalConfiguration.Web != nil {
|
||||
path = globalConfiguration.Web.Path
|
||||
}
|
||||
return client.Head(protocol + "://" + pingEntryPoint.Address + path + "ping")
|
||||
}
|
||||
145
cmd/traefik/storeconfig.go
Normal file
145
cmd/traefik/storeconfig.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/docker/libkv/store"
|
||||
)
|
||||
|
||||
func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "storeconfig",
|
||||
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
if kv == nil {
|
||||
return fmt.Errorf("error using command storeconfig, no Key-value store defined")
|
||||
}
|
||||
|
||||
fileConfig := traefikConfiguration.GlobalConfiguration.File
|
||||
if fileConfig != nil {
|
||||
traefikConfiguration.GlobalConfiguration.File = nil
|
||||
if len(fileConfig.Filename) == 0 && len(fileConfig.Directory) == 0 {
|
||||
fileConfig.Filename = traefikConfiguration.ConfigFile
|
||||
}
|
||||
}
|
||||
|
||||
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdlog.Printf("Storing configuration: %s\n", jsonConf)
|
||||
|
||||
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileConfig != nil {
|
||||
jsonConf, err = json.Marshal(fileConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
|
||||
config, err := fileConfig.LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdlog.Print("Writing config to KV")
|
||||
err = kv.StoreConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if traefikConfiguration.GlobalConfiguration.ACME != nil {
|
||||
var object cluster.Object
|
||||
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
// convert ACME json file to KV store
|
||||
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
object, err = localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// Create an empty account to create all the keys into the KV store
|
||||
account := &acme.Account{}
|
||||
account.Init()
|
||||
object = account
|
||||
}
|
||||
|
||||
meta := cluster.NewMetadata(object)
|
||||
err = meta.Marshall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source := staert.KvSource{
|
||||
Store: kv,
|
||||
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
|
||||
}
|
||||
err = source.StoreConfig(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Force to delete storagefile
|
||||
err = kv.Delete(kv.Prefix + "/acme/storagefile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// createKvSource creates KvSource
|
||||
// TLS support is enable for Consul and Etcd backends
|
||||
func createKvSource(traefikConfiguration *TraefikConfiguration) (*staert.KvSource, error) {
|
||||
var kv *staert.KvSource
|
||||
var kvStore store.Store
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case traefikConfiguration.Consul != nil:
|
||||
kvStore, err = traefikConfiguration.Consul.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Consul.Prefix,
|
||||
}
|
||||
case traefikConfiguration.Etcd != nil:
|
||||
kvStore, err = traefikConfiguration.Etcd.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Etcd.Prefix,
|
||||
}
|
||||
case traefikConfiguration.Zookeeper != nil:
|
||||
kvStore, err = traefikConfiguration.Zookeeper.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Zookeeper.Prefix,
|
||||
}
|
||||
case traefikConfiguration.Boltdb != nil:
|
||||
kvStore, err = traefikConfiguration.Boltdb.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Boltdb.Prefix,
|
||||
}
|
||||
}
|
||||
return kv, err
|
||||
}
|
||||
@@ -1,40 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
fmtlog "log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/collector"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"github.com/containous/traefik/provider/kubernetes"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/server"
|
||||
"github.com/containous/traefik/server/uuid"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/docker/libkv/store"
|
||||
)
|
||||
|
||||
func main() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
//traefik config inits
|
||||
traefikConfiguration := NewTraefikConfiguration()
|
||||
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
|
||||
@@ -46,116 +42,20 @@ Complete documentation is available at https://traefik.io`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
globalConfiguration := traefikConfiguration.GlobalConfiguration
|
||||
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
|
||||
// no filename, setting to global config file
|
||||
if len(traefikConfiguration.ConfigFile) != 0 {
|
||||
globalConfiguration.File.Filename = traefikConfiguration.ConfigFile
|
||||
} else {
|
||||
log.Errorln("Error using file configuration backend, no filename defined")
|
||||
}
|
||||
}
|
||||
if len(traefikConfiguration.ConfigFile) != 0 {
|
||||
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
|
||||
}
|
||||
run(&globalConfiguration)
|
||||
run(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
//storeconfig Command init
|
||||
var kv *staert.KvSource
|
||||
var err error
|
||||
|
||||
storeConfigCmd := &flaeg.Command{
|
||||
Name: "storeconfig",
|
||||
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
if kv == nil {
|
||||
return fmt.Errorf("Error using command storeconfig, no Key-value store defined")
|
||||
}
|
||||
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmtlog.Printf("Storing configuration: %s\n", jsonConf)
|
||||
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
// convert ACME json file to KV store
|
||||
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
object, err := localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meta := cluster.NewMetadata(object)
|
||||
err = meta.Marshall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
source := staert.KvSource{
|
||||
Store: kv,
|
||||
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
|
||||
}
|
||||
err = source.StoreConfig(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
|
||||
healthCheckCmd := &flaeg.Command{
|
||||
Name: "healthcheck",
|
||||
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
if traefikConfiguration.Web == nil {
|
||||
fmt.Println("Please enable the web provider to use healtcheck.")
|
||||
os.Exit(1)
|
||||
}
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
protocol := "http"
|
||||
if len(traefikConfiguration.Web.CertFile) > 0 {
|
||||
protocol = "https"
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client.Transport = tr
|
||||
}
|
||||
resp, err := client.Head(protocol + "://" + traefikConfiguration.Web.Address + "/ping")
|
||||
if err != nil {
|
||||
fmt.Printf("Error calling healthcheck: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Printf("Bad healthcheck status: %s\n", resp.Status)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("OK: %s\n", resp.Request.URL)
|
||||
os.Exit(0)
|
||||
return nil
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
storeConfigCmd := newStoreConfigCmd(traefikConfiguration, traefikPointersConfiguration)
|
||||
|
||||
//init flaeg source
|
||||
f := flaeg.New(traefikCmd, os.Args[1:])
|
||||
//add custom parsers
|
||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.RootCAs{}), &configuration.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(traefikTls.RootCAs{}), &traefikTls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
@@ -166,7 +66,7 @@ Complete documentation is available at https://traefik.io`,
|
||||
f.AddCommand(newVersionCmd())
|
||||
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
f.AddCommand(storeConfigCmd)
|
||||
f.AddCommand(healthCheckCmd)
|
||||
f.AddCommand(newHealthCheckCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
|
||||
usedCmd, err := f.GetCommand()
|
||||
if err != nil {
|
||||
@@ -194,11 +94,12 @@ Complete documentation is available at https://traefik.io`,
|
||||
|
||||
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
||||
|
||||
kv, err = CreateKvSource(traefikConfiguration)
|
||||
kv, err := createKvSource(traefikConfiguration)
|
||||
if err != nil {
|
||||
fmtlog.Printf("Error creating kv store: %s\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
storeConfigCmd.Run = runStoreConfig(kv, traefikConfiguration)
|
||||
|
||||
// IF a KV Store is enable and no sub-command called in args
|
||||
if kv != nil && usedCmd == traefikCmd {
|
||||
@@ -209,7 +110,15 @@ Complete documentation is available at https://traefik.io`,
|
||||
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
|
||||
}
|
||||
s.AddSource(kv)
|
||||
if _, err := s.LoadConfig(); err != nil {
|
||||
operation := func() error {
|
||||
_, err := s.LoadConfig()
|
||||
return err
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Load config error: %+v, retrying in %s", err, time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||
if err != nil {
|
||||
fmtlog.Printf("Error loading configuration: %s\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
@@ -223,70 +132,17 @@ Complete documentation is available at https://traefik.io`,
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func run(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||
func run(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
|
||||
configureLogging(globalConfiguration)
|
||||
|
||||
if len(configFile) > 0 {
|
||||
log.Infof("Using TOML configuration file %s", configFile)
|
||||
}
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
if len(globalConfiguration.EntryPoints) == 0 {
|
||||
globalConfiguration.EntryPoints = map[string]*configuration.EntryPoint{"http": {Address: ":80"}}
|
||||
globalConfiguration.DefaultEntryPoints = []string{"http"}
|
||||
}
|
||||
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||
|
||||
if globalConfiguration.Rancher != nil {
|
||||
// Ensure backwards compatibility for now
|
||||
if len(globalConfiguration.Rancher.AccessKey) > 0 ||
|
||||
len(globalConfiguration.Rancher.Endpoint) > 0 ||
|
||||
len(globalConfiguration.Rancher.SecretKey) > 0 {
|
||||
|
||||
if globalConfiguration.Rancher.API == nil {
|
||||
globalConfiguration.Rancher.API = &rancher.APIConfiguration{
|
||||
AccessKey: globalConfiguration.Rancher.AccessKey,
|
||||
SecretKey: globalConfiguration.Rancher.SecretKey,
|
||||
Endpoint: globalConfiguration.Rancher.Endpoint,
|
||||
}
|
||||
}
|
||||
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
|
||||
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
|
||||
}
|
||||
|
||||
if globalConfiguration.Rancher.Metadata != nil && len(globalConfiguration.Rancher.Metadata.Prefix) == 0 {
|
||||
globalConfiguration.Rancher.Metadata.Prefix = "latest"
|
||||
}
|
||||
}
|
||||
|
||||
if globalConfiguration.Debug {
|
||||
globalConfiguration.LogLevel = "DEBUG"
|
||||
}
|
||||
|
||||
// logging
|
||||
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
||||
if err != nil {
|
||||
log.Error("Error getting level", err)
|
||||
}
|
||||
log.SetLevel(level)
|
||||
if len(globalConfiguration.TraefikLogsFile) > 0 {
|
||||
dir := filepath.Dir(globalConfiguration.TraefikLogsFile)
|
||||
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
|
||||
err = log.OpenFile(globalConfiguration.TraefikLogsFile)
|
||||
defer func() {
|
||||
if err := log.CloseFile(); err != nil {
|
||||
log.Error("Error closing log", err)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
log.Error("Error opening file", err)
|
||||
} else {
|
||||
log.SetFormatter(&logrus.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
|
||||
}
|
||||
} else {
|
||||
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
||||
}
|
||||
jsonConf, _ := json.Marshal(globalConfiguration)
|
||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
@@ -294,14 +150,18 @@ func run(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
checkNewVersion()
|
||||
}
|
||||
|
||||
stats(globalConfiguration)
|
||||
|
||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||
svr := server.NewServer(*globalConfiguration)
|
||||
svr.Start()
|
||||
defer svr.Close()
|
||||
|
||||
sent, err := daemon.SdNotify(false, "READY=1")
|
||||
if !sent && err != nil {
|
||||
log.Error("Fail to notify", err)
|
||||
}
|
||||
|
||||
t, err := daemon.SdWatchdogEnabled(false)
|
||||
if err != nil {
|
||||
log.Error("Problem with watchdog", err)
|
||||
@@ -312,61 +172,113 @@ func run(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
safe.Go(func() {
|
||||
tick := time.Tick(t)
|
||||
for range tick {
|
||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||
log.Error("Fail to tick watchdog")
|
||||
_, errHealthCheck := healthCheck(*globalConfiguration)
|
||||
if globalConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||
log.Error("Fail to tick watchdog")
|
||||
}
|
||||
} else {
|
||||
log.Error(errHealthCheck)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
svr.Wait()
|
||||
log.Info("Shutting down")
|
||||
logrus.Exit(0)
|
||||
}
|
||||
|
||||
// CreateKvSource creates KvSource
|
||||
// TLS support is enable for Consul and Etcd backends
|
||||
func CreateKvSource(traefikConfiguration *TraefikConfiguration) (*staert.KvSource, error) {
|
||||
var kv *staert.KvSource
|
||||
var kvStore store.Store
|
||||
var err error
|
||||
func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
// configure default log flags
|
||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||
|
||||
switch {
|
||||
case traefikConfiguration.Consul != nil:
|
||||
kvStore, err = traefikConfiguration.Consul.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Consul.Prefix,
|
||||
if globalConfiguration.Debug {
|
||||
globalConfiguration.LogLevel = "DEBUG"
|
||||
}
|
||||
|
||||
// configure log level
|
||||
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
||||
if err != nil {
|
||||
log.Error("Error getting level", err)
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
// configure log output file
|
||||
logFile := globalConfiguration.TraefikLogsFile
|
||||
if len(logFile) > 0 {
|
||||
log.Warn("top-level traefikLogsFile has been deprecated -- please use traefiklog.filepath")
|
||||
}
|
||||
if globalConfiguration.TraefikLog != nil && len(globalConfiguration.TraefikLog.FilePath) > 0 {
|
||||
logFile = globalConfiguration.TraefikLog.FilePath
|
||||
}
|
||||
|
||||
// configure log format
|
||||
var formatter logrus.Formatter
|
||||
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" {
|
||||
formatter = &logrus.JSONFormatter{}
|
||||
} else {
|
||||
disableColors := false
|
||||
if len(logFile) > 0 {
|
||||
disableColors = true
|
||||
}
|
||||
case traefikConfiguration.Etcd != nil:
|
||||
kvStore, err = traefikConfiguration.Etcd.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Etcd.Prefix,
|
||||
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
|
||||
}
|
||||
log.SetFormatter(formatter)
|
||||
|
||||
if len(logFile) > 0 {
|
||||
dir := filepath.Dir(logFile)
|
||||
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
case traefikConfiguration.Zookeeper != nil:
|
||||
kvStore, err = traefikConfiguration.Zookeeper.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Zookeeper.Prefix,
|
||||
}
|
||||
case traefikConfiguration.Boltdb != nil:
|
||||
kvStore, err = traefikConfiguration.Boltdb.CreateStore()
|
||||
kv = &staert.KvSource{
|
||||
Store: kvStore,
|
||||
Prefix: traefikConfiguration.Boltdb.Prefix,
|
||||
|
||||
err = log.OpenFile(logFile)
|
||||
logrus.RegisterExitHandler(func() {
|
||||
if err := log.CloseFile(); err != nil {
|
||||
log.Error("Error closing log", err)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Error opening file", err)
|
||||
}
|
||||
}
|
||||
return kv, err
|
||||
}
|
||||
|
||||
func checkNewVersion() {
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
time.Sleep(10 * time.Minute)
|
||||
version.CheckNewVersion()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
version.CheckNewVersion()
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
version.CheckNewVersion()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func stats(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
if globalConfiguration.SendAnonymousUsage {
|
||||
log.Info(`
|
||||
Stats collection is enabled.
|
||||
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
|
||||
Help us improve Traefik by leaving this feature on :)
|
||||
More details on: https://docs.traefik.io/basic/#collected-data
|
||||
`)
|
||||
collect(globalConfiguration)
|
||||
} else {
|
||||
log.Info(`
|
||||
Stats collection is disabled.
|
||||
Help us improve Traefik by turning this feature on :)
|
||||
More details on: https://docs.traefik.io/basic/#collected-data
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
func collect(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
if err := collector.Collect(globalConfiguration); err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
79
collector/collector.go
Normal file
79
collector/collector.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
// collectorURL URL where the stats are send
|
||||
const collectorURL = "https://collect.traefik.io/619df80498b60f985d766ce62f912b7c"
|
||||
|
||||
// Collected data
|
||||
type data struct {
|
||||
Version string
|
||||
Codename string
|
||||
BuildDate string
|
||||
Configuration string
|
||||
Hash string
|
||||
}
|
||||
|
||||
// Collect anonymous data.
|
||||
func Collect(globalConfiguration *configuration.GlobalConfiguration) error {
|
||||
anonConfig, err := anonymize.Do(globalConfiguration, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig)
|
||||
|
||||
hashConf, err := hashstructure.Hash(globalConfiguration, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := &data{
|
||||
Version: version.Version,
|
||||
Codename: version.Codename,
|
||||
BuildDate: version.BuildDate,
|
||||
Hash: strconv.FormatUint(hashConf, 10),
|
||||
Configuration: base64.StdEncoding.EncodeToString([]byte(anonConfig)),
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = json.NewEncoder(buf).Encode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = makeHTTPClient().Post(collectorURL, "application/json; charset=utf-8", buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func makeHTTPClient() *http.Client {
|
||||
dialer := &net.Dialer{
|
||||
Timeout: configuration.DefaultDialTimeout,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: dialer.DialContext,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
return &http.Client{Transport: transport}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik-extra-service-fabric"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/ping"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
@@ -23,12 +23,16 @@ import (
|
||||
"github.com/containous/traefik/provider/marathon"
|
||||
"github.com/containous/traefik/provider/mesos"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/provider/web"
|
||||
"github.com/containous/traefik/provider/rest"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultInternalEntryPointName the name of the default internal entry point
|
||||
DefaultInternalEntryPointName = "traefik"
|
||||
|
||||
// DefaultHealthCheckInterval is the default health check interval.
|
||||
DefaultHealthCheckInterval = 30 * time.Second
|
||||
|
||||
@@ -37,17 +41,24 @@ const (
|
||||
|
||||
// DefaultIdleTimeout before closing an idle connection.
|
||||
DefaultIdleTimeout = 180 * time.Second
|
||||
|
||||
// DefaultGraceTimeout controls how long Traefik serves pending requests
|
||||
// prior to shutting down.
|
||||
DefaultGraceTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||
// It's populated from the traefik configuration file passed as an argument to the binary.
|
||||
type GlobalConfiguration struct {
|
||||
GraceTimeOut flaeg.Duration `short:"g" description:"Duration to give active requests a chance to finish before Traefik stops" export:"true"`
|
||||
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
|
||||
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
|
||||
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
|
||||
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
|
||||
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
|
||||
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
|
||||
TraefikLogsFile string `description:"Traefik logs file. Stdout is used when omitted or empty" export:"true"`
|
||||
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
|
||||
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
|
||||
LogLevel string `short:"l" description:"Log level" export:"true"`
|
||||
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
|
||||
Cluster *types.Cluster `description:"Enable clustering" export:"true"`
|
||||
@@ -58,14 +69,14 @@ type GlobalConfiguration struct {
|
||||
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
|
||||
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated
|
||||
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
|
||||
RootCAs RootCAs `description:"Add cert file for self-signed certificate"`
|
||||
RootCAs tls.RootCAs `description:"Add cert file for self-signed certificate"`
|
||||
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
|
||||
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
||||
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
||||
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"`
|
||||
Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated
|
||||
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
|
||||
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
|
||||
Web *web.Provider `description:"Enable Web backend with default settings" export:"true"`
|
||||
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
|
||||
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
|
||||
ConsulCatalog *consul.CatalogProvider `description:"Enable Consul catalog backend with default settings" export:"true"`
|
||||
@@ -78,6 +89,157 @@ type GlobalConfiguration struct {
|
||||
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
||||
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
|
||||
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
|
||||
ServiceFabric *servicefabric.Provider `description:"Enable Service Fabric backend with default settings" export:"true"`
|
||||
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
|
||||
API *api.Handler `description:"Enable api/dashboard" export:"true"`
|
||||
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||
Ping *ping.Handler `description:"Enable ping" export:"true"`
|
||||
}
|
||||
|
||||
// WebCompatibility is a configuration to handle compatibility with deprecated web provider options
|
||||
type WebCompatibility struct {
|
||||
Address string `description:"Web administration port" export:"true"`
|
||||
CertFile string `description:"SSL certificate" export:"true"`
|
||||
KeyFile string `description:"SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"Root path for dashboard and API" export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
Debug bool `export:"true"`
|
||||
}
|
||||
|
||||
func (gc *GlobalConfiguration) handleWebDeprecation() {
|
||||
if gc.Web != nil {
|
||||
log.Warn("web provider configuration is deprecated, you should use these options : api, rest provider, ping and metrics")
|
||||
|
||||
if gc.API != nil || gc.Metrics != nil || gc.Ping != nil || gc.Rest != nil {
|
||||
log.Warn("web option is ignored if you use it with one of these options : api, rest provider, ping or metrics")
|
||||
return
|
||||
}
|
||||
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{
|
||||
Address: gc.Web.Address,
|
||||
Auth: gc.Web.Auth,
|
||||
}
|
||||
if gc.Web.CertFile != "" {
|
||||
gc.EntryPoints[DefaultInternalEntryPointName].TLS = &tls.TLS{
|
||||
Certificates: []tls.Certificate{
|
||||
{
|
||||
CertFile: tls.FileOrContent(gc.Web.CertFile),
|
||||
KeyFile: tls.FileOrContent(gc.Web.KeyFile),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if gc.API == nil {
|
||||
gc.API = &api.Handler{
|
||||
EntryPoint: DefaultInternalEntryPointName,
|
||||
Statistics: gc.Web.Statistics,
|
||||
Dashboard: true,
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Ping == nil {
|
||||
gc.Ping = &ping.Handler{
|
||||
EntryPoint: DefaultInternalEntryPointName,
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Metrics == nil {
|
||||
gc.Metrics = gc.Web.Metrics
|
||||
}
|
||||
|
||||
if !gc.Debug {
|
||||
gc.Debug = gc.Web.Debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
|
||||
// It also takes care of maintaining backwards compatibility.
|
||||
func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
if len(gc.EntryPoints) == 0 {
|
||||
gc.EntryPoints = map[string]*EntryPoint{"http": {
|
||||
Address: ":80",
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
}}
|
||||
gc.DefaultEntryPoints = []string{"http"}
|
||||
}
|
||||
|
||||
gc.handleWebDeprecation()
|
||||
|
||||
if (gc.API != nil && gc.API.EntryPoint == DefaultInternalEntryPointName) ||
|
||||
(gc.Ping != nil && gc.Ping.EntryPoint == DefaultInternalEntryPointName) ||
|
||||
(gc.Metrics != nil && gc.Metrics.Prometheus != nil && gc.Metrics.Prometheus.EntryPoint == DefaultInternalEntryPointName) ||
|
||||
(gc.Rest != nil && gc.Rest.EntryPoint == DefaultInternalEntryPointName) {
|
||||
if _, ok := gc.EntryPoints[DefaultInternalEntryPointName]; !ok {
|
||||
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{Address: ":8080"}
|
||||
}
|
||||
}
|
||||
|
||||
// ForwardedHeaders must be remove in the next breaking version
|
||||
for entryPointName := range gc.EntryPoints {
|
||||
entryPoint := gc.EntryPoints[entryPointName]
|
||||
if entryPoint.ForwardedHeaders == nil {
|
||||
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||
if gc.LifeCycle == nil {
|
||||
gc.LifeCycle = &LifeCycle{}
|
||||
}
|
||||
|
||||
// Prefer legacy grace timeout parameter for backwards compatibility reasons.
|
||||
if gc.GraceTimeOut > 0 {
|
||||
log.Warn("top-level grace period configuration has been deprecated -- please use lifecycle grace period")
|
||||
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
|
||||
}
|
||||
|
||||
if gc.Rancher != nil {
|
||||
// Ensure backwards compatibility for now
|
||||
if len(gc.Rancher.AccessKey) > 0 ||
|
||||
len(gc.Rancher.Endpoint) > 0 ||
|
||||
len(gc.Rancher.SecretKey) > 0 {
|
||||
|
||||
if gc.Rancher.API == nil {
|
||||
gc.Rancher.API = &rancher.APIConfiguration{
|
||||
AccessKey: gc.Rancher.AccessKey,
|
||||
SecretKey: gc.Rancher.SecretKey,
|
||||
Endpoint: gc.Rancher.Endpoint,
|
||||
}
|
||||
}
|
||||
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
|
||||
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
|
||||
}
|
||||
|
||||
if gc.Rancher.Metadata != nil && len(gc.Rancher.Metadata.Prefix) == 0 {
|
||||
gc.Rancher.Metadata.Prefix = "latest"
|
||||
}
|
||||
}
|
||||
|
||||
if gc.API != nil {
|
||||
gc.API.Debug = gc.Debug
|
||||
}
|
||||
|
||||
if gc.Debug {
|
||||
gc.LogLevel = "DEBUG"
|
||||
}
|
||||
|
||||
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
|
||||
gc.Web.Path += "/"
|
||||
}
|
||||
|
||||
// Try to fallback to traefik config file in case the file provider is enabled
|
||||
// but has no file name configured.
|
||||
if gc.File != nil && len(gc.File.Filename) == 0 {
|
||||
if len(configFile) > 0 {
|
||||
gc.File.Filename = configFile
|
||||
} else {
|
||||
log.Errorln("Error using file configuration backend, no filename defined")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultEntryPoints holds default entry points
|
||||
@@ -118,68 +280,6 @@ func (dep *DefaultEntryPoints) Type() string {
|
||||
return "defaultentrypoints"
|
||||
}
|
||||
|
||||
// RootCAs hold the CA we want to have in root
|
||||
type RootCAs []FileOrContent
|
||||
|
||||
// FileOrContent hold a file path or content
|
||||
type FileOrContent string
|
||||
|
||||
func (f FileOrContent) String() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
func (f FileOrContent) Read() ([]byte, error) {
|
||||
var content []byte
|
||||
if _, err := os.Stat(f.String()); err == nil {
|
||||
content, err = ioutil.ReadFile(f.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
content = []byte(f)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (r *RootCAs) String() string {
|
||||
sliceOfString := make([]string, len([]FileOrContent(*r)))
|
||||
for key, value := range *r {
|
||||
sliceOfString[key] = value.String()
|
||||
}
|
||||
return strings.Join(sliceOfString, ",")
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (r *RootCAs) Set(value string) error {
|
||||
rootCAs := strings.Split(value, ",")
|
||||
if len(rootCAs) == 0 {
|
||||
return fmt.Errorf("bad RootCAs format: %s", value)
|
||||
}
|
||||
for _, rootCA := range rootCAs {
|
||||
*r = append(*r, FileOrContent(rootCA))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (r *RootCAs) Get() interface{} {
|
||||
return RootCAs(*r)
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (r *RootCAs) SetValue(val interface{}) {
|
||||
*r = RootCAs(val.(RootCAs))
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (r *RootCAs) Type() string {
|
||||
return "rootcas"
|
||||
}
|
||||
|
||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoints map[string]*EntryPoint
|
||||
|
||||
@@ -193,79 +293,105 @@ func (ep *EntryPoints) String() string {
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (ep *EntryPoints) Set(value string) error {
|
||||
result, err := parseEntryPointsConfiguration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := parseEntryPointsConfiguration(value)
|
||||
|
||||
var configTLS *TLS
|
||||
if len(result["TLS"]) > 0 {
|
||||
certs := Certificates{}
|
||||
if err := certs.Set(result["TLS"]); err != nil {
|
||||
var configTLS *tls.TLS
|
||||
if len(result["tls"]) > 0 {
|
||||
certs := tls.Certificates{}
|
||||
if err := certs.Set(result["tls"]); err != nil {
|
||||
return err
|
||||
}
|
||||
configTLS = &TLS{
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: certs,
|
||||
}
|
||||
} else if len(result["TLSACME"]) > 0 {
|
||||
configTLS = &TLS{
|
||||
Certificates: Certificates{},
|
||||
} else if len(result["tls_acme"]) > 0 {
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: tls.Certificates{},
|
||||
}
|
||||
}
|
||||
if len(result["CA"]) > 0 {
|
||||
files := strings.Split(result["CA"], ",")
|
||||
configTLS.ClientCAFiles = files
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
Optional: optional,
|
||||
}
|
||||
}
|
||||
var redirect *Redirect
|
||||
if len(result["RedirectEntryPoint"]) > 0 || len(result["RedirectRegex"]) > 0 || len(result["RedirectReplacement"]) > 0 {
|
||||
redirect = &Redirect{
|
||||
EntryPoint: result["RedirectEntryPoint"],
|
||||
Regex: result["RedirectRegex"],
|
||||
Replacement: result["RedirectReplacement"],
|
||||
var redirect *types.Redirect
|
||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||
redirect = &types.Redirect{
|
||||
EntryPoint: result["redirect_entrypoint"],
|
||||
Regex: result["redirect_regex"],
|
||||
Replacement: result["redirect_replacement"],
|
||||
}
|
||||
}
|
||||
|
||||
whiteListSourceRange := []string{}
|
||||
if len(result["WhiteListSourceRange"]) > 0 {
|
||||
whiteListSourceRange = strings.Split(result["WhiteListSourceRange"], ",")
|
||||
if len(result["whitelistsourcerange"]) > 0 {
|
||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
||||
}
|
||||
|
||||
compress := toBool(result, "Compress")
|
||||
compress := toBool(result, "compress")
|
||||
|
||||
var proxyProtocol *ProxyProtocol
|
||||
if len(result["ProxyProtocol"]) > 0 {
|
||||
trustedIPs := strings.Split(result["ProxyProtocol"], ",")
|
||||
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol = &ProxyProtocol{
|
||||
TrustedIPs: trustedIPs,
|
||||
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||
}
|
||||
if len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
(*ep)[result["Name"]] = &EntryPoint{
|
||||
Address: result["Address"],
|
||||
// TODO must be changed to false by default in the next breaking version.
|
||||
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
||||
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
}
|
||||
|
||||
fhTrustedIPs := result["forwardedheaders_trustedips"]
|
||||
if len(fhTrustedIPs) > 0 {
|
||||
// TODO must be removed in the next breaking version.
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
|
||||
}
|
||||
|
||||
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||
}
|
||||
|
||||
(*ep)[result["name"]] = &EntryPoint{
|
||||
Address: result["address"],
|
||||
TLS: configTLS,
|
||||
Redirect: redirect,
|
||||
Compress: compress,
|
||||
WhitelistSourceRange: whiteListSourceRange,
|
||||
ProxyProtocol: proxyProtocol,
|
||||
ForwardedHeaders: forwardedHeaders,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEntryPointsConfiguration(value string) (map[string]string, error) {
|
||||
regex := regexp.MustCompile(`(?:Name:(?P<Name>\S*))\s*(?:Address:(?P<Address>\S*))?\s*(?:TLS:(?P<TLS>\S*))?\s*(?P<TLSACME>TLS)?\s*(?:CA:(?P<CA>\S*))?\s*(?:Redirect\.EntryPoint:(?P<RedirectEntryPoint>\S*))?\s*(?:Redirect\.Regex:(?P<RedirectRegex>\S*))?\s*(?:Redirect\.Replacement:(?P<RedirectReplacement>\S*))?\s*(?:Compress:(?P<Compress>\S*))?\s*(?:WhiteListSourceRange:(?P<WhiteListSourceRange>\S*))?\s*(?:ProxyProtocol\.TrustedIPs:(?P<ProxyProtocol>\S*))?`)
|
||||
match := regex.FindAllStringSubmatch(value, -1)
|
||||
if match == nil {
|
||||
return nil, fmt.Errorf("bad EntryPoints format: %s", value)
|
||||
}
|
||||
matchResult := match[0]
|
||||
result := make(map[string]string)
|
||||
for i, name := range regex.SubexpNames() {
|
||||
if i != 0 && len(matchResult[i]) != 0 {
|
||||
result[name] = matchResult[i]
|
||||
func parseEntryPointsConfiguration(raw string) map[string]string {
|
||||
sections := strings.Fields(raw)
|
||||
|
||||
config := make(map[string]string)
|
||||
for _, part := range sections {
|
||||
field := strings.SplitN(part, ":", 2)
|
||||
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
||||
if len(field) > 1 {
|
||||
config[name] = field[1]
|
||||
} else {
|
||||
if strings.EqualFold(name, "TLS") {
|
||||
config["tls_acme"] = "TLS"
|
||||
} else {
|
||||
config[name] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
return config
|
||||
}
|
||||
|
||||
func toBool(conf map[string]string, key string) bool {
|
||||
@@ -296,136 +422,13 @@ func (ep *EntryPoints) Type() string {
|
||||
type EntryPoint struct {
|
||||
Network string
|
||||
Address string
|
||||
TLS *TLS `export:"true"`
|
||||
Redirect *Redirect `export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
TLS *tls.TLS `export:"true"`
|
||||
Redirect *types.Redirect `export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
WhitelistSourceRange []string
|
||||
Compress bool `export:"true"`
|
||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||
}
|
||||
|
||||
// Redirect configures a redirection of an entry point to another, or to an URL
|
||||
type Redirect struct {
|
||||
EntryPoint string
|
||||
Regex string
|
||||
Replacement string
|
||||
}
|
||||
|
||||
// TLS configures TLS for an entry point
|
||||
type TLS struct {
|
||||
MinVersion string `export:"true"`
|
||||
CipherSuites []string
|
||||
Certificates Certificates
|
||||
ClientCAFiles []string
|
||||
}
|
||||
|
||||
// MinVersion Map of allowed TLS minimum versions
|
||||
var MinVersion = map[string]uint16{
|
||||
`VersionTLS10`: tls.VersionTLS10,
|
||||
`VersionTLS11`: tls.VersionTLS11,
|
||||
`VersionTLS12`: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
// CipherSuites Map of TLS CipherSuites from crypto/tls
|
||||
// Available CipherSuites defined at https://golang.org/pkg/crypto/tls/#pkg-constants
|
||||
var CipherSuites = map[string]uint16{
|
||||
`TLS_RSA_WITH_RC4_128_SHA`: tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
`TLS_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
`TLS_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
`TLS_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
`TLS_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
`TLS_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
`TLS_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
`TLS_ECDHE_RSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
`TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
|
||||
// Certificates defines traefik certificates type
|
||||
// Certs and Keys could be either a file path, or the file content itself
|
||||
type Certificates []Certificate
|
||||
|
||||
//CreateTLSConfig creates a TLS config from Certificate structures
|
||||
func (certs *Certificates) CreateTLSConfig() (*tls.Config, error) {
|
||||
config := &tls.Config{}
|
||||
config.Certificates = []tls.Certificate{}
|
||||
certsSlice := []Certificate(*certs)
|
||||
for _, v := range certsSlice {
|
||||
var err error
|
||||
|
||||
certContent, err := v.CertFile.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyContent, err := v.KeyFile.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(certContent, keyContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Certificates = append(config.Certificates, cert)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (certs *Certificates) String() string {
|
||||
if len(*certs) == 0 {
|
||||
return ""
|
||||
}
|
||||
var result []string
|
||||
for _, certificate := range *certs {
|
||||
result = append(result, certificate.CertFile.String()+","+certificate.KeyFile.String())
|
||||
}
|
||||
return strings.Join(result, ";")
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (certs *Certificates) Set(value string) error {
|
||||
certificates := strings.Split(value, ";")
|
||||
for _, certificate := range certificates {
|
||||
files := strings.Split(certificate, ",")
|
||||
if len(files) != 2 {
|
||||
return fmt.Errorf("bad certificates format: %s", value)
|
||||
}
|
||||
*certs = append(*certs, Certificate{
|
||||
CertFile: FileOrContent(files[0]),
|
||||
KeyFile: FileOrContent(files[1]),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (certs *Certificates) Type() string {
|
||||
return "certificates"
|
||||
}
|
||||
|
||||
// Certificate holds a SSL cert/key pair
|
||||
// Certs and Key could be either a file path, or the file content itself
|
||||
type Certificate struct {
|
||||
CertFile FileOrContent
|
||||
KeyFile FileOrContent
|
||||
Compress bool `export:"true"`
|
||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||
}
|
||||
|
||||
// Retry contains request retry config
|
||||
@@ -453,5 +456,19 @@ type ForwardingTimeouts struct {
|
||||
|
||||
// ProxyProtocol contains Proxy-Protocol configuration
|
||||
type ProxyProtocol struct {
|
||||
Insecure bool
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// ForwardedHeaders Trust client forwarding headers
|
||||
type ForwardedHeaders struct {
|
||||
Insecure bool
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// LifeCycle contains configurations relevant to the lifecycle (such as the
|
||||
// shutdown phase) of Traefik.
|
||||
type LifeCycle struct {
|
||||
RequestAcceptGraceTimeout flaeg.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure"`
|
||||
GraceTimeOut flaeg.Duration `description:"Duration to give active requests a chance to finish before Traefik stops"`
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const defaultConfigFile = "traefik.toml"
|
||||
|
||||
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -16,36 +23,37 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "all parameters",
|
||||
value: "Name:foo Address:bar TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1",
|
||||
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:false Address::8000",
|
||||
expectedResult: map[string]string{
|
||||
"Name": "foo",
|
||||
"Address": "bar",
|
||||
"CA": "car",
|
||||
"TLS": "goo",
|
||||
"TLSACME": "TLS",
|
||||
"RedirectEntryPoint": "RedirectEntryPoint",
|
||||
"RedirectRegex": "RedirectRegex",
|
||||
"RedirectReplacement": "RedirectReplacement",
|
||||
"WhiteListSourceRange": "WhiteListSourceRange",
|
||||
"ProxyProtocol": "192.168.0.1",
|
||||
"Compress": "true",
|
||||
"name": "foo",
|
||||
"address": ":8000",
|
||||
"ca": "car",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
"redirect_entrypoint": "RedirectEntryPoint",
|
||||
"redirect_regex": "RedirectRegex",
|
||||
"redirect_replacement": "RedirectReplacement",
|
||||
"whitelistsourcerange": "WhiteListSourceRange",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"proxyprotocol_insecure": "false",
|
||||
"compress": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
value: "Name:foo Compress:on",
|
||||
value: "name:foo Compress:on",
|
||||
expectedResult: map[string]string{
|
||||
"Name": "foo",
|
||||
"Compress": "on",
|
||||
"name": "foo",
|
||||
"compress": "on",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TLS",
|
||||
value: "Name:foo TLS:goo TLS",
|
||||
expectedResult: map[string]string{
|
||||
"Name": "foo",
|
||||
"TLS": "goo",
|
||||
"TLSACME": "TLS",
|
||||
"name": "foo",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -55,14 +63,7 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf, err := parseEntryPointsConfiguration(test.value)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for key, value := range conf {
|
||||
fmt.Println(key, value)
|
||||
}
|
||||
conf := parseEntryPointsConfiguration(test.value)
|
||||
|
||||
assert.Len(t, conf, len(test.expectedResult))
|
||||
assert.Equal(t, test.expectedResult, conf)
|
||||
@@ -133,12 +134,12 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
expectedEntryPoint *EntryPoint
|
||||
}{
|
||||
{
|
||||
name: "all parameters",
|
||||
expression: "Name:foo Address:bar TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1",
|
||||
name: "all parameters camelcase",
|
||||
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car CA.Optional:false Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1 ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: "bar",
|
||||
Redirect: &Redirect{
|
||||
Address: ":8000",
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "RedirectEntryPoint",
|
||||
Regex: "RedirectRegex",
|
||||
Replacement: "RedirectReplacement",
|
||||
@@ -147,18 +148,127 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
WhitelistSourceRange: []string{"Range"},
|
||||
TLS: &TLS{
|
||||
ClientCAFiles: []string{"car"},
|
||||
Certificates: Certificates{
|
||||
TLS: &tls.TLS{
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: false,
|
||||
},
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: FileOrContent("goo"),
|
||||
KeyFile: FileOrContent("gii"),
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all parameters lowercase",
|
||||
expression: "name:foo address::8000 tls:goo,gii tls ca:car ca.optional:true redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.trustedIPs:192.168.0.1 forwardedHeaders.trustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "RedirectEntryPoint",
|
||||
Regex: "RedirectRegex",
|
||||
Replacement: "RedirectReplacement",
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
WhitelistSourceRange: []string{"Range"},
|
||||
TLS: &tls.TLS{
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
expression: "Name:foo",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure true",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure false",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders TrustedIPs",
|
||||
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure true",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure false",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol TrustedIPs",
|
||||
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
expression: "Name:foo Compress:on",
|
||||
@@ -166,6 +276,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -175,6 +286,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -193,3 +305,89 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
legacyGraceTimeout time.Duration
|
||||
lifeCycleGraceTimeout time.Duration
|
||||
wantGraceTimeout time.Duration
|
||||
}{
|
||||
{
|
||||
desc: "legacy grace timeout given only",
|
||||
legacyGraceTimeout: 5 * time.Second,
|
||||
wantGraceTimeout: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
desc: "legacy and life cycle grace timeouts given",
|
||||
legacyGraceTimeout: 5 * time.Second,
|
||||
lifeCycleGraceTimeout: 12 * time.Second,
|
||||
wantGraceTimeout: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
desc: "legacy grace timeout omitted",
|
||||
legacyGraceTimeout: 0,
|
||||
lifeCycleGraceTimeout: 12 * time.Second,
|
||||
wantGraceTimeout: 12 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
gc := &GlobalConfiguration{
|
||||
GraceTimeOut: flaeg.Duration(test.legacyGraceTimeout),
|
||||
}
|
||||
if test.lifeCycleGraceTimeout > 0 {
|
||||
gc.LifeCycle = &LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(test.lifeCycleGraceTimeout),
|
||||
}
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
gotGraceTimeout := time.Duration(gc.LifeCycle.GraceTimeOut)
|
||||
if gotGraceTimeout != test.wantGraceTimeout {
|
||||
t.Fatalf("got effective grace timeout %d, want %d", gotGraceTimeout, test.wantGraceTimeout)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
}{
|
||||
{
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
gc := &GlobalConfiguration{
|
||||
File: test.fileProvider,
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
gotFileProviderFilename := gc.File.Filename
|
||||
if gotFileProviderFilename != test.wantFileProviderFilename {
|
||||
t.Fatalf("got file provider file name %q, want %q", gotFileProviderFilename, test.wantFileProviderFilename)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,18 @@ set -o nounset
|
||||
|
||||
USAGE="$(basename "$0") <path to acme> <destination cert directory>"
|
||||
|
||||
# Platform variations
|
||||
case "$(uname)" in
|
||||
'Linux')
|
||||
# On Linux, -d should always work. --decode does not work with Alpine's busybox-binary
|
||||
CMD_DECODE_BASE64="base64 -d"
|
||||
;;
|
||||
*)
|
||||
# Max OS-X supports --decode and -D, but --decode may be supported by other platforms as well.
|
||||
CMD_DECODE_BASE64="base64 --decode"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Allow us to exit on a missing jq binary
|
||||
exit_jq() {
|
||||
echo "
|
||||
@@ -130,9 +142,11 @@ trap 'umask ${oldumask}' EXIT
|
||||
#
|
||||
# and sed:
|
||||
# echo "-----BEGIN RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
|
||||
# echo ${priv} | sed 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
|
||||
# echo "-----END RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
|
||||
#
|
||||
# echo ${priv} | sed -E 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
|
||||
# sed -i '$ d' "${pdir}/letsencrypt.key"
|
||||
# echo "-----END RSA PRIVATE KEY-----" >> "${pdir}/letsencrypt.key"
|
||||
# openssl rsa -noout -in "${pdir}/letsencrypt.key" -check # To check if the key is valid
|
||||
|
||||
# In the end, openssl was chosen because most users will need this script
|
||||
# *because* of openssl combined with the fact that it will refuse to write the
|
||||
# key if it does not parse out correctly. The other mechanisms were left as
|
||||
@@ -141,11 +155,16 @@ echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----
|
||||
| openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
|
||||
|
||||
# Process the certificates for each of the domains in acme.json
|
||||
for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' acme.json); do
|
||||
for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' ${acmefile}); do
|
||||
# Traefik stores a cert bundle for each domain. Within this cert
|
||||
# bundle there is both proper the certificate and the Let's Encrypt CA
|
||||
echo "Extracting cert bundle for ${domain}"
|
||||
cert=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate |
|
||||
select (.Domain == $domain )| .Certificate' ${acmefile}) || bad_acme
|
||||
echo "${cert}" | base64 --decode > "${cdir}/${domain}.pem"
|
||||
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
|
||||
|
||||
echo "Extracting private key for ${domain}"
|
||||
key=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate |
|
||||
select (.Domain == $domain )| .PrivateKey' ${acmefile}) || bad_acme
|
||||
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
|
||||
done
|
||||
|
||||
11
docs.Dockerfile
Normal file
11
docs.Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM alpine
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
|
||||
|
||||
COPY requirements.txt /mkdocs/
|
||||
WORKDIR /mkdocs
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add py-pip \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& pip install --user -r requirements.txt
|
||||
23
docs/archive.md
Normal file
23
docs/archive.md
Normal file
@@ -0,0 +1,23 @@
|
||||
## Current versions documentation
|
||||
|
||||
- [Latest stable](https://docs.traefik.io)
|
||||
|
||||
## Future version documentation
|
||||
|
||||
- [Experimental](https://master--traefik-docs.netlify.com/)
|
||||
|
||||
## Previous versions documentation
|
||||
|
||||
- [v1.5 aka Cancoillotte](http://v1-5.archive.docs.traefik.io/)
|
||||
|
||||
- [v1.4 aka Roquefort](http://v1-4.archive.docs.traefik.io/)
|
||||
|
||||
- [v1.3 aka Raclette](http://v1-3.archive.docs.traefik.io/)
|
||||
|
||||
- [v1.2 aka Morbier](http://v1-2.archive.docs.traefik.io/)
|
||||
|
||||
- [v1.1 aka Camembert](http://v1-1.archive.docs.traefik.io/)
|
||||
|
||||
## More
|
||||
|
||||
[Change log](https://github.com/containous/traefik/blob/master/CHANGELOG.md)
|
||||
189
docs/basics.md
189
docs/basics.md
@@ -62,10 +62,13 @@ And here is another example with client certificate authentication:
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
clientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.key"
|
||||
[entryPoints.https.tls]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
optional = false
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.key"
|
||||
```
|
||||
|
||||
- We enable SSL on `https` by giving a certificate and a key.
|
||||
@@ -86,6 +89,7 @@ Following is the list of existing modifier rules:
|
||||
|
||||
- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
|
||||
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.
|
||||
- `ReplacePathRegex: ^/api/v2/(.*) /api/$1`: Replaces the path with a regular expression and adds the old path to the `X-Replaced-Path` header. Separate the regular expression and the replacement by a space.
|
||||
|
||||
#### Matchers
|
||||
|
||||
@@ -257,6 +261,11 @@ Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
|
||||
Custom headers can be configured through the frontends, to add headers to either requests or responses that match the frontend's rules.
|
||||
This allows for setting headers such as `X-Script-Name` to be added to the request, or custom headers to be added to the response.
|
||||
|
||||
!!! warning
|
||||
If the custom header name is the same as one header name of the request or response, it will be replaced.
|
||||
|
||||
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, and the `X-Custom-Response-Header` added to the response.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
@@ -269,7 +278,20 @@ This allows for setting headers such as `X-Script-Name` to be added to the reque
|
||||
rule = "PathPrefixStrip:/cheese"
|
||||
```
|
||||
|
||||
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, and the `X-Custom-Response-Header` added to the response.
|
||||
In this second example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, the `X-Custom-Request-Header` removed to the request and the `X-Custom-Response-Header` removed to the response.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.headers.customresponseheaders]
|
||||
X-Custom-Response-Header = ""
|
||||
[frontends.frontend1.headers.customrequestheaders]
|
||||
X-Script-Name = "test"
|
||||
X-Custom-Request-Header = ""
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefixStrip:/cheese"
|
||||
```
|
||||
|
||||
#### Security headers
|
||||
|
||||
@@ -305,7 +327,7 @@ A backend is responsible to load-balance the traffic coming from one or more fro
|
||||
|
||||
Various methods of load-balancing are supported:
|
||||
|
||||
- `wrr`: Weighted Round Robin
|
||||
- `wrr`: Weighted Round Robin.
|
||||
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
|
||||
It also rolls back to original weights if the servers have changed.
|
||||
|
||||
@@ -322,16 +344,13 @@ It can be configured using:
|
||||
|
||||
For example:
|
||||
|
||||
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend
|
||||
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
|
||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
|
||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
|
||||
|
||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
|
||||
also be applied to each backend.
|
||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
|
||||
|
||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
|
||||
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
|
||||
evaluate the maximum connections.
|
||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
@@ -354,21 +373,19 @@ The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
|
||||
On subsequent requests, the client will be directed to the backend stored in the cookie if it is still healthy.
|
||||
If not, a new backend will be assigned.
|
||||
|
||||
To activate sticky session:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
# Enable sticky session
|
||||
[backends.backend1.loadbalancer.stickiness]
|
||||
```
|
||||
|
||||
To customize the cookie name:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.loadbalancer.stickiness]
|
||||
cookieName = "my_cookie"
|
||||
# Customize the cookie name
|
||||
#
|
||||
# Optional
|
||||
# Default: a sha1 (6 chars)
|
||||
#
|
||||
# cookieName = "my_cookie"
|
||||
```
|
||||
|
||||
The deprecated way:
|
||||
@@ -450,8 +467,8 @@ Here is an example of backends and servers definition:
|
||||
|
||||
Træfik's configuration has two parts:
|
||||
|
||||
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
|
||||
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
|
||||
- The [static Træfik configuration](/basics#static-trfik-configuration) which is loaded only at the beginning.
|
||||
- The [dynamic Træfik configuration](/basics#dynamic-trfik-configuration) which can be hot-reloaded (no need to restart the process).
|
||||
|
||||
### Static Træfik configuration
|
||||
|
||||
@@ -515,6 +532,7 @@ The dynamic configuration concerns :
|
||||
- [Frontends](/basics/#frontends)
|
||||
- [Backends](/basics/#backends)
|
||||
- [Servers](/basics/#servers)
|
||||
- HTTPS Certificates
|
||||
|
||||
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/configuration/commons).
|
||||
|
||||
@@ -535,7 +553,7 @@ traefik [command] [--flag=flag_argument]
|
||||
List of Træfik available commands with description :
|
||||
|
||||
- `version` : Print version
|
||||
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
|
||||
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
||||
- `bug`: The easiest way to submit a pre-filled issue.
|
||||
- `healthcheck`: Calls Traefik `/ping` to check health.
|
||||
|
||||
@@ -570,7 +588,7 @@ This command allows to check the health of Traefik. Its exit status is `0` if Tr
|
||||
This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism.
|
||||
|
||||
!!! note
|
||||
The [`web` provider](/configuration/backends/web) must be enabled to allow `/ping` calls by the `healthcheck` command.
|
||||
The [`ping`](/configuration/ping) must be enabled to allow the `healthcheck` command to call `/ping`.
|
||||
|
||||
```bash
|
||||
traefik healthcheck
|
||||
@@ -578,3 +596,122 @@ traefik healthcheck
|
||||
```bash
|
||||
OK: http://:8082/ping
|
||||
```
|
||||
|
||||
|
||||
## Collected Data
|
||||
|
||||
**This feature is disabled by default.**
|
||||
|
||||
You can read the public proposal on this topic [here](https://github.com/containous/traefik/issues/2369).
|
||||
|
||||
### Why ?
|
||||
|
||||
In order to help us learn more about how Træfik is being used and improve it, we collect anonymous usage statistics from running instances.
|
||||
Those data help us prioritize our developments and focus on what's more important (for example, which configuration backend is used and which is not used).
|
||||
|
||||
### 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:
|
||||
- token, user name, password, URL, IP, domain, email, etc, are removed
|
||||
|
||||
!!! note
|
||||
We do not collect the dynamic configuration (frontends & backends).
|
||||
|
||||
!!! note
|
||||
We do not collect data behind the scenes to run advertising programs or to sell such data to third-party.
|
||||
|
||||
#### Here is an example
|
||||
|
||||
- Source configuration:
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[Docker]
|
||||
endpoint = "tcp://10.10.10.10:2375"
|
||||
domain = "foo.bir"
|
||||
exposedByDefault = true
|
||||
swarmMode = true
|
||||
|
||||
[Docker.TLS]
|
||||
CA = "dockerCA"
|
||||
Cert = "dockerCert"
|
||||
Key = "dockerKey"
|
||||
InsecureSkipVerify = true
|
||||
|
||||
[ECS]
|
||||
Domain = "foo.bar"
|
||||
ExposedByDefault = true
|
||||
Clusters = ["foo-bar"]
|
||||
Region = "us-west-2"
|
||||
AccessKeyID = "AccessKeyID"
|
||||
SecretAccessKey = "SecretAccessKey"
|
||||
```
|
||||
|
||||
- Obfuscated and anonymous configuration:
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[Docker]
|
||||
Endpoint = "xxxx"
|
||||
Domain = "xxxx"
|
||||
ExposedByDefault = true
|
||||
SwarmMode = true
|
||||
|
||||
[Docker.TLS]
|
||||
CA = "xxxx"
|
||||
Cert = "xxxx"
|
||||
Key = "xxxx"
|
||||
InsecureSkipVerify = false
|
||||
|
||||
[ECS]
|
||||
Domain = "xxxx"
|
||||
ExposedByDefault = true
|
||||
Clusters = []
|
||||
Region = "us-west-2"
|
||||
AccessKeyID = "xxxx"
|
||||
SecretAccessKey = "xxxx"
|
||||
```
|
||||
|
||||
### Show me the code !
|
||||
|
||||
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/containous/traefik/blob/master/collector/collector.go)
|
||||
|
||||
By default we anonymize all configuration fields, except fields tagged with `export=true`.
|
||||
|
||||
You can check all fields in the [godoc](https://godoc.org/github.com/containous/traefik/configuration#GlobalConfiguration).
|
||||
|
||||
### How to enable this ?
|
||||
|
||||
You can enable the collecting system by:
|
||||
|
||||
- adding this line in the configuration TOML file:
|
||||
|
||||
```toml
|
||||
# Send anonymous usage data
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
sendAnonymousUsage = true
|
||||
```
|
||||
|
||||
- adding this flag in the CLI:
|
||||
|
||||
```bash
|
||||
./traefik --sendAnonymousUsage=true
|
||||
```
|
||||
|
||||
@@ -20,6 +20,12 @@ See also [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) an
|
||||
#
|
||||
email = "test@traefik.io"
|
||||
|
||||
# File used for certificates storage.
|
||||
#
|
||||
# Optional (Deprecated)
|
||||
#
|
||||
#storageFile = "acme.json"
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
@@ -55,7 +61,7 @@ entryPoint = "https"
|
||||
#
|
||||
# acmeLogging = true
|
||||
|
||||
# Enable on demand certificate.
|
||||
# Enable on demand certificate. (Deprecated)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
@@ -89,6 +95,10 @@ entryPoint = "https"
|
||||
# main = "local4.com"
|
||||
```
|
||||
|
||||
!!! note
|
||||
ACME entryPoint has to be relied to the port 443, otherwise ACME Challenges can not be done.
|
||||
It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
### `storage`
|
||||
|
||||
```toml
|
||||
@@ -100,7 +110,7 @@ storage = "acme.json"
|
||||
|
||||
File or key used for certificates storage.
|
||||
|
||||
**WARNING** If you use Traefik in Docker, you have 2 options:
|
||||
**WARNING** If you use Træfik in Docker, you have 2 options:
|
||||
|
||||
- create a file on your host and mount it as a volume:
|
||||
```toml
|
||||
@@ -118,6 +128,14 @@ storage = "/etc/traefik/acme/acme.json"
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! note
|
||||
`storage` replaces `storageFile` which is deprecated.
|
||||
|
||||
!!! note
|
||||
During Træfik configuration migration from a configuration file to a KV store (thanks to `storeconfig` subcommand as described [here](/user-guide/kv-config/#store-configuration-in-key-value-store)), if ACME certificates have to be migrated too, use both `storageFile` and `storage`.
|
||||
`storageFile` will contain the path to the `acme.json` file to migrate.
|
||||
`storage` will contain the key where the certificates will be stored.
|
||||
|
||||
### `dnsProvider`
|
||||
|
||||
```toml
|
||||
@@ -129,25 +147,33 @@ dnsProvider = "digitalocean"
|
||||
|
||||
Use a DNS based acme challenge rather than external HTTPS access, e.g. for a firewalled server.
|
||||
|
||||
Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables with access keys to enable setting it:
|
||||
Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it:
|
||||
|
||||
| Provider | Configuration |
|
||||
|----------------------------------------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| [Cloudflare](https://www.cloudflare.com) | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `DO_AUTH_TOKEN` |
|
||||
| [DNSimple](https://dnsimple.com) | `DNSIMPLE_EMAIL`, `DNSIMPLE_OAUTH_TOKEN` |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET` |
|
||||
| [Exoscale](https://www.exoscale.ch) | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET` |
|
||||
| [Gandi](https://www.gandi.net) | `GANDI_API_KEY` |
|
||||
| [Linode](https://www.linode.com) | `LINODE_API_KEY` |
|
||||
| manual | none, but run Traefik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| [Namecheap](https://www.namecheap.com) | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` |
|
||||
| RFC2136 | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, or configured user/instance IAM profile. |
|
||||
| [dyn](https://dyn.com) | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` |
|
||||
| [VULTR](https://www.vultr.com) | `VULTR_API_KEY` |
|
||||
| [OVH](https://www.ovh.com) | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` |
|
||||
| [pdns](https://www.powerdns.com) | `PDNS_API_KEY`, `PDNS_API_URL` |
|
||||
| Provider Name | Provider code | Configuration |
|
||||
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` |
|
||||
| manual | - | none, but run Træfik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_HOSTED_ZONE_ID` or configured user/instance IAM profile. |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` |
|
||||
|
||||
### `delayDontCheckDNS`
|
||||
|
||||
@@ -163,7 +189,7 @@ If `delayDontCheckDNS` is greater than zero, avoid this & instead just wait so m
|
||||
|
||||
Useful if internal networks block external DNS queries.
|
||||
|
||||
### `onDemand`
|
||||
### `onDemand` (Deprecated)
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
@@ -180,7 +206,10 @@ This will request a certificate from Let's Encrypt during the first TLS handshak
|
||||
TLS handshakes will be slow when requesting a hostname 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)
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
!!! warning
|
||||
This option is deprecated.
|
||||
|
||||
### `onHostRule`
|
||||
|
||||
@@ -230,7 +259,7 @@ main = "local4.com"
|
||||
```
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
All domains must have A/AAAA records pointing to Traefik.
|
||||
All domains must have A/AAAA records pointing to Træfik.
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
206
docs/configuration/api.md
Normal file
206
docs/configuration/api.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# API Definition
|
||||
|
||||
```toml
|
||||
# API definition
|
||||
[api]
|
||||
# Name of the related entry point
|
||||
#
|
||||
# Optional
|
||||
# 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.
|
||||
# Additionally, the log level will be set to DEBUG.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
debug = true
|
||||
```
|
||||
|
||||
## Web UI
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## API
|
||||
|
||||
| Path | Method | Description |
|
||||
|-----------------------------------------------------------------|------------------|-------------------------------------------|
|
||||
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
|
||||
| `/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}/backends` | `GET` | List backends |
|
||||
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend |
|
||||
| `/api/providers/{provider}/backends/{backend}/servers` | `GET` | List servers in backend |
|
||||
| `/api/providers/{provider}/backends/{backend}/servers/{server}` | `GET` | Get a server in a backend |
|
||||
| `/api/providers/{provider}/frontends` | `GET` | List frontends |
|
||||
| `/api/providers/{provider}/frontends/{frontend}` | `GET` | Get a frontend |
|
||||
| `/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 |
|
||||
|
||||
!!! 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
|
||||
|
||||
```shell
|
||||
curl -s "http://localhost:8080/api" | jq .
|
||||
```
|
||||
```json
|
||||
{
|
||||
"file": {
|
||||
"frontends": {
|
||||
"frontend2": {
|
||||
"routes": {
|
||||
"test_2": {
|
||||
"rule": "Path:/test"
|
||||
}
|
||||
},
|
||||
"backend": "backend1"
|
||||
},
|
||||
"frontend1": {
|
||||
"routes": {
|
||||
"test_1": {
|
||||
"rule": "Host:test.localhost"
|
||||
}
|
||||
},
|
||||
"backend": "backend2"
|
||||
}
|
||||
},
|
||||
"backends": {
|
||||
"backend2": {
|
||||
"loadBalancer": {
|
||||
"method": "drr"
|
||||
},
|
||||
"servers": {
|
||||
"server2": {
|
||||
"weight": 2,
|
||||
"URL": "http://172.17.0.5:80"
|
||||
},
|
||||
"server1": {
|
||||
"weight": 1,
|
||||
"url": "http://172.17.0.4:80"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backend1": {
|
||||
"loadBalancer": {
|
||||
"method": "wrr"
|
||||
},
|
||||
"circuitBreaker": {
|
||||
"expression": "NetworkErrorRatio() > 0.5"
|
||||
},
|
||||
"servers": {
|
||||
"server2": {
|
||||
"weight": 1,
|
||||
"url": "http://172.17.0.3:80"
|
||||
},
|
||||
"server1": {
|
||||
"weight": 10,
|
||||
"url": "http://172.17.0.2:80"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Health
|
||||
|
||||
```shell
|
||||
curl -s "http://localhost:8080/health" | jq .
|
||||
```
|
||||
```json
|
||||
{
|
||||
// Træfik PID
|
||||
"pid": 2458,
|
||||
// Træfik server uptime (formated time)
|
||||
"uptime": "39m6.885931127s",
|
||||
// Træfik server uptime in seconds
|
||||
"uptime_sec": 2346.885931127,
|
||||
// current server date
|
||||
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
|
||||
// current server date in seconds
|
||||
"unixtime": 1444235544,
|
||||
// count HTTP response status code in realtime
|
||||
"status_code_count": {
|
||||
"502": 1
|
||||
},
|
||||
// count HTTP response status code since Træfik started
|
||||
"total_status_code_count": {
|
||||
"200": 7,
|
||||
"404": 21,
|
||||
"502": 13
|
||||
},
|
||||
// count HTTP response
|
||||
"count": 1,
|
||||
// count HTTP response
|
||||
"total_count": 41,
|
||||
// sum of all response time (formated time)
|
||||
"total_response_time": "35.456865605s",
|
||||
// sum of all response time in seconds
|
||||
"total_response_time_sec": 35.456865605,
|
||||
// average response time (formated time)
|
||||
"average_response_time": "864.8016ms",
|
||||
// average response time in seconds
|
||||
"average_response_time_sec": 0.8648016000000001,
|
||||
|
||||
// request statistics [requires --web.statistics to be set]
|
||||
// ten most recent requests with 4xx and 5xx status codes
|
||||
"recent_errors": [
|
||||
{
|
||||
// status code
|
||||
"status_code": 500,
|
||||
// description of status code
|
||||
"status": "Internal Server Error",
|
||||
// request HTTP method
|
||||
"method": "GET",
|
||||
// request hostname
|
||||
"host": "localhost",
|
||||
// request path
|
||||
"path": "/path",
|
||||
// RFC 3339 formatted date/time
|
||||
"time": "2016-10-21T16:59:15.418495872-07:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
You can enable Traefik to export internal metrics to different monitoring systems.
|
||||
```toml
|
||||
[api]
|
||||
# ...
|
||||
|
||||
# Enable more detailed statistics.
|
||||
[api.statistics]
|
||||
|
||||
# Number of recent errors logged.
|
||||
#
|
||||
# Default: 10
|
||||
#
|
||||
recentErrors = 10
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
| Path | Method | Description |
|
||||
|------------|---------------|-------------------------|
|
||||
| `/metrics` | `GET` | Export internal metrics |
|
||||
@@ -1,6 +1,4 @@
|
||||
# Consul Backend
|
||||
|
||||
## Consul Key-Value backend
|
||||
# Consul Key-Value backend
|
||||
|
||||
Træfik can be configured to use Consul as a backend configuration.
|
||||
|
||||
@@ -61,76 +59,3 @@ prefix = "traefik"
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
|
||||
## Consul Catalog backend
|
||||
|
||||
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul Catalog configuration backend
|
||||
################################################################
|
||||
|
||||
# Enable Consul Catalog configuration backend.
|
||||
[consulCatalog]
|
||||
|
||||
# Consul server endpoint.
|
||||
#
|
||||
# Required
|
||||
# Default: "127.0.0.1:8500"
|
||||
#
|
||||
endpoint = "127.0.0.1:8500"
|
||||
|
||||
# Expose Consul catalog services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedByDefault = false
|
||||
|
||||
# Prefix for Consul catalog tags.
|
||||
#
|
||||
# Optional
|
||||
# Default: "traefik"
|
||||
#
|
||||
prefix = "traefik"
|
||||
|
||||
# Default frontEnd Rule for Consul services.
|
||||
#
|
||||
# The format is a Go Template with:
|
||||
# - ".ServiceName", ".Domain" and ".Attributes" available
|
||||
# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
|
||||
# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
|
||||
#
|
||||
# Optional
|
||||
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
|
||||
#
|
||||
#frontEndRule = "Host:{{.ServiceName}}.{{Domain}}"
|
||||
```
|
||||
|
||||
This backend will create routes matching on hostname based on the service name used in Consul.
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
### Tags
|
||||
|
||||
Additional settings can be defined using Consul Catalog tags.
|
||||
|
||||
| Tag | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend.circuitbreaker=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend, ex: `NetworkErrorRatio() > 0.` |
|
||||
| `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.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). |
|
||||
| `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.backend.loadbalancer=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) |
|
||||
|
||||
93
docs/configuration/backends/consulcatalog.md
Normal file
93
docs/configuration/backends/consulcatalog.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Consul Catalog backend
|
||||
|
||||
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul Catalog configuration backend
|
||||
################################################################
|
||||
|
||||
# Enable Consul Catalog configuration backend.
|
||||
[consulCatalog]
|
||||
|
||||
# Consul server endpoint.
|
||||
#
|
||||
# Required
|
||||
# Default: "127.0.0.1:8500"
|
||||
#
|
||||
endpoint = "127.0.0.1:8500"
|
||||
|
||||
# Expose Consul catalog services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedByDefault = false
|
||||
|
||||
# Default domain used.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
domain = "consul.localhost"
|
||||
|
||||
# Prefix for Consul catalog tags.
|
||||
#
|
||||
# Optional
|
||||
# Default: "traefik"
|
||||
#
|
||||
prefix = "traefik"
|
||||
|
||||
# Default frontEnd Rule for Consul services.
|
||||
#
|
||||
# The format is a Go Template with:
|
||||
# - ".ServiceName", ".Domain" and ".Attributes" available
|
||||
# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
|
||||
# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
|
||||
#
|
||||
# Optional
|
||||
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
|
||||
#
|
||||
#frontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
|
||||
```
|
||||
|
||||
This backend will create routes matching on hostname based on the service name used in Consul.
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
### Tags
|
||||
|
||||
Additional settings can be defined using Consul Catalog tags.
|
||||
|
||||
| Tag | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend.circuitbreaker=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend, ex: `NetworkErrorRatio() > 0.` |
|
||||
| `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.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). |
|
||||
| `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.backend.loadbalancer=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) |
|
||||
|
||||
### Examples
|
||||
|
||||
If you want that Træfik uses Consul tags correctly you need to defined them like that:
|
||||
```json
|
||||
traefik.enable=true
|
||||
traefik.tags=api
|
||||
traefik.tags=external
|
||||
```
|
||||
|
||||
If the prefix defined in Træfik configuration is `bla`, tags need to be defined like that:
|
||||
```json
|
||||
bla.enable=true
|
||||
bla.tags=api
|
||||
bla.tags=external
|
||||
```
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
# Docker Backend
|
||||
|
||||
Træfik can be configured to use Docker as a backend configuration.
|
||||
@@ -144,50 +145,106 @@ 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.
|
||||
|
||||
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"
|
||||
services:
|
||||
whoami:
|
||||
deploy:
|
||||
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. |
|
||||
| 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`. |
|
||||
|
||||
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### On Service
|
||||
|
||||
Services labels can be used for overriding default behaviour
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| `traefik.<service-name>.frontend.redirect` | Overrides `traefik.frontend.redirect`. |
|
||||
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
|
||||
|
||||
!!! note
|
||||
If a label is defined both as a `container label` and a `service label` (for example `traefik.<service-name>.port=PORT` and `traefik.port=PORT` ), the `service label` is used to defined the `<service-name>` property (`port` in the example).
|
||||
|
||||
It's possible to mix `container labels` and `service labels`, in this case `container labels` are used as default value for missing `service labels` but no frontends are going to be created with the `container labels`.
|
||||
|
||||
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||
|
||||
!!! warning
|
||||
when running inside a container, Træfik will need network access through:
|
||||
When running inside a container, Træfik will need network access through:
|
||||
|
||||
`docker network connect <network> <traefik-container>`
|
||||
|
||||
@@ -129,10 +129,13 @@ Labels can be used on task containers to override default behaviour:
|
||||
| `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.port=80` | override the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `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.healthcheck.path=/health` | enable health checks for the backend, hitting the container at `path` |
|
||||
| `traefik.backend.healthcheck.interval=1s` | configure the health check interval |
|
||||
| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | override default frontend priority |
|
||||
|
||||
@@ -31,6 +31,16 @@ watch = true
|
||||
#
|
||||
prefix = "/traefik"
|
||||
|
||||
# Force to use API V3 (otherwise still use API V2)
|
||||
#
|
||||
# Deprecated
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
useAPIV3 = true
|
||||
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
@@ -59,3 +69,7 @@ prefix = "/traefik"
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
!!! note
|
||||
The option `useAPIV3` allows using Etcd API V3 only if it's set to true.
|
||||
This option is **deprecated** and API V2 won't be supported in the future.
|
||||
|
||||
@@ -8,6 +8,10 @@ You have three choices:
|
||||
- [Rules in a Separate File](/configuration/backends/file/#rules-in-a-separate-file)
|
||||
- [Multiple `.toml` Files](/configuration/backends/file/#multiple-toml-files)
|
||||
|
||||
To enable the file backend, you must either pass the `--file` option to the Træfik binary or put the `[file]` section (with or without inner settings) in the configuration file.
|
||||
|
||||
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
|
||||
|
||||
Add your configuration at the end of the global configuration file `traefik.toml`:
|
||||
@@ -24,11 +28,8 @@ defaultEntryPoints = ["http", "https"]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||
|
||||
[file]
|
||||
|
||||
@@ -81,8 +82,19 @@ defaultEntryPoints = ["http", "https"]
|
||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
|
||||
# HTTPS certificate
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
```
|
||||
|
||||
!!! 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
|
||||
|
||||
Put your rules in a separate file, for example `rules.toml`:
|
||||
@@ -97,12 +109,6 @@ Put your rules in a separate file, for example `rules.toml`:
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
|
||||
[file]
|
||||
filename = "rules.toml"
|
||||
@@ -149,11 +155,24 @@ filename = "rules.toml"
|
||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
|
||||
# HTTPS certificate
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[[tlsConfiguration.certificates]]
|
||||
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||
```
|
||||
|
||||
## Multiple `.toml` Files
|
||||
|
||||
You could have multiple `.toml` files in a directory:
|
||||
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||
|
||||
```toml
|
||||
[file]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
||||
|
||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
|
||||
## Configuration
|
||||
@@ -57,6 +57,20 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
# Default: false
|
||||
#
|
||||
# disablePassHostHeaders = true
|
||||
|
||||
# Enable PassTLSCert Headers.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# enablePassTLSCert = true
|
||||
|
||||
# Override default configuration template.
|
||||
#
|
||||
# Optional
|
||||
# Default: <built-in template>
|
||||
#
|
||||
# filename = "kubernetes.tmpl"
|
||||
```
|
||||
|
||||
### `endpoint`
|
||||
@@ -88,6 +102,20 @@ Annotations can be used on containers to override default behaviour for the whol
|
||||
Override the default frontend rule type. Default: `PathPrefix`.
|
||||
- `traefik.frontend.priority: "3"`
|
||||
Override the default frontend rule priority.
|
||||
- `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`.
|
||||
- `traefik.frontend.entryPoints: http,https`
|
||||
Override the default frontend endpoints.
|
||||
- `traefik.frontend.passTLSCert: true`
|
||||
Override the default frontend PassTLSCert value. Default: `false`.
|
||||
|
||||
!!! note
|
||||
Please note that `traefik.frontend.redirect.regex` and `traefik.frontend.redirect.replacement` do not have to be set if `traefik.frontend.redirect.entryPoint` is defined for the redirection (they will not be used in this case).
|
||||
|
||||
|
||||
Annotations can be used on the Kubernetes service to override default behaviour:
|
||||
|
||||
@@ -100,8 +128,6 @@ Annotations can be used on the Kubernetes service to override default behaviour:
|
||||
- `traefik.backend.loadbalancer.sticky=true`
|
||||
Enable backend sticky sessions (DEPRECATED)
|
||||
|
||||
You can find here an example [ingress](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheese-ingress.yaml) and [replication controller](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik.yaml).
|
||||
|
||||
Additionally, an annotation can be used on Kubernetes services to set the [circuit breaker expression](/basics/#backends) for a backend.
|
||||
|
||||
- `traefik.backend.circuitbreaker: <expression>`
|
||||
@@ -114,14 +140,40 @@ As known from nginx when used as Kubernetes Ingress Controller, a list of IP-Ran
|
||||
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.
|
||||
|
||||
#### Security annotations
|
||||
|
||||
The following security annotations can be applied to the ingress object to add security features:
|
||||
|
||||
| Annotation | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/allowed-hosts:EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `ingress.kubernetes.io/custom-request-headers:EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/custom-response-headers:EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/proxy-headers:EXPR ` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `ingress.kubernetes.io/ssl-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `ingress.kubernetes.io/ssl-temporary-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `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||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-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`. |
|
||||
| `ingress.kubernetes.io/custom-frame-options-value:VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `ingress.kubernetes.io/content-type-nosniff:true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `ingress.kubernetes.io/browser-xss-filter:true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `ingress.kubernetes.io/content-security-policy:VALUE` | Adds CSP Header with the custom value. |
|
||||
| `ingress.kubernetes.io/public-key:VALUE` | Adds pinned HTST public key header. |
|
||||
| `ingress.kubernetes.io/referrer-policy:VALUE` | Adds referrer policy header. |
|
||||
| `ingress.kubernetes.io/is-development:false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### Authentication
|
||||
|
||||
Is possible to add additional authentication annotations in the Ingress rule.
|
||||
The source of the authentication is a secret that contains usernames and passwords inside the the key auth.
|
||||
The source of the authentication is a secret that contains usernames and passwords inside the key auth.
|
||||
|
||||
- `ingress.kubernetes.io/auth-type`: `basic`
|
||||
- `ingress.kubernetes.io/auth-secret`
|
||||
- `ingress.kubernetes.io/auth-secret`: `mysecret`
|
||||
Contains the usernames and passwords with access to the paths defined in the Ingress Rule.
|
||||
|
||||
The secret must be created in the same namespace as the Ingress rule.
|
||||
|
||||
@@ -68,6 +68,16 @@ domain = "marathon.localhost"
|
||||
#
|
||||
# marathonLBCompatibility = true
|
||||
|
||||
# Enable filtering using Marathon constraints..
|
||||
# If enabled, Traefik will read Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html
|
||||
# Each individual constraint will be treated as a verbatim compounded tag.
|
||||
# i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# filterMarathonConstraints = true
|
||||
|
||||
# Enable Marathon basic authentication.
|
||||
#
|
||||
# Optional
|
||||
@@ -140,12 +150,15 @@ domain = "marathon.localhost"
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
|
||||
### On Containers
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behaviour.
|
||||
|
||||
Labels can be used on containers to override default behaviour:
|
||||
They may be specified on one of two levels: Application or service.
|
||||
|
||||
### Application Level
|
||||
|
||||
The following labels can be defined on Marathon applications. They adjust the behaviour for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
@@ -170,9 +183,9 @@ Labels can be used on containers to override default behaviour:
|
||||
| `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`. |
|
||||
|
||||
### On Services
|
||||
### Service Level
|
||||
|
||||
If several ports need to be exposed from a container, the services labels can be used:
|
||||
For applications that expose multiple ports, specific labels can be used to extract one frontend/backend configuration pair per port. Each such pair is called a _service_. The (freely choosable) name of the service is an integral part of the service label name.
|
||||
|
||||
| Label | Description |
|
||||
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------|
|
||||
|
||||
@@ -110,22 +110,31 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
To enable Traefik to fetch information about the Environment it's deployed in only, you need to create an `Environment API Key`.
|
||||
This can be found within the API Key advanced options.
|
||||
|
||||
Add these labels to traefik docker deployment to autogenerated these values:
|
||||
```
|
||||
io.rancher.container.agent.role: environment
|
||||
io.rancher.container.create_agent: true
|
||||
```
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
|
||||
Labels can be used on task containers to override default behaviour:
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------|
|
||||
| `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=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `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.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `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) |
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| `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=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement: http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `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) |
|
||||
91
docs/configuration/backends/rest.md
Normal file
91
docs/configuration/backends/rest.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Rest Backend
|
||||
|
||||
Træfik can be configured:
|
||||
|
||||
- using a RESTful api.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable rest backend.
|
||||
[rest]
|
||||
# Name of the related entry point
|
||||
#
|
||||
# Optional
|
||||
# Default: "traefik"
|
||||
#
|
||||
entryPoint = "traefik"
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Path | Method | Description |
|
||||
|------------------------------|--------|-----------------|
|
||||
| `/api/providers/web` | `PUT` | update provider |
|
||||
| `/api/providers/rest` | `PUT` | update provider |
|
||||
|
||||
!!! warning
|
||||
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
|
||||
|
||||
|
||||
```shell
|
||||
curl -XPUT @file "http://localhost:8080/api"
|
||||
```
|
||||
with `@file`
|
||||
```json
|
||||
{
|
||||
"frontends": {
|
||||
"frontend2": {
|
||||
"routes": {
|
||||
"test_2": {
|
||||
"rule": "Path:/test"
|
||||
}
|
||||
},
|
||||
"backend": "backend1"
|
||||
},
|
||||
"frontend1": {
|
||||
"routes": {
|
||||
"test_1": {
|
||||
"rule": "Host:test.localhost"
|
||||
}
|
||||
},
|
||||
"backend": "backend2"
|
||||
}
|
||||
},
|
||||
"backends": {
|
||||
"backend2": {
|
||||
"loadBalancer": {
|
||||
"method": "drr"
|
||||
},
|
||||
"servers": {
|
||||
"server2": {
|
||||
"weight": 2,
|
||||
"URL": "http://172.17.0.5:80"
|
||||
},
|
||||
"server1": {
|
||||
"weight": 1,
|
||||
"url": "http://172.17.0.4:80"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backend1": {
|
||||
"loadBalancer": {
|
||||
"method": "wrr"
|
||||
},
|
||||
"circuitBreaker": {
|
||||
"expression": "NetworkErrorRatio() > 0.5"
|
||||
},
|
||||
"servers": {
|
||||
"server2": {
|
||||
"weight": 1,
|
||||
"url": "http://172.17.0.3:80"
|
||||
},
|
||||
"server1": {
|
||||
"weight": 10,
|
||||
"url": "http://172.17.0.2:80"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
114
docs/configuration/backends/servicefabric.md
Normal file
114
docs/configuration/backends/servicefabric.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Service Fabric Backend
|
||||
|
||||
Træfik can be configured to use Service Fabric as a backend configuration.
|
||||
|
||||
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||
|
||||
## Service Fabric
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Service Fabric provider
|
||||
################################################################
|
||||
|
||||
# Enable Service Fabric configuration backend
|
||||
[serviceFabric]
|
||||
|
||||
# Service Fabric Management Endpoint
|
||||
#
|
||||
# Required
|
||||
#
|
||||
clusterManagementUrl = "https://localhost:19080"
|
||||
|
||||
# Service Fabric Management Endpoint API Version
|
||||
#
|
||||
# Required
|
||||
# Default: "3.0"
|
||||
#
|
||||
apiVersion = "3.0"
|
||||
|
||||
# Service Fabric Polling Interval (in seconds)
|
||||
#
|
||||
# Required
|
||||
# Default: 10
|
||||
#
|
||||
refreshSeconds = 10
|
||||
|
||||
# Enable TLS connection.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [serviceFabric.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/servicefabric.crt"
|
||||
# key = "/etc/ssl/servicefabric.key"
|
||||
# insecureskipverify = true
|
||||
```
|
||||
|
||||
## Labels
|
||||
|
||||
The provider uses labels to configure how services are exposed through Træfik.
|
||||
These can be set using Extensions and the Property Manager API
|
||||
|
||||
#### Extensions
|
||||
|
||||
Set labels with extensions through the services `ServiceManifest.xml` file.
|
||||
Here is an example of an extension setting Træfik labels:
|
||||
|
||||
```xml
|
||||
<StatelessServiceType ServiceTypeName="WebServiceType">
|
||||
<Extensions>
|
||||
<Extension Name="Traefik">
|
||||
<Labels xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
|
||||
<Label Key="traefik.frontend.rule.example2">PathPrefixStrip: /a/path/to/strip</Label>
|
||||
<Label Key="traefik.expose">true</Label>
|
||||
<Label Key="traefik.frontend.passHostHeader">true</Label>
|
||||
</Labels>
|
||||
</Extension>
|
||||
</Extensions>
|
||||
</StatelessServiceType>
|
||||
```
|
||||
|
||||
#### Property Manager
|
||||
|
||||
Set Labels with the property manager API to overwrite and add labels, while your service is running.
|
||||
Here is an example of adding a frontend rule using the property manager API.
|
||||
|
||||
```shell
|
||||
curl -X PUT \
|
||||
'http://localhost:19080/Names/GettingStartedApplication2/WebService/$/GetProperty?api-version=6.0&IncludeValues=true' \
|
||||
-d '{
|
||||
"PropertyName": "traefik.frontend.rule.default",
|
||||
"Value": {
|
||||
"Kind": "String",
|
||||
"Data": "PathPrefixStrip: /a/path/to/strip"
|
||||
},
|
||||
"CustomTypeId": "LabelType"
|
||||
}'
|
||||
```
|
||||
|
||||
!!! note
|
||||
This functionality will be released in a future version of the [sfctl](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-lifecycle-sfctl) tool.
|
||||
|
||||
## Available Labels
|
||||
|
||||
Labels, set through extensions or the property manager, can be used on services to override default behaviour.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>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.<br>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.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.expose=true` | Expose this service using træfik |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
|
||||
| `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` | Set 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.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group |
|
||||
@@ -1,5 +1,8 @@
|
||||
# Web Backend
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
The web provider is deprecated, please use the [api](/configuration/api.md), the [ping](/configuration/ping.md), the [metrics](/configuration/metrics) and the [rest](/configuration/backends/rest.md) provider.
|
||||
|
||||
Træfik can be configured:
|
||||
|
||||
- using a RESTful api.
|
||||
@@ -158,6 +161,31 @@ pushinterval = "10s"
|
||||
# ...
|
||||
```
|
||||
|
||||
### InfluxDB
|
||||
|
||||
```toml
|
||||
[web]
|
||||
# ...
|
||||
|
||||
# InfluxDB metrics exporter type
|
||||
[web.metrics.influxdb]
|
||||
|
||||
# InfluxDB's address.
|
||||
#
|
||||
# Required
|
||||
# Default: "localhost:8089"
|
||||
#
|
||||
address = "localhost:8089"
|
||||
|
||||
# InfluxDB push interval
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
#
|
||||
pushinterval = "10s"
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
## Statistics
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ watch = true
|
||||
# Prefix used for KV store.
|
||||
#
|
||||
# Optional
|
||||
# Default: "/traefik"
|
||||
# Default: "traefik"
|
||||
#
|
||||
prefix = "/traefik"
|
||||
prefix = "traefik"
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
|
||||
@@ -3,14 +3,23 @@
|
||||
## Main Section
|
||||
|
||||
```toml
|
||||
# Duration to give active requests a chance to finish before Traefik stops.
|
||||
# DEPRECATED - for general usage instruction see [lifeCycle.graceTimeOut].
|
||||
#
|
||||
# If both the deprecated option and the new one are given, the deprecated one
|
||||
# takes precedence.
|
||||
# A value of zero is equivalent to omitting the parameter, causing
|
||||
# [lifeCycle.graceTimeOut] to be effective. Pass zero to the new option in
|
||||
# order to disable the grace period.
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
# Default: "0s"
|
||||
#
|
||||
# graceTimeOut = "10s"
|
||||
|
||||
# Enable debug mode.
|
||||
# This will install HTTP handlers to expose Go expvars under /debug/vars and
|
||||
# pprof profiling data under /debug/pprof.
|
||||
# Additionally, the log level will be set to DEBUG.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
@@ -152,6 +161,11 @@ constraints = ["tag==api", "tag!=v*-beta"]
|
||||
```toml
|
||||
# Traefik logs file
|
||||
# If not defined, logs to stdout
|
||||
#
|
||||
# DEPRECATED - see [traefikLog] lower down
|
||||
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
|
||||
# Optional
|
||||
#
|
||||
traefikLogsFile = "log/traefik.log"
|
||||
|
||||
# Log level
|
||||
@@ -165,6 +179,23 @@ traefikLogsFile = "log/traefik.log"
|
||||
logLevel = "ERROR"
|
||||
```
|
||||
|
||||
## Traefik Logs
|
||||
|
||||
By default the Traefik log is written to stdout in text format.
|
||||
|
||||
To write the logs into a logfile specify the `filePath`.
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
### Access Logs
|
||||
|
||||
Access logs are written when `[accessLog]` is defined.
|
||||
@@ -246,6 +277,36 @@ Custom error pages are easiest to implement using the file provider.
|
||||
For dynamic providers, the corresponding template file needs to be customized accordingly and referenced in the Traefik configuration.
|
||||
|
||||
|
||||
## Rate limiting
|
||||
|
||||
Rate limiting can be configured per frontend.
|
||||
Multiple sets of rates can be added to each frontend, but the time periods must be unique.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
|
||||
These can "burst" up to 10 and 200 in each period respectively.
|
||||
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
```toml
|
||||
@@ -281,6 +342,38 @@ Given provider-specific support, the value may be overridden on a per-backend ba
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
|
||||
## Life Cycle
|
||||
|
||||
Controls the behavior of Traefik during the shutdown phase.
|
||||
|
||||
```toml
|
||||
[lifeCycle]
|
||||
|
||||
# Duration to keep accepting requests prior to initiating the graceful
|
||||
# termination period (as defined by the `graceTimeOut` option). This
|
||||
# option is meant to give downstream load-balancers sufficient time to
|
||||
# take Traefik out of rotation.
|
||||
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
# If no units are provided, the value is parsed assuming seconds.
|
||||
# The zero duration disables the request accepting grace period, i.e.,
|
||||
# Traefik will immediately proceed to the grace period.
|
||||
#
|
||||
# Optional
|
||||
# Default: 0
|
||||
#
|
||||
# requestAcceptGraceTimeout = "10s"
|
||||
|
||||
# Duration to give active requests a chance to finish before Traefik stops.
|
||||
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
# If no units are provided, the value is parsed assuming seconds.
|
||||
# Note: in this time frame no new requests are accepted.
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
#
|
||||
# graceTimeOut = "10s"
|
||||
```
|
||||
|
||||
## Timeouts
|
||||
|
||||
### Responding Timeouts
|
||||
|
||||
@@ -27,13 +27,16 @@ To redirect an http entrypoint to an https entrypoint (with SNI support).
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||
```
|
||||
|
||||
!!! 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).
|
||||
|
||||
## Rewriting URL
|
||||
|
||||
To redirect an entrypoint rewriting the URL.
|
||||
@@ -47,13 +50,35 @@ To redirect an entrypoint rewriting the URL.
|
||||
replacement = "http://mydomain/$1"
|
||||
```
|
||||
|
||||
!!! 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).
|
||||
|
||||
## TLS
|
||||
|
||||
Define an entrypoint with SNI support.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
```
|
||||
|
||||
!!! note
|
||||
If an empty TLS configuration is done, default self-signed certificates are generated.
|
||||
|
||||
## TLS Mutual Authentication
|
||||
|
||||
Only accept clients that present a certificate signed by a specified Certificate Authority (CA).
|
||||
TLS Mutual Authentication can be `optional` or not.
|
||||
If it's `optional`, Træfik will authorize connection with certificates not signed by a specified Certificate Authority (CA).
|
||||
Otherwise, Træfik will only accept clients that present a certificate signed by a specified Certificate Authority (CA).
|
||||
`ClientCAFiles` can be configured with multiple `CA:s` in the same file or use multiple files containing one or several `CA:s`.
|
||||
The `CA:s` has to be in PEM format.
|
||||
|
||||
All clients will be required to present a valid cert.
|
||||
By default, `ClientCAFiles` is not optional, all clients will be required to present a valid cert.
|
||||
The requirement will apply to all server certs in the entrypoint.
|
||||
|
||||
In the example below both `snitest.com` and `snitest.org` will require client certs
|
||||
@@ -63,15 +88,21 @@ In the example below both `snitest.com` and `snitest.org` will require client ce
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
optional = false
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
|
||||
If this parameter exists, the new ones are not checked.
|
||||
|
||||
## Authentication
|
||||
|
||||
@@ -118,10 +149,10 @@ Otherwise, the response from the auth server is returned.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entrypoints.http]
|
||||
[entryPoints.http]
|
||||
# ...
|
||||
# To enable forward auth on an entrypoint
|
||||
[entrypoints.http.auth.forward]
|
||||
[entryPoints.http.auth.forward]
|
||||
address = "https://authserver.com/auth"
|
||||
|
||||
# Trust existing X-Forwarded-* headers.
|
||||
@@ -136,14 +167,14 @@ Otherwise, the response from the auth server is returned.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
[entrypoints.http.auth.forward.tls]
|
||||
[entryPoints.http.auth.forward.tls]
|
||||
cert = "authserver.crt"
|
||||
key = "authserver.key"
|
||||
```
|
||||
|
||||
## Specify Minimum TLS Version
|
||||
|
||||
To specify an https entry point with a minimum TLS version, and specifying an array of cipher suites (from crypto/tls).
|
||||
To specify an https entry point with a minimum TLS version, and specifying an array of cipher suites (from [crypto/tls](https://godoc.org/crypto/tls#pkg-constants)).
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
@@ -188,17 +219,52 @@ To enable IP whitelisting at the entrypoint level.
|
||||
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||
```
|
||||
|
||||
## ProxyProtocol Support
|
||||
## ProxyProtocol
|
||||
|
||||
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
||||
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here.
|
||||
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here (in testing environment, you can trust everyone using `insecure = true`).
|
||||
|
||||
!!! 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.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.proxyProtocol]
|
||||
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||
address = ":80"
|
||||
|
||||
# Enable ProxyProtocol
|
||||
[entryPoints.http.proxyProtocol]
|
||||
# List of trusted IPs
|
||||
#
|
||||
# Required
|
||||
# Default: []
|
||||
#
|
||||
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||
|
||||
# Insecure mode FOR TESTING ENVIRONNEMENT ONLY
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# insecure = true
|
||||
```
|
||||
|
||||
## Forwarded Header
|
||||
|
||||
Only IPs in `trustedIPs` will be authorized to trust the client forwarded headers (`X-Forwarded-*`).
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
# Enable Forwarded Headers
|
||||
[entryPoints.http.forwardedHeaders]
|
||||
# List of trusted IPs
|
||||
#
|
||||
# Required
|
||||
# Default: []
|
||||
#
|
||||
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||
```
|
||||
²
|
||||
126
docs/configuration/metrics.md
Normal file
126
docs/configuration/metrics.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Metrics Definition
|
||||
|
||||
## Prometheus
|
||||
|
||||
```toml
|
||||
# Metrics definition
|
||||
[metrics]
|
||||
#...
|
||||
|
||||
# To enable Traefik to export internal metrics to Prometheus
|
||||
[metrics.prometheus]
|
||||
|
||||
# Name of the related entry point
|
||||
#
|
||||
# Optional
|
||||
# Default: "traefik"
|
||||
#
|
||||
entryPoint = "traefik"
|
||||
|
||||
# Buckets for latency metrics
|
||||
#
|
||||
# Optional
|
||||
# Default: [0.1, 0.3, 1.2, 5]
|
||||
#
|
||||
buckets = [0.1,0.3,1.2,5.0]
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
## DataDog
|
||||
|
||||
```toml
|
||||
# Metrics definition
|
||||
[metrics]
|
||||
#...
|
||||
|
||||
# DataDog metrics exporter type
|
||||
[metrics.datadog]
|
||||
|
||||
# DataDog's address.
|
||||
#
|
||||
# Required
|
||||
# Default: "localhost:8125"
|
||||
#
|
||||
address = "localhost:8125"
|
||||
|
||||
# DataDog push interval
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
#
|
||||
pushInterval = "10s"
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
## StatsD
|
||||
|
||||
```toml
|
||||
# Metrics definition
|
||||
[metrics]
|
||||
#...
|
||||
|
||||
# StatsD metrics exporter type
|
||||
[metrics.statsd]
|
||||
|
||||
# StatD's address.
|
||||
#
|
||||
# Required
|
||||
# Default: "localhost:8125"
|
||||
#
|
||||
address = "localhost:8125"
|
||||
|
||||
# StatD push interval
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
#
|
||||
pushInterval = "10s"
|
||||
|
||||
# ...
|
||||
```
|
||||
### InfluxDB
|
||||
|
||||
```toml
|
||||
[metrics]
|
||||
# ...
|
||||
|
||||
# InfluxDB metrics exporter type
|
||||
[metrics.influxdb]
|
||||
|
||||
# InfluxDB's address.
|
||||
#
|
||||
# Required
|
||||
# Default: "localhost:8089"
|
||||
#
|
||||
address = "localhost:8089"
|
||||
|
||||
# InfluxDB push interval
|
||||
#
|
||||
# Optional
|
||||
# Default: "10s"
|
||||
#
|
||||
pushinterval = "10s"
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
## Statistics
|
||||
|
||||
```toml
|
||||
# Metrics definition
|
||||
[metrics]
|
||||
# ...
|
||||
|
||||
# Enable more detailed statistics.
|
||||
[metrics.statistics]
|
||||
|
||||
# Number of recent errors logged.
|
||||
#
|
||||
# Default: 10
|
||||
#
|
||||
recentErrors = 10
|
||||
|
||||
# ...
|
||||
```
|
||||
42
docs/configuration/ping.md
Normal file
42
docs/configuration/ping.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Ping Definition
|
||||
|
||||
```toml
|
||||
# Ping definition
|
||||
[ping]
|
||||
# Name of the related entry point
|
||||
#
|
||||
# Optional
|
||||
# Default: "traefik"
|
||||
#
|
||||
entryPoint = "traefik"
|
||||
```
|
||||
|
||||
| Path | Method | Description |
|
||||
|---------|---------------|----------------------------------------------------------------------------------------------------|
|
||||
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
|
||||
|
||||
|
||||
!!! warning
|
||||
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
|
||||
|
||||
### Example
|
||||
|
||||
```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
|
||||
```
|
||||
@@ -22,7 +22,7 @@ If you want your users to access some of your microservices from the Internet, y
|
||||
- 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
|
||||
|
||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
||||
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||
|
||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||
|
||||
@@ -44,7 +44,7 @@ Run it and forget it!
|
||||
- Hot-reloading of configuration. No need to restart the process
|
||||
- Circuit breakers, retry
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Metrics (Rest, Prometheus, Datadog, Statd)
|
||||
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Clean AngularJS Web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Access Logs (JSON, CLF)
|
||||
@@ -129,7 +129,7 @@ Start it from within the `traefik` folder:
|
||||
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.
|
||||
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:
|
||||
|
||||
|
||||
4
docs/theme/partials/footer.html
vendored
4
docs/theme/partials/footer.html
vendored
@@ -20,7 +20,7 @@
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
{% import "partials/language.html" as lang %}
|
||||
{% import "partials/language.html" as lang with context %}
|
||||
|
||||
<!-- Application footer -->
|
||||
<footer class="md-footer">
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<!-- Social links -->
|
||||
{% block social %}
|
||||
{% include "partials/social.html" %}
|
||||
{% include "partials/social.html" %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Docker & Traefik
|
||||
|
||||
In this use case, we want to use Traefik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
||||
In this use case, we want to use Træfik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
||||
|
||||
We also want to automatically _discover any services_ on the Docker host and let Traefik reconfigure itself automatically when containers get created (or shut down) so HTTP traffic can be routed accordingly.
|
||||
We also want to automatically _discover any services_ on the Docker host and let Træfik reconfigure itself automatically when containers get created (or shut down) so HTTP traffic can be routed accordingly.
|
||||
|
||||
In addition, we want to use Let's Encrypt to automatically generate and renew SSL certificates per hostname.
|
||||
|
||||
@@ -19,7 +19,7 @@ In real-life, you'll want to use your own domain and have the DNS configured acc
|
||||
Docker containers can only communicate with each other over TCP when they share at least one network.
|
||||
This makes sense from a topological point of view in the context of networking, since Docker under the hood creates IPTable rules so containers can't reach other containers _unless you'd want to_.
|
||||
|
||||
In this example, we're going to use a single network called `web` where all containers that are handling HTTP traffic (including Traefik) will reside in.
|
||||
In this example, we're going to use a single network called `web` where all containers that are handling HTTP traffic (including Træfik) will reside in.
|
||||
|
||||
On the Docker host, run the following command:
|
||||
|
||||
@@ -27,7 +27,7 @@ On the Docker host, run the following command:
|
||||
docker network create web
|
||||
```
|
||||
|
||||
Now, let's create a directory on the server where we will configure the rest of Traefik:
|
||||
Now, let's create a directory on the server where we will configure the rest of Træfik:
|
||||
|
||||
```shell
|
||||
mkdir -p /opt/traefik
|
||||
@@ -41,7 +41,7 @@ touch /opt/traefik/acme.json && chmod 600 /opt/traefik/acme.json
|
||||
touch /opt/traefik/traefik.toml
|
||||
```
|
||||
|
||||
The `docker-compose.yml` file will provide us with a simple, consistent and more importantly, a deterministic way to create Traefik.
|
||||
The `docker-compose.yml` file will provide us with a simple, consistent and more importantly, a deterministic way to create Træfik.
|
||||
|
||||
The contents of the file is as follows:
|
||||
|
||||
@@ -59,8 +59,8 @@ services:
|
||||
- web
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /srv/traefik/traefik.toml:/traefik.toml
|
||||
- /srv/traefik/acme.json:/acme.json
|
||||
- /opt/traefik/traefik.toml:/traefik.toml
|
||||
- /opt/traefik/acme.json:/acme.json
|
||||
container_name: traefik
|
||||
|
||||
networks:
|
||||
@@ -69,16 +69,16 @@ 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 Traefik 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 it's 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`.
|
||||
|
||||
Let's take a look at a simple `traefik.toml` configuration as well before we'll create the Traefik container:
|
||||
Let's take a look at a simple `traefik.toml` configuration as well before we'll create the Træfik container:
|
||||
|
||||
```toml
|
||||
debug = false
|
||||
checkNewVersion = true
|
||||
|
||||
logLevel = "ERROR"
|
||||
defaultEntryPoints = ["https","http"]
|
||||
|
||||
@@ -109,17 +109,17 @@ OnHostRule = true
|
||||
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
|
||||
- Check for new versions of Traefik periodically
|
||||
- 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 Traefik by default, we'll get into this in a bit!**
|
||||
- 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!**
|
||||
- Enable automatic request and configuration of SSL certificates using Let's Encrypt.
|
||||
These certificates will be stored in the `acme.json` file, which you can back-up yourself and store off-premises.
|
||||
|
||||
Alright, let's boot the container. From the `/opt/traefik` directory, run `docker-compose up -d` which will create and start the Traefik container.
|
||||
Alright, let's boot the container. From the `/opt/traefik` directory, run `docker-compose up -d` which will create and start the Træfik container.
|
||||
|
||||
## Exposing Web Services to the Outside World
|
||||
|
||||
Now that we've fully configured and started Traefik, it's time to get our applications running!
|
||||
Now that we've fully configured and started Træfik, it's time to get our applications running!
|
||||
|
||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||
|
||||
@@ -148,6 +148,10 @@ services:
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
|
||||
db:
|
||||
image: my-docker-registry.com/back-end/5.7
|
||||
@@ -190,10 +194,10 @@ Since the `traefik` container we've created and started earlier is also attached
|
||||
|
||||
### Labels
|
||||
|
||||
As mentioned earlier, we don't want containers exposed automatically by Traefik.
|
||||
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 Traefik how to create it's internal routing configuration.
|
||||
Thanks to Docker labels, we can tell Træfik how to create it's 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:
|
||||
|
||||
@@ -203,31 +207,56 @@ Let's take a look at the labels themselves for the `app` service, which is a HTT
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
```
|
||||
|
||||
We use both `container labels` and `service labels`.
|
||||
|
||||
#### Container labels
|
||||
|
||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||
|
||||
We also tell Traefik to use the `web` network to route HTTP traffic to this container.
|
||||
With the `frontend.rule` label, we tell Traefik 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.
|
||||
With the `traefik.enable` label, we tell Traefik to include this container in it's internal configuration.
|
||||
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.
|
||||
|
||||
Finally but not unimportantly, we tell Traefik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
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.
|
||||
|
||||
Finally but not unimportantly, we tell Træfik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
|
||||
### Service labels
|
||||
|
||||
`Service labels` allow managing many routes for the same container.
|
||||
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/bakends creation.
|
||||
|
||||
In the example, two service names are defined : `default` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
||||
- `default` has only one `service label` : `traefik.default.protocol`.
|
||||
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `default` frontend and backend.
|
||||
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
Træfik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||
|
||||
#### Gotchas and tips
|
||||
|
||||
- Always specify the correct port where the container expects HTTP traffic using `traefik.port` label.
|
||||
If a container exposes multiple ports, Traefik may forward traffic to the wrong port.
|
||||
If a container exposes multiple ports, Træfik may forward traffic to the wrong port.
|
||||
Even if a container only exposes one port, you should always write configuration defensively and explicitly.
|
||||
- Should you choose to enable the `exposedbydefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Traefik will automatically be reachable from the outside world, for everyone and everyone to see.
|
||||
- Should you choose to enable the `exposedbydefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Træfik will automatically be reachable from the outside world, for everyone and everyone to see.
|
||||
Usually, this is a bad idea.
|
||||
- With the `traefik.frontend.auth.basic` label, it's possible for Traefik to provide a HTTP basic-auth challenge for the endpoints you provide the label for.
|
||||
- Traefik has built-in support to automatically export [Prometheus](https://prometheus.io) metrics
|
||||
- Traefik supports websockets out of the box. In the example above, the `events`-service could be a NodeJS-based application which allows clients to connect using websocket protocol.
|
||||
- With the `traefik.frontend.auth.basic` label, it's possible for Træfik to provide a HTTP basic-auth challenge for the endpoints you provide the label for.
|
||||
- Træfik has built-in support to automatically export [Prometheus](https://prometheus.io) metrics
|
||||
- Træfik supports websockets out of the box. In the example above, the `events`-service could be a NodeJS-based application which allows clients to connect using websocket protocol.
|
||||
Thanks to the fact that HTTPS in our example is enforced, these websockets are automatically secure as well (WSS)
|
||||
|
||||
### Final thoughts
|
||||
|
||||
Using Traefik as a Layer-7 load balancer in combination with both Docker and Let's Encrypt provides you with an extremely flexible, powerful and self-configuring solution for your projects.
|
||||
Using Træfik as a Layer-7 load balancer in combination with both Docker and Let's Encrypt provides you with an extremely flexible, powerful and self-configuring solution for your projects.
|
||||
|
||||
With Let's Encrypt, your endpoints are automatically secured with production-ready SSL certificates that are renewed automatically as well.
|
||||
|
||||
@@ -47,6 +47,9 @@ defaultEntryPoints = ["http", "https"]
|
||||
keyFile = "examples/traefik.key"
|
||||
```
|
||||
|
||||
!!! 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)
|
||||
|
||||
## Let's Encrypt support
|
||||
|
||||
### Basic example
|
||||
@@ -137,7 +140,7 @@ This configuration allows generating a Let's Encrypt certificate during the firs
|
||||
* TLS handshakes will be slow when requesting a hostname certificate for the first time, this can leads to DDoS attacks.
|
||||
* Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
|
||||
|
||||
That's why, it's better to use the `onHostRule` optin if possible.
|
||||
That's why, it's better to use the `onHostRule` option if possible.
|
||||
|
||||
### DNS challenge
|
||||
|
||||
@@ -170,7 +173,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 has described [in this section](/configuration/acme/#dnsprovider).
|
||||
These variables are described [in this section](/configuration/acme/#dnsprovider).
|
||||
|
||||
### OnHostRule option and provided certificates
|
||||
|
||||
@@ -198,7 +201,7 @@ Traefik will only try to generate a Let's encrypt certificate if the domain cann
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Before to use Let's Encrypt in a Traefik cluster, take a look to [the key-value store explanations](/user-guide/kv-config) and more precisely to [this section](/user-guide/kv-config/#store-configuration-in-key-value-store) in the way to know how to migrate from a acme local storage *(acme.json file)* to a key-value store configuration.
|
||||
Before you use Let's Encrypt in a Traefik cluster, take a look to [the key-value store explanations](/user-guide/kv-config) and more precisely at [this section](/user-guide/kv-config/#store-configuration-in-key-value-store), which will describe how to migrate from a acme local storage *(acme.json file)* to a key-value store configuration.
|
||||
|
||||
#### Configuration
|
||||
|
||||
|
||||
@@ -15,12 +15,16 @@ on your machine, as it is the quickest way to get a local Kubernetes cluster set
|
||||
|
||||
### Role Based Access Control configuration (Kubernetes 1.6+ only)
|
||||
|
||||
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) in 1.6+ to allow fine-grained control of Kubernetes resources and api.
|
||||
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) in 1.6+ to allow fine-grained control of Kubernetes resources and API.
|
||||
|
||||
If your cluster is configured with RBAC, you may need to authorize Træfik to use the Kubernetes API using ClusterRole and ClusterRoleBinding resources:
|
||||
If your cluster is configured with RBAC, you will need to authorize Træfik to use the Kubernetes API. There are two ways to set up the proper permission: Via namespace-specific RoleBindings or a single, global ClusterRoleBinding.
|
||||
|
||||
RoleBindings per namespace enable to restrict granted permissions to the very namespaces only that Træfik is watching over, thereby following the least-privileges principle. This is the preferred approach if Træfik is not supposed to watch all namespaces, and the set of namespaces does not change dynamically. Otherwise, a single ClusterRoleBinding must be employed.
|
||||
|
||||
!!! note
|
||||
your cluster may have suitable ClusterRoles already setup, but the following should work everywhere
|
||||
RoleBindings per namespace are available in Træfik 1.5 and later. Please use ClusterRoleBindings for older versions.
|
||||
|
||||
For the sake of simplicity, this guide will use a ClusterRoleBinding:
|
||||
|
||||
```yaml
|
||||
---
|
||||
@@ -68,18 +72,20 @@ subjects:
|
||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml
|
||||
```
|
||||
|
||||
For namespaced restrictions, one RoleBinding is required per watched namespace along with a corresponding configuration of Træfik's `kubernetes.namespaces` parameter.
|
||||
|
||||
## Deploy Træfik using a Deployment or DaemonSet
|
||||
|
||||
It is possible to use Træfik with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) object,
|
||||
whereas both options have their own pros and cons:
|
||||
|
||||
|
||||
- The scalability is much better when using a Deployment, because you will have a Single-Pod-per-Node model when using the DeaemonSet.
|
||||
- It is possible to exclusively run a Service on a dedicated set of machines using taints and tolerations with a DaemonSet.
|
||||
- On the other hand the DaemonSet allows you to access any Node directly on Port 80 and 443, where you have to setup a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with a Deployment.
|
||||
|
||||
The Deployment objects looks like this:
|
||||
|
||||
```yml
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
@@ -118,6 +124,7 @@ kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: traefik-ingress-service
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
k8s-app: traefik-ingress-lb
|
||||
@@ -182,6 +189,7 @@ kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: traefik-ingress-service
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
k8s-app: traefik-ingress-lb
|
||||
@@ -249,7 +257,7 @@ curl $(minikube ip)
|
||||
404 page not found
|
||||
```
|
||||
|
||||
If you decided to use the deployment, then you need to target the correct NodePort, which can be seen then you execute `kubectl get services --namespace=kube-system`.
|
||||
If you decided to use the deployment, then you need to target the correct NodePort, which can be seen when you execute `kubectl get services --namespace=kube-system`.
|
||||
|
||||
```sh
|
||||
curl $(minikube ip):<NODEPORT>
|
||||
@@ -261,6 +269,8 @@ curl $(minikube ip):<NODEPORT>
|
||||
!!! note
|
||||
We expect to see a 404 response here as we haven't yet given Træfik any configuration.
|
||||
|
||||
All further examples below assume a DaemonSet installation. Deployment users will need to append the NodePort when constructing requests.
|
||||
|
||||
## Deploy Træfik using Helm Chart
|
||||
|
||||
Instead of installing Træfik via an own object, you can also use the Træfik Helm chart.
|
||||
@@ -325,6 +335,72 @@ echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
|
||||
|
||||
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik Web UI.
|
||||
|
||||
## Basic Authentication
|
||||
|
||||
It's possible to add additional authentication annotations in the Ingress rule.
|
||||
The source of the authentication is a secret that contains usernames and passwords inside the key auth.
|
||||
To read about basic auth limitations see the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page.
|
||||
|
||||
#### Creating the Secret
|
||||
|
||||
A. Use `htpasswd` to create a file containing the username and the base64-encoded password:
|
||||
|
||||
```shell
|
||||
htpasswd -c ./auth myusername
|
||||
```
|
||||
|
||||
You will be prompted for a password which you will have to enter twice.
|
||||
`htpasswd` will create a file with the following:
|
||||
|
||||
```shell
|
||||
cat auth
|
||||
```
|
||||
```
|
||||
myusername:$apr1$78Jyn/1K$ERHKVRPPlzAX8eBtLuvRZ0
|
||||
```
|
||||
|
||||
B. Now use `kubectl` to create a secret in the monitoring namespace using the file created by `htpasswd`.
|
||||
|
||||
```shell
|
||||
kubectl create secret generic mysecret --from-file auth --namespace=monitoring
|
||||
```
|
||||
|
||||
!!! note
|
||||
Secret must be in same namespace as the ingress rule.
|
||||
|
||||
C. Create the ingress using the following annotations to specify basic auth and that the username and password is stored in `mysecret`.
|
||||
|
||||
- `ingress.kubernetes.io/auth-type: "basic"`
|
||||
- `ingress.kubernetes.io/auth-secret: "mysecret"`
|
||||
|
||||
Following is a full ingress example based on Prometheus:
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: prometheus-dashboard
|
||||
namespace: monitoring
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
ingress.kubernetes.io/auth-type: "basic"
|
||||
ingress.kubernetes.io/auth-secret: "mysecret"
|
||||
spec:
|
||||
rules:
|
||||
- host: dashboard.prometheus.example.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: prometheus
|
||||
servicePort: 9090
|
||||
```
|
||||
|
||||
You can apply the example ingress as following:
|
||||
|
||||
```shell
|
||||
kubectl create -f prometheus-ingress.yaml -n monitoring
|
||||
```
|
||||
|
||||
## Name based routing
|
||||
|
||||
In this example we are going to setup websites for 3 of the United Kingdoms best loved cheeses, Cheddar, Stilton and Wensleydale.
|
||||
|
||||
@@ -76,15 +76,18 @@ defaultEntryPoints = ["http", "https"]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = """-----BEGIN CERTIFICATE-----
|
||||
certFile = """-----BEGIN CERTIFICATE-----
|
||||
<cert file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
KeyFile = """-----BEGIN CERTIFICATE-----
|
||||
keyFile = """-----BEGIN CERTIFICATE-----
|
||||
<key file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
[entryPoints.other-https]
|
||||
address = ":4443"
|
||||
[entryPoints.other-https.tls]
|
||||
|
||||
[consul]
|
||||
endpoint = "127.0.0.1:8500"
|
||||
@@ -108,6 +111,7 @@ And there, the same global configuration in the Key-value Store (using `prefix =
|
||||
| `/traefik/entrypoints/https/tls/certificates/0/keyfile` | `integration/fixtures/https/snitest.com.key` |
|
||||
| `/traefik/entrypoints/https/tls/certificates/1/certfile` | `--BEGIN CERTIFICATE--<cert file content>--END CERTIFICATE--` |
|
||||
| `/traefik/entrypoints/https/tls/certificates/1/keyfile` | `--BEGIN CERTIFICATE--<key file content>--END CERTIFICATE--` |
|
||||
| `/traefik/entrypoints/other-https/address` | `:4443` |
|
||||
| `/traefik/consul/endpoint` | `127.0.0.1:8500` |
|
||||
| `/traefik/consul/watch` | `true` |
|
||||
| `/traefik/consul/prefix` | `traefik` |
|
||||
@@ -148,6 +152,37 @@ This variable must be initialized with the ACL token value.
|
||||
|
||||
If Traefik is launched into a Docker container, the variable `CONSUL_HTTP_TOKEN` can be initialized with the `-e` Docker option : `-e "CONSUL_HTTP_TOKEN=[consul-acl-token-value]"`
|
||||
|
||||
If a Consul ACL is used to restrict Træfik read/write access, one of the following configurations is needed.
|
||||
|
||||
- HCL format :
|
||||
|
||||
```
|
||||
key "traefik" {
|
||||
policy = "write"
|
||||
},
|
||||
|
||||
session "" {
|
||||
policy = "write"
|
||||
}
|
||||
```
|
||||
|
||||
- JSON format :
|
||||
|
||||
```json
|
||||
{
|
||||
"key": {
|
||||
"traefik": {
|
||||
"policy": "write"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
"": {
|
||||
"policy": "write"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### TLS support
|
||||
|
||||
To connect to a Consul endpoint using SSL, simply specify `https://` in the `consul.endpoint` property
|
||||
@@ -181,7 +216,7 @@ Remember the command `traefik --help` to display the updated list of flags.
|
||||
|
||||
## Dynamic configuration in Key-value store
|
||||
|
||||
Following our example, we will provide backends/frontends rules to Træfik.
|
||||
Following our example, we will provide backends/frontends rules and HTTPS certificates to Træfik.
|
||||
|
||||
!!! note
|
||||
This section is independent of the way Træfik got its static configuration.
|
||||
@@ -234,6 +269,21 @@ Here is the toml configuration we would like to store in the store :
|
||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = "path/to/your.cert"
|
||||
keyFile = "path/to/your.key"
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https","other-https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = """-----BEGIN CERTIFICATE-----
|
||||
<cert file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
keyFile = """-----BEGIN CERTIFICATE-----
|
||||
<key file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
```
|
||||
|
||||
And there, the same dynamic configuration in a KV Store (using `prefix = "traefik"`):
|
||||
@@ -279,6 +329,22 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
|
||||
- certificate 1
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|--------------------|
|
||||
| `/traefik/tlsconfiguration/1/entrypoints` | `https` |
|
||||
| `/traefik/tlsconfiguration/1/certificate/certfile` | `path/to/your.cert`|
|
||||
| `/traefik/tlsconfiguration/1/certificate/keyfile` | `path/to/your.key` |
|
||||
|
||||
- certificate 2
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|-----------------------|
|
||||
| `/traefik/tlsconfiguration/2/entrypoints` | `https,other-https` |
|
||||
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<cert file content>` |
|
||||
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<key file content>` |
|
||||
|
||||
### Atomic configuration changes
|
||||
|
||||
Træfik can watch the backends/frontends configuration changes and generate its configuration automatically.
|
||||
@@ -291,6 +357,10 @@ As a result, it may be possible for Træfik to read an intermediate configuratio
|
||||
To solve this problem, Træfik supports a special key called `/traefik/alias`.
|
||||
If set, Træfik use the value as an alternative key prefix.
|
||||
|
||||
!!! note
|
||||
The field `useAPIV3` allows using Etcd V3 API which should support updating multiple keys atomically with Etcd.
|
||||
Etcd API V2 is deprecated and, in the future, Træfik will support API V3 by default.
|
||||
|
||||
Given the key structure below, Træfik will use the `http://172.17.0.2:80` as its only backend (frontend keys have been omitted for brevity).
|
||||
|
||||
| Key | Value |
|
||||
@@ -309,9 +379,9 @@ Here, although the `/traefik_configurations/2/...` keys have been set, the old c
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
|
||||
Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` configuration becomes active atomically.
|
||||
|
||||
@@ -323,9 +393,9 @@ Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://1
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/url` | `http://172.17.0.3:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server1/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
|
||||
!!! note
|
||||
Træfik *will not watch for key changes in the `/traefik_configurations` prefix*. It will only watch for changes in the `/traefik/alias`.
|
||||
@@ -343,8 +413,11 @@ traefik storeconfig [flags] ...
|
||||
```
|
||||
This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store).
|
||||
Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store.
|
||||
|
||||
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
|
||||
|
||||
If you configured a file backend `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
|
||||
|
||||
To upload your ACME certificates to the KV store, get your Traefik TOML file and add the new `storage` option in the `acme` section:
|
||||
|
||||
```toml
|
||||
|
||||
@@ -28,6 +28,24 @@ Following is the order by which Traefik tries to identify the port (the first on
|
||||
1. The port from the application's `portDefinitions` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
||||
1. The port from the application's `ipAddressPerTask` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
||||
|
||||
## Applications with multiple ports
|
||||
|
||||
Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _service_ per port using [specific labels](/configuration/backends/marathon#service-level).
|
||||
|
||||
For instance, assume that a Marathon application exposes a web API on port 80 and an admin interface on port 8080. It would then be possible to make each service available by specifying the following Marathon labels:
|
||||
|
||||
```
|
||||
traefik.web.port=80
|
||||
```
|
||||
|
||||
```
|
||||
traefik.admin.port=8080
|
||||
```
|
||||
|
||||
(Note that the service names `web` and `admin` can be chosen arbitrarily.)
|
||||
|
||||
Technically, Traefik will create one pair of frontend and backend configurations for each service.
|
||||
|
||||
## Achieving high availability
|
||||
|
||||
### Scenarios
|
||||
|
||||
@@ -86,7 +86,7 @@ docker $(docker-machine config mhs-demo0) run \
|
||||
-c /dev/null \
|
||||
--docker \
|
||||
--docker.domain=traefik \
|
||||
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):3376 \
|
||||
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):2376 \
|
||||
--docker.tls \
|
||||
--docker.tls.ca=/ssl/ca.pem \
|
||||
--docker.tls.cert=/ssl/server.pem \
|
||||
@@ -105,7 +105,7 @@ Let's explain this command:
|
||||
| `-v /var/lib/boot2docker/:/ssl` | mount the ssl keys generated by docker-machine |
|
||||
| `-c /dev/null` | empty config file |
|
||||
| `--docker` | enable docker backend |
|
||||
| `--docker.endpoint=tcp://172.18.0.1:3376` | connect to the swarm master using the docker_gwbridge network |
|
||||
| `--docker.endpoint=tcp://172.18.0.1:2376` | connect to the swarm master using the docker_gwbridge network |
|
||||
| `--docker.tls` | enable TLS using the docker-machine keys |
|
||||
| `--web` | activate the webUI on port 8080 |
|
||||
|
||||
|
||||
200
examples/cluster/docker-compose.yml
Normal file
200
examples/cluster/docker-compose.yml
Normal file
@@ -0,0 +1,200 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
|
||||
## KV part ##
|
||||
|
||||
# CONSUL
|
||||
|
||||
consul:
|
||||
image: progrium/consul
|
||||
command: -server -bootstrap -log-level debug -ui-dir /ui
|
||||
ports:
|
||||
- "8400:8400"
|
||||
- "8500:8500"
|
||||
- "8600:53/udp"
|
||||
expose:
|
||||
- "8300"
|
||||
- "8301"
|
||||
- "8301/udp"
|
||||
- "8302"
|
||||
- "8302/udp"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.2
|
||||
|
||||
# ETCD V3
|
||||
|
||||
etcd3:
|
||||
image: quay.io/coreos/etcd:v3.2.9
|
||||
command: /usr/local/bin/etcd --data-dir=/etcd-data --name node1 --initial-advertise-peer-urls http://10.0.1.12:2380 --listen-peer-urls http://10.0.1.12:2380 --advertise-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --listen-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --initial-cluster node1=http://10.0.1.12:2380 --debug
|
||||
ports:
|
||||
- "4001:4001"
|
||||
- "2380:2380"
|
||||
- "2379:2379"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.12
|
||||
|
||||
etcdctl-ping:
|
||||
image: tenstartups/etcdctl
|
||||
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage"
|
||||
environment:
|
||||
ETCDCTL_DIAL_: "TIMEOUT 10s"
|
||||
ETCDCTL_API : "3"
|
||||
networks:
|
||||
- net
|
||||
|
||||
## BOULDER part ##
|
||||
|
||||
boulder:
|
||||
image: containous/boulder:release
|
||||
environment:
|
||||
FAKE_DNS: 172.17.0.1
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
extra_hosts:
|
||||
- le.wtf:127.0.0.1
|
||||
- boulder:127.0.0.1
|
||||
ports:
|
||||
- 4000:4000 # ACME
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- bhsm
|
||||
- bmysql
|
||||
- brabbitmq
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.3
|
||||
|
||||
bhsm:
|
||||
image: letsencrypt/boulder-tools:2016-11-02
|
||||
hostname: boulder-hsm
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so
|
||||
expose:
|
||||
- 5657
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.4
|
||||
aliases:
|
||||
- boulder-hsm
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
hostname: boulder-mysql
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.5
|
||||
aliases:
|
||||
- boulder-mysql
|
||||
|
||||
brabbitmq:
|
||||
image: rabbitmq:3-alpine
|
||||
hostname: boulder-rabbitmq
|
||||
environment:
|
||||
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.6
|
||||
aliases:
|
||||
- boulder-rabbitmq
|
||||
|
||||
## TRAEFIK part ##
|
||||
|
||||
traefik-storeconfig:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
volumes:
|
||||
- "./traefik.toml:/traefik.toml:ro"
|
||||
command: storeconfig --debug
|
||||
networks:
|
||||
- net
|
||||
|
||||
traefik01:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
- "443:443"
|
||||
- "5001:443" # Needed for SNI challenge
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.8
|
||||
|
||||
traefik02:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
ports:
|
||||
- "88:80"
|
||||
- "8888:8080"
|
||||
- "8443:443"
|
||||
depends_on:
|
||||
- traefik01
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.9
|
||||
|
||||
whoami01:
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam01"
|
||||
- "traefik.frontend.rule=Host:who01.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.10
|
||||
|
||||
whoami02:
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam02"
|
||||
- "traefik.frontend.rule=Host:who02.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
- net
|
||||
|
||||
networks:
|
||||
net:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 10.0.1.0/26
|
||||
216
examples/cluster/manage_cluster_docker_environment.sh
Executable file
216
examples/cluster/manage_cluster_docker_environment.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
# Initialize variables
|
||||
readonly basedir=$(dirname $0)
|
||||
readonly doc_file=$basedir"/docker-compose.yml"
|
||||
export COMPOSE_PROJECT_NAME="cluster"
|
||||
|
||||
# Stop and remove Docker environment
|
||||
down_environment() {
|
||||
echo "DOWN Docker environment"
|
||||
! docker-compose -f $doc_file down -v &>/dev/null && \
|
||||
echo "[ERROR] Unable to stop the Docker environment" && exit 11
|
||||
return 0
|
||||
}
|
||||
|
||||
# Create and start Docker-compose environment or subpart of its services (if services are listed)
|
||||
# $@ : List of services to start (optional)
|
||||
up_environment() {
|
||||
echo "START Docker environment "$@
|
||||
! docker-compose -f $doc_file up -d $@ &>/dev/null && \
|
||||
echo "[ERROR] Unable to start Docker environment ${@}" && exit 21
|
||||
return 0
|
||||
}
|
||||
|
||||
# Stop and remove Docker environment
|
||||
delete_services() {
|
||||
echo "DELETE services "$@
|
||||
! docker-compose -f $doc_file stop $@ &>/dev/null && \
|
||||
echo "[ERROR] Unable to stop services "$@ && exit 31
|
||||
! docker-compose -f $doc_file rm -vf $@ &>/dev/null && \
|
||||
echo "[ERROR] Unable to delete services "$@ && exit 31
|
||||
return 0
|
||||
}
|
||||
|
||||
start_consul() {
|
||||
up_environment consul
|
||||
waiting_counter=12
|
||||
# Not start Traefik store config if consul is not started
|
||||
echo "WAIT for consul..."
|
||||
sleep 5
|
||||
while [[ -z $(curl -s http://10.0.1.2:8500/v1/status/leader) ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[ERROR] Unable to start consul container in the allowed time, the Docker environment will be stopped"
|
||||
down_environment
|
||||
exit 41
|
||||
fi
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
start_etcd3() {
|
||||
up_environment etcd3
|
||||
waiting_counter=12
|
||||
# Not start Traefik store config if consul is not started
|
||||
echo "WAIT for ETCD3..."
|
||||
while [[ -z $(curl -s --connect-timeout 2 http://10.0.1.12:2379/version) ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[ERROR] Unable to start etcd3 container in the allowed time, the Docker environment will be stopped"
|
||||
down_environment
|
||||
exit 51
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
start_storeconfig_consul() {
|
||||
# Create traefik.toml with consul provider
|
||||
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||
echo '
|
||||
[consul]
|
||||
endpoint = "10.0.1.2:8500"
|
||||
watch = true
|
||||
prefix = "traefik"' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
delete_services traefik-storeconfig
|
||||
|
||||
}
|
||||
|
||||
start_storeconfig_etcd3() {
|
||||
# Create traefik.toml with consul provider
|
||||
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||
echo '
|
||||
[etcd]
|
||||
endpoint = "10.0.1.12:2379"
|
||||
watch = true
|
||||
prefix = "/traefik"
|
||||
useAPIV3 = true' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
# Don't start Traefik store config if ETCD3 is not started
|
||||
echo "Delete storage file key..."
|
||||
while [[ $(docker-compose -f $doc_file up --exit-code-from etcdctl-ping etcdctl-ping &>/dev/null) -ne 0 && $waiting_counter -gt 0 ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
done
|
||||
delete_services traefik-storeconfig etcdctl-ping
|
||||
}
|
||||
|
||||
start_traefik() {
|
||||
up_environment traefik01
|
||||
# Waiting for the first instance which is mapped to the host as leader before to start the second one
|
||||
waiting_counter=5
|
||||
echo "WAIT for traefik leader..."
|
||||
sleep 10
|
||||
while [[ -z $(curl -s --connect-timeout 3 http://10.0.1.8:8080/ping) ]]; do
|
||||
sleep 2
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[ERROR] Unable to start Traefik leader container in the allowed time, the Docker environment will be stopped"
|
||||
down_environment
|
||||
exit 51
|
||||
fi
|
||||
done
|
||||
up_environment whoami01
|
||||
waiting_counter=5
|
||||
echo "WAIT for whoami..."
|
||||
sleep 10
|
||||
while [[ -z $(curl -s --connect-timeout 3 http://10.0.1.10) ]]; do
|
||||
sleep 2
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[ERROR] Unable to start whoami container in the allowed time, the Docker environment will be stopped"
|
||||
down_environment
|
||||
exit 52
|
||||
fi
|
||||
done
|
||||
up_environment traefik02 whoami02
|
||||
}
|
||||
|
||||
# Start boulder services
|
||||
start_boulder() {
|
||||
echo "Start boulder environment"
|
||||
up_environment bmysql brabbitmq bhsm boulder
|
||||
waiting_counter=12
|
||||
# Not start Traefik if boulder is not started
|
||||
echo "WAIT for boulder..."
|
||||
while [[ -z $(curl -s http://10.0.1.3:4000/directory) ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
echo "[ERROR] Unable to start boulder container in the allowed time, the Docker environment will be stopped"
|
||||
down_environment
|
||||
exit 61
|
||||
fi
|
||||
done
|
||||
echo "Boulder started."
|
||||
}
|
||||
|
||||
# Script usage
|
||||
show_usage() {
|
||||
echo
|
||||
echo "USAGE : manage_cluster_docker_environment.sh [--start [--consul|--etcd3]|--stop|--restart [--consul|--etcd3]]"
|
||||
echo
|
||||
}
|
||||
|
||||
# Main method
|
||||
# $@ All parameters given
|
||||
main() {
|
||||
|
||||
[[ $# -lt 1 && $# -gt 2 ]] && show_usage && exit 1
|
||||
|
||||
case $1 in
|
||||
"--start")
|
||||
[[ $# -ne 2 ]] && show_usage && exit 2
|
||||
# The domains who01.localhost.com and who02.localhost.com have to refer 127.0.0.1
|
||||
# I, the /etc/hosts file
|
||||
for whoami_idx in "01" "02"; do
|
||||
[[ -z $(cat /etc/hosts | grep "127.0.0.1" | grep -vE "^#" | grep "who${whoami_idx}.localhost.com") ]] && \
|
||||
echo "[ERROR] Domain who${whoami_idx}.localhost.com has to refer to 127.0.0.1 into /etc/hosts file." && \
|
||||
exit 3
|
||||
done
|
||||
case $2 in
|
||||
"--etcd3")
|
||||
echo "USE ETCD V3 AS KV STORE"
|
||||
export TRAEFIK_CMD="--etcd --etcd.endpoint=10.0.1.12:2379 --etcd.useAPIV3=true"
|
||||
start_boulder && \
|
||||
start_etcd3 && \
|
||||
start_storeconfig_etcd3 && \
|
||||
start_traefik
|
||||
;;
|
||||
"--consul")
|
||||
echo "USE CONSUL AS KV STORE"
|
||||
export TRAEFIK_CMD="--consul --consul.endpoint=10.0.1.2:8500"
|
||||
start_boulder && \
|
||||
start_consul && \
|
||||
start_storeconfig_consul && \
|
||||
start_traefik
|
||||
;;
|
||||
*)
|
||||
show_usage && exit 4
|
||||
;;
|
||||
esac
|
||||
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||
;;
|
||||
"--stop")
|
||||
! down_environment
|
||||
echo "ENVIRONMENT SUCCESSFULLY STOPPED"
|
||||
;;
|
||||
"--restart")
|
||||
[[ $# -ne 2 ]] && show_usage && exit 5
|
||||
down_environment
|
||||
main --start $2
|
||||
;;
|
||||
*)
|
||||
show_usage && exit 6
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main $@
|
||||
27
examples/cluster/traefik.toml.tmpl
Normal file
27
examples/cluster/traefik.toml.tmpl
Normal file
@@ -0,0 +1,27 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "traefik/acme/account"
|
||||
entryPoint = "https"
|
||||
OnHostRule = true
|
||||
caServer = "http://traefik.boulder.com:4000/directory"
|
||||
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[docker]
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
domain = "localhost.com"
|
||||
watch = true
|
||||
exposedbydefault = false
|
||||
@@ -23,3 +23,14 @@ curl -i -H "Accept: application/json" -X PUT -d "Host:test.localhost" ht
|
||||
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend
|
||||
curl -i -H "Accept: application/json" -X PUT -d "http" http://localhost:8500/v1/kv/traefik/frontends/frontend2/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d "Path:/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule
|
||||
|
||||
|
||||
# certificate 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d "https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/certfile
|
||||
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/keyfile
|
||||
|
||||
# certificate 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d "http,https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/certfile
|
||||
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/keyfile
|
||||
|
||||
@@ -1,25 +1,115 @@
|
||||
#!/bin/sh
|
||||
#! /usr/bin/env bash
|
||||
|
||||
# backend 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="NetworkErrorRatio() > 0.5" http://localhost:2379/v2/keys/traefik/backends/backend1/circuitbreaker/expression
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.2:80" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server1/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="10" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server1/weight
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.3:80" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server2/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server2/weight
|
||||
#
|
||||
# Insert data in ETCD V3
|
||||
function insert_etcd2_data() {
|
||||
# backend 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="NetworkErrorRatio() > 0.5" http://localhost:2379/v2/keys/traefik/backends/backend1/circuitbreaker/expression
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.2:80" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server1/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="10" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server1/weight
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.3:80" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server2/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:2379/v2/keys/traefik/backends/backend1/servers/server2/weight
|
||||
|
||||
# backend 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="drr" http://localhost:2379/v2/keys/traefik/backends/backend2/loadbalancer/method
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.4:80" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server1/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server1/weight
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.5:80" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server2/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="2" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server2/weight
|
||||
# backend 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="drr" http://localhost:2379/v2/keys/traefik/backends/backend2/loadbalancer/method
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.4:80" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server1/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server1/weight
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.5:80" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server2/url
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="2" http://localhost:2379/v2/keys/traefik/backends/backend2/servers/server2/weight
|
||||
|
||||
# frontend 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="backend2" http://localhost:2379/v2/keys/traefik/frontends/frontend1/backend
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:2379/v2/keys/traefik/frontends/frontend1/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="Host:test.localhost" http://localhost:2379/v2/keys/traefik/frontends/frontend1/routes/test_1/rule
|
||||
# frontend 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="backend2" http://localhost:2379/v2/keys/traefik/frontends/frontend1/backend
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:2379/v2/keys/traefik/frontends/frontend1/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="Host:test.localhost" http://localhost:2379/v2/keys/traefik/frontends/frontend1/routes/test_1/rule
|
||||
|
||||
# frontend 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="backend1" http://localhost:2379/v2/keys/traefik/frontends/frontend2/backend
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:2379/v2/keys/traefik/frontends/frontend2/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="Path:/test" http://localhost:2379/v2/keys/traefik/frontends/frontend2/routes/test_2/rule
|
||||
# frontend 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="backend1" http://localhost:2379/v2/keys/traefik/frontends/frontend2/backend
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:2379/v2/keys/traefik/frontends/frontend2/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="Path:/test" http://localhost:2379/v2/keys/traefik/frontends/frontend2/routes/test_2/rule
|
||||
|
||||
# certificate 1
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="https" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.crt" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/certificate/certfile
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test1.key" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair1/certificate/keyfile
|
||||
|
||||
# certificate 2
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="http,https" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/entrypoints
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.crt" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/certificate/certfile
|
||||
curl -i -H "Accept: application/json" -X PUT -d value="/tmp/test2.key" http://localhost:2379/v2/keys/traefik/tlsconfiguration/pair2/certificate/keyfile
|
||||
}
|
||||
|
||||
#
|
||||
# Insert data in ETCD V3
|
||||
# $1 = ECTD IP address
|
||||
# Note : This function allows adding data in a ETCD V3 which is directly installed on a host
|
||||
# or in container which binds its port 2379 on a host in the way to allows etcd_client container to access it.
|
||||
function insert_etcd3_data() {
|
||||
|
||||
readonly etcd_ip=$1
|
||||
# backend 1
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend1/circuitbreaker/expression" "NetworkErrorRatio() > 0.5"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend1/servers/server1/url" "http://172.17.0.2:80"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend1/servers/server1/weight" "10"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend1/servers/server2/url" "http://172.17.0.3:80"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend1/servers/server2/weight" "1"
|
||||
|
||||
# backend 2
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend2/loadbalancer/method" "drr"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend2/servers/server1/url" "http://172.17.0.4:80"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend2/servers/server1/weight" "1"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend2/servers/server2/url" "http://172.17.0.5:80"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/backends/backend2/servers/server2/weight" "2"
|
||||
|
||||
# frontend 1
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend1/backend" "backend2"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik//frontends/frontend1/entrypoints" "http"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend1/routes/test_1/rule" "Host:test.localhost"
|
||||
|
||||
# frontend 2
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend2/backend" "backend1"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend2/entrypoints" "http"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/frontends/frontend2/routes/test_2/rule" "Path:/test"
|
||||
|
||||
# certificate 1
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/entrypoints" "https"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/certificate/certfile" "/tmp/test1.crt"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair1/certificate/keyfile" "/tmp/test1.key"
|
||||
|
||||
# certificate 2
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/entrypoints" "https"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/certificate/certfile" "/tmp/test2.crt"
|
||||
docker container run --rm -ti -e ETCDCTL_DIAL_="TIMEOUT 10s" -e ETCDCTL_API="3" tenstartups/etcdctl --endpoints=[$etcd_ip:2379] put "/traefik/tlsconfiguration/pair2/certificate/keyfile" "/tmp/test2.key"
|
||||
}
|
||||
|
||||
function show_usage() {
|
||||
echo "USAGE : etcd-config.sh ETCD_API_VERSION [ETCD_IP_ADDRESS]"
|
||||
echo " ETCD_API_VERSION : Values V2 or V3 (V3 requires ETCD_IP_ADDRESS)"
|
||||
echo " ETCD_IP_ADDRESS : Host ETCD IP address (not 127.0.0.1)"
|
||||
}
|
||||
|
||||
function main() {
|
||||
case $# in
|
||||
1)
|
||||
if [[ $1 == "V2" ]]; then
|
||||
insert_etcd2_data
|
||||
else
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
if [[ $1 == "V3" && $2 != "127.0.0.1" && ! -z $(echo $2 | grep -oE "([0-9]+(\.)?){4}") ]]; then
|
||||
insert_etcd3_data $2
|
||||
else
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main $@
|
||||
|
||||
13
generate.go
13
generate.go
@@ -1,9 +1,10 @@
|
||||
/*
|
||||
Copyright
|
||||
*/
|
||||
|
||||
//go:generate rm -vf autogen/gen.go
|
||||
//go:generate rm -vf autogen/gentemplates/gen.go
|
||||
//go:generate rm -vf autogen/genstatic/gen.go
|
||||
//go:generate mkdir -p static
|
||||
//go:generate go-bindata -pkg autogen -o autogen/gen.go ./static/... ./templates/...
|
||||
//go:generate go-bindata -pkg gentemplates -nometadata -nocompress -o autogen/gentemplates/gen.go ./templates/...
|
||||
//go:generate gofmt -s -w autogen/gentemplates/gen.go
|
||||
//go:generate go-bindata -pkg genstatic -nocompress -o autogen/genstatic/gen.go ./static/...
|
||||
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
|
||||
131
glide.lock
generated
131
glide.lock
generated
@@ -1,5 +1,5 @@
|
||||
hash: 9d176906cf25eaa3224c750b1dd3a5c8ce3340a58df3c562e57572c6294e16f0
|
||||
updated: 2017-09-30T18:32:16.848940186+02:00
|
||||
hash: 2ca4d2b4f55342c6a722f70e0ef2e85ac2a38d8395dc206ad3f71a785b9f050f
|
||||
updated: 2017-12-15T10:34:41.246378337+01:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||
@@ -56,13 +56,14 @@ imports:
|
||||
- service/route53
|
||||
- service/sts
|
||||
- name: github.com/Azure/azure-sdk-for-go
|
||||
version: 088007b3b08cc02b27f2eadfdcd870958460ce7e
|
||||
version: f7bb4db3ea4c73dc58bd284c38ea644a79324be0
|
||||
subpackages:
|
||||
- arm/dns
|
||||
- name: github.com/Azure/go-autorest
|
||||
version: a2fdd780c9a50455cecd249b00bdc3eb73a78e31
|
||||
version: f6be1abbb5abd0517522f850dd785990d373da7e
|
||||
subpackages:
|
||||
- autorest
|
||||
- autorest/adal
|
||||
- autorest/azure
|
||||
- autorest/date
|
||||
- autorest/to
|
||||
@@ -87,17 +88,29 @@ imports:
|
||||
- name: github.com/codegangsta/cli
|
||||
version: bf4a526f48af7badd25d2cb02d587e1b01be3b50
|
||||
- name: github.com/containous/flaeg
|
||||
version: b5d2dc5878df07c2d74413348186982e7b865871
|
||||
version: 60c87a513a955ca7225e1b1c772581cea8420cb4
|
||||
- name: github.com/containous/mux
|
||||
version: af6ea922f7683d9706834157e6b0610e22ccb2db
|
||||
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
||||
- name: github.com/containous/staert
|
||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||
version: af517d5b70db9c4b0505e0144fcc62b054057d2a
|
||||
- name: github.com/containous/traefik-extra-service-fabric
|
||||
version: ca1fb57108293caad285b1c366b763f6c6ab71c9
|
||||
- name: github.com/coreos/bbolt
|
||||
version: 3c6cbfb299c11444eb2f8c9d48f0d2ce09157423
|
||||
- name: github.com/coreos/etcd
|
||||
version: c400d05d0aa73e21e431c16145e558d624098018
|
||||
version: f1d7dd87da3e8feab4aaf675b8e29c6a5ed5f58b
|
||||
subpackages:
|
||||
- auth/authpb
|
||||
- client
|
||||
- clientv3
|
||||
- clientv3/concurrency
|
||||
- etcdserver/api/v3rpc/rpctypes
|
||||
- etcdserver/etcdserverpb
|
||||
- mvcc/mvccpb
|
||||
- pkg/pathutil
|
||||
- pkg/srv
|
||||
- pkg/types
|
||||
- version
|
||||
- name: github.com/coreos/go-oidc
|
||||
version: 5644a2f50e2d2d5ba0b474bc5bc55fea1925936d
|
||||
subpackages:
|
||||
@@ -106,6 +119,10 @@ imports:
|
||||
- key
|
||||
- oauth2
|
||||
- oidc
|
||||
- name: github.com/coreos/go-semver
|
||||
version: 8ab6407b697782a06568d4b7f1db25550ec2e4c6
|
||||
subpackages:
|
||||
- semver
|
||||
- name: github.com/coreos/go-systemd
|
||||
version: 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
||||
subpackages:
|
||||
@@ -121,11 +138,11 @@ imports:
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/decker502/dnspod-go
|
||||
version: 68650ee11e182e30773781d391c66a0c80ccf9f2
|
||||
version: f33a2c6040fc2550a631de7b3a53bddccdcd73fb
|
||||
- name: github.com/dgrijalva/jwt-go
|
||||
version: d2709f9f1f31ebcda9651b03077758c1f3a0018c
|
||||
- name: github.com/dnsimple/dnsimple-go
|
||||
version: 5a5b427618a76f9eed5ede0f3e6306fbd9311d2e
|
||||
version: f2d9b723cc9547d182e24ac2e527ae25d25fc93f
|
||||
subpackages:
|
||||
- dnsimple
|
||||
- name: github.com/docker/distribution
|
||||
@@ -201,14 +218,20 @@ imports:
|
||||
- name: github.com/docker/go-units
|
||||
version: 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||
- name: github.com/docker/leadership
|
||||
version: 0a913e2d71a12fd14a028452435cb71ac8d82cb6
|
||||
version: af20da7d3e62be9259835e93261acf931b5adecf
|
||||
repo: https://github.com/containous/leadership.git
|
||||
vcs: git
|
||||
- name: github.com/docker/libkv
|
||||
version: 93ab0e6c056d325dfbb11e1d58a3b4f5f62e7f3c
|
||||
version: 5e4bb288a9a74320bb03f5c18d6bdbab0d8049de
|
||||
repo: https://github.com/abronan/libkv.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- store
|
||||
- store/boltdb
|
||||
- store/consul
|
||||
- store/etcd
|
||||
- store/etcd/v2
|
||||
- store/etcd/v3
|
||||
- store/zookeeper
|
||||
- name: github.com/docker/libtrust
|
||||
version: 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||
@@ -219,7 +242,7 @@ imports:
|
||||
- name: github.com/eapache/queue
|
||||
version: 44cc805cf13205b55f69e14bcb69867d1ae92f98
|
||||
- name: github.com/edeckers/auroradnsclient
|
||||
version: 8b777c170cfd377aa16bb4368f093017dddef3f9
|
||||
version: 398f53855ba258191157e20fabfaccca5e13cea9
|
||||
subpackages:
|
||||
- records
|
||||
- requests
|
||||
@@ -233,20 +256,24 @@ imports:
|
||||
subpackages:
|
||||
- log
|
||||
- swagger
|
||||
- name: github.com/exoscale/egoscale
|
||||
version: 325740036187ddae3a5b74be00fbbc70011c4d96
|
||||
- name: github.com/fatih/color
|
||||
version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: dd6cbd4c2d71294a19fb89158f2a00d427f174ab
|
||||
version: 03b46169666c53b9cc953b875ac5714e5103e064
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||
- name: github.com/go-ini/ini
|
||||
version: e7fea39b01aea8d5671f6858f0532f56e8bff3a5
|
||||
version: f384f410798cbe7cdce40eec40b79ed32bb4f1ad
|
||||
- name: github.com/go-kit/kit
|
||||
version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41
|
||||
subpackages:
|
||||
- log
|
||||
- metrics
|
||||
- metrics/dogstatsd
|
||||
- metrics/generic
|
||||
- metrics/influx
|
||||
- metrics/internal/lv
|
||||
- metrics/internal/ratemap
|
||||
- metrics/multi
|
||||
@@ -273,9 +300,11 @@ imports:
|
||||
- name: github.com/golang/glog
|
||||
version: 44145f04b68cf362d9c4df2182967c2275eaefed
|
||||
- name: github.com/golang/protobuf
|
||||
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
||||
version: 4bd1920723d7b7c925de087aa32e2187708897f7
|
||||
subpackages:
|
||||
- jsonpb
|
||||
- proto
|
||||
- ptypes/any
|
||||
- name: github.com/google/go-github
|
||||
version: fe7d11f8add400587b6718d9f39a62e42cb04c28
|
||||
subpackages:
|
||||
@@ -307,11 +336,19 @@ imports:
|
||||
- name: github.com/huandu/xstrings
|
||||
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||
- name: github.com/imdario/mergo
|
||||
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
|
||||
version: 7fe0c75c13abdee74b09fcacef5ea1c6bba6a874
|
||||
- name: github.com/influxdata/influxdb
|
||||
version: 2d474a3089bcfce6b472779be9470a1f0ef3d5e4
|
||||
subpackages:
|
||||
- client/v2
|
||||
- models
|
||||
- pkg/escape
|
||||
- name: github.com/JamesClonk/vultr
|
||||
version: 0f156dd232bc4ebf8a32ba83fec57c0e4c9db69f
|
||||
version: 2fd0705ce648e602e6c9c57329a174270a4f6688
|
||||
subpackages:
|
||||
- lib
|
||||
- name: github.com/jjcollinge/servicefabric
|
||||
version: 8026935326c842b71dee8e2329c1fda41a7a92f4
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jonboulle/clockwork
|
||||
@@ -320,8 +357,12 @@ imports:
|
||||
version: 77ed1c8a01217656d2080ad51981f6e99adaa177
|
||||
- name: github.com/kr/logfmt
|
||||
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
||||
- name: github.com/mailgun/minheap
|
||||
version: 7c28d80e2ada649fc8ab1a37b86d30a2633bd47c
|
||||
- name: github.com/mailgun/timetools
|
||||
version: 7e6055773c5137efbeb3bd2410d705fe10ab6bfd
|
||||
- name: github.com/mailgun/ttlmap
|
||||
version: c1c17f74874f2a5ea48bfb06b5459d4ef2689749
|
||||
- name: github.com/mailru/easyjson
|
||||
version: d5b7844b561a7bc640052f1b935f7b800330d7e0
|
||||
subpackages:
|
||||
@@ -331,7 +372,7 @@ imports:
|
||||
- name: github.com/Masterminds/semver
|
||||
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||
- name: github.com/Masterminds/sprig
|
||||
version: 9526be0327b26ad31aa70296a7b10704883976d5
|
||||
version: e039e20e500c2c025d9145be375e27cf42a94174
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
||||
repo: https://github.com/mattn/go-colorable
|
||||
@@ -372,6 +413,8 @@ imports:
|
||||
version: 8060d9f51305bbe024b99679454e62f552cd0b0b
|
||||
- name: github.com/mitchellh/copystructure
|
||||
version: d23ffcb85de31694d6ccaa23ccb4a03e55c1303f
|
||||
- name: github.com/mitchellh/hashstructure
|
||||
version: 2bca23e0e452137f789efbc8610126fd8b94f73b
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: d0303fe809921458f417bcf828397a65db30a7e4
|
||||
- name: github.com/mitchellh/reflectwalk
|
||||
@@ -383,7 +426,7 @@ imports:
|
||||
repo: https://github.com/ijc25/Gotty.git
|
||||
vcs: git
|
||||
- name: github.com/NYTimes/gziphandler
|
||||
version: 97ae7fbaf81620fe97840685304a78a306a39c64
|
||||
version: d6f46609c7629af3a02d791a4666866eed3cbd3e
|
||||
- name: github.com/ogier/pflag
|
||||
version: 45c278ab3607870051a2ea9040bb85fcb8557481
|
||||
- name: github.com/opencontainers/go-digest
|
||||
@@ -394,7 +437,7 @@ imports:
|
||||
- specs-go
|
||||
- specs-go/v1
|
||||
- name: github.com/ovh/go-ovh
|
||||
version: d2207178e10e4527e8f222fd8707982df8c3af17
|
||||
version: 4b1fea467323b74c5f462f0947f402b428ca0626
|
||||
subpackages:
|
||||
- ovh
|
||||
- name: github.com/pborman/uuid
|
||||
@@ -428,16 +471,12 @@ imports:
|
||||
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
||||
- name: github.com/PuerkitoBio/urlesc
|
||||
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
|
||||
- name: github.com/pyr/egoscale
|
||||
version: 987e683a7552f34ee586217d1cc8507d52e80ab9
|
||||
subpackages:
|
||||
- src/egoscale
|
||||
- name: github.com/rancher/go-rancher
|
||||
version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b
|
||||
version: 52e2f489534007ae843065468c5a1920d542afa4
|
||||
subpackages:
|
||||
- client
|
||||
- v2
|
||||
- name: github.com/rancher/go-rancher-metadata
|
||||
version: 95d4962a8f0420be24fb49c2cb4f5491284c62f1
|
||||
version: d2103caca5873119ff423d29cba09b4d03cd69b8
|
||||
subpackages:
|
||||
- metadata
|
||||
- name: github.com/ryanuber/go-glob
|
||||
@@ -452,8 +491,6 @@ imports:
|
||||
version: 10f801ebc38b33738c9d17d50860f484a0988ff5
|
||||
- name: github.com/spf13/pflag
|
||||
version: cb88ea77998c3f024757528e3305022ab50b43be
|
||||
- name: github.com/streamrail/concurrent-map
|
||||
version: 8bf1e9bacbf65b10c81d0f4314cf2b1ebef728b5
|
||||
- name: github.com/stretchr/objx
|
||||
version: cbeaeb16a013161a98496fad62933b1d21786672
|
||||
- name: github.com/stretchr/testify
|
||||
@@ -480,8 +517,10 @@ imports:
|
||||
version: 824e85271811af89640ea25620c67f6c2eed987e
|
||||
- name: github.com/urfave/negroni
|
||||
version: 490e6a555d47ca891a89a150d0c1ef3922dfffe9
|
||||
- name: github.com/VividCortex/gohistogram
|
||||
version: 51564d9861991fb0ad0f531c99ef602d0f9866e6
|
||||
- name: github.com/vulcand/oxy
|
||||
version: 648088ee0902cf8d8337826ae2a82444008720e2
|
||||
version: 812cebb8c764f2a78cb806267648b8728b4599ad
|
||||
repo: https://github.com/containous/oxy.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
@@ -489,6 +528,7 @@ imports:
|
||||
- connlimit
|
||||
- forward
|
||||
- memmetrics
|
||||
- ratelimit
|
||||
- roundrobin
|
||||
- stream
|
||||
- utils
|
||||
@@ -504,7 +544,7 @@ imports:
|
||||
- plugin/rewrite
|
||||
- router
|
||||
- name: github.com/xenolf/lego
|
||||
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd
|
||||
version: 67c86d860a797ce2483f50d9174d4ed24984bef2
|
||||
subpackages:
|
||||
- acme
|
||||
- providers/dns
|
||||
@@ -522,6 +562,7 @@ imports:
|
||||
- providers/dns/linode
|
||||
- providers/dns/namecheap
|
||||
- providers/dns/ns1
|
||||
- providers/dns/otc
|
||||
- providers/dns/ovh
|
||||
- providers/dns/pdns
|
||||
- providers/dns/rackspace
|
||||
@@ -537,7 +578,7 @@ imports:
|
||||
- pbkdf2
|
||||
- scrypt
|
||||
- name: golang.org/x/net
|
||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||
version: c8c74377599bd978aee1cf3b9b63a8634051cec2
|
||||
subpackages:
|
||||
- context
|
||||
- context/ctxhttp
|
||||
@@ -563,9 +604,10 @@ imports:
|
||||
- unix
|
||||
- windows
|
||||
- name: golang.org/x/text
|
||||
version: 2910a502d2bf9e43193af9d68ca516529614eed3
|
||||
version: 4ee4af566555f5fbe026368b75596286a312663a
|
||||
subpackages:
|
||||
- cases
|
||||
- internal
|
||||
- internal/tag
|
||||
- language
|
||||
- runes
|
||||
@@ -580,7 +622,7 @@ imports:
|
||||
subpackages:
|
||||
- rate
|
||||
- name: google.golang.org/api
|
||||
version: 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
||||
version: 1575df15c1bb8b18ad4d9bc5ca495cc85b0764fe
|
||||
subpackages:
|
||||
- dns/v1
|
||||
- gensupport
|
||||
@@ -598,11 +640,17 @@ imports:
|
||||
- internal/remote_api
|
||||
- internal/urlfetch
|
||||
- urlfetch
|
||||
- name: google.golang.org/genproto
|
||||
version: 09f6ed296fc66555a25fe4ce95173148778dfa85
|
||||
subpackages:
|
||||
- googleapis/rpc/status
|
||||
- name: google.golang.org/grpc
|
||||
version: cdee119ee21e61eef7093a41ba148fa83585e143
|
||||
version: b3ddf786825de56a4178401b7e174ee332173b66
|
||||
subpackages:
|
||||
- codes
|
||||
- connectivity
|
||||
- credentials
|
||||
- grpclb/grpc_lb_v1
|
||||
- grpclog
|
||||
- internal
|
||||
- keepalive
|
||||
@@ -610,6 +658,7 @@ imports:
|
||||
- naming
|
||||
- peer
|
||||
- stats
|
||||
- status
|
||||
- tap
|
||||
- transport
|
||||
- name: gopkg.in/fsnotify.v1
|
||||
@@ -617,9 +666,9 @@ imports:
|
||||
- name: gopkg.in/inf.v0
|
||||
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
|
||||
- name: gopkg.in/ini.v1
|
||||
version: e7fea39b01aea8d5671f6858f0532f56e8bff3a5
|
||||
version: 5b3e00af70a9484542169a976dcab8d03e601a17
|
||||
- name: gopkg.in/ns1/ns1-go.v2
|
||||
version: 2abc76c60bf88ba33b15d1d87a13f624d8dff956
|
||||
version: c563826f4cbef9c11bebeb9f20a3f7afe9c1e2f4
|
||||
subpackages:
|
||||
- rest
|
||||
- rest/model/account
|
||||
@@ -746,7 +795,7 @@ imports:
|
||||
- transport
|
||||
testImports:
|
||||
- name: github.com/Azure/go-ansiterm
|
||||
version: 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
|
||||
version: d6e3b3328b783f23731bc4d058875b0371ff8109
|
||||
subpackages:
|
||||
- winterm
|
||||
- name: github.com/docker/cli
|
||||
@@ -787,7 +836,7 @@ testImports:
|
||||
- name: github.com/gorilla/mux
|
||||
version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf
|
||||
- name: github.com/libkermit/compose
|
||||
version: 2048f803f56422a65b455f918d4a61704dc94603
|
||||
version: 4a33a16f1446ba205c4da7b09105d5bdc293b432
|
||||
subpackages:
|
||||
- check
|
||||
- name: github.com/libkermit/docker
|
||||
@@ -800,7 +849,7 @@ testImports:
|
||||
- libcontainer/system
|
||||
- libcontainer/user
|
||||
- name: github.com/stvp/go-udp-testing
|
||||
version: 06eb4f886d9f8242b0c176cf0d3ce5ec2cedda05
|
||||
version: c4434f09ec131ecf30f986d5dcb1636508bfa49a
|
||||
- name: github.com/vdemeester/shakers
|
||||
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||
- name: github.com/xeipuuv/gojsonpointer
|
||||
|
||||
39
glide.yaml
39
glide.yaml
@@ -11,8 +11,10 @@ import:
|
||||
version: 10f801ebc38b33738c9d17d50860f484a0988ff5
|
||||
- package: github.com/cenk/backoff
|
||||
- package: github.com/containous/flaeg
|
||||
- package: github.com/containous/traefik-extra-service-fabric
|
||||
version: v1.0.5
|
||||
- package: github.com/vulcand/oxy
|
||||
version: 648088ee0902cf8d8337826ae2a82444008720e2
|
||||
version: 7b6e758ab449705195df638765c4ca472248908a
|
||||
repo: https://github.com/containous/oxy.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
@@ -22,10 +24,11 @@ import:
|
||||
- roundrobin
|
||||
- stream
|
||||
- utils
|
||||
- ratelimit
|
||||
- package: github.com/urfave/negroni
|
||||
version: 490e6a555d47ca891a89a150d0c1ef3922dfffe9
|
||||
- package: github.com/containous/staert
|
||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||
version: ^v2.0.0
|
||||
- package: github.com/docker/docker
|
||||
version: 75c7536d2e2e328b644bf69153de879d1d197988
|
||||
- package: github.com/docker/go-connections
|
||||
@@ -35,19 +38,23 @@ import:
|
||||
- tlsconfig
|
||||
- package: github.com/docker/go-units
|
||||
version: 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||
- package: github.com/coreos/etcd
|
||||
version: v3.2.9
|
||||
- package: github.com/docker/libkv
|
||||
repo: https://github.com/abronan/libkv.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- store
|
||||
- store/boltdb
|
||||
- store/consul
|
||||
- store/etcd
|
||||
- store/etcd/v2
|
||||
- store/etcd/v3
|
||||
- store/zookeeper
|
||||
- package: github.com/elazarl/go-bindata-assetfs
|
||||
- package: github.com/containous/mux
|
||||
- package: github.com/hashicorp/consul
|
||||
subpackages:
|
||||
- api
|
||||
- package: github.com/streamrail/concurrent-map
|
||||
- package: github.com/thoas/stats
|
||||
version: 152b5d051953fdb6e45f14b6826962aadc032324
|
||||
- package: github.com/unrolled/render
|
||||
@@ -58,7 +65,7 @@ import:
|
||||
- package: github.com/vulcand/predicate
|
||||
version: 19b9dde14240d94c804ae5736ad0e1de10bf8fe6
|
||||
- package: github.com/xenolf/lego
|
||||
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd
|
||||
version: 67c86d860a797ce2483f50d9174d4ed24984bef2
|
||||
subpackages:
|
||||
- acme
|
||||
- package: gopkg.in/fsnotify.v1
|
||||
@@ -80,10 +87,16 @@ import:
|
||||
- package: github.com/abbot/go-http-auth
|
||||
- package: github.com/NYTimes/gziphandler
|
||||
- package: github.com/docker/leadership
|
||||
repo: https://github.com/containous/leadership.git
|
||||
vcs: git
|
||||
- package: github.com/satori/go.uuid
|
||||
version: ^1.1.0
|
||||
- package: k8s.io/client-go
|
||||
version: v2.0.0
|
||||
- package: github.com/influxdata/influxdb
|
||||
version: v1.3.7
|
||||
subpackages:
|
||||
- client/v2
|
||||
- package: github.com/gambol99/go-marathon
|
||||
version: dd6cbd4c2d71294a19fb89158f2a00d427f174ab
|
||||
- package: github.com/ArthurHlt/go-eureka-client
|
||||
@@ -102,10 +115,13 @@ import:
|
||||
- log
|
||||
- metrics
|
||||
- metrics/dogstatsd
|
||||
- metrics/internal/lv
|
||||
- metrics/internal/ratemap
|
||||
- metrics/multi
|
||||
- metrics/prometheus
|
||||
- metrics/statsd
|
||||
- util/conn
|
||||
- metrics/influx
|
||||
- package: github.com/prometheus/client_golang
|
||||
version: 08fd2e12372a66e68e30523c7642e0cbc3e4fbde
|
||||
subpackages:
|
||||
@@ -123,7 +139,7 @@ import:
|
||||
- package: golang.org/x/sys
|
||||
version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
||||
- package: golang.org/x/net
|
||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||
version: c8c74377599bd978aee1cf3b9b63a8634051cec2
|
||||
subpackages:
|
||||
- http2
|
||||
- context
|
||||
@@ -163,9 +179,9 @@ import:
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/golang/protobuf
|
||||
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
||||
version: 4bd1920723d7b7c925de087aa32e2187708897f7
|
||||
- package: github.com/rancher/go-rancher
|
||||
version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b
|
||||
version: 52e2f489534007ae843065468c5a1920d542afa4
|
||||
- package: golang.org/x/oauth2
|
||||
version: 7fdf09982454086d5570c7db3e11f360194830ca
|
||||
subpackages:
|
||||
@@ -173,11 +189,11 @@ import:
|
||||
- package: golang.org/x/time
|
||||
version: 8be79e1e0910c292df4e79c241bb7e8f7e725959
|
||||
- package: github.com/rancher/go-rancher-metadata
|
||||
version: 95d4962a8f0420be24fb49c2cb4f5491284c62f1
|
||||
version: d2103caca5873119ff423d29cba09b4d03cd69b8
|
||||
- package: github.com/googleapis/gax-go
|
||||
version: 9af46dd5a1713e8b5cd71106287eba3cefdde50b
|
||||
- package: google.golang.org/grpc
|
||||
version: v1.2.0
|
||||
version: v1.5.2
|
||||
- package: github.com/unrolled/secure
|
||||
version: 824e85271811af89640ea25620c67f6c2eed987e
|
||||
- package: github.com/Nvveen/Gotty
|
||||
@@ -201,6 +217,7 @@ import:
|
||||
- package: github.com/armon/go-proxyproto
|
||||
version: 48572f11356f1843b694f21a290d4f1006bc5e47
|
||||
- package: github.com/mitchellh/copystructure
|
||||
- package: github.com/mitchellh/hashstructure
|
||||
testImport:
|
||||
- package: github.com/stvp/go-udp-testing
|
||||
- package: github.com/docker/libcompose
|
||||
@@ -210,7 +227,7 @@ testImport:
|
||||
repo: https://github.com/containous/check.git
|
||||
vcs: git
|
||||
- package: github.com/libkermit/compose
|
||||
version: 2048f803f56422a65b455f918d4a61704dc94603
|
||||
version: 4a33a16f1446ba205c4da7b09105d5bdc293b432
|
||||
subpackages:
|
||||
- check
|
||||
- package: github.com/libkermit/docker
|
||||
|
||||
@@ -28,10 +28,11 @@ func GetHealthCheck() *HealthCheck {
|
||||
|
||||
// Options are the public health check options.
|
||||
type Options struct {
|
||||
Path string
|
||||
Port int
|
||||
Interval time.Duration
|
||||
LB LoadBalancer
|
||||
Path string
|
||||
Port int
|
||||
Transport http.RoundTripper
|
||||
Interval time.Duration
|
||||
LB LoadBalancer
|
||||
}
|
||||
|
||||
func (opt Options) String() string {
|
||||
@@ -132,7 +133,7 @@ func checkBackend(currentBackend *BackendHealthCheck) {
|
||||
|
||||
func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) {
|
||||
if backend.Port == 0 {
|
||||
return http.NewRequest("GET", serverURL.String()+backend.Path, nil)
|
||||
return http.NewRequest(http.MethodGet, serverURL.String()+backend.Path, nil)
|
||||
}
|
||||
|
||||
// copy the url and add the port to the host
|
||||
@@ -141,12 +142,13 @@ func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request
|
||||
u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(backend.Port))
|
||||
u.Path = u.Path + backend.Path
|
||||
|
||||
return http.NewRequest("GET", u.String(), nil)
|
||||
return http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
}
|
||||
|
||||
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool {
|
||||
client := http.Client{
|
||||
Timeout: backend.requestTimeout,
|
||||
Timeout: backend.requestTimeout,
|
||||
Transport: backend.Options.Transport,
|
||||
}
|
||||
req, err := backend.newRequest(serverURL)
|
||||
if err != nil {
|
||||
@@ -159,5 +161,5 @@ func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool {
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err == nil && resp.StatusCode == 200
|
||||
return err == nil && resp.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
@@ -92,6 +92,26 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithWildcard(c *check.C
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnDemand option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
||||
onDemand: true,
|
||||
domainToCheck: wildcardDomain}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test onHostRule option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
||||
onDemand: false,
|
||||
domainToCheck: wildcardDomain}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -3,7 +3,9 @@ package integration
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/try"
|
||||
@@ -101,3 +103,236 @@ func (s *SimpleSuite) TestPrintHelp(c *check.C) {
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestRequestAcceptGraceTimeout(c *check.C) {
|
||||
s.createComposeProject(c, "reqacceptgrace")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
whoami := "http://" + s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress + ":80"
|
||||
|
||||
file := s.adaptFile(c, "fixtures/reqacceptgrace.toml", struct {
|
||||
Server string
|
||||
}{whoami})
|
||||
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()
|
||||
|
||||
// Wait for Traefik to turn ready.
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Make sure exposed service is ready.
|
||||
err = try.GetRequest("http://127.0.0.1:8000/service", 3*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Send SIGTERM to Traefik.
|
||||
proc, err := os.FindProcess(cmd.Process.Pid)
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = proc.Signal(syscall.SIGTERM)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Give Traefik time to process the SIGTERM and send a request half-way
|
||||
// into the request accepting grace period, by which requests should
|
||||
// still get served.
|
||||
time.Sleep(5 * time.Second)
|
||||
resp, err := http.Get("http://127.0.0.1:8000/service")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer resp.Body.Close()
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
// Expect Traefik to shut down gracefully once the request accepting grace
|
||||
// period has elapsed.
|
||||
waitErr := make(chan error)
|
||||
go func() {
|
||||
waitErr <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-waitErr:
|
||||
c.Assert(err, checker.IsNil)
|
||||
case <-time.After(10 * time.Second):
|
||||
// By now we are ~5 seconds out of the request accepting grace period
|
||||
// (start + 5 seconds sleep prior to the mid-grace period request +
|
||||
// 10 seconds timeout = 15 seconds > 10 seconds grace period).
|
||||
// Something must have gone wrong if we still haven't terminated at
|
||||
// this point.
|
||||
c.Fatal("Traefik did not terminate in time")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestApiOnSameEntryPoint(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd("--defaultEntryPoints=http", "--entryPoints=Name:http Address::8000", "--api.entryPoint=http", "--debug", "--docker")
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// TODO validate : run on 80
|
||||
// Expected a 404 as we did not configure anything
|
||||
err = try.GetRequest("http://127.0.0.1:8000/test", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestStatsWithMultipleEntryPoint(c *check.C) {
|
||||
s.createComposeProject(c, "stats")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
whoami1 := "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80"
|
||||
whoami2 := "http://" + s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress + ":80"
|
||||
|
||||
file := s.adaptFile(c, "fixtures/simple_stats.toml", struct {
|
||||
Server1 string
|
||||
Server2 string
|
||||
}{whoami1, whoami2})
|
||||
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", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/health", 1*time.Second, try.BodyContains(`"total_status_code_count":{"200":2}`))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd(withConfigFile("./fixtures/simple_auth.toml"))
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/api", 1*time.Second, try.StatusCodeIs(http.StatusUnauthorized))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestWebCompatibilityWithoutPath(c *check.C) {
|
||||
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd("--defaultEntryPoints=http", "--entryPoints=Name:http Address::8000", "--web", "--debug", "--docker")
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// TODO validate : run on 80
|
||||
// Expected a 404 as we did not configure anything
|
||||
err = try.GetRequest("http://127.0.0.1:8000/test", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestWebCompatibilityWithPath(c *check.C) {
|
||||
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd("--defaultEntryPoints=http", "--entryPoints=Name:http Address::8000", "--web.path=/test", "--debug", "--docker")
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// TODO validate : run on 80
|
||||
// Expected a 404 as we did not configure anything
|
||||
err = try.GetRequest("http://127.0.0.1:8000/notfound", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/test/api", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/test/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/test/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
|
||||
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd("--entryPoints=Name:http Address::8000", "--debug", "--docker", "--api")
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
|
||||
|
||||
s.createComposeProject(c, "base")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
cmd, output := s.traefikCmd("--defaultEntryPoints=https,http", "--entryPoints=Name:http Address::8000", "--debug", "--docker", "--api")
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
@@ -24,19 +24,20 @@ func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
||||
s.composeProject.Start(c)
|
||||
|
||||
consul := s.composeProject.Container(c, "consul")
|
||||
|
||||
s.consulIP = consul.NetworkSettings.IPAddress
|
||||
config := api.DefaultConfig()
|
||||
config.Address = s.consulIP + ":8500"
|
||||
consulClient, err := api.NewClient(config)
|
||||
if err != nil {
|
||||
c.Fatalf("Error creating consul client. %v", err)
|
||||
}
|
||||
s.consulClient = consulClient
|
||||
s.createConsulClient(config, c)
|
||||
|
||||
// Wait for consul to elect itself leader
|
||||
err = try.Do(3*time.Second, func() error {
|
||||
leader, err := consulClient.Status().Leader()
|
||||
err := s.waitToElectConsulLeader()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
|
||||
return try.Do(15*time.Second, func() error {
|
||||
leader, err := s.consulClient.Status().Leader()
|
||||
|
||||
if err != nil || len(leader) == 0 {
|
||||
return fmt.Errorf("Leader not found. %v", err)
|
||||
@@ -44,7 +45,17 @@ func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) {
|
||||
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
func (s *ConsulCatalogSuite) createConsulClient(config *api.Config, c *check.C) *api.Client {
|
||||
consulClient, err := api.NewClient(config)
|
||||
if err != nil {
|
||||
c.Fatalf("Error creating consul client. %v", err)
|
||||
}
|
||||
s.consulClient = consulClient
|
||||
return consulClient
|
||||
}
|
||||
func (s *ConsulCatalogSuite) startConsulService(c *check.C) {
|
||||
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) registerService(name string, address string, port int, tags []string) error {
|
||||
@@ -332,3 +343,164 @@ func (s *ConsulCatalogSuite) TestBasicAuthSimpleService(c *check.C) {
|
||||
err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) TestRefreshConfigTagChange(c *check.C) {
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||
"--consulCatalog",
|
||||
"--consulCatalog.exposedByDefault=false",
|
||||
"--consulCatalog.watch=true",
|
||||
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||
"--consulCatalog.domain=consul.localhost")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
nginx := s.composeProject.Container(c, "nginx1")
|
||||
|
||||
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "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)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 5*time.Second, try.BodyContains("nginx1"))
|
||||
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"})
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.consul.localhost"
|
||||
|
||||
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"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) TestCircuitBreaker(c *check.C) {
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||
"--retry",
|
||||
"--retry.attempts=1",
|
||||
"--forwardingTimeouts.dialTimeout=5s",
|
||||
"--forwardingTimeouts.responseHeaderTimeout=10s",
|
||||
"--consulCatalog",
|
||||
"--consulCatalog.exposedByDefault=false",
|
||||
"--consulCatalog.watch=true",
|
||||
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||
"--consulCatalog.domain=consul.localhost")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
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")
|
||||
|
||||
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "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"})
|
||||
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"})
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||
defer s.deregisterService("test", nginx3.NetworkSettings.IPAddress)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.consul.localhost"
|
||||
|
||||
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) TestRefreshConfigPortChange(c *check.C) {
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||
"--consulCatalog",
|
||||
"--consulCatalog.exposedByDefault=false",
|
||||
"--consulCatalog.watch=true",
|
||||
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||
"--consulCatalog.domain=consul.localhost")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
nginx := s.composeProject.Container(c, "nginx1")
|
||||
|
||||
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 81, []string{"name=nginx1", "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)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.consul.localhost"
|
||||
|
||||
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"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1", "traefik.enable=true"})
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||
|
||||
defer s.deregisterService("test", nginx.NetworkSettings.IPAddress)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.Request(req, 20*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) TestRetryWithConsulServer(c *check.C) {
|
||||
//Scale consul to 0 to be able to start traefik before and test retry
|
||||
s.composeProject.Scale(c, "consul", 0)
|
||||
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||
"--consulCatalog",
|
||||
"--consulCatalog.watch=false",
|
||||
"--consulCatalog.exposedByDefault=true",
|
||||
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||
"--consulCatalog.domain=consul.localhost")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// Wait for Traefik to turn ready.
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.consul.localhost"
|
||||
|
||||
// Request should fail
|
||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Scale consul to 1
|
||||
s.composeProject.Scale(c, "consul", 1)
|
||||
s.waitToElectConsulLeader()
|
||||
|
||||
nginx := s.composeProject.Container(c, "nginx1")
|
||||
// Register service
|
||||
err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{})
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||
|
||||
// Provider consul catalog should be present
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.BodyContains("consul_catalog"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Should be ok
|
||||
err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -179,7 +181,7 @@ func (s *ConsulSuite) TestNominalConfiguration(c *check.C) {
|
||||
req.Host = "test.localhost"
|
||||
|
||||
err = try.Request(req, 500*time.Millisecond,
|
||||
try.StatusCodeIs(200),
|
||||
try.StatusCodeIs(http.StatusOK),
|
||||
try.BodyContainsOr(whoami3IP, whoami4IP))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
@@ -187,7 +189,7 @@ func (s *ConsulSuite) TestNominalConfiguration(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.Request(req, 500*time.Millisecond,
|
||||
try.StatusCodeIs(200),
|
||||
try.StatusCodeIs(http.StatusOK),
|
||||
try.BodyContainsOr(whoami1IP, whoami2IP))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
@@ -332,10 +334,10 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik finish without error
|
||||
cmd.Wait()
|
||||
err = cmd.Wait()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//CHECK
|
||||
checkmap := map[string]string{
|
||||
expectedData := map[string]string{
|
||||
"/traefik/loglevel": "DEBUG",
|
||||
"/traefik/defaultentrypoints/0": "http",
|
||||
"/traefik/entrypoints/http/address": ":8000",
|
||||
@@ -343,10 +345,10 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
|
||||
"/traefik/consul/endpoint": consulHost + ":8500",
|
||||
}
|
||||
|
||||
for key, value := range checkmap {
|
||||
for key, value := range expectedData {
|
||||
var p *store.KVPair
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
p, err = s.kv.Get(key)
|
||||
p, err = s.kv.Get(key, nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -355,6 +357,54 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TestCommandStoreConfigWithFile(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
|
||||
cmd, display := s.traefikCmd(
|
||||
"storeconfig",
|
||||
withConfigFile("fixtures/simple_default.toml"),
|
||||
"--consul.endpoint="+consulHost+":8500",
|
||||
"--file.filename=fixtures/file/dir/simple1.toml")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik finish without error
|
||||
err = cmd.Wait()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
expectedData := map[string]string{
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://172.17.0.2:80",
|
||||
"/traefik/frontends/frontend1/backend": "backend1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Path:/test1",
|
||||
}
|
||||
|
||||
for key, value := range expectedData {
|
||||
var p *store.KVPair
|
||||
err = try.Do(10*time.Second, func() error {
|
||||
p, err = s.kv.Get(key, nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(string(p.Value), checker.Equals, value)
|
||||
}
|
||||
|
||||
checkNotExistsMap := []string{
|
||||
"/traefik/file",
|
||||
}
|
||||
|
||||
for _, value := range checkNotExistsMap {
|
||||
err = try.Do(10*time.Second, func() error {
|
||||
if exists, err := s.kv.Exists(value, nil); err == nil && exists {
|
||||
return fmt.Errorf("%s key is not suppose to exist in KV", value)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
String string
|
||||
Int int
|
||||
@@ -454,3 +504,157 @@ func datastoreContains(datastore *cluster.Datastore, expectedValue string) func(
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
// start Træfik
|
||||
file := s.adaptFile(c, "fixtures/consul/simple_https.toml", struct{ ConsulHost string }{consulHost})
|
||||
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()
|
||||
|
||||
// prepare to config
|
||||
whoami1IP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
||||
whoami2IP := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
|
||||
whoami3IP := s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress
|
||||
whoami4IP := s.composeProject.Container(c, "whoami4").NetworkSettings.IPAddress
|
||||
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgCert, err := ioutil.ReadFile("fixtures/https/snitest.org.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := map[string]string{
|
||||
"traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"traefik/backends/backend1/servers/server1/url": "http://" + whoami1IP + ":80",
|
||||
"traefik/backends/backend1/servers/server1/weight": "1",
|
||||
"traefik/backends/backend1/servers/server2/url": "http://" + whoami2IP + ":80",
|
||||
"traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"traefik/backends/backend2/servers/server1/url": "http://" + whoami3IP + ":80",
|
||||
"traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"traefik/backends/backend2/servers/server2/url": "http://" + whoami4IP + ":80",
|
||||
"traefik/backends/backend2/servers/server2/weight": "1",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"traefik/frontends/frontend1/backend": "backend2",
|
||||
"traefik/frontends/frontend1/entrypoints": "https",
|
||||
"traefik/frontends/frontend1/priority": "1",
|
||||
"traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
|
||||
}
|
||||
|
||||
frontend2 := map[string]string{
|
||||
"traefik/frontends/frontend2/backend": "backend1",
|
||||
"traefik/frontends/frontend2/entrypoints": "https",
|
||||
"traefik/frontends/frontend2/priority": "10",
|
||||
"traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
|
||||
}
|
||||
|
||||
tlsconfigure1 := map[string]string{
|
||||
"traefik/tlsconfiguration/snitestcom/entrypoints": "https",
|
||||
"traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
|
||||
"traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
|
||||
}
|
||||
|
||||
tlsconfigure2 := map[string]string{
|
||||
"traefik/tlsconfiguration/snitestorg/entrypoints": "https",
|
||||
"traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
|
||||
"traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
|
||||
}
|
||||
|
||||
// config backends,frontends and first tls keypair
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range tlsconfigure1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
tr1 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
},
|
||||
}
|
||||
|
||||
tr2 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.org",
|
||||
},
|
||||
}
|
||||
|
||||
// wait for consul
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7hG"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in consul and the request for host "snitest.org" will use the second keypair
|
||||
for key, value := range tlsconfigure2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for consul
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for traefik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
||||
@@ -49,6 +49,14 @@ func (s *DockerSuite) startContainerWithLabels(c *check.C, image string, labels
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) startContainerWithNameAndLabels(c *check.C, name string, image string, labels map[string]string, args ...string) string {
|
||||
return s.startContainerWithConfig(c, image, d.ContainerConfig{
|
||||
Name: name,
|
||||
Cmd: args,
|
||||
Labels: labels,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) startContainerWithConfig(c *check.C, image string, config d.ContainerConfig) string {
|
||||
if config.Name == "" {
|
||||
config.Name = namesgenerator.GetRandomName(10)
|
||||
@@ -60,6 +68,11 @@ func (s *DockerSuite) startContainerWithConfig(c *check.C, image string, config
|
||||
return strings.SplitAfter(container.Name, "/")[1]
|
||||
}
|
||||
|
||||
func (s *DockerSuite) stopAndRemoveContainerByName(c *check.C, name string) {
|
||||
s.project.Stop(c, name)
|
||||
s.project.Remove(c, name)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) SetUpSuite(c *check.C) {
|
||||
project := docker.NewProjectFromEnv(c)
|
||||
s.project = project
|
||||
@@ -87,7 +100,7 @@ func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
|
||||
|
||||
// TODO validate : run on 80
|
||||
// Expected a 404 as we did not comfigure anything
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(404))
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
@@ -108,7 +121,7 @@ func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
|
||||
req.Host = fmt.Sprintf("%s.docker.localhost", strings.Replace(name, "_", "-", -1))
|
||||
|
||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, 200)
|
||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
@@ -129,6 +142,11 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
||||
}
|
||||
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||
|
||||
// Start another container by replacing a '.' by a '-'
|
||||
labels = map[string]string{
|
||||
types.LabelFrontendRule: "Host:my-super.host",
|
||||
}
|
||||
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blablabla")
|
||||
// Start traefik
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
@@ -138,12 +156,20 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "my.super.host"
|
||||
req.Host = "my-super.host"
|
||||
|
||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "my.super.host"
|
||||
|
||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||
resp, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
@@ -179,3 +205,90 @@ func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
|
||||
err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// TestDockerContainersWithServiceLabels allows cheking the labels behavior
|
||||
// Use service label if defined and compete information with container labels.
|
||||
func (s *DockerSuite) TestDockerContainersWithServiceLabels(c *check.C) {
|
||||
file := s.adaptFileForHost(c, "fixtures/docker/simple.toml")
|
||||
defer os.Remove(file)
|
||||
// Start a container with some labels
|
||||
labels := map[string]string{
|
||||
types.LabelPrefix + "servicename.frontend.rule": "Host:my.super.host",
|
||||
types.LabelFrontendRule: "Host:my.wrong.host",
|
||||
types.LabelPort: "2375",
|
||||
}
|
||||
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||
|
||||
// Start traefik
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "my.super.host"
|
||||
|
||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
var version map[string]interface{}
|
||||
|
||||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
||||
file := s.adaptFileForHost(c, "fixtures/docker/simple.toml")
|
||||
defer os.Remove(file)
|
||||
// Start a container with some labels
|
||||
labels := map[string]string{
|
||||
types.LabelPrefix + "frontend.rule": "Host:my.super.host",
|
||||
types.LabelPort: "2375",
|
||||
}
|
||||
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||
|
||||
// Start traefik
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "my.super.host"
|
||||
|
||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
var version map[string]interface{}
|
||||
|
||||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/backends", 60*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
s.stopAndRemoveContainerByName(c, "powpow")
|
||||
defer s.project.Remove(c, "powpow")
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/backends", 10*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.NotNil)
|
||||
|
||||
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/backends", 60*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
685
integration/etcd3_test.go
Normal file
685
integration/etcd3_test.go
Normal file
@@ -0,0 +1,685 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/try"
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/etcd/v3"
|
||||
"github.com/go-check/check"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
const (
|
||||
// Services IP addresses fixed in the configuration
|
||||
ipEtcd string = "172.18.0.2"
|
||||
ipWhoami01 string = "172.18.0.3"
|
||||
ipWhoami02 string = "172.18.0.4"
|
||||
ipWhoami03 string = "172.18.0.5"
|
||||
ipWhoami04 string = "172.18.0.6"
|
||||
|
||||
traefikEtcdURL string = "http://127.0.0.1:8000/"
|
||||
traefikWebEtcdURL string = "http://127.0.0.1:8081/"
|
||||
)
|
||||
|
||||
// Etcd test suites (using libcompose)
|
||||
type Etcd3Suite struct {
|
||||
BaseSuite
|
||||
kv store.Store
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) SetUpTest(c *check.C) {
|
||||
s.createComposeProject(c, "etcd3")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
etcdv3.Register()
|
||||
url := ipEtcd + ":2379"
|
||||
kv, err := libkv.NewStore(
|
||||
store.ETCDV3,
|
||||
[]string{url},
|
||||
&store.Config{
|
||||
ConnectionTimeout: 30 * time.Second,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
c.Fatal("Cannot create store etcd")
|
||||
}
|
||||
s.kv = kv
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := kv.Exists("test", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TearDownTest(c *check.C) {
|
||||
// shutdown and delete compose project
|
||||
if s.composeProject != nil {
|
||||
s.composeProject.Stop(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TearDownSuite(c *check.C) {}
|
||||
|
||||
func (s *Etcd3Suite) TestSimpleConfiguration(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct {
|
||||
EtcdHost string
|
||||
UseAPIV3 bool
|
||||
}{
|
||||
ipEtcd,
|
||||
true,
|
||||
})
|
||||
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()
|
||||
|
||||
// TODO validate : run on 80
|
||||
// Expected a 404 as we did not configure anything
|
||||
err = try.GetRequest(traefikEtcdURL, 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestNominalConfiguration(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct {
|
||||
EtcdHost string
|
||||
UseAPIV3 bool
|
||||
}{
|
||||
ipEtcd,
|
||||
true,
|
||||
})
|
||||
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()
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"/traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"/traefik/backends/backend2/servers/server1/url": "http://" + ipWhoami03 + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + ipWhoami04 + ":80",
|
||||
"/traefik/backends/backend2/servers/server2/weight": "2",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend2",
|
||||
"/traefik/frontends/frontend1/entrypoints": "http",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost",
|
||||
}
|
||||
frontend2 := map[string]string{
|
||||
"/traefik/frontends/frontend2/backend": "backend1",
|
||||
"/traefik/frontends/frontend2/entrypoints": "http",
|
||||
"/traefik/frontends/frontend2/priority": "10",
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Path:/test",
|
||||
}
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 60*time.Second, try.BodyContains("Path:/test"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(http.MethodGet, traefikEtcdURL, nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.localhost"
|
||||
response, err := client.Do(req)
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
if !strings.Contains(string(body), ipWhoami03) &&
|
||||
!strings.Contains(string(body), ipWhoami04) {
|
||||
c.Fail()
|
||||
}
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, traefikEtcdURL+"test", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
response, err = client.Do(req)
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
body, err = ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
if !strings.Contains(string(body), ipWhoami01) &&
|
||||
!strings.Contains(string(body), ipWhoami02) {
|
||||
c.Fail()
|
||||
}
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, traefikEtcdURL+"test2", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test2.localhost"
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
||||
|
||||
resp, err = http.Get(traefikEtcdURL)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestGlobalConfiguration(c *check.C) {
|
||||
err := s.kv.Put("/traefik/entrypoints/http/address", []byte(":8001"), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/entrypoints/http/address", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start traefik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/simple_web.toml"),
|
||||
"--etcd",
|
||||
"--etcd.endpoint="+ipEtcd+":4001",
|
||||
"--etcd.useAPIV3=true")
|
||||
defer display(c)
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"/traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"/traefik/backends/backend2/servers/server1/url": "http://" + ipWhoami03 + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + ipWhoami04 + ":80",
|
||||
"/traefik/backends/backend2/servers/server2/weight": "2",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend2",
|
||||
"/traefik/frontends/frontend1/entrypoints": "http",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:test.localhost",
|
||||
}
|
||||
frontend2 := map[string]string{
|
||||
"/traefik/frontends/frontend2/backend": "backend1",
|
||||
"/traefik/frontends/frontend2/entrypoints": "http",
|
||||
"/traefik/frontends/frontend2/priority": "10",
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Path:/test",
|
||||
}
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("Path:/test"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8001/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.localhost"
|
||||
|
||||
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) {
|
||||
// start traefik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/simple_web.toml"),
|
||||
"--etcd",
|
||||
"--etcd.endpoint="+ipEtcd+":4001",
|
||||
"--etcd.useAPIV3=true")
|
||||
defer display(c)
|
||||
|
||||
//Copy the contents of the certificate files into ETCD
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgCert, err := ioutil.ReadFile("fixtures/https/snitest.org.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
globalConfig := map[string]string{
|
||||
"/traefik/entrypoints/https/address": ":4443",
|
||||
"/traefik/entrypoints/https/tls/certificates/0/certfile": string(snitestComCert),
|
||||
"/traefik/entrypoints/https/tls/certificates/0/keyfile": string(snitestComKey),
|
||||
"/traefik/entrypoints/https/tls/certificates/1/certfile": string(snitestOrgCert),
|
||||
"/traefik/entrypoints/https/tls/certificates/1/keyfile": string(snitestOrgKey),
|
||||
"/traefik/defaultentrypoints/0": "https",
|
||||
}
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"/traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"/traefik/backends/backend2/servers/server1/url": "http://" + ipWhoami03 + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + ipWhoami04 + ":80",
|
||||
"/traefik/backends/backend2/servers/server2/weight": "2",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend2",
|
||||
"/traefik/frontends/frontend1/entrypoints": "http",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
|
||||
}
|
||||
frontend2 := map[string]string{
|
||||
"/traefik/frontends/frontend2/backend": "backend1",
|
||||
"/traefik/frontends/frontend2/entrypoints": "http",
|
||||
"/traefik/frontends/frontend2/priority": "10",
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
|
||||
}
|
||||
for key, value := range globalConfig {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, try.KVExists(s.kv, "/traefik/frontends/frontend1/backend"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("Host:snitest.org"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
}
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
|
||||
defer conn.Close()
|
||||
err = conn.Handshake()
|
||||
c.Assert(err, checker.IsNil, check.Commentf("TLS handshake error"))
|
||||
|
||||
cs := conn.ConnectionState()
|
||||
err = cs.PeerCertificates[0].VerifyHostname("snitest.com")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername"))
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestCommandStoreConfig(c *check.C) {
|
||||
cmd, display := s.traefikCmd(
|
||||
"storeconfig",
|
||||
withConfigFile("fixtures/simple_web.toml"),
|
||||
"--etcd.endpoint="+ipEtcd+":4001",
|
||||
"--etcd.useAPIV3=true")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik finish without error
|
||||
cmd.Wait()
|
||||
|
||||
//CHECK
|
||||
checkmap := map[string]string{
|
||||
"/traefik/loglevel": "DEBUG",
|
||||
"/traefik/defaultentrypoints/0": "http",
|
||||
"/traefik/entrypoints/http/address": ":8000",
|
||||
"/traefik/web/address": ":8080",
|
||||
"/traefik/etcd/endpoint": ipEtcd + ":4001",
|
||||
}
|
||||
|
||||
for key, value := range checkmap {
|
||||
var p *store.KVPair
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
p, err = s.kv.Get(key, nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(string(p.Value), checker.Equals, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
// start Træfik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/etcd/simple_https.toml"),
|
||||
"--etcd",
|
||||
"--etcd.endpoint="+ipEtcd+":4001",
|
||||
"--etcd.useAPIV3=true")
|
||||
defer display(c)
|
||||
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgCert, err := ioutil.ReadFile("fixtures/https/snitest.org.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"/traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"/traefik/backends/backend2/servers/server1/url": "http://" + ipWhoami03 + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + ipWhoami04 + ":80",
|
||||
"/traefik/backends/backend2/servers/server2/weight": "2",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend2",
|
||||
"/traefik/frontends/frontend1/entrypoints": "https",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
|
||||
}
|
||||
|
||||
frontend2 := map[string]string{
|
||||
"/traefik/frontends/frontend2/backend": "backend1",
|
||||
"/traefik/frontends/frontend2/entrypoints": "https",
|
||||
"/traefik/frontends/frontend2/priority": "10",
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
|
||||
}
|
||||
|
||||
tlsconfigure1 := map[string]string{
|
||||
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
|
||||
}
|
||||
|
||||
tlsconfigure2 := map[string]string{
|
||||
"/traefik/tlsconfiguration/snitestorg/entrypoints": "https",
|
||||
"/traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
|
||||
"/traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
|
||||
}
|
||||
|
||||
// config backends,frontends and first tls keypair
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range tlsconfigure1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
tr1 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
},
|
||||
}
|
||||
|
||||
tr2 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.org",
|
||||
},
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in etcd and the request for host "snitest.org" will use the second keypair
|
||||
|
||||
for key, value := range tlsconfigure2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
|
||||
// start Træfik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/etcd/simple_https.toml"),
|
||||
"--etcd",
|
||||
"--etcd.endpoint="+ipEtcd+":4001",
|
||||
"--etcd.useAPIV3=true")
|
||||
defer display(c)
|
||||
|
||||
// prepare to config
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend1",
|
||||
"/traefik/frontends/frontend1/entrypoints": "https",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
|
||||
}
|
||||
|
||||
tlsconfigure1 := map[string]string{
|
||||
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
|
||||
}
|
||||
|
||||
// config backends,frontends and first tls keypair
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range tlsconfigure1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
tr1 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
},
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we delete the tls cert/key pairs,so the endpoint show use default cert/key pair
|
||||
for key := range tlsconfigure1 {
|
||||
err := s.kv.Delete(key)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 30*time.Second, try.BodyNotContains("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "TRAEFIK DEFAULT CERT")
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/containous/traefik/integration/try"
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/etcd/v2"
|
||||
"github.com/go-check/check"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
@@ -43,7 +43,7 @@ func (s *EtcdSuite) SetUpTest(c *check.C) {
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := kv.Exists("test")
|
||||
_, err := kv.Exists("test", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -61,7 +61,13 @@ func (s *EtcdSuite) TearDownSuite(c *check.C) {}
|
||||
func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdHost string }{etcdHost})
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct {
|
||||
EtcdHost string
|
||||
UseAPIV3 bool
|
||||
}{
|
||||
etcdHost,
|
||||
false,
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
@@ -79,7 +85,13 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
|
||||
func (s *EtcdSuite) TestNominalConfiguration(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdHost string }{etcdHost})
|
||||
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct {
|
||||
EtcdHost string
|
||||
UseAPIV3 bool
|
||||
}{
|
||||
etcdHost,
|
||||
false,
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
@@ -138,12 +150,12 @@ func (s *EtcdSuite) TestNominalConfiguration(c *check.C) {
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule")
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
// wait for Træfik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains("Path:/test"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
@@ -196,12 +208,12 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/entrypoints/http/address")
|
||||
_, err := s.kv.Exists("/traefik/entrypoints/http/address", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start traefik
|
||||
// start Træfik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/simple_web.toml"),
|
||||
"--etcd",
|
||||
@@ -261,7 +273,7 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule")
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -270,7 +282,7 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("Path:/test"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
// check
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8001/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.localhost"
|
||||
@@ -281,7 +293,7 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
|
||||
|
||||
func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
// start traefik
|
||||
// start Træfik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/simple_web.toml"),
|
||||
"--etcd",
|
||||
@@ -293,7 +305,7 @@ func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C)
|
||||
whoami3IP := s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress
|
||||
whoami4IP := s.composeProject.Container(c, "whoami4").NetworkSettings.IPAddress
|
||||
|
||||
//Copy the contents of the certificate files into ETCD
|
||||
// Copy the contents of the certificate files into ETCD
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
@@ -371,7 +383,7 @@ func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C)
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("Host:snitest.org"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
// check
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
@@ -399,10 +411,10 @@ func (s *EtcdSuite) TestCommandStoreConfig(c *check.C) {
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik finish without error
|
||||
// wait for Træfik finish without error
|
||||
cmd.Wait()
|
||||
|
||||
//CHECK
|
||||
// CHECK
|
||||
checkmap := map[string]string{
|
||||
"/traefik/loglevel": "DEBUG",
|
||||
"/traefik/defaultentrypoints/0": "http",
|
||||
@@ -414,7 +426,7 @@ func (s *EtcdSuite) TestCommandStoreConfig(c *check.C) {
|
||||
for key, value := range checkmap {
|
||||
var p *store.KVPair
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
p, err = s.kv.Get(key)
|
||||
p, err = s.kv.Get(key, nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -422,3 +434,161 @@ func (s *EtcdSuite) TestCommandStoreConfig(c *check.C) {
|
||||
c.Assert(string(p.Value), checker.Equals, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
// start Træfik
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/etcd/simple_https.toml"),
|
||||
"--etcd",
|
||||
"--etcd.endpoint="+etcdHost+":4001",
|
||||
"--etcd.watch=true",
|
||||
)
|
||||
defer display(c)
|
||||
|
||||
// prepare to config
|
||||
whoami1IP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
||||
whoami2IP := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
|
||||
whoami3IP := s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress
|
||||
whoami4IP := s.composeProject.Container(c, "whoami4").NetworkSettings.IPAddress
|
||||
|
||||
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgCert, err := ioutil.ReadFile("fixtures/https/snitest.org.cert")
|
||||
c.Assert(err, checker.IsNil)
|
||||
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + whoami1IP + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + whoami2IP + ":80",
|
||||
"/traefik/backends/backend1/servers/server2/weight": "1",
|
||||
}
|
||||
backend2 := map[string]string{
|
||||
"/traefik/backends/backend2/loadbalancer/method": "drr",
|
||||
"/traefik/backends/backend2/servers/server1/url": "http://" + whoami3IP + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + whoami4IP + ":80",
|
||||
"/traefik/backends/backend2/servers/server2/weight": "1",
|
||||
}
|
||||
frontend1 := map[string]string{
|
||||
"/traefik/frontends/frontend1/backend": "backend2",
|
||||
"/traefik/frontends/frontend1/entrypoints": "https",
|
||||
"/traefik/frontends/frontend1/priority": "1",
|
||||
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
|
||||
}
|
||||
|
||||
frontend2 := map[string]string{
|
||||
"/traefik/frontends/frontend2/backend": "backend1",
|
||||
"/traefik/frontends/frontend2/entrypoints": "https",
|
||||
"/traefik/frontends/frontend2/priority": "10",
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
|
||||
}
|
||||
|
||||
tlsconfigure1 := map[string]string{
|
||||
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
|
||||
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
|
||||
}
|
||||
|
||||
tlsconfigure2 := map[string]string{
|
||||
"/traefik/tlsconfiguration/snitestorg/entrypoints": "https",
|
||||
"/traefik/tlsconfiguration/snitestorg/certificate/keyfile": string(snitestOrgKey),
|
||||
"/traefik/tlsconfiguration/snitestorg/certificate/certfile": string(snitestOrgCert),
|
||||
}
|
||||
|
||||
// config backends,frontends and first tls keypair
|
||||
for key, value := range backend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range frontend2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range tlsconfigure1 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
tr1 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
},
|
||||
}
|
||||
|
||||
tr2 := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.org",
|
||||
},
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in etcd and the request for host "snitest.org" will use the second keypair
|
||||
|
||||
for key, value := range tlsconfigure2 {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// wait for etcd
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestorg/certificate/keyfile", nil)
|
||||
return err
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ defaultEntryPoints = ["http", "https"]
|
||||
address = ":5001"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
KeyFile = "fixtures/acme/ssl/wildcard.key"
|
||||
certFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
keyFile = "fixtures/acme/ssl/wildcard.key"
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
|
||||
23
integration/fixtures/acme/acme_provided_dynamic.toml
Normal file
23
integration/fixtures/acme/acme_provided_dynamic.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8080"
|
||||
[entryPoints.https]
|
||||
address = ":5001"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "/dev/null"
|
||||
entryPoint = "https"
|
||||
onDemand = {{.OnDemand}}
|
||||
OnHostRule = {{.OnHostRule}}
|
||||
caServer = "http://{{.BoulderHost}}:4000/directory"
|
||||
|
||||
[file]
|
||||
filename = "fixtures/acme/certificates.toml"
|
||||
watch = true
|
||||
16
integration/fixtures/acme/certificates.toml
Normal file
16
integration/fixtures/acme/certificates.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[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"
|
||||
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
keyFile = "fixtures/acme/ssl/wildcard.key"
|
||||
20
integration/fixtures/consul/simple_https.toml
Normal file
20
integration/fixtures/consul/simple_https.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
defaultEntryPoints = ["http","https"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
|
||||
|
||||
[consul]
|
||||
endpoint = "{{.ConsulHost}}:8500"
|
||||
prefix = "traefik"
|
||||
watch = true
|
||||
|
||||
[web]
|
||||
address = ":8081"
|
||||
@@ -6,6 +6,8 @@ logLevel = "DEBUG"
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[web]
|
||||
|
||||
[docker]
|
||||
|
||||
# It's dynamagic !
|
||||
|
||||
@@ -11,6 +11,7 @@ logLevel = "DEBUG"
|
||||
endpoint = "{{.EtcdHost}}:2379"
|
||||
prefix = "/traefik"
|
||||
watch = true
|
||||
useAPIV3 = {{.UseAPIV3}}
|
||||
|
||||
[web]
|
||||
address = ":8081"
|
||||
|
||||
20
integration/fixtures/etcd/simple_https.toml
Normal file
20
integration/fixtures/etcd/simple_https.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
defaultEntryPoints = ["http","https"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
|
||||
|
||||
#[etcd]
|
||||
# endpoint = "{{.EtcdHost}}:2379"
|
||||
# prefix = "/traefik"
|
||||
# watch = true
|
||||
|
||||
[web]
|
||||
address = ":8081"
|
||||
@@ -7,8 +7,8 @@ RootCAs = [ """{{ .CertContent }}""" ]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = """{{ .CertContent }}"""
|
||||
KeyFile = """{{ .KeyContent }}"""
|
||||
certFile = """{{ .CertContent }}"""
|
||||
keyFile = """{{ .KeyContent }}"""
|
||||
|
||||
|
||||
[web]
|
||||
|
||||
@@ -7,8 +7,8 @@ InsecureSkipVerify = true
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = """{{ .CertContent }}"""
|
||||
KeyFile = """{{ .KeyContent }}"""
|
||||
certFile = """{{ .CertContent }}"""
|
||||
keyFile = """{{ .KeyContent }}"""
|
||||
|
||||
|
||||
[web]
|
||||
|
||||
@@ -6,13 +6,15 @@ defaultEntryPoints = ["https"]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1.crt"]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["fixtures/https/clientca/ca1.crt"]
|
||||
optional = true
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.com.cert"
|
||||
KeyFile = "fixtures/https/snitest.com.key"
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.org.cert"
|
||||
KeyFile = "fixtures/https/snitest.org.key"
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
@@ -6,13 +6,14 @@ defaultEntryPoints = ["https"]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1and2.crt"]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["fixtures/https/clientca/ca1and2.crt"]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.com.cert"
|
||||
KeyFile = "fixtures/https/snitest.com.key"
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.org.cert"
|
||||
KeyFile = "fixtures/https/snitest.org.key"
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
@@ -6,13 +6,15 @@ defaultEntryPoints = ["https"]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1.crt", "fixtures/https/clientca/ca2.crt"]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["fixtures/https/clientca/ca1.crt", "fixtures/https/clientca/ca2.crt"]
|
||||
optional = false
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.com.cert"
|
||||
KeyFile = "fixtures/https/snitest.com.key"
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.org.cert"
|
||||
KeyFile = "fixtures/https/snitest.org.key"
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
67
integration/fixtures/https/dynamic_https.toml
Normal file
67
integration/fixtures/https/dynamic_https.toml
Normal file
@@ -0,0 +1,67 @@
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://127.0.0.1:9010"
|
||||
[backends.backend2]
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://127.0.0.1:9020"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:snitest.com"
|
||||
[frontends.frontend2]
|
||||
backend = "backend2"
|
||||
[frontends.frontend2.routes.test_2]
|
||||
rule = "Host:snitest.org"
|
||||
|
||||
[[tlsConfiguration]]
|
||||
entryPoints = ["https"]
|
||||
[tlsConfiguration.certificate]
|
||||
certFile = """-----BEGIN CERTIFICATE-----
|
||||
MIIC/zCCAeegAwIBAgIJALAYHG/vGqWEMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
|
||||
BAMMC3NuaXRlc3Qub3JnMB4XDTE1MTEyMzIyMDU0NFoXDTI1MTEyMDIyMDU0NFow
|
||||
FjEUMBIGA1UEAwwLc25pdGVzdC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQC8b2Qv68Xnv4wgJ6HNupxABSUA5KXmv9g7pwwsFMSOK15o2qGFzx/x
|
||||
9loIi5pMIYIy4SVwJNrYUi772nCYMqSIVXlwct/CE70j2Jb2geIHu3jHbFWXruWb
|
||||
W1tGGUYzvnsOUziPE3rLWa/NObNYLLlUKJaxfHrxnpuKpQUsXzoLl25cJEVr4jg2
|
||||
ZITpdraxaBLisdlWY7EwwHBLu2nxH5Rn+nIjenFfdUwKF9s5dGy63tfBc8LX9yJk
|
||||
+kOwy1al/Wxa0DUb6rSt0QDCcD+rXnjk2zWPtsHz1btwtqM+FLtN5z0Lmnx7DF3C
|
||||
tCf1TMzduzZ6aeHk77zc664ZQun5cH33AgMBAAGjUDBOMB0GA1UdDgQWBBRn/nNz
|
||||
PUsmDKmKv3GGo3km5KKvUDAfBgNVHSMEGDAWgBRn/nNzPUsmDKmKv3GGo3km5KKv
|
||||
UDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBkuutIcbBdESgvNLLr
|
||||
k/8HUDuFm72lYHZFE+c76CxqYN52w02NCTiq1InoDUvqZXb/StATBwRRduTUPCj9
|
||||
KUkC7pOjAFxjzjExsHrtZSq01WinrxNI+qSKvI8jFngMHnwN1omTt7/D7nxeW5Of
|
||||
FJTkElnxtELAGHoIwZ+bKprnexefpn9UW84VJvJ2crSR63vBvdTrgsrEGW6kQj1I
|
||||
62laDpax4+x8t2h+sfG6uNIA1cFrG8Sk+O2Bi3ogB7Y/4e8r6WA23IRP+aSv0J2b
|
||||
k5fvuuXbIc979pQOoO03zG0S7Wpmpsw+9dQB9TOxGITOLfCZwEuIhnv+M9lLqCks
|
||||
7H2A
|
||||
-----END CERTIFICATE-----"""
|
||||
keyFile = """-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r/YO6cMLBTEjiteaNqh
|
||||
hc8f8fZaCIuaTCGCMuElcCTa2FIu+9pwmDKkiFV5cHLfwhO9I9iW9oHiB7t4x2xV
|
||||
l67lm1tbRhlGM757DlM4jxN6y1mvzTmzWCy5VCiWsXx68Z6biqUFLF86C5duXCRF
|
||||
a+I4NmSE6Xa2sWgS4rHZVmOxMMBwS7tp8R+UZ/pyI3pxX3VMChfbOXRsut7XwXPC
|
||||
1/ciZPpDsMtWpf1sWtA1G+q0rdEAwnA/q1545Ns1j7bB89W7cLajPhS7Tec9C5p8
|
||||
ewxdwrQn9UzM3bs2emnh5O+83OuuGULp+XB99wIDAQABAoIBAGOn9bByXQQnhZAr
|
||||
5aLMIn6pOdyzEBptM4q42fMmOJ2HyjJiDjKaTCbHRu5mBoBk6FrIP+iDVUo6jKad
|
||||
7BZSEjoYGlWiKzyU+97NWWmdX1D/kOzHGq1RzhTPyAHWtA4Bm0sEMFFa2AJbuGIt
|
||||
NfBYFtuva6MKVmsamuBETewdoLEnxzzDFcuOaxXRfTC/ikWcYyB4KEWA5fjroUQC
|
||||
Llo9/UTGTkh1Hynv9AXY6Qia/RbrIQjKveKCRj6PjxyE/qN9qfmngczz2pK0hRhL
|
||||
Z+K06y8G+Yj1I1zm5jNg1kakVQKoBsnaYkmIUBUSmWv6ERotedOWtOAMlOKa+0l2
|
||||
DS7Ou2ECgYEA91doi+3XrMVsgyTEm/ArzEyRUfM5dCSvBCRFhO7QQp2OYAbjJk5S
|
||||
pmdpqmwTsXNNMU+XNkWCLug5pk0PTJwP0mVLE2fLYqCCXoyaMltQ0Yk2gaun/RwE
|
||||
w5EfyMwOQakLFY/ODvduQfyNpaoWgFz4/WPNTVNCGs04LepSGKaFNy0CgYEAwwgV
|
||||
jKeFA+QZGooTInyk07ZlAbenEPu/c2y3UUFxclP0CjP2/VBOpz9B62vhzCKbjD1c
|
||||
/L3x1CKC4n4lbeyHi4vrF69LX9SHr1Jm0SUtyKeV3g0EAzIWI0HFhVUkMvtbibQ4
|
||||
HXrLVCJO77xetQ7RQszss1z9g3WotAAiBMiQgDMCgYBTLjoilOIrYFmV4Q+dwa95
|
||||
DWbxwHJZ9NxG8EvQ4N95B7OR578Matqwy6ZlgeM9kiErrDCWN9oIHGEG5HN4uCM6
|
||||
BoaxB/8GNCSj13Uj6kHLtfF2ulvMa1fOzUd7J+TDgC4SGkKaFewmlOCuDf1zPdEe
|
||||
pimtD4rzqIB0MJFbaOT0IQKBgDBPjlb7IB3ooLdMQJUoXwP6iGa2gXHZioEjCv3b
|
||||
wihZ13e3i5UQEYuoRcH1RUd1wyYoBSKuQnsT2WwVZ1wlXSYaELAbQgaI9NtfBA0G
|
||||
sqKjsKICg13vSECPiEgQ4Rin3vLra4MR6c/7d6Y2+RbMhtWPQYrkm/+2Y4XDCqo4
|
||||
rGK1AoGAOFZ3RVhuwXzFdKNe32LM1wm1eZ7waxjI4bQS2xUN/3C/uWS7A3LaSlc3
|
||||
eRG3DaVpez4DQVupZDHMgxJUYqqKynUj6GD1YiaxGROj3TYCu6e7OxyhalhCllSu
|
||||
w/X5M802XqzLjeec5zHoZDfknnAkgR9MsxZYmZPFaDyL6GOKUB8=
|
||||
-----END RSA PRIVATE KEY-----"""
|
||||
19
integration/fixtures/https/dynamic_https_sni.toml
Normal file
19
integration/fixtures/https/dynamic_https_sni.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
[entryPoints.https02]
|
||||
address = ":8443"
|
||||
[entryPoints.https02.tls]
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[file]
|
||||
|
||||
fileName = "{{.DynamicConfFileName}}"
|
||||
watch = true
|
||||
@@ -7,11 +7,11 @@ defaultEntryPoints = ["https"]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.com.cert"
|
||||
KeyFile = "fixtures/https/snitest.com.key"
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
CertFile = "fixtures/https/snitest.org.cert"
|
||||
KeyFile = "fixtures/https/snitest.org.key"
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
24
integration/fixtures/proxy-protocol/with.toml
Normal file
24
integration/fixtures/proxy-protocol/with.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
logLevel = "DEBUG"
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
[entryPoints.http.proxyProtocol]
|
||||
trustedIPs = ["{{.HaproxyIP}}"]
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://{{.WhoamiIP}}"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/whoami"
|
||||
24
integration/fixtures/proxy-protocol/without.toml
Normal file
24
integration/fixtures/proxy-protocol/without.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
logLevel = "DEBUG"
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
[entryPoints.http.proxyProtocol]
|
||||
trustedIPs = ["1.2.3.4"]
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://{{.WhoamiIP}}"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/whoami"
|
||||
30
integration/fixtures/ratelimit/simple.toml
Normal file
30
integration/fixtures/ratelimit/simple.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://{{.Server1}}:80"
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
passHostHeader = true
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/"
|
||||
[frontends.frontend1.ratelimit]
|
||||
extractorfunc = "client.ip"
|
||||
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||
period = "60s"
|
||||
average = 4
|
||||
burst = 5
|
||||
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||
period = "3s"
|
||||
average = 1
|
||||
burst = 2
|
||||
22
integration/fixtures/reqacceptgrace.toml
Normal file
22
integration/fixtures/reqacceptgrace.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[lifeCycle]
|
||||
requestAcceptGraceTimeout = "10s"
|
||||
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend]
|
||||
[backends.backend.servers.server]
|
||||
url = "{{.Server}}"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend]
|
||||
backend = "backend"
|
||||
[frontends.frontend.routes.service]
|
||||
rule = "Path:/service"
|
||||
16
integration/fixtures/simple_auth.toml
Normal file
16
integration/fixtures/simple_auth.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
logLevel = "DEBUG"
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[entryPoints.traefik]
|
||||
address = ":8001"
|
||||
[entryPoints.traefik.auth.basic]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
|
||||
|
||||
[api]
|
||||
|
||||
[ping]
|
||||
30
integration/fixtures/simple_stats.toml
Normal file
30
integration/fixtures/simple_stats.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
debug=true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[api]
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "{{ .Server1 }}"
|
||||
[backends.backend2]
|
||||
[backends.backend2.servers.server1]
|
||||
url = "{{ .Server2 }}"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
entrypoints=["http"]
|
||||
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefix:/whoami"
|
||||
|
||||
[frontends.frontend2]
|
||||
backend = "backend2"
|
||||
entrypoints=["traefik"]
|
||||
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "PathPrefix:/whoami"
|
||||
@@ -1,14 +1,15 @@
|
||||
defaultEntryPoints = ["wss"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
InsecureSkipVerify=true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.wss]
|
||||
address = ":8000"
|
||||
[entryPoints.wss.tls]
|
||||
[[entryPoints.wss.tls.certificates]]
|
||||
CertFile = "resources/tls/local.cert"
|
||||
KeyFile = "resources/tls/local.key"
|
||||
certFile = "resources/tls/local.cert"
|
||||
keyFile = "resources/tls/local.key"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
@@ -24,4 +25,4 @@ logLevel = "DEBUG"
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/ws"
|
||||
rule = "Path:/echo,/ws"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
@@ -22,7 +24,9 @@ var LocalhostKey []byte
|
||||
// GRPCSuite
|
||||
type GRPCSuite struct{ BaseSuite }
|
||||
|
||||
type myserver struct{}
|
||||
type myserver struct {
|
||||
stopStreamExample chan bool
|
||||
}
|
||||
|
||||
func (s *GRPCSuite) SetUpSuite(c *check.C) {
|
||||
var err error
|
||||
@@ -36,7 +40,15 @@ func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*
|
||||
return &helloworld.HelloReply{Message: "Hello " + in.Name}, nil
|
||||
}
|
||||
|
||||
func startGRPCServer(lis net.Listener) error {
|
||||
func (s *myserver) StreamExample(in *helloworld.StreamExampleRequest, server helloworld.Greeter_StreamExampleServer) error {
|
||||
data := make([]byte, 512)
|
||||
rand.Read(data)
|
||||
server.Send(&helloworld.StreamExampleReply{Data: string(data)})
|
||||
<-s.stopStreamExample
|
||||
return nil
|
||||
}
|
||||
|
||||
func startGRPCServer(lis net.Listener, server *myserver) error {
|
||||
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -45,26 +57,30 @@ func startGRPCServer(lis net.Listener) error {
|
||||
creds := credentials.NewServerTLSFromCert(&cert)
|
||||
serverOption := grpc.Creds(creds)
|
||||
|
||||
var s *grpc.Server = grpc.NewServer(serverOption)
|
||||
s := grpc.NewServer(serverOption)
|
||||
defer s.Stop()
|
||||
|
||||
helloworld.RegisterGreeterServer(s, &myserver{})
|
||||
helloworld.RegisterGreeterServer(s, server)
|
||||
return s.Serve(lis)
|
||||
}
|
||||
|
||||
func callHelloClientGRPC() (string, error) {
|
||||
func getHelloClientGRPC() (helloworld.GreeterClient, func() error, error) {
|
||||
roots := x509.NewCertPool()
|
||||
roots.AppendCertsFromPEM(LocalhostCert)
|
||||
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
||||
conn, err := grpc.Dial("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient))
|
||||
if err != nil {
|
||||
return nil, func() error { return nil }, err
|
||||
}
|
||||
return helloworld.NewGreeterClient(conn), conn.Close, nil
|
||||
|
||||
}
|
||||
|
||||
func callHelloClientGRPC(name string) (string, error) {
|
||||
client, closer, err := getHelloClientGRPC()
|
||||
defer closer()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
client := helloworld.NewGreeterClient(conn)
|
||||
|
||||
name := "World"
|
||||
r, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: name})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -72,13 +88,26 @@ func callHelloClientGRPC() (string, error) {
|
||||
return r.Message, nil
|
||||
}
|
||||
|
||||
func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func() error, error) {
|
||||
client, closer, err := getHelloClientGRPC()
|
||||
if err != nil {
|
||||
return nil, closer, err
|
||||
}
|
||||
t, err := client.StreamExample(context.Background(), &helloworld.StreamExampleRequest{})
|
||||
if err != nil {
|
||||
return nil, closer, err
|
||||
}
|
||||
|
||||
return t, closer, nil
|
||||
}
|
||||
|
||||
func (s *GRPCSuite) TestGRPC(c *check.C) {
|
||||
lis, err := net.Listen("tcp", ":0")
|
||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
go func() {
|
||||
err := startGRPCServer(lis)
|
||||
err := startGRPCServer(lis, &myserver{})
|
||||
c.Log(err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}()
|
||||
@@ -106,7 +135,7 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
|
||||
c.Assert(err, check.IsNil)
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC()
|
||||
response, err = callHelloClientGRPC("World")
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -120,7 +149,7 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
go func() {
|
||||
err := startGRPCServer(lis)
|
||||
err := startGRPCServer(lis, &myserver{})
|
||||
c.Log(err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}()
|
||||
@@ -148,10 +177,70 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||
c.Assert(err, check.IsNil)
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC()
|
||||
response, err = callHelloClientGRPC("World")
|
||||
return err
|
||||
})
|
||||
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(response, check.Equals, "Hello World")
|
||||
}
|
||||
|
||||
func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||
stopStreamExample := make(chan bool)
|
||||
defer func() { stopStreamExample <- true }()
|
||||
lis, err := net.Listen("tcp", ":0")
|
||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
go func() {
|
||||
err := startGRPCServer(lis, &myserver{
|
||||
stopStreamExample: stopStreamExample,
|
||||
})
|
||||
c.Log(err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
||||
CertContent string
|
||||
KeyContent string
|
||||
GRPCServerPort string
|
||||
}{
|
||||
CertContent: string(LocalhostCert),
|
||||
KeyContent: string(LocalhostKey),
|
||||
GRPCServerPort: port,
|
||||
})
|
||||
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, check.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:127.0.0.1"))
|
||||
c.Assert(err, check.IsNil)
|
||||
var client helloworld.Greeter_StreamExampleClient
|
||||
client, closer, err := callStreamExampleClientGRPC()
|
||||
defer closer()
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
received := make(chan bool)
|
||||
go func() {
|
||||
tr, err := client.Recv()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(len(tr.Data), check.Equals, 512)
|
||||
received <- true
|
||||
}()
|
||||
|
||||
err = try.Do(time.Second*10, func() error {
|
||||
select {
|
||||
case <-received:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("failed to receive stream data")
|
||||
}
|
||||
})
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user