mirror of
https://github.com/containous/traefik.git
synced 2025-09-06 05:44:21 +03:00
Compare commits
496 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c766439fed | ||
|
0ecfbb8279 | ||
|
82d631572c | ||
|
fa4d2d325d | ||
|
c469bbb70c | ||
|
b3e6c7f598 | ||
|
40afd641a9 | ||
|
fba3db5291 | ||
|
023cda1398 | ||
|
3cf6d7e9e5 | ||
|
0d657a09b0 | ||
|
07176396e2 | ||
|
5183c98fb7 | ||
|
5a57515c6b | ||
|
3490b9d35d | ||
|
dd0a20a668 | ||
|
d13cef6ff6 | ||
|
9f149977d6 | ||
|
e65544f414 | ||
|
8010758b29 | ||
|
75d92c6967 | ||
|
0f67cc7818 | ||
|
f428a752a5 | ||
|
3fe3784b6c | ||
|
e4d63331cf | ||
|
8ae521db64 | ||
|
24fde36b20 | ||
|
7c55a4fd0c | ||
|
7b1c0a97f7 | ||
|
8392846bd4 | ||
|
07db6a2df1 | ||
|
cc9bb4b1f8 | ||
|
de91b99639 | ||
|
c582ea5ff0 | ||
|
b5de37e722 | ||
|
ee9032f0bf | ||
|
11a68ce7f9 | ||
|
0dbac0af0d | ||
|
9541ee4cf6 | ||
|
2958a67ce5 | ||
|
eebbf6ebbb | ||
|
0133178b84 | ||
|
9af5ba34ae | ||
|
9b24e13418 | ||
|
8fd880a3f1 | ||
|
89700666b9 | ||
|
343e0547df | ||
|
a7d5e6ce4f | ||
|
d342ae68d8 | ||
|
a87cd3fc2c | ||
|
28a4d65b38 | ||
|
253dbfab94 | ||
|
727f79f432 | ||
|
6a56eb480b | ||
|
155d0900bb | ||
|
a59a165cd7 | ||
|
5d540be81b | ||
|
05f2449d84 | ||
|
44fa364cdd | ||
|
04a25b841f | ||
|
efbbff671c | ||
|
27c2c721ed | ||
|
abe16d4480 | ||
|
445bb8189b | ||
|
ff7dfdcd43 | ||
|
5e662c9dbd | ||
|
78d60b3651 | ||
|
7a7992a639 | ||
|
39f8f6868a | ||
|
556915cab6 | ||
|
bff654b843 | ||
|
3a875e2954 | ||
|
bdb63ac785 | ||
|
9a5dc54f85 | ||
|
48524a58ff | ||
|
38bd49b97e | ||
|
28054a0be3 | ||
|
250a0863f6 | ||
|
b1764a6864 | ||
|
41f8f0113b | ||
|
db63e84a9f | ||
|
e0a4c58081 | ||
|
d2b47a5681 | ||
|
106e5c1f92 | ||
|
c00a9fae0c | ||
|
087bbd2e3e | ||
|
e16f2bb23d | ||
|
8d0bacf146 | ||
|
354f69b2f6 | ||
|
39e6b16069 | ||
|
b30272d896 | ||
|
755822bf14 | ||
|
99ffc26d40 | ||
|
4a8f032304 | ||
|
a0b775a7c0 | ||
|
0ab0bdf818 | ||
|
fce32ea5c7 | ||
|
8d3c77a0b9 | ||
|
00de73bdfc | ||
|
96197af3f1 | ||
|
dacde21c27 | ||
|
0d3b2ed230 | ||
|
fa4226c742 | ||
|
7cb4c42772 | ||
|
99f251451e | ||
|
d5f9a80b6c | ||
|
d324040adc | ||
|
da5eba17d8 | ||
|
434596b103 | ||
|
71a185c70e | ||
|
cbbb5f4ccb | ||
|
89ec25f718 | ||
|
e5b688214c | ||
|
225dbcce0a | ||
|
b22dc213e8 | ||
|
ad12a7264e | ||
|
29059b77a8 | ||
|
cdaa64a4b2 | ||
|
bc4296729f | ||
|
3a3630f3ef | ||
|
93ce747205 | ||
|
1493a4c815 | ||
|
54be6beaab | ||
|
e9fc9fdf12 | ||
|
ba4670eddc | ||
|
5a67d0ac84 | ||
|
be362f0d9f | ||
|
a394e6a3e3 | ||
|
1a5f1977c4 | ||
|
feee8ad72e | ||
|
c9e78c4f4a | ||
|
d0e2349dfd | ||
|
d516cbfe6c | ||
|
86fd5b4c97 | ||
|
1131a972cd | ||
|
2048f77178 | ||
|
a70c6f25ea | ||
|
490427f94d | ||
|
7cc91a8244 | ||
|
4f951a242b | ||
|
c095fc1eab | ||
|
c1182377db | ||
|
02473328e7 | ||
|
2b00cdf330 | ||
|
18cf49755e | ||
|
3a7de0be5c | ||
|
a1b610ee03 | ||
|
4d99b84e5b | ||
|
e20d13c44e | ||
|
18e9064d25 | ||
|
fad3038df2 | ||
|
8e4c4f8407 | ||
|
68bd24d065 | ||
|
d15a17b634 | ||
|
fa1090b6eb | ||
|
483ef486af | ||
|
175659a3dd | ||
|
dd85cbca39 | ||
|
22b97b7214 | ||
|
db68dd3bc1 | ||
|
85b9c19871 | ||
|
2bfc237e53 | ||
|
d74ea22d7d | ||
|
8004132a3a | ||
|
a6f4183cde | ||
|
51e9f3ede2 | ||
|
bfc7b3d183 | ||
|
8a348423ae | ||
|
e4952cd145 | ||
|
5b0bf5d150 | ||
|
79180dc021 | ||
|
599c95e5f6 | ||
|
e1ed8b71f6 | ||
|
6ca142bf20 | ||
|
6b20d2a5f3 | ||
|
bef55db120 | ||
|
3bb3658d7d | ||
|
a4034ce1e2 | ||
|
d9fc66fdbc | ||
|
3ebfd729cf | ||
|
6adb346cee | ||
|
318ff52ff3 | ||
|
b7b0f8f68d | ||
|
94bb7a1435 | ||
|
913a297e8d | ||
|
d469d426f8 | ||
|
ec05fbcf19 | ||
|
686faf0556 | ||
|
fe2d4e0d38 | ||
|
c500873586 | ||
|
fc788eb426 | ||
|
87eac1dc1a | ||
|
91d9b9811f | ||
|
71beb4b08f | ||
|
d26f06e2d1 | ||
|
dca08af003 | ||
|
4c740e26d7 | ||
|
131f581f77 | ||
|
9236a43a4d | ||
|
7f4eddf6d6 | ||
|
d1e631a487 | ||
|
0b78375211 | ||
|
15540764a0 | ||
|
82234cbbb2 | ||
|
22392daef7 | ||
|
7f3ae6edb0 | ||
|
1a993f5dfb | ||
|
4e527304d0 | ||
|
841be8d806 | ||
|
055cd01bb7 | ||
|
e34c364d5e | ||
|
926eb099f1 | ||
|
710508dc40 | ||
|
b4ea68b88a | ||
|
2bf9acd95e | ||
|
a8cb905255 | ||
|
567387aee0 | ||
|
5b71e3184a | ||
|
e1724444ac | ||
|
cf8940e80e | ||
|
fe1b982d13 | ||
|
221ae2427b | ||
|
29f780863b | ||
|
8aaca8e55c | ||
|
2dda3d2feb | ||
|
22ebaedb45 | ||
|
7065f00443 | ||
|
15732269da | ||
|
7b06be8f5e | ||
|
d2dcec40e1 | ||
|
2af6cc4d1b | ||
|
56c6174d61 | ||
|
66e914a8ab | ||
|
8ae9607d9b | ||
|
5c0297fb61 | ||
|
f5bf9a2cda | ||
|
987ab7612d | ||
|
a186d5f87a | ||
|
801e0f9ef7 | ||
|
874ea62dd5 | ||
|
ac20ddfc6c | ||
|
f0b991e1a8 | ||
|
adf385fdf3 | ||
|
7af6bc093d | ||
|
3708fa864b | ||
|
28276e1b37 | ||
|
b0efd685a9 | ||
|
422aacf8e6 | ||
|
f6576cce27 | ||
|
e068ee09ca | ||
|
d3b48cdd22 | ||
|
91e3bdff48 | ||
|
4299d1526b | ||
|
c26b36cf4f | ||
|
3095da64d7 | ||
|
07f961ecba | ||
|
8d9caaec71 | ||
|
91634d5c1c | ||
|
f5463c3d38 | ||
|
73b70393d4 | ||
|
3db6e185e0 | ||
|
d174ed75c7 | ||
|
513d261f10 | ||
|
4430befe90 | ||
|
acf425b6cf | ||
|
1c4eb4322b | ||
|
3f3fa61a51 | ||
|
ddf24039e8 | ||
|
98b35affd5 | ||
|
b3cc1e1af1 | ||
|
5b6a5f8aa9 | ||
|
3e6d2391f7 | ||
|
664ee9d82f | ||
|
c9cc3c9895 | ||
|
00c7e5c72b | ||
|
2b770ae2f8 | ||
|
558b31f4d9 | ||
|
174a5e7f13 | ||
|
952fcf5d09 | ||
|
c821f191b0 | ||
|
3322e564fd | ||
|
7bf5d557c1 | ||
|
0c1e06199c | ||
|
85a20b9a39 | ||
|
931a124349 | ||
|
ab52f4d91d | ||
|
f3182ef29b | ||
|
5641af437e | ||
|
1c8d3ded3d | ||
|
c2a445370e | ||
|
8e5355f2d9 | ||
|
2492157833 | ||
|
7c375e8fd9 | ||
|
53b5d8ac33 | ||
|
e5a8fb390e | ||
|
79cbae0c73 | ||
|
22b0b8b750 | ||
|
ddbddf6edf | ||
|
adcf58da68 | ||
|
05f6b79e29 | ||
|
649cb548d0 | ||
|
14db2343c9 | ||
|
67eb0c8de0 | ||
|
870f378782 | ||
|
82a58010f5 | ||
|
f652c58367 | ||
|
468d138be7 | ||
|
f409d2f435 | ||
|
5780a17794 | ||
|
9b765d23fa | ||
|
4476861d9f | ||
|
e12ddca1a5 | ||
|
084d00a156 | ||
|
404a73a712 | ||
|
3b2410d904 | ||
|
bd5009058b | ||
|
d3f79c7ad3 | ||
|
3f65503a79 | ||
|
6ac1216f8c | ||
|
1cae35f96b | ||
|
0d13e91a62 | ||
|
b1b600e09e | ||
|
3692e1c4bd | ||
|
dcbd82ac3b | ||
|
d4f0541027 | ||
|
a30d8e7819 | ||
|
8ee6bf044a | ||
|
6632247c9c | ||
|
d68389dc52 | ||
|
4a43273ee5 | ||
|
66f52a6e21 | ||
|
640bfc4eff | ||
|
408ef0f5b7 | ||
|
b9f76394aa | ||
|
a96f483d56 | ||
|
84cb9f15a4 | ||
|
d4da14cf18 | ||
|
4ad4b8e0b8 | ||
|
bb29d9c8ca | ||
|
e72e65858f | ||
|
a42845502e | ||
|
bea5ad3f13 | ||
|
5a0440d6f8 | ||
|
38b62d4ae3 | ||
|
462d8b3e74 | ||
|
291c3b6dbc | ||
|
df225d9170 | ||
|
592e981bd2 | ||
|
3d7c44735a | ||
|
81fddb4ccf | ||
|
c9d4c5ae3e | ||
|
be5b1fd92b | ||
|
d78c419627 | ||
|
dc52abf4ce | ||
|
a13549cc28 | ||
|
baf4c474e3 | ||
|
a58750992d | ||
|
17546c3a08 | ||
|
067f13b61c | ||
|
e249983c77 | ||
|
454b191370 | ||
|
a882a9d79f | ||
|
89fc835bb2 | ||
|
364958cbaf | ||
|
1b6af2045e | ||
|
be09ff8e43 | ||
|
99c8bffcbf | ||
|
03d16d12d5 | ||
|
1624c51cb5 | ||
|
83aabefcc5 | ||
|
dfece708e1 | ||
|
5d0f82ffbd | ||
|
361dc94002 | ||
|
cc0fdf15ef | ||
|
928675a847 | ||
|
12c1131b0c | ||
|
bb1dde0469 | ||
|
ced69b8397 | ||
|
013808956c | ||
|
009057cb87 | ||
|
82cb21fca3 | ||
|
7e8937a332 | ||
|
e5dcfa0a2e | ||
|
f4520a011a | ||
|
98dd6ca460 | ||
|
c3d9312240 | ||
|
5ea761e19f | ||
|
46a7860427 | ||
|
af9b63eaed | ||
|
9a26e0db16 | ||
|
efe6989fd3 | ||
|
aa1c9b80e3 | ||
|
6981df3b9a | ||
|
0d1ed625a8 | ||
|
710fc56c6a | ||
|
d5a15d6756 | ||
|
b376da1829 | ||
|
f7f17f0057 | ||
|
d06b9c2992 | ||
|
99ca5d0a03 | ||
|
4783c7f70a | ||
|
d89bdfbd27 | ||
|
1e324ad3bc | ||
|
52737e91e5 | ||
|
1872e2b63d | ||
|
3c5605b793 | ||
|
9a2b7cf5be | ||
|
1a20e9f9b4 | ||
|
14d79e4eef | ||
|
71f48d2aef | ||
|
312adca226 | ||
|
d35c6e77d7 | ||
|
1de21c86ae | ||
|
c709a592eb | ||
|
a54c544eb4 | ||
|
7d936ec6aa | ||
|
f63ec1332f | ||
|
d340ccd601 | ||
|
95e8f0a31e | ||
|
97ddfcb17a | ||
|
7bb5f9a1e4 | ||
|
11297b38c5 | ||
|
fc19ab2868 | ||
|
5e01c0a7db | ||
|
f1c3d820f7 | ||
|
0757a75732 | ||
|
f0ea45a0f8 | ||
|
45f2335a60 | ||
|
d629939cf3 | ||
|
404f76dcb9 | ||
|
498ce6b00c | ||
|
e3a8fd116d | ||
|
d33e09bcf3 | ||
|
fb3bad3887 | ||
|
3a736ad4a8 | ||
|
c1b0c41769 | ||
|
c03274703e | ||
|
4cd08e88f6 | ||
|
e2c4872030 | ||
|
d4f190e995 | ||
|
039107e837 | ||
|
ef6c211275 | ||
|
1f3accc0d7 | ||
|
2815f80063 | ||
|
fa645abee3 | ||
|
a86649def3 | ||
|
1fc4c56bc4 | ||
|
79dd72f53d | ||
|
ffa060ce56 | ||
|
5ce9719951 | ||
|
914aa7d372 | ||
|
4a88cbde3a | ||
|
4882519c0f | ||
|
7abe68fac1 | ||
|
e62cca1e7c | ||
|
a016741918 | ||
|
2f95810fa3 | ||
|
16e2c3b1e0 | ||
|
bc8a92caa9 | ||
|
3a5b67a3e1 | ||
|
2a596b8162 | ||
|
e059239bc3 | ||
|
986ad9fc57 | ||
|
1bb3d9be73 | ||
|
ae31f19ef6 | ||
|
c170ddc7ae | ||
|
58b6d92ce2 | ||
|
87a4d73556 | ||
|
4c54a003fa | ||
|
a5f3eabf8b | ||
|
3bf6c59d23 | ||
|
ef83dea95c | ||
|
686c23d25b | ||
|
b153e90ec5 | ||
|
38cc36980f | ||
|
b83fb525a8 | ||
|
e26e0955b3 | ||
|
7ada80b619 | ||
|
056e0fe2d9 | ||
|
9be0c67d5c | ||
|
664bc9cae0 | ||
|
959c7dc783 | ||
|
8e333d0a03 | ||
|
5afcf17706 | ||
|
61b22316d6 | ||
|
d2f51fccb9 | ||
|
c13db04f6d | ||
|
d3aa056151 | ||
|
1c60f0b53b | ||
|
ca2b85f453 | ||
|
b80479f9ef | ||
|
d1112a0feb | ||
|
a73baded88 | ||
|
94fa95d747 | ||
|
9f6484a328 | ||
|
40c0ed092e |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
glide.lock binary
|
68
.github/CONTRIBUTING.md
vendored
68
.github/CONTRIBUTING.md
vendored
@@ -2,16 +2,9 @@
|
||||
|
||||
### Building
|
||||
|
||||
You need either [Docker](https://github.com/docker/docker) and `make`, or `go` and `glide` in order to build traefik.
|
||||
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` and `glide` (Method 2) in order to build traefik.
|
||||
|
||||
#### Setting up your `go` environment
|
||||
|
||||
- You need `go` v1.5
|
||||
- You need to set `export GO15VENDOREXPERIMENT=1` environment variable
|
||||
- You need `go-bindata` to be able to use `go generate` command (needed to build) : `go get github.com/jteeuwen/go-bindata/...`.
|
||||
- If you clone Træfɪk into something like `~/go/src/github.com/traefik`, your `GOPATH` variable will have to be set to `~/go`: export `GOPATH=~/go`.
|
||||
|
||||
#### Using `Docker` and `Makefile`
|
||||
#### Method 1: Using `Docker` and `Makefile`
|
||||
|
||||
You need to run the `binary` target. This will create binaries for Linux platform in the `dist` folder.
|
||||
|
||||
@@ -19,7 +12,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.5
|
||||
Step 0 : FROM golang:1.7
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/Masterminds/glide
|
||||
[...]
|
||||
@@ -33,32 +26,50 @@ $ ls dist/
|
||||
traefik*
|
||||
```
|
||||
|
||||
#### Using `glide`
|
||||
#### Method 2: Using `go` and `glide`
|
||||
|
||||
###### Setting up your `go` environment
|
||||
|
||||
- You need `go` v1.7+
|
||||
- It is recommended you clone Træfɪk into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
|
||||
- This will allow your `GOPATH` and `PATH` variable to be set to `~/go` via:
|
||||
```
|
||||
$ export GOPATH=~/go
|
||||
$ export PATH=$PATH:$GOPATH/bin
|
||||
```
|
||||
|
||||
This can be verified via `$ go env`
|
||||
- You will want to add those 2 export lines to your `.bashrc` or `.bash_profile`
|
||||
- You need `go-bindata` to be able to use `go generate` command (needed to build) : `$ go get github.com/jteeuwen/go-bindata/...` (Please note, the ellipses are required)
|
||||
|
||||
###### Setting up your `glide` environment
|
||||
|
||||
- Glide can be installed either via homebrew: `$ brew install glide` or via the official glide script: `$ curl https://glide.sh/get | sh`
|
||||
|
||||
The idea behind `glide` is the following :
|
||||
|
||||
- when checkout(ing) a project, **run `glide install`** to install
|
||||
(`go get …`) the dependencies in the `GOPATH`.
|
||||
- when checkout(ing) a project, run `$ glide install -v` from the cloned directory to install
|
||||
(`go get …`) the dependencies in your `GOPATH`.
|
||||
- if you need another dependency, import and use it in
|
||||
the source, and **run `glide get github.com/Masterminds/cookoo`** to save it in
|
||||
the source, and run `$ glide get github.com/Masterminds/cookoo` to save it in
|
||||
`vendor` and add it to your `glide.yaml`.
|
||||
|
||||
```bash
|
||||
$ glide install
|
||||
# generate
|
||||
$ glide install --strip-vendor
|
||||
# generate (Only required to integrate other components such as web dashboard)
|
||||
$ go generate
|
||||
# Simple go build
|
||||
# Standard go build
|
||||
$ go build
|
||||
# Using gox to build multiple platform
|
||||
$ gox "linux darwin" "386 amd64 arm" \
|
||||
-output="dist/traefik_{{.OS}}-{{.Arch}}"
|
||||
# run other commands like tests
|
||||
$ go test ./...
|
||||
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
##### Method 1: `Docker` and `make`
|
||||
|
||||
You can run unit tests using the `test-unit` target and the
|
||||
integration test using the `test-integration` target.
|
||||
|
||||
@@ -77,7 +88,7 @@ ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
|
||||
Test success
|
||||
```
|
||||
|
||||
For development purpose, you can specifiy which tests to run by using:
|
||||
For development purposes, you can specify which tests to run by using:
|
||||
```
|
||||
# Run every tests in the MyTest suite
|
||||
TESTFLAGS="-check.f MyTestSuite" make test-integration
|
||||
@@ -94,6 +105,15 @@ TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
||||
|
||||
More: https://labix.org/gocheck
|
||||
|
||||
##### Method 2: `go` and `glide`
|
||||
|
||||
- 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
|
||||
```
|
||||
- Note that `$ go test ./...` will run all tests (including the ones in the vendor directory for the dependencies that glide have fetched). If you only want to run the tests for traefik use `$ go test $(glide novendor)` instead.
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
|
||||
@@ -113,13 +133,13 @@ Then install mkdocs with pip
|
||||
$ pip install mkdocs
|
||||
```
|
||||
|
||||
To test documentaion localy run `mkdocs serve` in the root directory, this should start a server localy to preview your changes.
|
||||
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
|
||||
|
||||
```
|
||||
$ mkdocs serve
|
||||
INFO - Building documentation...
|
||||
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
|
||||
INFO - Cleaning site directory
|
||||
INFO - Building documentation...
|
||||
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
|
||||
INFO - Cleaning site directory
|
||||
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
|
||||
[I 160505 22:31:24 handlers:59] Start watching changes
|
||||
[I 160505 22:31:24 handlers:61] Start detecting changes
|
||||
|
13
.github/ISSUE_TEMPLATE
vendored
Normal file
13
.github/ISSUE_TEMPLATE
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
### What version of Traefik are you using (`traefik version`)?
|
||||
|
||||
|
||||
### What is your environment & configuration (arguments, toml...)?
|
||||
|
||||
|
||||
### What did you do?
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
### What did you see instead?
|
26
.github/cpr.sh
vendored
Executable file
26
.github/cpr.sh
vendored
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# git config --global alias.cpr '!sh .github/cpr.sh'
|
||||
|
||||
set -e # stop on error
|
||||
|
||||
usage="$(basename "$0") pr -- Checkout a Pull Request locally"
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Illegal number of parameters"
|
||||
echo "$usage" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
||||
|
||||
set -x # echo on
|
||||
|
||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
||||
pr=$1
|
||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
||||
|
||||
git remote add $remote git@github.com:$remote/traefik.git
|
||||
git fetch $remote $branch
|
||||
git checkout -t $remote/$branch
|
27
.github/rmpr.sh
vendored
Executable file
27
.github/rmpr.sh
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# git config --global alias.rmpr '!sh .github/rmpr.sh'
|
||||
|
||||
set -e # stop on error
|
||||
|
||||
usage="$(basename "$0") pr -- remove a Pull Request local branch & remote"
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Illegal number of parameters"
|
||||
echo "$usage" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
||||
|
||||
set -x # echo on
|
||||
|
||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
||||
pr=$1
|
||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
||||
|
||||
# clean
|
||||
git checkout $initial
|
||||
git branch -D $branch
|
||||
git remote remove $remote
|
36
.github/rpr.sh
vendored
Executable file
36
.github/rpr.sh
vendored
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# git config --global alias.rpr '!sh .github/rpr.sh'
|
||||
|
||||
set -e # stop on error
|
||||
|
||||
usage="$(basename "$0") pr remote/branch -- rebase a Pull Request against a remote branch"
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Illegal number of parameters"
|
||||
echo "$usage" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
||||
|
||||
set -x # echo on
|
||||
|
||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
||||
pr=$1
|
||||
base=$2
|
||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
||||
|
||||
clean ()
|
||||
{
|
||||
git checkout $initial
|
||||
.github/rmpr.sh $pr
|
||||
}
|
||||
|
||||
trap clean EXIT
|
||||
|
||||
.github/cpr.sh $pr
|
||||
|
||||
git rebase $base
|
||||
git push -f $remote $branch
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
gen.go
|
||||
.idea
|
||||
.intellij
|
||||
log
|
||||
*.iml
|
||||
traefik
|
||||
traefik.toml
|
||||
@@ -13,3 +12,4 @@ static/
|
||||
site/
|
||||
*.log
|
||||
*.exe
|
||||
.DS_Store
|
||||
|
82
.travis.yml
82
.travis.yml
@@ -1,33 +1,67 @@
|
||||
branches:
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg=
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: $TRAVIS_TAG
|
||||
- CODENAME: reblochon
|
||||
matrix:
|
||||
- DOCKER_VERSION=1.9.1
|
||||
- DOCKER_VERSION=1.10.1
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
- CODENAME: morbier
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- env: DOCKER_VERSION=1.10.3
|
||||
- env: DOCKER_VERSION=1.12.6
|
||||
|
||||
before_install:
|
||||
- sudo -E apt-get -yq update
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-engine=${DOCKER_VERSION}*
|
||||
install:
|
||||
- sudo service docker stop
|
||||
- sudo curl https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION} -o /usr/bin/docker
|
||||
- sudo chmod +x /usr/bin/docker
|
||||
- sudo service docker start
|
||||
- sleep 5
|
||||
- docker version
|
||||
- pip install --user mkdocs
|
||||
- pip install --user pymdown-extensions
|
||||
- docker version
|
||||
- pip install --user -r requirements.txt
|
||||
before_script:
|
||||
- make validate
|
||||
- make binary
|
||||
- make validate
|
||||
- make binary
|
||||
script:
|
||||
- make test-unit
|
||||
- make test-integration
|
||||
- make crossbinary
|
||||
- make image
|
||||
- travis_retry make test-unit
|
||||
- travis_retry make test-integration
|
||||
after_failure:
|
||||
- docker ps
|
||||
after_success:
|
||||
- make deploy
|
||||
- make deploy-pr
|
||||
- make crossbinary
|
||||
- make image
|
||||
before_deploy:
|
||||
- mkdocs build --clean
|
||||
- tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .
|
||||
deploy:
|
||||
- provider: pages
|
||||
edge: true
|
||||
github_token: ${GITHUB_TOKEN}
|
||||
local_dir: site
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: releases
|
||||
api_key: ${GITHUB_TOKEN}
|
||||
file: dist/traefik*
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: script
|
||||
script: sh script/deploy.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: script
|
||||
script: sh script/deploy-docker.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
|
762
CHANGELOG.md
762
CHANGELOG.md
@@ -1,5 +1,767 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.2.2](https://github.com/containous/traefik/tree/v1.2.2) (2017-04-11)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.2.1...v1.2.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Carry PR 1271 [\#1417](https://github.com/containous/traefik/pull/1417) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix postloadconfig acme & Docker filter empty rule [\#1401](https://github.com/containous/traefik/pull/1401) ([emilevauge](https://github.com/emilevauge))
|
||||
|
||||
## [v1.2.1](https://github.com/containous/traefik/tree/v1.2.1) (2017-03-27)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.2.0...v1.2.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- bump lego 0e2937900 [\#1347](https://github.com/containous/traefik/pull/1347) ([emilevauge](https://github.com/emilevauge))
|
||||
- k8s: Do not log service fields when GetService is failing. [\#1331](https://github.com/containous/traefik/pull/1331) ([timoreimann](https://github.com/timoreimann))
|
||||
|
||||
## [v1.2.0](https://github.com/containous/traefik/tree/v1.2.0) (2017-03-20)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.2...v1.2.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Docker: Added warning if network could not be found [\#1310](https://github.com/containous/traefik/pull/1310) ([zweizeichen](https://github.com/zweizeichen))
|
||||
- Add filter on task status in addition to desired status \(Docker Provider - swarm\) [\#1304](https://github.com/containous/traefik/pull/1304) ([Yshayy](https://github.com/Yshayy))
|
||||
- Abort Kubernetes Ingress update if Kubernetes API call fails [\#1295](https://github.com/containous/traefik/pull/1295) ([Regner](https://github.com/Regner))
|
||||
- Small fixes [\#1291](https://github.com/containous/traefik/pull/1291) ([emilevauge](https://github.com/emilevauge))
|
||||
- Rename health check URL parameter to path. [\#1285](https://github.com/containous/traefik/pull/1285) ([timoreimann](https://github.com/timoreimann))
|
||||
- Update Oxy, fix for \#1199 [\#1278](https://github.com/containous/traefik/pull/1278) ([akanto](https://github.com/akanto))
|
||||
- Fix metrics registering [\#1258](https://github.com/containous/traefik/pull/1258) ([matevzmihalic](https://github.com/matevzmihalic))
|
||||
- Update DefaultMaxIdleConnsPerHost default in docs. [\#1239](https://github.com/containous/traefik/pull/1239) ([timoreimann](https://github.com/timoreimann))
|
||||
- Update WSS/WS Proto \[Fixes \#670\] [\#1225](https://github.com/containous/traefik/pull/1225) ([dtomcej](https://github.com/dtomcej))
|
||||
- Bump go-rancher version [\#1219](https://github.com/containous/traefik/pull/1219) ([SantoDE](https://github.com/SantoDE))
|
||||
- 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))
|
||||
- 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))
|
||||
- ECS: Docs - info about cred. resolution and required access policies [\#1137](https://github.com/containous/traefik/pull/1137) ([rickard-von-essen](https://github.com/rickard-von-essen))
|
||||
- Healthcheck tests and doc [\#1132](https://github.com/containous/traefik/pull/1132) ([Juliens](https://github.com/Juliens))
|
||||
- Fix travis deploy [\#1128](https://github.com/containous/traefik/pull/1128) ([emilevauge](https://github.com/emilevauge))
|
||||
- Prepare release v1.2.0 rc1 [\#1126](https://github.com/containous/traefik/pull/1126) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix checkout initial before calling rmpr [\#1124](https://github.com/containous/traefik/pull/1124) ([emilevauge](https://github.com/emilevauge))
|
||||
- Feature rancher integration [\#1120](https://github.com/containous/traefik/pull/1120) ([SantoDE](https://github.com/SantoDE))
|
||||
- Fix glide go units [\#1119](https://github.com/containous/traefik/pull/1119) ([emilevauge](https://github.com/emilevauge))
|
||||
- Carry \#818 — Add systemd watchdog feature [\#1116](https://github.com/containous/traefik/pull/1116) ([vdemeester](https://github.com/vdemeester))
|
||||
- Skip file permission check on Windows [\#1115](https://github.com/containous/traefik/pull/1115) ([StefanScherer](https://github.com/StefanScherer))
|
||||
- Fix Docker API version for Windows [\#1113](https://github.com/containous/traefik/pull/1113) ([StefanScherer](https://github.com/StefanScherer))
|
||||
- Fix git rpr [\#1109](https://github.com/containous/traefik/pull/1109) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix docker version specifier [\#1108](https://github.com/containous/traefik/pull/1108) ([timoreimann](https://github.com/timoreimann))
|
||||
- Merge v1.1.2 master [\#1105](https://github.com/containous/traefik/pull/1105) ([emilevauge](https://github.com/emilevauge))
|
||||
- add sh before script in deploy... [\#1103](https://github.com/containous/traefik/pull/1103) ([emilevauge](https://github.com/emilevauge))
|
||||
- \[doc\] typo fixes for kubernetes user guide [\#1102](https://github.com/containous/traefik/pull/1102) ([bamarni](https://github.com/bamarni))
|
||||
- add skip\_cleanup in deploy [\#1101](https://github.com/containous/traefik/pull/1101) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix k8s example UI port. [\#1098](https://github.com/containous/traefik/pull/1098) ([ddunkin](https://github.com/ddunkin))
|
||||
- Fix marathon provider [\#1090](https://github.com/containous/traefik/pull/1090) ([diegooliveira](https://github.com/diegooliveira))
|
||||
- Add an ECS provider [\#1088](https://github.com/containous/traefik/pull/1088) ([lpetre](https://github.com/lpetre))
|
||||
- Update comment to reflect the code [\#1087](https://github.com/containous/traefik/pull/1087) ([np](https://github.com/np))
|
||||
- update NYTimes/gziphandler fixes \#1059 [\#1084](https://github.com/containous/traefik/pull/1084) ([JamesKyburz](https://github.com/JamesKyburz))
|
||||
- Ensure that we don't add balancees with no health check runs if there is a health check defined on it [\#1080](https://github.com/containous/traefik/pull/1080) ([jangie](https://github.com/jangie))
|
||||
- Add FreeBSD & OpenBSD to crossbinary [\#1078](https://github.com/containous/traefik/pull/1078) ([geoffgarside](https://github.com/geoffgarside))
|
||||
- Fix metrics for multiple entry points [\#1071](https://github.com/containous/traefik/pull/1071) ([matevzmihalic](https://github.com/matevzmihalic))
|
||||
- Allow setting load balancer method and sticky using service annotations [\#1068](https://github.com/containous/traefik/pull/1068) ([bakins](https://github.com/bakins))
|
||||
- Fix travis script [\#1067](https://github.com/containous/traefik/pull/1067) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add missing fmt verb specifier in k8s provider. [\#1066](https://github.com/containous/traefik/pull/1066) ([timoreimann](https://github.com/timoreimann))
|
||||
- Add git rpr command [\#1063](https://github.com/containous/traefik/pull/1063) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix k8s example [\#1062](https://github.com/containous/traefik/pull/1062) ([emilevauge](https://github.com/emilevauge))
|
||||
- Replace underscores to dash in autogenerated urls \(docker provider\) [\#1061](https://github.com/containous/traefik/pull/1061) ([WTFKr0](https://github.com/WTFKr0))
|
||||
- Don't run go test on .glide cache folder [\#1057](https://github.com/containous/traefik/pull/1057) ([vdemeester](https://github.com/vdemeester))
|
||||
- Allow setting circuitbreaker expression via Kubernetes annotation [\#1056](https://github.com/containous/traefik/pull/1056) ([bakins](https://github.com/bakins))
|
||||
- Improving instrumentation. [\#1042](https://github.com/containous/traefik/pull/1042) ([enxebre](https://github.com/enxebre))
|
||||
- Update user guide for upcoming `docker stack deploy` [\#1041](https://github.com/containous/traefik/pull/1041) ([twelvelabs](https://github.com/twelvelabs))
|
||||
- Support sticky sessions under SWARM Mode. \#1024 [\#1033](https://github.com/containous/traefik/pull/1033) ([foleymic](https://github.com/foleymic))
|
||||
- Allow for wildcards in k8s ingress host, fixes \#792 [\#1029](https://github.com/containous/traefik/pull/1029) ([sheerun](https://github.com/sheerun))
|
||||
- Don't fetch ACME certificates for frontends using non-TLS entrypoints \(\#989\) [\#1023](https://github.com/containous/traefik/pull/1023) ([syfonseq](https://github.com/syfonseq))
|
||||
- Return Proper Non-ACME certificate - Fixes Issue 672 [\#1018](https://github.com/containous/traefik/pull/1018) ([dtomcej](https://github.com/dtomcej))
|
||||
- Fix docs build and add missing benchmarks page [\#1017](https://github.com/containous/traefik/pull/1017) ([csabapalfi](https://github.com/csabapalfi))
|
||||
- Set a NopCloser request body with retry middleware [\#1016](https://github.com/containous/traefik/pull/1016) ([bamarni](https://github.com/bamarni))
|
||||
- instruct to flatten dependencies with glide [\#1010](https://github.com/containous/traefik/pull/1010) ([bamarni](https://github.com/bamarni))
|
||||
- check permissions on acme.json during startup [\#1009](https://github.com/containous/traefik/pull/1009) ([bamarni](https://github.com/bamarni))
|
||||
- \[doc\] few tweaks on the basics page [\#1005](https://github.com/containous/traefik/pull/1005) ([bamarni](https://github.com/bamarni))
|
||||
- Import order as goimports does [\#1004](https://github.com/containous/traefik/pull/1004) ([vdemeester](https://github.com/vdemeester))
|
||||
- See the right go report badge [\#991](https://github.com/containous/traefik/pull/991) ([guilhem](https://github.com/guilhem))
|
||||
- Add multiple values for one rule to docs [\#978](https://github.com/containous/traefik/pull/978) ([j0hnsmith](https://github.com/j0hnsmith))
|
||||
- Add ACME/Let’s Encrypt integration tests [\#975](https://github.com/containous/traefik/pull/975) ([trecloux](https://github.com/trecloux))
|
||||
- deploy.sh: upload release source tarball [\#969](https://github.com/containous/traefik/pull/969) ([Mic92](https://github.com/Mic92))
|
||||
- toml zookeeper doc fix [\#948](https://github.com/containous/traefik/pull/948) ([brdude](https://github.com/brdude))
|
||||
- Add Rule AddPrefix [\#931](https://github.com/containous/traefik/pull/931) ([Juliens](https://github.com/Juliens))
|
||||
- Add bug command [\#921](https://github.com/containous/traefik/pull/921) ([emilevauge](https://github.com/emilevauge))
|
||||
- \(WIP\) feat: HealthCheck [\#918](https://github.com/containous/traefik/pull/918) ([Juliens](https://github.com/Juliens))
|
||||
- Add ability to set authenticated user in request header [\#889](https://github.com/containous/traefik/pull/889) ([ViViDboarder](https://github.com/ViViDboarder))
|
||||
- IP-per-task: [\#841](https://github.com/containous/traefik/pull/841) ([diegooliveira](https://github.com/diegooliveira))
|
||||
|
||||
## [v1.2.0-rc2](https://github.com/containous/traefik/tree/v1.2.0-rc2) (2017-03-01)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.2.0-rc1...v1.2.0-rc2)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Are there plans to support the service type ExternalName in Kubernetes? [\#1142](https://github.com/containous/traefik/issues/1142)
|
||||
- Kubernetes Ingress and sticky support [\#911](https://github.com/containous/traefik/issues/911)
|
||||
- kubernetes client does not support InsecureSkipVerify [\#876](https://github.com/containous/traefik/issues/876)
|
||||
- Support active health checking like HAProxy [\#824](https://github.com/containous/traefik/issues/824)
|
||||
- Allow k8s ingress controller serviceAccountToken and serviceAccountCACert to be changed [\#611](https://github.com/containous/traefik/issues/611)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[rancher\] invalid memory address or nil pointer dereference [\#1134](https://github.com/containous/traefik/issues/1134)
|
||||
- Kubernetes default backend should work [\#1073](https://github.com/containous/traefik/issues/1073)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Are release Download links broken? [\#1201](https://github.com/containous/traefik/issues/1201)
|
||||
- Bind to specific ip address [\#1193](https://github.com/containous/traefik/issues/1193)
|
||||
- DNS01 challenge use the wrong zone through route53 [\#1192](https://github.com/containous/traefik/issues/1192)
|
||||
- Reverse proxy https to http backends fails [\#1180](https://github.com/containous/traefik/issues/1180)
|
||||
- Swarm Mode + Letsecrypt + KV Store [\#1176](https://github.com/containous/traefik/issues/1176)
|
||||
- docker deploy -c example.yml e [\#1169](https://github.com/containous/traefik/issues/1169)
|
||||
- Traefik not finding dynamically added services \(Docker Swarm Mode\) [\#1168](https://github.com/containous/traefik/issues/1168)
|
||||
- Traefik with Kubernetes backend - keep getting 401 on all GET requests to kube-apiserver [\#1166](https://github.com/containous/traefik/issues/1166)
|
||||
- Near line 15 \(last key parsed 'backends.backend-monitor-viz.servers'\): Key 'backends.backend-monitor-viz.servers.server-monitor\_viz-1' has already been defined. [\#1154](https://github.com/containous/traefik/issues/1154)
|
||||
- How to reuse SSL certificates automatically fetched from Let´s encrypt? [\#1152](https://github.com/containous/traefik/issues/1152)
|
||||
- Dynamically ban ip when backend repeatedly returns specified status code. \( 403 \) [\#1136](https://github.com/containous/traefik/issues/1136)
|
||||
- Always get 404 accessing my nginx backend service [\#1112](https://github.com/containous/traefik/issues/1112)
|
||||
- Incomplete Docu [\#1091](https://github.com/containous/traefik/issues/1091)
|
||||
- LoadCertificateForDomains: runtime error: invalid memory address [\#1069](https://github.com/containous/traefik/issues/1069)
|
||||
- Traefik creating backends & mappings for ingress annotated with ingress.class: nginx [\#1058](https://github.com/containous/traefik/issues/1058)
|
||||
- ACME file format description [\#1012](https://github.com/containous/traefik/issues/1012)
|
||||
- SwarmMode - Not routing on worker node [\#838](https://github.com/containous/traefik/issues/838)
|
||||
- Migrate k8s to kubernetes/client-go [\#678](https://github.com/containous/traefik/issues/678)
|
||||
- Support for sticky session with kubernetes ingress as backend [\#674](https://github.com/containous/traefik/issues/674)
|
||||
|
||||
**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))
|
||||
- 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))
|
||||
- ECS: Docs - info about cred. resolution and required access policies [\#1137](https://github.com/containous/traefik/pull/1137) ([rickard-von-essen](https://github.com/rickard-von-essen))
|
||||
- Healthcheck tests and doc [\#1132](https://github.com/containous/traefik/pull/1132) ([Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.2.0-rc1](https://github.com/containous/traefik/tree/v1.2.0-rc1) (2017-02-06)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.2...v1.2.0-rc1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add FreeBSD and OpenBSD to release builds [\#923](https://github.com/containous/traefik/issues/923)
|
||||
- Write authenticated user to header key [\#802](https://github.com/containous/traefik/issues/802)
|
||||
- Question: Wildcard Host for Kubernetes Ingress [\#792](https://github.com/containous/traefik/issues/792)
|
||||
- First commit prometheus middleware. [\#1022](https://github.com/containous/traefik/pull/1022) ([enxebre](https://github.com/enxebre))
|
||||
- Use deployment primitives from travis [\#843](https://github.com/containous/traefik/pull/843) ([guilhem](https://github.com/guilhem))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Increase Docker API version to work with Windows Containers [\#1094](https://github.com/containous/traefik/issues/1094)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- How could I know whether forwarding path is correctly set? [\#1111](https://github.com/containous/traefik/issues/1111)
|
||||
- ACME + Docker-compose labels [\#1099](https://github.com/containous/traefik/issues/1099)
|
||||
- Loadbalance between 2 containers in Docker Swarm Mode [\#1095](https://github.com/containous/traefik/issues/1095)
|
||||
- Add DNS01 letsencrypt challenge support through AWS. [\#1093](https://github.com/containous/traefik/issues/1093)
|
||||
- New Release Cut [\#1092](https://github.com/containous/traefik/issues/1092)
|
||||
- Marathon integration changed default backend server port from task-level to application-level [\#1072](https://github.com/containous/traefik/issues/1072)
|
||||
- websockets not working when compress = true in toml config. [\#1059](https://github.com/containous/traefik/issues/1059)
|
||||
- Proxying 403 http status into the application [\#1044](https://github.com/containous/traefik/issues/1044)
|
||||
- Normalize auto generated frontend-rule \(docker\) [\#1043](https://github.com/containous/traefik/issues/1043)
|
||||
- Traefik with Consul catalog backend + Registrator [\#1039](https://github.com/containous/traefik/issues/1039)
|
||||
- \[Configuration help\] Can't connect to docker containers under a domain path [\#1032](https://github.com/containous/traefik/issues/1032)
|
||||
- Kubernetes and etcd backend : `storeconfig` fails. [\#1031](https://github.com/containous/traefik/issues/1031)
|
||||
- kubernetes: Undefined backend 'X/' for frontend X/" [\#1026](https://github.com/containous/traefik/issues/1026)
|
||||
- TLS handshake error [\#1025](https://github.com/containous/traefik/issues/1025)
|
||||
- Traefik failing on POST request [\#1008](https://github.com/containous/traefik/issues/1008)
|
||||
- how config traffic.toml http 80 without basic auth, traefik WebUI 8080 with basic auth [\#1001](https://github.com/containous/traefik/issues/1001)
|
||||
- Docs 404 [\#995](https://github.com/containous/traefik/issues/995)
|
||||
- Disable acme for non https endpoints [\#989](https://github.com/containous/traefik/issues/989)
|
||||
- Add parameter to configure TLS entrypoints with ca-bundle file [\#984](https://github.com/containous/traefik/issues/984)
|
||||
- docker multiple networks routing [\#970](https://github.com/containous/traefik/issues/970)
|
||||
- don't add Docker containers not on the same network as traefik [\#959](https://github.com/containous/traefik/issues/959)
|
||||
- Multiple frontend routes [\#957](https://github.com/containous/traefik/issues/957)
|
||||
- SNI based routing without TLS offloading [\#933](https://github.com/containous/traefik/issues/933)
|
||||
- NEO4J + traefik proxy Issues [\#907](https://github.com/containous/traefik/issues/907)
|
||||
- ACME OnDemand ignores entrypoint certificate [\#672](https://github.com/containous/traefik/issues/672)
|
||||
- Ability to use self-signed certificates for local development [\#399](https://github.com/containous/traefik/issues/399)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix checkout initial before calling rmpr [\#1124](https://github.com/containous/traefik/pull/1124) ([emilevauge](https://github.com/emilevauge))
|
||||
- Feature rancher integration [\#1120](https://github.com/containous/traefik/pull/1120) ([SantoDE](https://github.com/SantoDE))
|
||||
- Fix glide go units [\#1119](https://github.com/containous/traefik/pull/1119) ([emilevauge](https://github.com/emilevauge))
|
||||
- Carry \#818 — Add systemd watchdog feature [\#1116](https://github.com/containous/traefik/pull/1116) ([vdemeester](https://github.com/vdemeester))
|
||||
- Skip file permission check on Windows [\#1115](https://github.com/containous/traefik/pull/1115) ([StefanScherer](https://github.com/StefanScherer))
|
||||
- Fix Docker API version for Windows [\#1113](https://github.com/containous/traefik/pull/1113) ([StefanScherer](https://github.com/StefanScherer))
|
||||
- Fix git rpr [\#1109](https://github.com/containous/traefik/pull/1109) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix docker version specifier [\#1108](https://github.com/containous/traefik/pull/1108) ([timoreimann](https://github.com/timoreimann))
|
||||
- Merge v1.1.2 master [\#1105](https://github.com/containous/traefik/pull/1105) ([emilevauge](https://github.com/emilevauge))
|
||||
- add sh before script in deploy... [\#1103](https://github.com/containous/traefik/pull/1103) ([emilevauge](https://github.com/emilevauge))
|
||||
- \[doc\] typo fixes for kubernetes user guide [\#1102](https://github.com/containous/traefik/pull/1102) ([bamarni](https://github.com/bamarni))
|
||||
- add skip\_cleanup in deploy [\#1101](https://github.com/containous/traefik/pull/1101) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix k8s example UI port. [\#1098](https://github.com/containous/traefik/pull/1098) ([ddunkin](https://github.com/ddunkin))
|
||||
- Fix marathon provider [\#1090](https://github.com/containous/traefik/pull/1090) ([diegooliveira](https://github.com/diegooliveira))
|
||||
- Add an ECS provider [\#1088](https://github.com/containous/traefik/pull/1088) ([lpetre](https://github.com/lpetre))
|
||||
- Update comment to reflect the code [\#1087](https://github.com/containous/traefik/pull/1087) ([np](https://github.com/np))
|
||||
- update NYTimes/gziphandler fixes \#1059 [\#1084](https://github.com/containous/traefik/pull/1084) ([JamesKyburz](https://github.com/JamesKyburz))
|
||||
- Ensure that we don't add balancees with no health check runs if there is a health check defined on it [\#1080](https://github.com/containous/traefik/pull/1080) ([jangie](https://github.com/jangie))
|
||||
- Add FreeBSD & OpenBSD to crossbinary [\#1078](https://github.com/containous/traefik/pull/1078) ([geoffgarside](https://github.com/geoffgarside))
|
||||
- Fix metrics for multiple entry points [\#1071](https://github.com/containous/traefik/pull/1071) ([matevzmihalic](https://github.com/matevzmihalic))
|
||||
- Allow setting load balancer method and sticky using service annotations [\#1068](https://github.com/containous/traefik/pull/1068) ([bakins](https://github.com/bakins))
|
||||
- Fix travis script [\#1067](https://github.com/containous/traefik/pull/1067) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add missing fmt verb specifier in k8s provider. [\#1066](https://github.com/containous/traefik/pull/1066) ([timoreimann](https://github.com/timoreimann))
|
||||
- Add git rpr command [\#1063](https://github.com/containous/traefik/pull/1063) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix k8s example [\#1062](https://github.com/containous/traefik/pull/1062) ([emilevauge](https://github.com/emilevauge))
|
||||
- Replace underscores to dash in autogenerated urls \(docker provider\) [\#1061](https://github.com/containous/traefik/pull/1061) ([WTFKr0](https://github.com/WTFKr0))
|
||||
- Don't run go test on .glide cache folder [\#1057](https://github.com/containous/traefik/pull/1057) ([vdemeester](https://github.com/vdemeester))
|
||||
- Allow setting circuitbreaker expression via Kubernetes annotation [\#1056](https://github.com/containous/traefik/pull/1056) ([bakins](https://github.com/bakins))
|
||||
- Improving instrumentation. [\#1042](https://github.com/containous/traefik/pull/1042) ([enxebre](https://github.com/enxebre))
|
||||
- Update user guide for upcoming `docker stack deploy` [\#1041](https://github.com/containous/traefik/pull/1041) ([twelvelabs](https://github.com/twelvelabs))
|
||||
- Support sticky sessions under SWARM Mode. \#1024 [\#1033](https://github.com/containous/traefik/pull/1033) ([foleymic](https://github.com/foleymic))
|
||||
- Allow for wildcards in k8s ingress host, fixes \#792 [\#1029](https://github.com/containous/traefik/pull/1029) ([sheerun](https://github.com/sheerun))
|
||||
- Don't fetch ACME certificates for frontends using non-TLS entrypoints \(\#989\) [\#1023](https://github.com/containous/traefik/pull/1023) ([syfonseq](https://github.com/syfonseq))
|
||||
- Return Proper Non-ACME certificate - Fixes Issue 672 [\#1018](https://github.com/containous/traefik/pull/1018) ([dtomcej](https://github.com/dtomcej))
|
||||
- Fix docs build and add missing benchmarks page [\#1017](https://github.com/containous/traefik/pull/1017) ([csabapalfi](https://github.com/csabapalfi))
|
||||
- Set a NopCloser request body with retry middleware [\#1016](https://github.com/containous/traefik/pull/1016) ([bamarni](https://github.com/bamarni))
|
||||
- instruct to flatten dependencies with glide [\#1010](https://github.com/containous/traefik/pull/1010) ([bamarni](https://github.com/bamarni))
|
||||
- check permissions on acme.json during startup [\#1009](https://github.com/containous/traefik/pull/1009) ([bamarni](https://github.com/bamarni))
|
||||
- \[doc\] few tweaks on the basics page [\#1005](https://github.com/containous/traefik/pull/1005) ([bamarni](https://github.com/bamarni))
|
||||
- Import order as goimports does [\#1004](https://github.com/containous/traefik/pull/1004) ([vdemeester](https://github.com/vdemeester))
|
||||
- See the right go report badge [\#991](https://github.com/containous/traefik/pull/991) ([guilhem](https://github.com/guilhem))
|
||||
- Add multiple values for one rule to docs [\#978](https://github.com/containous/traefik/pull/978) ([j0hnsmith](https://github.com/j0hnsmith))
|
||||
- Add ACME/Let’s Encrypt integration tests [\#975](https://github.com/containous/traefik/pull/975) ([trecloux](https://github.com/trecloux))
|
||||
- deploy.sh: upload release source tarball [\#969](https://github.com/containous/traefik/pull/969) ([Mic92](https://github.com/Mic92))
|
||||
- toml zookeeper doc fix [\#948](https://github.com/containous/traefik/pull/948) ([brdude](https://github.com/brdude))
|
||||
- Add Rule AddPrefix [\#931](https://github.com/containous/traefik/pull/931) ([Juliens](https://github.com/Juliens))
|
||||
- Add bug command [\#921](https://github.com/containous/traefik/pull/921) ([emilevauge](https://github.com/emilevauge))
|
||||
- \(WIP\) feat: HealthCheck [\#918](https://github.com/containous/traefik/pull/918) ([Juliens](https://github.com/Juliens))
|
||||
- Add ability to set authenticated user in request header [\#889](https://github.com/containous/traefik/pull/889) ([ViViDboarder](https://github.com/ViViDboarder))
|
||||
- IP-per-task: [\#841](https://github.com/containous/traefik/pull/841) ([diegooliveira](https://github.com/diegooliveira))
|
||||
|
||||
## [v1.1.2](https://github.com/containous/traefik/tree/v1.1.2) (2016-12-15)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.1...v1.1.2)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Problem during HTTPS redirection [\#952](https://github.com/containous/traefik/issues/952)
|
||||
- nil pointer with kubernetes ingress [\#934](https://github.com/containous/traefik/issues/934)
|
||||
- ConsulCatalog and File not working [\#903](https://github.com/containous/traefik/issues/903)
|
||||
- Traefik can not start [\#902](https://github.com/containous/traefik/issues/902)
|
||||
- Cannot connect to Kubernetes server failed to decode watch event [\#532](https://github.com/containous/traefik/issues/532)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Updating certificates with configuration file. [\#968](https://github.com/containous/traefik/issues/968)
|
||||
- Let's encrypt retrieving certificate from wrong IP [\#962](https://github.com/containous/traefik/issues/962)
|
||||
- let's encrypt and dashboard? [\#961](https://github.com/containous/traefik/issues/961)
|
||||
- Working HTTPS example for GKE? [\#960](https://github.com/containous/traefik/issues/960)
|
||||
- GKE design pattern [\#958](https://github.com/containous/traefik/issues/958)
|
||||
- Consul Catalog constraints does not seem to work [\#954](https://github.com/containous/traefik/issues/954)
|
||||
- Issue in building traefik from master [\#949](https://github.com/containous/traefik/issues/949)
|
||||
- Proxy http application to https doesn't seem to work correctly for all services [\#937](https://github.com/containous/traefik/issues/937)
|
||||
- Excessive requests to kubernetes apiserver [\#922](https://github.com/containous/traefik/issues/922)
|
||||
- I am getting a connection error while creating traefik with consul backend "dial tcp 127.0.0.1:8500: getsockopt: connection refused" [\#917](https://github.com/containous/traefik/issues/917)
|
||||
- SwarmMode - 1.13 RC2 - DNS RR - Individual IPs not retrieved [\#913](https://github.com/containous/traefik/issues/913)
|
||||
- Panic in kubernetes ingress \(traefik 1.1.0\) [\#910](https://github.com/containous/traefik/issues/910)
|
||||
- Kubernetes updating deployment image requires Ingress to be remade [\#909](https://github.com/containous/traefik/issues/909)
|
||||
- \[ACME\] Too many currently pending authorizations [\#905](https://github.com/containous/traefik/issues/905)
|
||||
- WEB UI Authentication and Let's Encrypt : error 404 [\#754](https://github.com/containous/traefik/issues/754)
|
||||
- Traefik as ingress controller for SNI based routing in kubernetes [\#745](https://github.com/containous/traefik/issues/745)
|
||||
- Kubernetes Ingress backend: using self-signed certificates [\#486](https://github.com/containous/traefik/issues/486)
|
||||
- Kubernetes Ingress backend: can't find token and ca.crt [\#484](https://github.com/containous/traefik/issues/484)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix duplicate acme certificates [\#972](https://github.com/containous/traefik/pull/972) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix leadership panic [\#956](https://github.com/containous/traefik/pull/956) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix redirect regex [\#947](https://github.com/containous/traefik/pull/947) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add operation recover [\#944](https://github.com/containous/traefik/pull/944) ([emilevauge](https://github.com/emilevauge))
|
||||
|
||||
## [v1.1.1](https://github.com/containous/traefik/tree/v1.1.1) (2016-11-29)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0...v1.1.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Getting "Kubernetes connection error failed to decode watch event : unexpected EOF" every minute in Traefik log [\#732](https://github.com/containous/traefik/issues/732)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- 1.1.0 kubernetes panic: send on closed channel [\#877](https://github.com/containous/traefik/issues/877)
|
||||
- digest auth example is incorrect [\#869](https://github.com/containous/traefik/issues/869)
|
||||
- Marathon & Mesos providers' GroupsAsSubDomains option broken [\#867](https://github.com/containous/traefik/issues/867)
|
||||
- 404 responses when a new Marathon leader is elected [\#653](https://github.com/containous/traefik/issues/653)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- traefik:latest fails to auto-detect Docker containers [\#901](https://github.com/containous/traefik/issues/901)
|
||||
- Panic error on bare metal Kubernetes \(installed with Kubeadm\) [\#897](https://github.com/containous/traefik/issues/897)
|
||||
- api backend readOnly: what is the purpose of this setting [\#893](https://github.com/containous/traefik/issues/893)
|
||||
- file backend: using external file - doesn't work [\#892](https://github.com/containous/traefik/issues/892)
|
||||
- auth support for web backend [\#891](https://github.com/containous/traefik/issues/891)
|
||||
- Basic auth with docker labels [\#890](https://github.com/containous/traefik/issues/890)
|
||||
- file vs inline config [\#888](https://github.com/containous/traefik/issues/888)
|
||||
- combine Host and HostRegexp rules [\#882](https://github.com/containous/traefik/issues/882)
|
||||
- \[Question\] Traefik + Kubernetes + Let's Encrypt \(ssl not used\) [\#881](https://github.com/containous/traefik/issues/881)
|
||||
- Traefik security for dashboard [\#880](https://github.com/containous/traefik/issues/880)
|
||||
- Kubernetes Nginx Deployment Panic [\#879](https://github.com/containous/traefik/issues/879)
|
||||
- Kubernetes Example Address already in use [\#872](https://github.com/containous/traefik/issues/872)
|
||||
- ETCD Backend - frontend/backends missing [\#866](https://github.com/containous/traefik/issues/866)
|
||||
- \[Swarm mode\] Dashboard does not work on RC4 [\#864](https://github.com/containous/traefik/issues/864)
|
||||
- Docker v1.1.0 image does not exist [\#861](https://github.com/containous/traefik/issues/861)
|
||||
- ConsulService catalog do not support multiple rules [\#859](https://github.com/containous/traefik/issues/859)
|
||||
- Update official docker repo [\#858](https://github.com/containous/traefik/issues/858)
|
||||
- Still a memory leak with k8s - 1.1 RC4 [\#844](https://github.com/containous/traefik/issues/844)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix Swarm panic [\#908](https://github.com/containous/traefik/pull/908) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix k8s panic [\#900](https://github.com/containous/traefik/pull/900) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix missing value for k8s watch request parameter [\#874](https://github.com/containous/traefik/pull/874) ([codablock](https://github.com/codablock))
|
||||
- Fix GroupsAsSubDomains option for Mesos and Marathon [\#868](https://github.com/containous/traefik/pull/868) ([ryanleary](https://github.com/ryanleary))
|
||||
- Normalize backend even if is user-defined [\#865](https://github.com/containous/traefik/pull/865) ([WTFKr0](https://github.com/WTFKr0))
|
||||
- consul/kv.tmpl: weight default value should be a int [\#826](https://github.com/containous/traefik/pull/826) ([klausenbusk](https://github.com/klausenbusk))
|
||||
|
||||
## [v1.1.0](https://github.com/containous/traefik/tree/v1.1.0) (2016-11-17)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.1.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Support healthcheck if present for docker [\#666](https://github.com/containous/traefik/issues/666)
|
||||
- Standard unit for traefik latency in access log [\#559](https://github.com/containous/traefik/issues/559)
|
||||
- \[CI\] wiredep marked as unmaintained [\#550](https://github.com/containous/traefik/issues/550)
|
||||
- Feature Request: Enable Health checks to containers. [\#540](https://github.com/containous/traefik/issues/540)
|
||||
- Feature Request: SSL Cipher Selection [\#535](https://github.com/containous/traefik/issues/535)
|
||||
- Error with -consulcatalog and missing load balance method on 1.0.0 [\#524](https://github.com/containous/traefik/issues/524)
|
||||
- Running Traefik with Docker 1.12 Swarm Mode [\#504](https://github.com/containous/traefik/issues/504)
|
||||
- Kubernetes provider: should allow the master url to be override [\#501](https://github.com/containous/traefik/issues/501)
|
||||
- \[FRONTEND\]\[LE\] Pre-generate SSL certificates for "Host:" rules [\#483](https://github.com/containous/traefik/issues/483)
|
||||
- Frontend Rule evolution [\#437](https://github.com/containous/traefik/issues/437)
|
||||
- Add a Changelog [\#388](https://github.com/containous/traefik/issues/388)
|
||||
- Add label matching for kubernetes ingests [\#363](https://github.com/containous/traefik/issues/363)
|
||||
- Acme in HA Traefik Scenario [\#348](https://github.com/containous/traefik/issues/348)
|
||||
- HTTP Basic Auth support [\#77](https://github.com/containous/traefik/issues/77)
|
||||
- Session affinity / stickiness / persistence [\#5](https://github.com/containous/traefik/issues/5)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- 1.1.0-rc4 dashboard UX not displaying [\#828](https://github.com/containous/traefik/issues/828)
|
||||
- Traefik stopped serving on upgrade to v1.1.0-rc3 [\#807](https://github.com/containous/traefik/issues/807)
|
||||
- cannot access webui/dashboard [\#796](https://github.com/containous/traefik/issues/796)
|
||||
- Traefik cannot read constraints from KV [\#794](https://github.com/containous/traefik/issues/794)
|
||||
- HTTP2 - configuration [\#790](https://github.com/containous/traefik/issues/790)
|
||||
- Cannot provide multiple certificates using flag [\#757](https://github.com/containous/traefik/issues/757)
|
||||
- Allow multiple certificates on a single entrypoint when trying to use TLS? [\#747](https://github.com/containous/traefik/issues/747)
|
||||
- traefik \* Users: unsupported type: slice [\#743](https://github.com/containous/traefik/issues/743)
|
||||
- \[Docker swarm mode\] The traefik.docker.network seems to have no effect [\#719](https://github.com/containous/traefik/issues/719)
|
||||
- traefik hangs - stops handling requests [\#662](https://github.com/containous/traefik/issues/662)
|
||||
- Add long jobs in exponential backoff providers [\#626](https://github.com/containous/traefik/issues/626)
|
||||
- Tip of tree crashes on invalid pointer on Marathon provider [\#624](https://github.com/containous/traefik/issues/624)
|
||||
- ACME: revoke certificate on agreement update [\#579](https://github.com/containous/traefik/issues/579)
|
||||
- WebUI: Providers tabs disappeared [\#577](https://github.com/containous/traefik/issues/577)
|
||||
- traefik version command contains incorrect information when building from master branch [\#569](https://github.com/containous/traefik/issues/569)
|
||||
- Case sensitive domain names breaks routing [\#562](https://github.com/containous/traefik/issues/562)
|
||||
- Flag --etcd.endpoint default [\#508](https://github.com/containous/traefik/issues/508)
|
||||
- Conditional ACME on demand generation [\#505](https://github.com/containous/traefik/issues/505)
|
||||
- Important delay with streams \(Mozilla EventSource\) [\#503](https://github.com/containous/traefik/issues/503)
|
||||
- Traefik crashing [\#458](https://github.com/containous/traefik/issues/458)
|
||||
- traefik.toml constraints error: `Expected map but found 'string'.` [\#451](https://github.com/containous/traefik/issues/451)
|
||||
- Multiple path separators in the url path causing redirect [\#167](https://github.com/containous/traefik/issues/167)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- All path rules require paths to be lowercase [\#851](https://github.com/containous/traefik/issues/851)
|
||||
- The UI stops working after a time and have to restart the service. [\#840](https://github.com/containous/traefik/issues/840)
|
||||
- Incorrect Dashboard page returned [\#831](https://github.com/containous/traefik/issues/831)
|
||||
- LoadBalancing doesn't work in single node Swarm-mode [\#815](https://github.com/containous/traefik/issues/815)
|
||||
- cannot connect to docker daemon [\#813](https://github.com/containous/traefik/issues/813)
|
||||
- Let's encrypt configuration not working [\#805](https://github.com/containous/traefik/issues/805)
|
||||
- Multiple subdomains for Marathon backend. [\#785](https://github.com/containous/traefik/issues/785)
|
||||
- traefik-1.1.0-rc1: build error [\#781](https://github.com/containous/traefik/issues/781)
|
||||
- dependencies installation error [\#755](https://github.com/containous/traefik/issues/755)
|
||||
- k8s provider w/ acme? [\#752](https://github.com/containous/traefik/issues/752)
|
||||
- Swarm Docs - How to use a FQDN [\#744](https://github.com/containous/traefik/issues/744)
|
||||
- Documented ProvidersThrottleDuration value is invalid [\#741](https://github.com/containous/traefik/issues/741)
|
||||
- Sensible configuration for consulCatalog [\#737](https://github.com/containous/traefik/issues/737)
|
||||
- Traefik ignoring container listening in more than one TCP port [\#734](https://github.com/containous/traefik/issues/734)
|
||||
- Loadbalaning issues with traefik and Docker Swarm cluster [\#730](https://github.com/containous/traefik/issues/730)
|
||||
- issues with marathon app ids containing a dot [\#726](https://github.com/containous/traefik/issues/726)
|
||||
- Error when using HA acme in kubernetes with etcd [\#725](https://github.com/containous/traefik/issues/725)
|
||||
- \[Docker swarm mode\] No round robin when using service [\#718](https://github.com/containous/traefik/issues/718)
|
||||
- Dose it support docker swarm mode [\#712](https://github.com/containous/traefik/issues/712)
|
||||
- Kubernetes - Undefined backend [\#710](https://github.com/containous/traefik/issues/710)
|
||||
- How Routing traffic depending on path not domain in docker [\#706](https://github.com/containous/traefik/issues/706)
|
||||
- Constraints on Consul Catalogue not working as expected [\#703](https://github.com/containous/traefik/issues/703)
|
||||
- Global InsecureSkipVerify does not work [\#700](https://github.com/containous/traefik/issues/700)
|
||||
- Traefik crashes when using Consul catalog [\#699](https://github.com/containous/traefik/issues/699)
|
||||
- \[documentation/feature\] Consul/etcd support atomic multiple key changes now [\#698](https://github.com/containous/traefik/issues/698)
|
||||
- How to configure which network to use when starting traefik binary? [\#694](https://github.com/containous/traefik/issues/694)
|
||||
- How to get multiple host headers working for docker labels? [\#692](https://github.com/containous/traefik/issues/692)
|
||||
- Requests with URL-encoded characters are not forwarded correctly [\#684](https://github.com/containous/traefik/issues/684)
|
||||
- File Watcher for rules does not work [\#683](https://github.com/containous/traefik/issues/683)
|
||||
- Issue with global InsecureSkipVerify = true and self signed certificates [\#667](https://github.com/containous/traefik/issues/667)
|
||||
- Docker exposedbydefault = false didn't work [\#663](https://github.com/containous/traefik/issues/663)
|
||||
- swarm documentation needs update [\#656](https://github.com/containous/traefik/issues/656)
|
||||
- \[ACME\] Auto SAN Detection [\#655](https://github.com/containous/traefik/issues/655)
|
||||
- Fronting a domain with DNS A-record round-robin & ACME [\#654](https://github.com/containous/traefik/issues/654)
|
||||
- Overriding toml configuration with environment variables [\#650](https://github.com/containous/traefik/issues/650)
|
||||
- marathon provider exposedByDefault = false [\#647](https://github.com/containous/traefik/issues/647)
|
||||
- Add status URL for service up checks [\#642](https://github.com/containous/traefik/issues/642)
|
||||
- acme's storage file, containing private key, is word readable [\#638](https://github.com/containous/traefik/issues/638)
|
||||
- wildcard domain with exclusions [\#633](https://github.com/containous/traefik/issues/633)
|
||||
- Enable evenly distribution among backend [\#631](https://github.com/containous/traefik/issues/631)
|
||||
- Traefik sporadically failing when proxying requests [\#615](https://github.com/containous/traefik/issues/615)
|
||||
- TCP Proxy [\#608](https://github.com/containous/traefik/issues/608)
|
||||
- How to use in Windows? [\#605](https://github.com/containous/traefik/issues/605)
|
||||
- `ClientCAFiles` ignored [\#604](https://github.com/containous/traefik/issues/604)
|
||||
- Let`s Encrypt enable in etcd [\#600](https://github.com/containous/traefik/issues/600)
|
||||
- Support HTTP Basic Auth [\#599](https://github.com/containous/traefik/issues/599)
|
||||
- Consul KV seem broken [\#587](https://github.com/containous/traefik/issues/587)
|
||||
- HTTPS entryPoint not working [\#574](https://github.com/containous/traefik/issues/574)
|
||||
- Traefik stuck when used as frontend for a streaming API [\#560](https://github.com/containous/traefik/issues/560)
|
||||
- Exclude some frontends in consul catalog [\#555](https://github.com/containous/traefik/issues/555)
|
||||
- Update docs with new Mesos provider [\#548](https://github.com/containous/traefik/issues/548)
|
||||
- Can I use Traefik without a domain name? [\#539](https://github.com/containous/traefik/issues/539)
|
||||
- docker run syntax in swarm example has changed [\#528](https://github.com/containous/traefik/issues/528)
|
||||
- Priortities in 1.0.0 not behaving [\#506](https://github.com/containous/traefik/issues/506)
|
||||
- Route by path [\#500](https://github.com/containous/traefik/issues/500)
|
||||
- Secure WebSockets [\#467](https://github.com/containous/traefik/issues/467)
|
||||
- Container IP Lost [\#375](https://github.com/containous/traefik/issues/375)
|
||||
- Multiple routes support with Docker or Marathon labels [\#118](https://github.com/containous/traefik/issues/118)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix path case sensitive v1.1 [\#855](https://github.com/containous/traefik/pull/855) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix golint in v1.1 [\#849](https://github.com/containous/traefik/pull/849) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix Kubernetes watch leak [\#845](https://github.com/containous/traefik/pull/845) ([emilevauge](https://github.com/emilevauge))
|
||||
- Pass Version, Codename and Date to crosscompiled [\#842](https://github.com/containous/traefik/pull/842) ([guilhem](https://github.com/guilhem))
|
||||
- Add Nvd3 Dependency to fix UI / Dashboard [\#829](https://github.com/containous/traefik/pull/829) ([SantoDE](https://github.com/SantoDE))
|
||||
- Fix mkdoc theme [\#823](https://github.com/containous/traefik/pull/823) ([emilevauge](https://github.com/emilevauge))
|
||||
- Prepare release v1.1.0 rc4 [\#822](https://github.com/containous/traefik/pull/822) ([emilevauge](https://github.com/emilevauge))
|
||||
- Check that we serve HTTP/2 [\#820](https://github.com/containous/traefik/pull/820) ([trecloux](https://github.com/trecloux))
|
||||
- Fix multiple issues [\#814](https://github.com/containous/traefik/pull/814) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix ACME renew & add version check [\#783](https://github.com/containous/traefik/pull/783) ([emilevauge](https://github.com/emilevauge))
|
||||
- Use first port by default [\#782](https://github.com/containous/traefik/pull/782) ([guilhem](https://github.com/guilhem))
|
||||
- Prepare release v1.1.0-rc3 [\#779](https://github.com/containous/traefik/pull/779) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix ResponseRecorder Flush [\#776](https://github.com/containous/traefik/pull/776) ([emilevauge](https://github.com/emilevauge))
|
||||
- Use sdnotify for systemd [\#768](https://github.com/containous/traefik/pull/768) ([guilhem](https://github.com/guilhem))
|
||||
- Fix providers throttle duration doc [\#760](https://github.com/containous/traefik/pull/760) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix mapstructure issue with anonymous slice [\#759](https://github.com/containous/traefik/pull/759) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix multiple certificates using flag [\#758](https://github.com/containous/traefik/pull/758) ([emilevauge](https://github.com/emilevauge))
|
||||
- Really fix deploy ghr... [\#748](https://github.com/containous/traefik/pull/748) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fixes deploy ghr [\#742](https://github.com/containous/traefik/pull/742) ([emilevauge](https://github.com/emilevauge))
|
||||
- prepare v1.1.0-rc2 [\#740](https://github.com/containous/traefik/pull/740) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix case sensitive host [\#733](https://github.com/containous/traefik/pull/733) ([emilevauge](https://github.com/emilevauge))
|
||||
- Update Kubernetes examples [\#731](https://github.com/containous/traefik/pull/731) ([Starefossen](https://github.com/Starefossen))
|
||||
- fIx marathon template with dots in ID [\#728](https://github.com/containous/traefik/pull/728) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix networkMap construction in ListServices [\#724](https://github.com/containous/traefik/pull/724) ([vincentlepot](https://github.com/vincentlepot))
|
||||
- Add basic compatibility with marathon-lb [\#720](https://github.com/containous/traefik/pull/720) ([guilhem](https://github.com/guilhem))
|
||||
- Add Ed's video at ContainerCamp [\#717](https://github.com/containous/traefik/pull/717) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add documentation for Træfik on docker swarm mode [\#715](https://github.com/containous/traefik/pull/715) ([vdemeester](https://github.com/vdemeester))
|
||||
- Remove duplicated link to Kubernetes.io in README.md [\#713](https://github.com/containous/traefik/pull/713) ([oscerd](https://github.com/oscerd))
|
||||
- Show current version in web UI [\#709](https://github.com/containous/traefik/pull/709) ([vhf](https://github.com/vhf))
|
||||
- Add support for docker healthcheck 👼 [\#708](https://github.com/containous/traefik/pull/708) ([vdemeester](https://github.com/vdemeester))
|
||||
- Fix syntax in Swarm example. Resolves \#528 [\#707](https://github.com/containous/traefik/pull/707) ([billglover](https://github.com/billglover))
|
||||
- Add HTTP compression [\#702](https://github.com/containous/traefik/pull/702) ([tuier](https://github.com/tuier))
|
||||
- Carry PR 446 - Add sticky session support \(round two!\) [\#701](https://github.com/containous/traefik/pull/701) ([emilevauge](https://github.com/emilevauge))
|
||||
- Remove unused endpoint when using constraints with Marathon provider [\#697](https://github.com/containous/traefik/pull/697) ([tuier](https://github.com/tuier))
|
||||
- Replace imagelayers.io with microbadger [\#696](https://github.com/containous/traefik/pull/696) ([solidnerd](https://github.com/solidnerd))
|
||||
- Selectable TLS Versions [\#690](https://github.com/containous/traefik/pull/690) ([dtomcej](https://github.com/dtomcej))
|
||||
- Carry pr 439 [\#689](https://github.com/containous/traefik/pull/689) ([emilevauge](https://github.com/emilevauge))
|
||||
- Disable gorilla/mux URL cleaning to prevent sending redirect [\#688](https://github.com/containous/traefik/pull/688) ([ydubreuil](https://github.com/ydubreuil))
|
||||
- Some fixes [\#687](https://github.com/containous/traefik/pull/687) ([emilevauge](https://github.com/emilevauge))
|
||||
- feat\(constraints\): Supports constraints for Marathon provider [\#686](https://github.com/containous/traefik/pull/686) ([tuier](https://github.com/tuier))
|
||||
- Update docs to improve contribution setup [\#685](https://github.com/containous/traefik/pull/685) ([dtomcej](https://github.com/dtomcej))
|
||||
- Add basic auth support for web backend [\#677](https://github.com/containous/traefik/pull/677) ([SantoDE](https://github.com/SantoDE))
|
||||
- Document accepted values for logLevel. [\#676](https://github.com/containous/traefik/pull/676) ([jimmycuadra](https://github.com/jimmycuadra))
|
||||
- If Marathon doesn't have healthcheck, assume it's ok [\#665](https://github.com/containous/traefik/pull/665) ([gomes](https://github.com/gomes))
|
||||
- ACME: renew certificates 30 days before expiry [\#660](https://github.com/containous/traefik/pull/660) ([JayH5](https://github.com/JayH5))
|
||||
- Update broken link and add a comment to sample config file [\#658](https://github.com/containous/traefik/pull/658) ([Yggdrasil](https://github.com/Yggdrasil))
|
||||
- Add possibility to use BindPort IPAddress 👼 [\#657](https://github.com/containous/traefik/pull/657) ([vdemeester](https://github.com/vdemeester))
|
||||
- Update marathon [\#648](https://github.com/containous/traefik/pull/648) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add backend features to docker [\#646](https://github.com/containous/traefik/pull/646) ([jangie](https://github.com/jangie))
|
||||
- enable consul catalog to use maxconn [\#645](https://github.com/containous/traefik/pull/645) ([jangie](https://github.com/jangie))
|
||||
- Adopt the Code Of Coduct from http://contributor-covenant.org [\#641](https://github.com/containous/traefik/pull/641) ([errm](https://github.com/errm))
|
||||
- Use secure mode 600 instead of 644 for acme.json [\#639](https://github.com/containous/traefik/pull/639) ([discordianfish](https://github.com/discordianfish))
|
||||
- docker clarification, fix dead urls, misc typos [\#637](https://github.com/containous/traefik/pull/637) ([djalal](https://github.com/djalal))
|
||||
- add PING handler to dashboard API [\#630](https://github.com/containous/traefik/pull/630) ([jangie](https://github.com/jangie))
|
||||
- Migrate to JobBackOff [\#628](https://github.com/containous/traefik/pull/628) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add long job exponential backoff [\#627](https://github.com/containous/traefik/pull/627) ([emilevauge](https://github.com/emilevauge))
|
||||
- HA acme support [\#625](https://github.com/containous/traefik/pull/625) ([emilevauge](https://github.com/emilevauge))
|
||||
- Bump go v1.7 [\#620](https://github.com/containous/traefik/pull/620) ([emilevauge](https://github.com/emilevauge))
|
||||
- Make duration logging consistent [\#619](https://github.com/containous/traefik/pull/619) ([jangie](https://github.com/jangie))
|
||||
- fix for nil clientTLS causing issue [\#617](https://github.com/containous/traefik/pull/617) ([jangie](https://github.com/jangie))
|
||||
- Add ability for marathon provider to set maxconn values, loadbalancer algorithm, and circuit breaker expression [\#616](https://github.com/containous/traefik/pull/616) ([jangie](https://github.com/jangie))
|
||||
- Make systemd unit installable [\#613](https://github.com/containous/traefik/pull/613) ([keis](https://github.com/keis))
|
||||
- Merge v1.0.2 master [\#610](https://github.com/containous/traefik/pull/610) ([emilevauge](https://github.com/emilevauge))
|
||||
- update staert and flaeg [\#609](https://github.com/containous/traefik/pull/609) ([cocap10](https://github.com/cocap10))
|
||||
- \#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))
|
||||
- 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))
|
||||
- Upgrade directives name since we use angular-ui-bootstrap [\#578](https://github.com/containous/traefik/pull/578) ([micaelmbagira](https://github.com/micaelmbagira))
|
||||
- Fix basic docs for configuration of multiple rules [\#576](https://github.com/containous/traefik/pull/576) ([ajaegle](https://github.com/ajaegle))
|
||||
- Fix k8s watch [\#573](https://github.com/containous/traefik/pull/573) ([errm](https://github.com/errm))
|
||||
- Add requirements.txt for netlify [\#567](https://github.com/containous/traefik/pull/567) ([emilevauge](https://github.com/emilevauge))
|
||||
- Merge v1.0.1 master [\#565](https://github.com/containous/traefik/pull/565) ([emilevauge](https://github.com/emilevauge))
|
||||
- Move webui to FountainJS with Webpack [\#558](https://github.com/containous/traefik/pull/558) ([micaelmbagira](https://github.com/micaelmbagira))
|
||||
- Add global InsecureSkipVerify option to disable certificate checking [\#557](https://github.com/containous/traefik/pull/557) ([stuart-c](https://github.com/stuart-c))
|
||||
- Move version.go in its own package… [\#553](https://github.com/containous/traefik/pull/553) ([vdemeester](https://github.com/vdemeester))
|
||||
- Upgrade libkermit and dependencies [\#552](https://github.com/containous/traefik/pull/552) ([vdemeester](https://github.com/vdemeester))
|
||||
- Add command storeconfig [\#551](https://github.com/containous/traefik/pull/551) ([cocap10](https://github.com/cocap10))
|
||||
- Add basic/digest auth [\#547](https://github.com/containous/traefik/pull/547) ([emilevauge](https://github.com/emilevauge))
|
||||
- Bump node to 6 for webui [\#546](https://github.com/containous/traefik/pull/546) ([vdemeester](https://github.com/vdemeester))
|
||||
- Bump golang to 1.6.3 [\#545](https://github.com/containous/traefik/pull/545) ([vdemeester](https://github.com/vdemeester))
|
||||
- Fix typos [\#538](https://github.com/containous/traefik/pull/538) ([jimt](https://github.com/jimt))
|
||||
- Kubernetes user-guide [\#519](https://github.com/containous/traefik/pull/519) ([errm](https://github.com/errm))
|
||||
- Implement Kubernetes Selectors, minor kube endpoint fix [\#516](https://github.com/containous/traefik/pull/516) ([pnegahdar](https://github.com/pnegahdar))
|
||||
- Carry \#358 : Option to disable expose of all docker containers [\#514](https://github.com/containous/traefik/pull/514) ([vdemeester](https://github.com/vdemeester))
|
||||
- Remove traefik.frontend.value support in docker… [\#510](https://github.com/containous/traefik/pull/510) ([vdemeester](https://github.com/vdemeester))
|
||||
- Use KvStores as global config sources [\#481](https://github.com/containous/traefik/pull/481) ([cocap10](https://github.com/cocap10))
|
||||
- Add endpoint option to authenticate by client tls cert. [\#461](https://github.com/containous/traefik/pull/461) ([andersbetner](https://github.com/andersbetner))
|
||||
- add mesos provider inspired by mesos-dns & marathon provider [\#353](https://github.com/containous/traefik/pull/353) ([skydjol](https://github.com/skydjol))
|
||||
|
||||
## [v1.1.0-rc4](https://github.com/containous/traefik/tree/v1.1.0-rc4) (2016-11-10)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc3...v1.1.0-rc4)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Feature Request: Enable Health checks to containers. [\#540](https://github.com/containous/traefik/issues/540)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Traefik stopped serving on upgrade to v1.1.0-rc3 [\#807](https://github.com/containous/traefik/issues/807)
|
||||
- Traefik cannot read constraints from KV [\#794](https://github.com/containous/traefik/issues/794)
|
||||
- HTTP2 - configuration [\#790](https://github.com/containous/traefik/issues/790)
|
||||
- Allow multiple certificates on a single entrypoint when trying to use TLS? [\#747](https://github.com/containous/traefik/issues/747)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- LoadBalancing doesn't work in single node Swarm-mode [\#815](https://github.com/containous/traefik/issues/815)
|
||||
- cannot connect to docker daemon [\#813](https://github.com/containous/traefik/issues/813)
|
||||
- Let's encrypt configuration not working [\#805](https://github.com/containous/traefik/issues/805)
|
||||
- Question: Wildcard Host for Kubernetes Ingress [\#792](https://github.com/containous/traefik/issues/792)
|
||||
- Multiple subdomains for Marathon backend. [\#785](https://github.com/containous/traefik/issues/785)
|
||||
- traefik-1.1.0-rc1: build error [\#781](https://github.com/containous/traefik/issues/781)
|
||||
- Multiple routes support with Docker or Marathon labels [\#118](https://github.com/containous/traefik/issues/118)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Prepare release v1.1.0 rc4 [\#822](https://github.com/containous/traefik/pull/822) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix multiple issues [\#814](https://github.com/containous/traefik/pull/814) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix ACME renew & add version check [\#783](https://github.com/containous/traefik/pull/783) ([emilevauge](https://github.com/emilevauge))
|
||||
- Use first port by default [\#782](https://github.com/containous/traefik/pull/782) ([guilhem](https://github.com/guilhem))
|
||||
|
||||
## [v1.1.0-rc3](https://github.com/containous/traefik/tree/v1.1.0-rc3) (2016-10-26)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc2...v1.1.0-rc3)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Cannot provide multiple certificates using flag [\#757](https://github.com/containous/traefik/issues/757)
|
||||
- traefik \* Users: unsupported type: slice [\#743](https://github.com/containous/traefik/issues/743)
|
||||
- \[Docker swarm mode\] The traefik.docker.network seems to have no effect [\#719](https://github.com/containous/traefik/issues/719)
|
||||
- Case sensitive domain names breaks routing [\#562](https://github.com/containous/traefik/issues/562)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- dependencies installation error [\#755](https://github.com/containous/traefik/issues/755)
|
||||
- k8s provider w/ acme? [\#752](https://github.com/containous/traefik/issues/752)
|
||||
- Documented ProvidersThrottleDuration value is invalid [\#741](https://github.com/containous/traefik/issues/741)
|
||||
- Loadbalaning issues with traefik and Docker Swarm cluster [\#730](https://github.com/containous/traefik/issues/730)
|
||||
- issues with marathon app ids containing a dot [\#726](https://github.com/containous/traefik/issues/726)
|
||||
- How Routing traffic depending on path not domain in docker [\#706](https://github.com/containous/traefik/issues/706)
|
||||
- Traefik crashes when using Consul catalog [\#699](https://github.com/containous/traefik/issues/699)
|
||||
- File Watcher for rules does not work [\#683](https://github.com/containous/traefik/issues/683)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix ResponseRecorder Flush [\#776](https://github.com/containous/traefik/pull/776) ([emilevauge](https://github.com/emilevauge))
|
||||
- Use sdnotify for systemd [\#768](https://github.com/containous/traefik/pull/768) ([guilhem](https://github.com/guilhem))
|
||||
- Fix providers throttle duration doc [\#760](https://github.com/containous/traefik/pull/760) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix mapstructure issue with anonymous slice [\#759](https://github.com/containous/traefik/pull/759) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix multiple certificates using flag [\#758](https://github.com/containous/traefik/pull/758) ([emilevauge](https://github.com/emilevauge))
|
||||
- Really fix deploy ghr... [\#748](https://github.com/containous/traefik/pull/748) ([emilevauge](https://github.com/emilevauge))
|
||||
|
||||
## [v1.1.0-rc2](https://github.com/containous/traefik/tree/v1.1.0-rc2) (2016-10-17)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0-rc1...v1.1.0-rc2)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Support healthcheck if present for docker [\#666](https://github.com/containous/traefik/issues/666)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Sensible configuration for consulCatalog [\#737](https://github.com/containous/traefik/issues/737)
|
||||
- Traefik ignoring container listening in more than one TCP port [\#734](https://github.com/containous/traefik/issues/734)
|
||||
- Error when using HA acme in kubernetes with etcd [\#725](https://github.com/containous/traefik/issues/725)
|
||||
- \[Docker swarm mode\] No round robin when using service [\#718](https://github.com/containous/traefik/issues/718)
|
||||
- Dose it support docker swarm mode [\#712](https://github.com/containous/traefik/issues/712)
|
||||
- Kubernetes - Undefined backend [\#710](https://github.com/containous/traefik/issues/710)
|
||||
- Constraints on Consul Catalogue not working as expected [\#703](https://github.com/containous/traefik/issues/703)
|
||||
- docker run syntax in swarm example has changed [\#528](https://github.com/containous/traefik/issues/528)
|
||||
- Secure WebSockets [\#467](https://github.com/containous/traefik/issues/467)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix case sensitive host [\#733](https://github.com/containous/traefik/pull/733) ([emilevauge](https://github.com/emilevauge))
|
||||
- Update Kubernetes examples [\#731](https://github.com/containous/traefik/pull/731) ([Starefossen](https://github.com/Starefossen))
|
||||
- fIx marathon template with dots in ID [\#728](https://github.com/containous/traefik/pull/728) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix networkMap construction in ListServices [\#724](https://github.com/containous/traefik/pull/724) ([vincentlepot](https://github.com/vincentlepot))
|
||||
- Add basic compatibility with marathon-lb [\#720](https://github.com/containous/traefik/pull/720) ([guilhem](https://github.com/guilhem))
|
||||
- Add Ed's video at ContainerCamp [\#717](https://github.com/containous/traefik/pull/717) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add documentation for Træfik on docker swarm mode [\#715](https://github.com/containous/traefik/pull/715) ([vdemeester](https://github.com/vdemeester))
|
||||
- Remove duplicated link to Kubernetes.io in README.md [\#713](https://github.com/containous/traefik/pull/713) ([oscerd](https://github.com/oscerd))
|
||||
- Show current version in web UI [\#709](https://github.com/containous/traefik/pull/709) ([vhf](https://github.com/vhf))
|
||||
- Add support for docker healthcheck 👼 [\#708](https://github.com/containous/traefik/pull/708) ([vdemeester](https://github.com/vdemeester))
|
||||
- Fix syntax in Swarm example. Resolves \#528 [\#707](https://github.com/containous/traefik/pull/707) ([billglover](https://github.com/billglover))
|
||||
|
||||
## [v1.1.0-rc1](https://github.com/containous/traefik/tree/v1.1.0-rc1) (2016-09-30)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.1.0-rc1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Feature Request: SSL Cipher Selection [\#535](https://github.com/containous/traefik/issues/535)
|
||||
- Error with -consulcatalog and missing load balance method on 1.0.0 [\#524](https://github.com/containous/traefik/issues/524)
|
||||
- Running Traefik with Docker 1.12 Swarm Mode [\#504](https://github.com/containous/traefik/issues/504)
|
||||
- Kubernetes provider: should allow the master url to be override [\#501](https://github.com/containous/traefik/issues/501)
|
||||
- \[FRONTEND\]\[LE\] Pre-generate SSL certificates for "Host:" rules [\#483](https://github.com/containous/traefik/issues/483)
|
||||
- Frontend Rule evolution [\#437](https://github.com/containous/traefik/issues/437)
|
||||
- Add a Changelog [\#388](https://github.com/containous/traefik/issues/388)
|
||||
- Add label matching for kubernetes ingests [\#363](https://github.com/containous/traefik/issues/363)
|
||||
- Acme in HA Traefik Scenario [\#348](https://github.com/containous/traefik/issues/348)
|
||||
- HTTP Basic Auth support [\#77](https://github.com/containous/traefik/issues/77)
|
||||
- Session affinity / stickiness / persistence [\#5](https://github.com/containous/traefik/issues/5)
|
||||
- Kubernetes provider: traefik.frontend.rule.type logging [\#668](https://github.com/containous/traefik/pull/668) ([yvespp](https://github.com/yvespp))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- traefik hangs - stops handling requests [\#662](https://github.com/containous/traefik/issues/662)
|
||||
- Add long jobs in exponential backoff providers [\#626](https://github.com/containous/traefik/issues/626)
|
||||
- Tip of tree crashes on invalid pointer on Marathon provider [\#624](https://github.com/containous/traefik/issues/624)
|
||||
- ACME: revoke certificate on agreement update [\#579](https://github.com/containous/traefik/issues/579)
|
||||
- WebUI: Providers tabs disappeared [\#577](https://github.com/containous/traefik/issues/577)
|
||||
- traefik version command contains incorrect information when building from master branch [\#569](https://github.com/containous/traefik/issues/569)
|
||||
- Flag --etcd.endpoint default [\#508](https://github.com/containous/traefik/issues/508)
|
||||
- Conditional ACME on demand generation [\#505](https://github.com/containous/traefik/issues/505)
|
||||
- Important delay with streams \(Mozilla EventSource\) [\#503](https://github.com/containous/traefik/issues/503)
|
||||
- Traefik crashing [\#458](https://github.com/containous/traefik/issues/458)
|
||||
- traefik.toml constraints error: `Expected map but found 'string'.` [\#451](https://github.com/containous/traefik/issues/451)
|
||||
- Multiple path separators in the url path causing redirect [\#167](https://github.com/containous/traefik/issues/167)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Global InsecureSkipVerify does not work [\#700](https://github.com/containous/traefik/issues/700)
|
||||
- \[documentation/feature\] Consul/etcd support atomic multiple key changes now [\#698](https://github.com/containous/traefik/issues/698)
|
||||
- How to configure which network to use when starting traefik binary? [\#694](https://github.com/containous/traefik/issues/694)
|
||||
- How to get multiple host headers working for docker labels? [\#692](https://github.com/containous/traefik/issues/692)
|
||||
- Requests with URL-encoded characters are not forwarded correctly [\#684](https://github.com/containous/traefik/issues/684)
|
||||
- Issue with global InsecureSkipVerify = true and self signed certificates [\#667](https://github.com/containous/traefik/issues/667)
|
||||
- Docker exposedbydefault = false didn't work [\#663](https://github.com/containous/traefik/issues/663)
|
||||
- \[ACME\] Auto SAN Detection [\#655](https://github.com/containous/traefik/issues/655)
|
||||
- Fronting a domain with DNS A-record round-robin & ACME [\#654](https://github.com/containous/traefik/issues/654)
|
||||
- Overriding toml configuration with environment variables [\#650](https://github.com/containous/traefik/issues/650)
|
||||
- marathon provider exposedByDefault = false [\#647](https://github.com/containous/traefik/issues/647)
|
||||
- Add status URL for service up checks [\#642](https://github.com/containous/traefik/issues/642)
|
||||
- acme's storage file, containing private key, is word readable [\#638](https://github.com/containous/traefik/issues/638)
|
||||
- wildcard domain with exclusions [\#633](https://github.com/containous/traefik/issues/633)
|
||||
- Enable evenly distribution among backend [\#631](https://github.com/containous/traefik/issues/631)
|
||||
- Traefik sporadically failing when proxying requests [\#615](https://github.com/containous/traefik/issues/615)
|
||||
- TCP Proxy [\#608](https://github.com/containous/traefik/issues/608)
|
||||
- How to use in Windows? [\#605](https://github.com/containous/traefik/issues/605)
|
||||
- `ClientCAFiles` ignored [\#604](https://github.com/containous/traefik/issues/604)
|
||||
- Let`s Encrypt enable in etcd [\#600](https://github.com/containous/traefik/issues/600)
|
||||
- Support HTTP Basic Auth [\#599](https://github.com/containous/traefik/issues/599)
|
||||
- Consul KV seem broken [\#587](https://github.com/containous/traefik/issues/587)
|
||||
- HTTPS entryPoint not working [\#574](https://github.com/containous/traefik/issues/574)
|
||||
- Traefik stuck when used as frontend for a streaming API [\#560](https://github.com/containous/traefik/issues/560)
|
||||
- Exclude some frontends in consul catalog [\#555](https://github.com/containous/traefik/issues/555)
|
||||
- Can I use Traefik without a domain name? [\#539](https://github.com/containous/traefik/issues/539)
|
||||
- Priortities in 1.0.0 not behaving [\#506](https://github.com/containous/traefik/issues/506)
|
||||
- Route by path [\#500](https://github.com/containous/traefik/issues/500)
|
||||
- Container IP Lost [\#375](https://github.com/containous/traefik/issues/375)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add HTTP compression [\#702](https://github.com/containous/traefik/pull/702) ([tuier](https://github.com/tuier))
|
||||
- Carry PR 446 - Add sticky session support \(round two!\) [\#701](https://github.com/containous/traefik/pull/701) ([emilevauge](https://github.com/emilevauge))
|
||||
- Remove unused endpoint when using constraints with Marathon provider [\#697](https://github.com/containous/traefik/pull/697) ([tuier](https://github.com/tuier))
|
||||
- Replace imagelayers.io with microbadger [\#696](https://github.com/containous/traefik/pull/696) ([solidnerd](https://github.com/solidnerd))
|
||||
- Selectable TLS Versions [\#690](https://github.com/containous/traefik/pull/690) ([dtomcej](https://github.com/dtomcej))
|
||||
- Carry pr 439 [\#689](https://github.com/containous/traefik/pull/689) ([emilevauge](https://github.com/emilevauge))
|
||||
- Disable gorilla/mux URL cleaning to prevent sending redirect [\#688](https://github.com/containous/traefik/pull/688) ([ydubreuil](https://github.com/ydubreuil))
|
||||
- Some fixes [\#687](https://github.com/containous/traefik/pull/687) ([emilevauge](https://github.com/emilevauge))
|
||||
- feat\(constraints\): Supports constraints for Marathon provider [\#686](https://github.com/containous/traefik/pull/686) ([tuier](https://github.com/tuier))
|
||||
- Update docs to improve contribution setup [\#685](https://github.com/containous/traefik/pull/685) ([dtomcej](https://github.com/dtomcej))
|
||||
- Add basic auth support for web backend [\#677](https://github.com/containous/traefik/pull/677) ([SantoDE](https://github.com/SantoDE))
|
||||
- Document accepted values for logLevel. [\#676](https://github.com/containous/traefik/pull/676) ([jimmycuadra](https://github.com/jimmycuadra))
|
||||
- If Marathon doesn't have healthcheck, assume it's ok [\#665](https://github.com/containous/traefik/pull/665) ([gomes](https://github.com/gomes))
|
||||
- ACME: renew certificates 30 days before expiry [\#660](https://github.com/containous/traefik/pull/660) ([JayH5](https://github.com/JayH5))
|
||||
- Update broken link and add a comment to sample config file [\#658](https://github.com/containous/traefik/pull/658) ([Yggdrasil](https://github.com/Yggdrasil))
|
||||
- Add possibility to use BindPort IPAddress 👼 [\#657](https://github.com/containous/traefik/pull/657) ([vdemeester](https://github.com/vdemeester))
|
||||
- Update marathon [\#648](https://github.com/containous/traefik/pull/648) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add backend features to docker [\#646](https://github.com/containous/traefik/pull/646) ([jangie](https://github.com/jangie))
|
||||
- enable consul catalog to use maxconn [\#645](https://github.com/containous/traefik/pull/645) ([jangie](https://github.com/jangie))
|
||||
- Adopt the Code Of Coduct from http://contributor-covenant.org [\#641](https://github.com/containous/traefik/pull/641) ([errm](https://github.com/errm))
|
||||
- Use secure mode 600 instead of 644 for acme.json [\#639](https://github.com/containous/traefik/pull/639) ([discordianfish](https://github.com/discordianfish))
|
||||
- docker clarification, fix dead urls, misc typos [\#637](https://github.com/containous/traefik/pull/637) ([djalal](https://github.com/djalal))
|
||||
- add PING handler to dashboard API [\#630](https://github.com/containous/traefik/pull/630) ([jangie](https://github.com/jangie))
|
||||
- Migrate to JobBackOff [\#628](https://github.com/containous/traefik/pull/628) ([emilevauge](https://github.com/emilevauge))
|
||||
- Add long job exponential backoff [\#627](https://github.com/containous/traefik/pull/627) ([emilevauge](https://github.com/emilevauge))
|
||||
- HA acme support [\#625](https://github.com/containous/traefik/pull/625) ([emilevauge](https://github.com/emilevauge))
|
||||
- Bump go v1.7 [\#620](https://github.com/containous/traefik/pull/620) ([emilevauge](https://github.com/emilevauge))
|
||||
- Make duration logging consistent [\#619](https://github.com/containous/traefik/pull/619) ([jangie](https://github.com/jangie))
|
||||
- fix for nil clientTLS causing issue [\#617](https://github.com/containous/traefik/pull/617) ([jangie](https://github.com/jangie))
|
||||
- Add ability for marathon provider to set maxconn values, loadbalancer algorithm, and circuit breaker expression [\#616](https://github.com/containous/traefik/pull/616) ([jangie](https://github.com/jangie))
|
||||
- Make systemd unit installable [\#613](https://github.com/containous/traefik/pull/613) ([keis](https://github.com/keis))
|
||||
- Merge v1.0.2 master [\#610](https://github.com/containous/traefik/pull/610) ([emilevauge](https://github.com/emilevauge))
|
||||
- update staert and flaeg [\#609](https://github.com/containous/traefik/pull/609) ([cocap10](https://github.com/cocap10))
|
||||
- \#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))
|
||||
- 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))
|
||||
- Upgrade directives name since we use angular-ui-bootstrap [\#578](https://github.com/containous/traefik/pull/578) ([micaelmbagira](https://github.com/micaelmbagira))
|
||||
- Fix basic docs for configuration of multiple rules [\#576](https://github.com/containous/traefik/pull/576) ([ajaegle](https://github.com/ajaegle))
|
||||
- Fix k8s watch [\#573](https://github.com/containous/traefik/pull/573) ([errm](https://github.com/errm))
|
||||
- Add requirements.txt for netlify [\#567](https://github.com/containous/traefik/pull/567) ([emilevauge](https://github.com/emilevauge))
|
||||
- Merge v1.0.1 master [\#565](https://github.com/containous/traefik/pull/565) ([emilevauge](https://github.com/emilevauge))
|
||||
- Move webui to FountainJS with Webpack [\#558](https://github.com/containous/traefik/pull/558) ([micaelmbagira](https://github.com/micaelmbagira))
|
||||
- Add global InsecureSkipVerify option to disable certificate checking [\#557](https://github.com/containous/traefik/pull/557) ([stuart-c](https://github.com/stuart-c))
|
||||
- Move version.go in its own package… [\#553](https://github.com/containous/traefik/pull/553) ([vdemeester](https://github.com/vdemeester))
|
||||
- Upgrade libkermit and dependencies [\#552](https://github.com/containous/traefik/pull/552) ([vdemeester](https://github.com/vdemeester))
|
||||
- Add command storeconfig [\#551](https://github.com/containous/traefik/pull/551) ([cocap10](https://github.com/cocap10))
|
||||
- Add basic/digest auth [\#547](https://github.com/containous/traefik/pull/547) ([emilevauge](https://github.com/emilevauge))
|
||||
- Bump node to 6 for webui [\#546](https://github.com/containous/traefik/pull/546) ([vdemeester](https://github.com/vdemeester))
|
||||
- Bump golang to 1.6.3 [\#545](https://github.com/containous/traefik/pull/545) ([vdemeester](https://github.com/vdemeester))
|
||||
- Fix typos [\#538](https://github.com/containous/traefik/pull/538) ([jimt](https://github.com/jimt))
|
||||
- Kubernetes user-guide [\#519](https://github.com/containous/traefik/pull/519) ([errm](https://github.com/errm))
|
||||
- Implement Kubernetes Selectors, minor kube endpoint fix [\#516](https://github.com/containous/traefik/pull/516) ([pnegahdar](https://github.com/pnegahdar))
|
||||
- Carry \#358 : Option to disable expose of all docker containers [\#514](https://github.com/containous/traefik/pull/514) ([vdemeester](https://github.com/vdemeester))
|
||||
- Remove traefik.frontend.value support in docker… [\#510](https://github.com/containous/traefik/pull/510) ([vdemeester](https://github.com/vdemeester))
|
||||
- Use KvStores as global config sources [\#481](https://github.com/containous/traefik/pull/481) ([cocap10](https://github.com/cocap10))
|
||||
- Add endpoint option to authenticate by client tls cert. [\#461](https://github.com/containous/traefik/pull/461) ([andersbetner](https://github.com/andersbetner))
|
||||
- add mesos provider inspired by mesos-dns & marathon provider [\#353](https://github.com/containous/traefik/pull/353) ([skydjol](https://github.com/skydjol))
|
||||
|
||||
## [v1.0.2](https://github.com/containous/traefik/tree/v1.0.2) (2016-08-02)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.1...v1.0.2)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- ACME: revoke certificate on agreement update [\#579](https://github.com/containous/traefik/issues/579)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Exclude some frontends in consul catalog [\#555](https://github.com/containous/traefik/issues/555)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bump oxy version, fix streaming [\#584](https://github.com/containous/traefik/pull/584) ([emilevauge](https://github.com/emilevauge))
|
||||
- Fix ACME TOS [\#582](https://github.com/containous/traefik/pull/582) ([emilevauge](https://github.com/emilevauge))
|
||||
|
||||
## [v1.0.1](https://github.com/containous/traefik/tree/v1.0.1) (2016-07-19)
|
||||
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.0.1)
|
||||
|
||||
|
74
CODE_OF_CONDUCT.md
Normal file
74
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at contact@containo.us
|
||||
All complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
13
Makefile
13
Makefile
@@ -13,7 +13,7 @@ SRCS = $(shell git ls-files '*.go' | grep -v '^external/')
|
||||
BIND_DIR := "dist"
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
@@ -45,7 +45,7 @@ test-integration: build ## run the integration tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-integration
|
||||
|
||||
validate: build ## validate gofmt, golint and go vet
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell
|
||||
|
||||
build: dist
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
@@ -73,7 +73,7 @@ run-dev:
|
||||
generate-webui: build-webui
|
||||
if [ ! -d "static" ]; then \
|
||||
mkdir -p static; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui gulp; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build; \
|
||||
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
||||
fi
|
||||
|
||||
@@ -82,12 +82,5 @@ lint:
|
||||
|
||||
fmt:
|
||||
gofmt -s -l -w $(SRCS)
|
||||
|
||||
deploy:
|
||||
./script/deploy.sh
|
||||
|
||||
deploy-pr:
|
||||
./script/deploy-pr.sh
|
||||
|
||||
help: ## this help
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
38
README.md
38
README.md
@@ -5,15 +5,15 @@
|
||||
|
||||
[](https://travis-ci.org/containous/traefik)
|
||||
[](https://docs.traefik.io)
|
||||
[](http://goreportcard.com/report/containous/traefik)
|
||||
[](https://imagelayers.io/?images=traefik)
|
||||
[](http://goreportcard.com/report/containous/traefik)
|
||||
[](https://microbadger.com/images/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://traefik.herokuapp.com)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
|
||||
|
||||
Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Kubernetes](http://kubernetes.io/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically.
|
||||
Træfɪk (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Kubernetes](http://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Mesos](https://github.com/apache/mesos), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), [Eureka](https://github.com/Netflix/eureka), Rest API, file...) to manage its configuration automatically and dynamically.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -45,14 +45,14 @@ Run it and forget it!
|
||||
- [It's fast](http://docs.traefik.io/benchmarks)
|
||||
- No dependency hell, single binary made with go
|
||||
- Rest API
|
||||
- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
|
||||
- Watchers for backends, can listen change in backends to apply a new configuration automatically
|
||||
- Multiple backends supported: Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, and more to come
|
||||
- Watchers for backends, can listen for changes in backends to apply a new configuration automatically
|
||||
- Hot-reloading of configuration. No need to restart the process
|
||||
- Graceful shutdown http connections
|
||||
- Circuit breakers on backends
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Rest Metrics
|
||||
- [Tiny](https://imagelayers.io/?images=traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included
|
||||
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included
|
||||
- SSL backends support
|
||||
- SSL frontend support (with SNI)
|
||||
- Clean AngularJS Web UI
|
||||
@@ -60,12 +60,19 @@ Run it and forget it!
|
||||
- HTTP/2 support
|
||||
- Retry request if network error
|
||||
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
|
||||
- High Availability with cluster mode
|
||||
|
||||
## Demo
|
||||
## Quickstart
|
||||
|
||||
You can have a quick look at Træfɪk in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
|
||||
|
||||
Here is a talk (in french) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Lets'Encrypt.
|
||||
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Kubernetes.
|
||||
|
||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||
|
||||
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
|
||||
|
||||
[](http://www.youtube.com/watch?v=QvAz9mVx5TI)
|
||||
|
||||
@@ -84,7 +91,7 @@ You can access to a simple HTML frontend of Træfik.
|
||||
- [Manners](https://github.com/mailgun/manners): graceful shutdown of http.Handler servers
|
||||
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
|
||||
|
||||
## Quick start
|
||||
## Test it
|
||||
|
||||
- The simple way: grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
|
||||
|
||||
@@ -112,10 +119,14 @@ You can find the complete documentation [here](https://docs.traefik.io).
|
||||
|
||||
Please refer to [this section](.github/CONTRIBUTING.md).
|
||||
|
||||
## Code Of Conduct
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
## Support
|
||||
|
||||
You can join [](https://traefik.herokuapp.com) to get basic support.
|
||||
If you prefer a commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||
|
||||
## Træfɪk here and there
|
||||
|
||||
@@ -148,9 +159,10 @@ Founded in 2014, Asteris creates next-generation infrastructure software for the
|
||||
|
||||
- Emile Vauge [@emilevauge](https://github.com/emilevauge)
|
||||
- Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
||||
- Samuel Berthe [@samber](https://github.com/samber)
|
||||
- Russell Clare [@Russell-IO](https://github.com/Russell-IO)
|
||||
- Ed Robinson [@errm](https://github.com/errm)
|
||||
- Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
|
||||
- Manuel Laufenberg [@SantoDE](https://github.com/SantoDE)
|
||||
|
||||
## Credits
|
||||
|
||||
|
245
acme/account.go
Normal file
245
acme/account.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
// Account is used to store lets encrypt registration info
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *acme.RegistrationResource
|
||||
PrivateKey []byte
|
||||
DomainsCertificate DomainsCertificates
|
||||
ChallengeCerts map[string]*ChallengeCert
|
||||
}
|
||||
|
||||
// ChallengeCert stores a challenge certificate
|
||||
type ChallengeCert struct {
|
||||
Certificate []byte
|
||||
PrivateKey []byte
|
||||
certificate *tls.Certificate
|
||||
}
|
||||
|
||||
// Init inits acccount struct
|
||||
func (a *Account) Init() error {
|
||||
err := a.DomainsCertificate.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cert := range a.ChallengeCerts {
|
||||
if cert.certificate == nil {
|
||||
certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert.certificate = &certificate
|
||||
}
|
||||
if cert.certificate.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert.certificate.Leaf = leaf
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAccount creates an account
|
||||
func NewAccount(email string) (*Account, error) {
|
||||
// Create a user. New accounts need an email and private key to start
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
|
||||
domainsCerts.Init()
|
||||
return &Account{
|
||||
Email: email,
|
||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
|
||||
ChallengeCerts: map[string]*ChallengeCert{}}, nil
|
||||
}
|
||||
|
||||
// GetEmail returns email
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetRegistration returns lets encrypt registration resource
|
||||
func (a *Account) GetRegistration() *acme.RegistrationResource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
// GetPrivateKey returns private key
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
|
||||
return privateKey
|
||||
}
|
||||
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Certificate is used to store certificate info
|
||||
type Certificate struct {
|
||||
Domain string
|
||||
CertURL string
|
||||
CertStableURL string
|
||||
PrivateKey []byte
|
||||
Certificate []byte
|
||||
}
|
||||
|
||||
// DomainsCertificates stores a certificate for multiple domains
|
||||
type DomainsCertificates struct {
|
||||
Certs []*DomainsCertificate
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Len() int {
|
||||
return len(dc.Certs)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Swap(i, j int) {
|
||||
dc.Certs[i], dc.Certs[j] = dc.Certs[j], dc.Certs[i]
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Less(i, j int) bool {
|
||||
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[j].Domains) {
|
||||
return dc.Certs[i].tlsCert.Leaf.NotAfter.After(dc.Certs[j].tlsCert.Leaf.NotAfter)
|
||||
}
|
||||
if dc.Certs[i].Domains.Main == dc.Certs[j].Domains.Main {
|
||||
return strings.Join(dc.Certs[i].Domains.SANs, ",") < strings.Join(dc.Certs[j].Domains.SANs, ",")
|
||||
}
|
||||
return dc.Certs[i].Domains.Main < dc.Certs[j].Domains.Main
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) removeDuplicates() {
|
||||
sort.Sort(dc)
|
||||
for i := 0; i < len(dc.Certs); i++ {
|
||||
for i2 := i + 1; i2 < len(dc.Certs); i2++ {
|
||||
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[i2].Domains) {
|
||||
// delete
|
||||
log.Warnf("Remove duplicate cert: %+v, expiration :%s", dc.Certs[i2].Domains, dc.Certs[i2].tlsCert.Leaf.NotAfter.String())
|
||||
dc.Certs = append(dc.Certs[:i2], dc.Certs[i2+1:]...)
|
||||
i2--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init inits DomainsCertificates
|
||||
func (dc *DomainsCertificates) Init() error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
if domainsCertificate.tlsCert.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsCertificate.tlsCert.Leaf = leaf
|
||||
}
|
||||
}
|
||||
dc.removeDuplicates()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain Domain) error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domain, domainsCertificate.Domains) {
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsCertificate.Certificate = acmeCert
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Certificate to renew not found for domain " + domain.Main)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain Domain) (*DomainsCertificate, error) {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
|
||||
dc.Certs = append(dc.Certs, &cert)
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
domains := []string{}
|
||||
domains = append(domains, domainsCertificate.Domains.Main)
|
||||
domains = append(domains, domainsCertificate.Domains.SANs...)
|
||||
for _, domain := range domains {
|
||||
if domain == domainToFind {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// DomainsCertificate contains a certificate for multiple domains
|
||||
type DomainsCertificate struct {
|
||||
Domains Domain
|
||||
Certificate *Certificate
|
||||
tlsCert *tls.Certificate
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificate) needRenew() bool {
|
||||
for _, c := range dc.tlsCert.Certificate {
|
||||
crt, err := x509.ParseCertificate(c)
|
||||
if err != nil {
|
||||
// If there's an error, we assume the cert is broken, and needs update
|
||||
return true
|
||||
}
|
||||
// <= 30 days left, renew certificate
|
||||
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 30 * time.Hour))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
792
acme/acme.go
792
acme/acme.go
@@ -1,174 +1,54 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"io/ioutil"
|
||||
fmtlog "log"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/eapache/channels"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
)
|
||||
|
||||
// Account is used to store lets encrypt registration info
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *acme.RegistrationResource
|
||||
PrivateKey []byte
|
||||
DomainsCertificate DomainsCertificates
|
||||
}
|
||||
|
||||
// GetEmail returns email
|
||||
func (a Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetRegistration returns lets encrypt registration resource
|
||||
func (a Account) GetRegistration() *acme.RegistrationResource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
// GetPrivateKey returns private key
|
||||
func (a Account) GetPrivateKey() crypto.PrivateKey {
|
||||
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
|
||||
return privateKey
|
||||
}
|
||||
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Certificate is used to store certificate info
|
||||
type Certificate struct {
|
||||
Domain string
|
||||
CertURL string
|
||||
CertStableURL string
|
||||
PrivateKey []byte
|
||||
Certificate []byte
|
||||
}
|
||||
|
||||
// DomainsCertificates stores a certificate for multiple domains
|
||||
type DomainsCertificates struct {
|
||||
Certs []*DomainsCertificate
|
||||
lock *sync.RWMutex
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) init() error {
|
||||
if dc.lock == nil {
|
||||
dc.lock = &sync.RWMutex{}
|
||||
}
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain Domain) error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domain, domainsCertificate.Domains) {
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsCertificate.Certificate = acmeCert
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Certificate to renew not found for domain " + domain.Main)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain Domain) (*DomainsCertificate, error) {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
|
||||
dc.Certs = append(dc.Certs, &cert)
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
domains := []string{}
|
||||
domains = append(domains, domainsCertificate.Domains.Main)
|
||||
domains = append(domains, domainsCertificate.Domains.SANs...)
|
||||
for _, domain := range domains {
|
||||
if domain == domainToFind {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// DomainsCertificate contains a certificate for multiple domains
|
||||
type DomainsCertificate struct {
|
||||
Domains Domain
|
||||
Certificate *Certificate
|
||||
tlsCert *tls.Certificate
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificate) needRenew() bool {
|
||||
for _, c := range dc.tlsCert.Certificate {
|
||||
crt, err := x509.ParseCertificate(c)
|
||||
if err != nil {
|
||||
// If there's an error, we assume the cert is broken, and needs update
|
||||
return true
|
||||
}
|
||||
// <= 7 days left, renew certificate
|
||||
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 7 * time.Hour))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
var (
|
||||
// OSCPMustStaple enables OSCP stapling as from https://github.com/xenolf/lego/issues/270
|
||||
OSCPMustStaple = false
|
||||
)
|
||||
|
||||
// ACME allows to connect to lets encrypt and retrieve certs
|
||||
type ACME struct {
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
StorageFile string `description:"File used for certificates storage."`
|
||||
OnDemand bool `description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
storageLock sync.RWMutex
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
Storage string `description:"File or key used for certificates storage."`
|
||||
StorageFile string // deprecated
|
||||
OnDemand bool `description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."`
|
||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
DNSProvider string `description:"Use a DNS based challenge provider rather than HTTPS."`
|
||||
DelayDontCheckDNS int `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."`
|
||||
ACMELogging bool `description:"Enable debug logging of ACME actions."`
|
||||
client *acme.Client
|
||||
defaultCertificate *tls.Certificate
|
||||
store cluster.Store
|
||||
challengeProvider *challengeProvider
|
||||
checkOnDemandDomain func(domain string) bool
|
||||
jobs *channels.InfiniteChannel
|
||||
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
||||
}
|
||||
|
||||
//Domains parse []Domain
|
||||
@@ -212,60 +92,193 @@ type Domain struct {
|
||||
SANs []string
|
||||
}
|
||||
|
||||
// CreateConfig creates a tls.config from using ACME configuration
|
||||
func (a *ACME) CreateConfig(tlsConfig *tls.Config, CheckOnDemandDomain func(domain string) bool) error {
|
||||
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
|
||||
if len(a.StorageFile) == 0 {
|
||||
return errors.New("Empty StorageFile, please provide a filename for certs storage")
|
||||
}
|
||||
|
||||
log.Debugf("Generating default certificate...")
|
||||
if len(tlsConfig.Certificates) == 0 {
|
||||
// no certificates in TLS config, so we add a default one
|
||||
cert, err := generateDefaultCertificate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *cert)
|
||||
}
|
||||
var account *Account
|
||||
var needRegister bool
|
||||
|
||||
// if certificates in storage, load them
|
||||
if fileInfo, err := os.Stat(a.StorageFile); err == nil && fileInfo.Size() != 0 {
|
||||
log.Infof("Loading ACME certificates...")
|
||||
// load account
|
||||
account, err = a.loadAccount(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (a *ACME) init() error {
|
||||
if a.ACMELogging {
|
||||
acme.Logger = fmtlog.New(os.Stderr, "legolog: ", fmtlog.LstdFlags)
|
||||
} else {
|
||||
log.Infof("Generating ACME Account...")
|
||||
// Create a user. New accounts need an email and private key to start
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account = &Account{
|
||||
Email: a.Email,
|
||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}
|
||||
account.DomainsCertificate = DomainsCertificates{Certs: []*DomainsCertificate{}, lock: &sync.RWMutex{}}
|
||||
needRegister = true
|
||||
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
}
|
||||
|
||||
client, err := a.buildACMEClient(account)
|
||||
// no certificates in TLS config, so we add a default one
|
||||
cert, err := generateDefaultCertificate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.defaultCertificate = cert
|
||||
// TODO: to remove in the futurs
|
||||
if len(a.StorageFile) > 0 && len(a.Storage) == 0 {
|
||||
log.Warnf("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||
a.Storage = a.StorageFile
|
||||
}
|
||||
a.jobs = channels.NewInfiniteChannel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(a.Storage) == 0 {
|
||||
return errors.New("Empty Store, please provide a key for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
listener := func(object cluster.Object) error {
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
if !leadership.IsLeader() {
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
log.Errorf("Error building ACME client %+v: %s", object, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
datastore, err := cluster.NewDataStore(
|
||||
leadership.Pool.Ctx(),
|
||||
staert.KvSource{
|
||||
Store: leadership.Store,
|
||||
Prefix: a.Storage,
|
||||
},
|
||||
&Account{},
|
||||
listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.store = datastore
|
||||
a.challengeProvider = &challengeProvider{store: a.store}
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
||||
log.Infof("Starting ACME renew job...")
|
||||
defer log.Infof("Stopped ACME renew job...")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.renewCertificates()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
leadership.AddListener(func(elected bool) error {
|
||||
if elected {
|
||||
object, err := a.store.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
var needRegister bool
|
||||
if account == nil || len(account.Email) == 0 {
|
||||
account, err = NewAccount(a.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRegister = true
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if needRegister {
|
||||
// New users will need to register; be sure to save it
|
||||
log.Debugf("Register...")
|
||||
reg, err := a.client.Register()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Registration = reg
|
||||
}
|
||||
// The client has a URL to the current Let's Encrypt Subscriber
|
||||
// Agreement. The user will need to agree to it.
|
||||
log.Debugf("AgreeToTOS...")
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
// Let's Encrypt Subscriber Agreement renew ?
|
||||
reg, err := a.client.QueryRegistration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Registration = reg
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
|
||||
}
|
||||
}
|
||||
err = transaction.Commit(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.retrieveCertificates()
|
||||
a.renewCertificates()
|
||||
a.runJobs()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateLocalConfig creates a tls.config using local ACME configuration
|
||||
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error {
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(a.Storage) == 0 {
|
||||
return errors.New("Empty Store, please provide a filename for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
localStore := NewLocalStore(a.Storage)
|
||||
a.store = localStore
|
||||
a.challengeProvider = &challengeProvider{store: a.store}
|
||||
|
||||
var needRegister bool
|
||||
var account *Account
|
||||
|
||||
if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 {
|
||||
log.Infof("Loading ACME Account...")
|
||||
// load account
|
||||
object, err := localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account = object.(*Account)
|
||||
} else {
|
||||
log.Infof("Generating ACME Account...")
|
||||
account, err = NewAccount(a.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRegister = true
|
||||
}
|
||||
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01})
|
||||
wrapperChallengeProvider := newWrapperChallengeProvider()
|
||||
client.SetChallengeProvider(acme.TLSSNI01, wrapperChallengeProvider)
|
||||
|
||||
if needRegister {
|
||||
// New users will need to register; be sure to save it
|
||||
reg, err := client.Register()
|
||||
log.Infof("Register...")
|
||||
reg, err := a.client.Register()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -274,182 +287,302 @@ func (a *ACME) CreateConfig(tlsConfig *tls.Config, CheckOnDemandDomain func(doma
|
||||
|
||||
// The client has a URL to the current Let's Encrypt Subscriber
|
||||
// Agreement. The user will need to agree to it.
|
||||
err = client.AgreeToTOS()
|
||||
log.Debugf("AgreeToTOS...")
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
// Let's Encrypt Subscriber Agreement renew ?
|
||||
reg, err := a.client.QueryRegistration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Registration = reg
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
|
||||
}
|
||||
}
|
||||
// save account
|
||||
transaction, _, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = transaction.Commit(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
safe.Go(func() {
|
||||
a.retrieveCertificates(client, account)
|
||||
if err := a.renewCertificates(client, account); err != nil {
|
||||
log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if challengeCert, ok := wrapperChallengeProvider.getCertificate(clientHello.ServerName); ok {
|
||||
return challengeCert, nil
|
||||
}
|
||||
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok {
|
||||
return domainCert.tlsCert, nil
|
||||
}
|
||||
if a.OnDemand {
|
||||
if CheckOnDemandDomain != nil && !CheckOnDemandDomain(clientHello.ServerName) {
|
||||
return nil, nil
|
||||
}
|
||||
return a.loadCertificateOnDemand(client, account, clientHello)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
a.retrieveCertificates()
|
||||
a.renewCertificates()
|
||||
a.runJobs()
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := a.renewCertificates(client, account); err != nil {
|
||||
log.Errorf("Error renewing ACME certificate %+v: %s", account, err.Error())
|
||||
}
|
||||
}
|
||||
for range ticker.C {
|
||||
a.renewCertificates()
|
||||
}
|
||||
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ACME) retrieveCertificates(client *acme.Client, account *Account) {
|
||||
log.Infof("Retrieving ACME certificates...")
|
||||
for _, domain := range a.Domains {
|
||||
// check if cert isn't already loaded
|
||||
if _, exists := account.DomainsCertificate.exists(domain); !exists {
|
||||
domains := []string{}
|
||||
domains = append(domains, domain.Main)
|
||||
domains = append(domains, domain.SANs...)
|
||||
certificateResource, err := a.getDomainsCertificates(client, domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificate for domain %s: %s", domains, err.Error())
|
||||
continue
|
||||
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
//use regex to test for wildcard certs that might have been added into TLSConfig
|
||||
for k := range a.TLSConfig.NameToCertificate {
|
||||
selector := "^" + strings.Replace(k, "*.", ".*\\.?", -1) + "$"
|
||||
match, _ := regexp.MatchString(selector, domain)
|
||||
if match {
|
||||
return a.TLSConfig.NameToCertificate[k], nil
|
||||
}
|
||||
}
|
||||
if challengeCert, ok := a.challengeProvider.getCertificate(domain); ok {
|
||||
log.Debugf("ACME got challenge %s", domain)
|
||||
return challengeCert, nil
|
||||
}
|
||||
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
log.Debugf("ACME got domain cert %s", domain)
|
||||
return domainCert.tlsCert, nil
|
||||
}
|
||||
if a.OnDemand {
|
||||
if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(domain) {
|
||||
return nil, nil
|
||||
}
|
||||
return a.loadCertificateOnDemand(clientHello)
|
||||
}
|
||||
log.Debugf("ACME got nothing %s", domain)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (a *ACME) retrieveCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Infof("Retrieving ACME certificates...")
|
||||
for _, domain := range a.Domains {
|
||||
// check if cert isn't already loaded
|
||||
account := a.store.Get().(*Account)
|
||||
if _, exists := account.DomainsCertificate.exists(domain); !exists {
|
||||
domains := []string{}
|
||||
domains = append(domains, domain.Main)
|
||||
domains = append(domains, domain.SANs...)
|
||||
certificateResource, err := a.getDomainsCertificates(domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificate for domain %s: %s", domains, err.Error())
|
||||
continue
|
||||
}
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
log.Errorf("Error creating ACME store transaction from domain %s: %s", domain, err.Error())
|
||||
continue
|
||||
}
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificate for domain %s: %s", domains, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificate for domain %s: %s", domains, err.Error())
|
||||
continue
|
||||
}
|
||||
if err = a.saveAccount(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error())
|
||||
continue
|
||||
}
|
||||
log.Infof("Retrieved ACME certificates")
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) renewCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Debugf("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)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %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,
|
||||
}
|
||||
transaction, object, err := a.store.Begin()
|
||||
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())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Infof("Retrieved ACME certificates")
|
||||
}
|
||||
|
||||
func (a *ACME) renewCertificates(client *acme.Client, account *Account) error {
|
||||
log.Debugf("Testing certificate renew...")
|
||||
for _, certificateResource := range account.DomainsCertificate.Certs {
|
||||
if certificateResource.needRenew() {
|
||||
log.Debugf("Renewing certificate %+v", certificateResource.Domains)
|
||||
renewedCert, err := 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)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %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,
|
||||
}
|
||||
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = a.saveAccount(account); err != nil {
|
||||
log.Errorf("Error saving ACME account: %v", err)
|
||||
continue
|
||||
}
|
||||
func dnsOverrideDelay(delay int) error {
|
||||
var err error
|
||||
if delay > 0 {
|
||||
log.Debugf("Delaying %d seconds rather than validating DNS propagation", delay)
|
||||
acme.PreCheckDNS = func(_, _ string) (bool, error) {
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
return true, nil
|
||||
}
|
||||
} else if delay < 0 {
|
||||
err = fmt.Errorf("Invalid negative DelayDontCheckDNS: %d", delay)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *ACME) buildACMEClient(Account *Account) (*acme.Client, error) {
|
||||
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||
log.Debugf("Building ACME client...")
|
||||
caServer := "https://acme-v01.api.letsencrypt.org/directory"
|
||||
if len(a.CAServer) > 0 {
|
||||
caServer = a.CAServer
|
||||
}
|
||||
client, err := acme.NewClient(caServer, Account, acme.RSA4096)
|
||||
client, err := acme.NewClient(caServer, account, acme.RSA4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(a.DNSProvider) > 0 {
|
||||
log.Debugf("Using DNS Challenge provider: %s", a.DNSProvider)
|
||||
|
||||
err = dnsOverrideDelay(a.DelayDontCheckDNS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var provider acme.ChallengeProvider
|
||||
provider, err = dns.NewDNSChallengeProviderByName(a.DNSProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01})
|
||||
err = client.SetChallengeProvider(acme.DNS01, provider)
|
||||
} else {
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01})
|
||||
err = client.SetChallengeProvider(acme.TLSSNI01, a.challengeProvider)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (a *ACME) loadCertificateOnDemand(client *acme.Client, Account *Account, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if certificateResource, ok := Account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok {
|
||||
func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
if certificateResource, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
return certificateResource.tlsCert, nil
|
||||
}
|
||||
Certificate, err := a.getDomainsCertificates(client, []string{clientHello.ServerName})
|
||||
certificate, err := a.getDomainsCertificates([]string{domain})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Got certificate on demand for domain %s", clientHello.ServerName)
|
||||
cert, err := Account.DomainsCertificate.addCertificateForDomains(Certificate, Domain{Main: clientHello.ServerName})
|
||||
log.Debugf("Got certificate on demand for domain %s", domain)
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = a.saveAccount(Account); err != nil {
|
||||
account = object.(*Account)
|
||||
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: domain})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert.tlsCert, nil
|
||||
}
|
||||
|
||||
func (a *ACME) loadAccount(acmeConfig *ACME) (*Account, error) {
|
||||
a.storageLock.RLock()
|
||||
defer a.storageLock.RUnlock()
|
||||
Account := Account{
|
||||
DomainsCertificate: DomainsCertificates{},
|
||||
// LoadCertificateForDomains loads certificates from ACME for given domains
|
||||
func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
a.jobs.In() <- func() {
|
||||
log.Debugf("LoadCertificateForDomains %s...", domains)
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
operation := func() error {
|
||||
if a.client == nil {
|
||||
return fmt.Errorf("ACME client still not built")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error getting ACME client: %v, retrying in %s", err, time)
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 30 * time.Second
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME client: %v", err)
|
||||
return
|
||||
}
|
||||
account := a.store.Get().(*Account)
|
||||
var domain Domain
|
||||
if len(domains) == 0 {
|
||||
// no domain
|
||||
return
|
||||
|
||||
} else if len(domains) > 1 {
|
||||
domain = Domain{Main: domains[0], SANs: domains[1:]}
|
||||
} else {
|
||||
domain = Domain{Main: domains[0]}
|
||||
}
|
||||
if _, exists := account.DomainsCertificate.exists(domain); exists {
|
||||
// domain already exists
|
||||
return
|
||||
}
|
||||
certificate, err := a.getDomainsCertificates(domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificates %+v : %v", domains, err)
|
||||
return
|
||||
}
|
||||
log.Debugf("Got certificate for domains %+v", domains)
|
||||
transaction, object, err := a.store.Begin()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error creating transaction %+v : %v", domains, err)
|
||||
return
|
||||
}
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificates %+v : %v", domains, err)
|
||||
return
|
||||
}
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %v", account, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
file, err := ioutil.ReadFile(acmeConfig.StorageFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(file, &Account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = Account.DomainsCertificate.init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("Loaded ACME config from storage %s", acmeConfig.StorageFile)
|
||||
return &Account, nil
|
||||
}
|
||||
|
||||
func (a *ACME) saveAccount(Account *Account) error {
|
||||
a.storageLock.Lock()
|
||||
defer a.storageLock.Unlock()
|
||||
// write account to file
|
||||
data, err := json.MarshalIndent(Account, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(a.StorageFile, data, 0644)
|
||||
}
|
||||
|
||||
func (a *ACME) getDomainsCertificates(client *acme.Client, domains []string) (*Certificate, error) {
|
||||
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
log.Debugf("Loading ACME certificates %s...", domains)
|
||||
bundle := true
|
||||
certificate, failures := client.ObtainCertificate(domains, bundle, nil)
|
||||
certificate, failures := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if len(failures) > 0 {
|
||||
log.Error(failures)
|
||||
return nil, fmt.Errorf("Cannot obtain certificates %s+v", failures)
|
||||
@@ -463,3 +596,12 @@ func (a *ACME) getDomainsCertificates(client *acme.Client, domains []string) (*C
|
||||
Certificate: certificate.Certificate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ACME) runJobs() {
|
||||
safe.Go(func() {
|
||||
for job := range a.jobs.Out() {
|
||||
function := job.(func())
|
||||
function()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -1,9 +1,15 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
func TestDomainsSet(t *testing.T) {
|
||||
@@ -62,8 +68,10 @@ func TestDomainsSetAppend(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCertificatesRenew(t *testing.T) {
|
||||
foo1Cert, foo1Key, _ := generateKeyPair("foo1.com", time.Now())
|
||||
foo2Cert, foo2Key, _ := generateKeyPair("foo2.com", time.Now())
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: &sync.RWMutex{},
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: Domain{
|
||||
@@ -73,55 +81,8 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA6OqHGdwGy20+3Jcz9IgfN4IR322X2Hhwk6n8Hss/Ws7FeTZo
|
||||
PvXW8uHeI1bmQJsy9C6xo3odzO64o7prgMZl5eDw5fk1mmUij3J3nM3gwtc/Cc+8
|
||||
ADXGldauASdHBFTRvWQge0Pv/Q5U0fyL2VCHoR9mGv4CQ7nRNKPus0vYJMbXoTbO
|
||||
8z4sIbNz3Ov9o/HGMRb8D0rNPTMdC62tHSbiO1UoxLXr9dcBOGt786AsiRTJ8bq9
|
||||
GCVQgzd0Wftb8z6ddW2YuWrmExlkHdfC4oG0D5SU1QB4ldPyl7fhVWlfHwC1NX+c
|
||||
RnDSEeYkAcdvvIekdM/yH+z62XhwToM0E9TCzwIDAQABAoIBACq3EC3S50AZeeTU
|
||||
qgeXizoP1Z1HKQjfFa5PB1jSZ30M3LRdIQMi7NfASo/qmPGSROb5RUS42YxC34PP
|
||||
ZXXJbNiaxzM13/m/wHXURVFxhF3XQc1X1p+nPRMvutulS2Xk9E4qdbaFgBbFsRKN
|
||||
oUwqc6U97+jVWq72/gIManNhXnNn1n1SRLBEkn+WStMPn6ZvWRlpRMjhy0c1mpwg
|
||||
u6em92HvMvfKPQ60naUhdKp+q0rsLp2YKWjiytos9ENSYI5gAGLIDhKeqiD8f92E
|
||||
4FGPmNRipwxCE2SSvZFlM26tRloWVcBPktRN79hUejE8iopiqVS0+4h/phZ2wG0D
|
||||
18cqVpECgYEA+qmagnhm0LLvwVkUN0B2nRARQEFinZDM4Hgiv823bQvc9I8dVTqJ
|
||||
aIQm5y4Y5UA3xmyDsRoO7GUdd0oVeh9GwTONzMRCOny/mOuOC51wXPhKHhI0O22u
|
||||
sfbOHszl+bxl6ZQMUJa2/I8YIWBLU5P+fTgrfNwBEgZ3YPwUV5tyHNcCgYEA7eAv
|
||||
pjQkbJNRq/fv/67sojN7N9QoH84egN5cZFh5d8PJomnsvy5JDV4WaG1G6mJpqjdD
|
||||
YRVdFw5oZ4L8yCVdCeK9op896Uy51jqvfSe3+uKmNqE0qDHgaLubQNI8yYc5sacW
|
||||
fYJBmDR6rNIeE7Q2240w3CdKfREuXdDnhyTTEskCgYBFeAnFTP8Zqe2+hSSQJ4J4
|
||||
BwLw7u4Yww+0yja/N5E1XItRD/TOMRnx6GYrvd/ScVjD2kEpLRKju2ZOMC8BmHdw
|
||||
hgwvitjcAsTK6cWFPI3uhjVsXhkxuzUmR0Naz+iQrQEFmi1LjGmMV1AVt+1IbYSj
|
||||
SZTr1sFJMJeXPmWY3hDjIwKBgQC4H9fCJoorIL0PB5NVreishHzT8fw84ibqSTPq
|
||||
2DDtazcf6C3AresN1c4ydqN1uUdg4fXdp9OujRBzTwirQ4CIrmFrBye89g7CrBo6
|
||||
Hgxivh06G/3OUw0JBG5f9lvnAiy+Pj9CVxi+36A1NU7ioZP0zY0MW71koW/qXlFY
|
||||
YkCfQQKBgBqwND/c3mPg7iY4RMQ9XjrKfV9o6FMzA51lAinjujHlNgsBmqiR951P
|
||||
NA3kWZQ73D3IxeLEMaGHpvS7andPN3Z2qPhe+FbJKcF6ZZNTrFQkh/Fpz3wmYPo1
|
||||
GIL4+09kNgMRWapaROqI+/3+qJQ+GVJZIPfYC0poJOO6vYqifWe8
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`),
|
||||
Certificate: []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC+TCCAeGgAwIBAgIJAK78ukR/Qu4rMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
|
||||
BAMMCGZvbzEuY29tMB4XDTE2MDYxOTIyMDMyM1oXDTI2MDYxNzIyMDMyM1owEzER
|
||||
MA8GA1UEAwwIZm9vMS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDo6ocZ3AbLbT7clzP0iB83ghHfbZfYeHCTqfweyz9azsV5Nmg+9dby4d4jVuZA
|
||||
mzL0LrGjeh3M7rijumuAxmXl4PDl+TWaZSKPcneczeDC1z8Jz7wANcaV1q4BJ0cE
|
||||
VNG9ZCB7Q+/9DlTR/IvZUIehH2Ya/gJDudE0o+6zS9gkxtehNs7zPiwhs3Pc6/2j
|
||||
8cYxFvwPSs09Mx0Lra0dJuI7VSjEtev11wE4a3vzoCyJFMnxur0YJVCDN3RZ+1vz
|
||||
Pp11bZi5auYTGWQd18LigbQPlJTVAHiV0/KXt+FVaV8fALU1f5xGcNIR5iQBx2+8
|
||||
h6R0z/If7PrZeHBOgzQT1MLPAgMBAAGjUDBOMB0GA1UdDgQWBBRFLH1wF6BT51uq
|
||||
yWNqBnCrPFIglzAfBgNVHSMEGDAWgBRFLH1wF6BT51uqyWNqBnCrPFIglzAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAr7aH3Db6TeAZkg4Zd7SoF2q11
|
||||
erzv552PgQUyezMZcRBo2q1ekmUYyy2600CBiYg51G+8oUqjJKiKnBuaqbMX7pFa
|
||||
FsL7uToZCGA57cBaVejeB+p24P5bxoJGKCMeZcEBe5N93Tqu5WBxNEX7lQUo6TSs
|
||||
gSN2Olf3/grNKt5V4BduSIQZ+YHlPUWLTaz5B1MXKSUqjmabARP9lhjO14u9USvi
|
||||
dMBDFskJySQ6SUfz3fyoXELoDOVbRZETuSodpw+aFCbEtbcQCLT3A0FG+BEPayZH
|
||||
tt19zKUlr6e+YFpyjQPGZ7ZkY7iMgHEkhKrXx2DiZ1+cif3X1xfXWQr0S5+E
|
||||
-----END CERTIFICATE-----
|
||||
`),
|
||||
PrivateKey: foo1Key,
|
||||
Certificate: foo1Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -132,113 +93,19 @@ tt19zKUlr6e+YFpyjQPGZ7ZkY7iMgHEkhKrXx2DiZ1+cif3X1xfXWQr0S5+E
|
||||
Domain: "foo2.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEA7rIVuSrZ3FfYXhR3qaWwfVcgiqKS//yXFzNqkJS6mz9nRCNT
|
||||
lPawvrCFIRKdR7UO7xD7A5VTcbrGOAaTvrEaH7mB/4FGL+gN4AiTbVFpKXngAYEW
|
||||
A3//zeBZ7XUSWaQ+CNC+l796JeoDvQD++KwCke4rVD1pGN1hpVEeGhwzyKOYPKLo
|
||||
4+AGVe1LFWw4U/v8Iil1/gBBehZBILuhASpXy4W132LJPl76/EbGqh0nVz2UlFqU
|
||||
HRxO+2U2ba4YIpI+0/VOQ9Cq/TzHSUdTTLfBHE/Qb+aDBfptMWTRvAngLqUglOcZ
|
||||
Fi6SAljxEkJO6z6btmoVUWsoKBpbIHDC5++dZwIDAQABAoIBAAD8rYhRfAskNdnV
|
||||
vdTuwXcTOCg6md8DHWDULpmgc9EWhwfKGZthFcQEGNjVKd9VCVXFvTP7lxe+TPmI
|
||||
VW4Rb2k4LChxUWf7TqthfbKTBptMTLfU39Ft4xHn3pdTx5qlSjhhHJimCwxDFnbe
|
||||
nS9MDsqpsHYtttSKfc/gMP6spS4sNPZ/r9zseT3eWkBEhn+FQABxJiuPcQ7q7S+Q
|
||||
uOghmr7f3FeYvizQOhBtULsLrK/hsmQIIB4amS1QlpNWKbIoiUPNPjCA5PVQyAER
|
||||
waYjuc7imBbeD98L/z8bRTlEskSKjtPSEXGVHa9OYdBU+02Ci6TjKztUp6Ho7JE9
|
||||
tcHj+eECgYEA+9Ntv6RqIdpT/4/52JYiR+pOem3U8tweCOmUqm/p/AWyfAJTykqt
|
||||
cJ8RcK1MfM+uoa5Sjm8hIcA2XPVEqH2J50PC4w04Q3xtfsz3xs7KJWXQCoha8D0D
|
||||
ZIFNroEPnld0qOuJzpIIteXTrCLhSu17ZhN+Wk+5gJ7Ewu/QMM5OPjECgYEA8qbw
|
||||
zfwSjE6jkrqO70jzqSxgi2yjo0vMqv+BNBuhxhDTBXnKQI1KsHoiS0FkSLSJ9+DS
|
||||
CT3WEescD2Lumdm2s9HXvaMmnDSKBY58NqCGsNzZifSgmj1H/yS9FX8RXfSjXcxq
|
||||
RDvTbD52/HeaCiOxHZx8JjmJEb+ZKJC4MDvjtxcCgYBM516GvgEjYXdxfliAiijh
|
||||
6W4Z+Vyk5g/ODPc3rYG5U0wUjuljx7Z7xDghPusy2oGsIn5XvRxTIE35yXU0N1Jb
|
||||
69eiWzEpeuA9bv7kGdal4RfNf6K15wwYL1y3w/YvFuorg/LLwNEkK5Ge6e//X9Ll
|
||||
c2KM1fgCjXntRitAHGDMoQKBgDnkgodioLpA+N3FDN0iNqAiKlaZcOFA8G/LzfO0
|
||||
tAAhe3dO+2YzT6KTQSNbUqXWDSTKytHRowVbZrJ1FCA4xVJZunNQPaH/Fv8EY7ZU
|
||||
zk3cIzq61qZ2AHtrNIGwc2BLQb7bSm9FJsgojxLlJidNJLC/6Q7lo0JMyCnZfVhk
|
||||
sYu5AoGAZt/MfyFTKm674UddSNgGEt86PyVYbLMnRoAXOaNB38AE12kaYHPil1tL
|
||||
FnL8OQLpbX5Qo2JGgeZRlpMJ4Jxw2zzvUKr/n+6khaLxHmtX48hMu2QM7ZvnkZCs
|
||||
Kkgz6v+Wcqm94ugtl3HSm+u9xZzVQxN6gu/jZQv3VpQiAZHjPYc=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`),
|
||||
Certificate: []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC+TCCAeGgAwIBAgIJAK25/Z9Jz6IBMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
|
||||
BAMMCGZvbzIuY29tMB4XDTE2MDYyMDA5MzUyNloXDTI2MDYxODA5MzUyNlowEzER
|
||||
MA8GA1UEAwwIZm9vMi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDushW5KtncV9heFHeppbB9VyCKopL//JcXM2qQlLqbP2dEI1OU9rC+sIUhEp1H
|
||||
tQ7vEPsDlVNxusY4BpO+sRofuYH/gUYv6A3gCJNtUWkpeeABgRYDf//N4FntdRJZ
|
||||
pD4I0L6Xv3ol6gO9AP74rAKR7itUPWkY3WGlUR4aHDPIo5g8oujj4AZV7UsVbDhT
|
||||
+/wiKXX+AEF6FkEgu6EBKlfLhbXfYsk+Xvr8RsaqHSdXPZSUWpQdHE77ZTZtrhgi
|
||||
kj7T9U5D0Kr9PMdJR1NMt8EcT9Bv5oMF+m0xZNG8CeAupSCU5xkWLpICWPESQk7r
|
||||
Ppu2ahVRaygoGlsgcMLn751nAgMBAAGjUDBOMB0GA1UdDgQWBBQ6FZWqB9qI4NN+
|
||||
2jFY6xH8uoUTnTAfBgNVHSMEGDAWgBQ6FZWqB9qI4NN+2jFY6xH8uoUTnTAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCRhuf2dQhIEOmSOGgtRELF2wB6
|
||||
NWXt0lCty9x4u+zCvITXV8Z0C34VQGencO3H2bgyC3ZxNpPuwZfEc2Pxe8W6bDc/
|
||||
OyLckk9WLo00Tnr2t7rDOeTjEGuhXFZkhIbJbKdAH8cEXrxKR8UXWtZgTv/b8Hv/
|
||||
g6tbeH6TzBsdMoFtUCsyWxygYwnLU+quuYvE2s9FiCegf2mdYTCh/R5J5n/51gfB
|
||||
uC+NakKMfaCvNg3mOAFSYC/0r0YcKM/5ldKGTKTCVJAMhnmBnyRc/70rKkVRFy2g
|
||||
iIjUFs+9aAgfCiL0WlyyXYAtIev2gw4FHUVlcT/xKks+x8Kgj6e5LTIrRRwW
|
||||
-----END CERTIFICATE-----
|
||||
`),
|
||||
PrivateKey: foo2Key,
|
||||
Certificate: foo2Cert,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
foo1Cert, foo1Key, _ = generateKeyPair("foo1.com", time.Now())
|
||||
newCertificate := &Certificate{
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA1OdSuXK2zeSLf0UqgrI4pjkpaqhra++pnda4Li4jXo151svi
|
||||
Sn7DSynJOoq1jbfRJAoyDhxsBC4S4RuD54U5elJ4wLPZXmHRsvb+NwiHs9VmDqwu
|
||||
It21btuqeNMebkab5cnDnC6KKufMhXRcRAlluYXyCkQe/+N+LlUQd6Js34TixMpk
|
||||
eQOX4/OVrokSyVRnIq4u+o0Ufe7z5+41WVH63tcy7Hwi7244aLUzZCs+QQa2Dw6f
|
||||
qEwjbonr974fM68UxDjTZEQy9u24yDzajhDBp1OTAAklh7U+li3g9dSyNVBFXqEu
|
||||
nW2fyBvLqeJOSTihqfcrACB/YYhYOX94vMXELQIDAQABAoIBAFYK3t3fxI1VTiMz
|
||||
WsjTKh3TgC+AvVkz1ILbojfXoae22YS7hUrCDD82NgMYx+LsZPOBw1T8m5Lc4/hh
|
||||
3F8W8nHDHtYSWUjRk6QWOgsXwXAmUEahw0uH+qlA0ZZfDC9ZDexCLHHURTat03Qj
|
||||
4J4GhjwCLB2GBlk4IWisLCmNVR7HokrpfIw4oM1aB5E21Tl7zh/x7ikRijEkUsKw
|
||||
7YhaMeLJqBnMnAdV63hhF7FaDRjl8P2s/3octz/6pqDIABrDrUW3KAkNYCZIWdhF
|
||||
Kk0wRMbZ/WrYT9GIGoJe7coQC7ezTrlrEkAFEIPGHCLkgXB/0TyuSy0yY59e4zmi
|
||||
VvHoWUECgYEA/rOL2KJ/p+TZW7+YbsUzs0+F+M+G6UCr0nWfYN9MKmNtmns3eLDG
|
||||
+pIpBMc5mjqeJR/sCCdkD8OqHC202Y8e4sr0pKSBeBofh2BmXtpyu3QQ50Pa63RS
|
||||
SK6mYUrFqPmFFDbNGpFI4sIeI+Vf6hm96FQPnyPtUTGqk39m0RbWM/UCgYEA1f04
|
||||
Nf3wbqwqIHZjYpPmymfjleyMn3hGUjpi7pmI6inXGMk3nkeG1cbOhnfPxL5BWD12
|
||||
3RqHI2B4Z4r0BMyjctDNb1TxhMIpm5+PKm5KeeKfoYA85IS0mEeq6VdMm3mL1x/O
|
||||
3LYvcUvAEVf6pWX/+ZFLMudqhF3jbTrdNOC6ZFkCgYBKpEeJdyW+CD0CvEVpwPUD
|
||||
yXxTjE3XMZKpHLtWYlop2fWW3iFFh1jouci3k8L3xdHuw0oioZibXhYOJ/7l+yFs
|
||||
CVpknakrj0xKGiAmEBKriLojbClN80rh7fzoakc+29D6OY0mCgm4GndGwcO4EU8s
|
||||
NOZXFupHbyy0CRQSloSzuQKBgQC1Z/MtIlefGuijmHlsakGuuR+gS2ZzEj1bHBAe
|
||||
gZ4mFM46PuqdjblqpR0TtaI3AarXqVOI4SJLBU9NR+jR4MF3Zjeh9/q/NvKa8Usn
|
||||
B1Svu0TkXphAiZenuKnVIqLY8tNvzZFKXlAd1b+/dDwR10SHR3rebnxINmfEg7Bf
|
||||
UVvyEQKBgAEjI5O6LSkLNpbVn1l2IO8u8D2RkFqs/Sbx78uFta3f9Gddzb4wMnt3
|
||||
jVzymghCLp4Qf1ump/zC5bcQ8L97qmnjJ+H8X9HwmkqetuI362JNnz+12YKVDIWi
|
||||
wI7SJ8BwDqYMrLw6/nE+degn39KedGDH8gz5cZcdlKTZLjbuBOfU
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`),
|
||||
Certificate: []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC+TCCAeGgAwIBAgIJAPQiOiQcwYaRMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
|
||||
BAMMCGZvbzEuY29tMB4XDTE2MDYxOTIyMTE1NFoXDTI2MDYxNzIyMTE1NFowEzER
|
||||
MA8GA1UEAwwIZm9vMS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDU51K5crbN5It/RSqCsjimOSlqqGtr76md1rguLiNejXnWy+JKfsNLKck6irWN
|
||||
t9EkCjIOHGwELhLhG4PnhTl6UnjAs9leYdGy9v43CIez1WYOrC4i3bVu26p40x5u
|
||||
RpvlycOcLooq58yFdFxECWW5hfIKRB7/434uVRB3omzfhOLEymR5A5fj85WuiRLJ
|
||||
VGciri76jRR97vPn7jVZUfre1zLsfCLvbjhotTNkKz5BBrYPDp+oTCNuiev3vh8z
|
||||
rxTEONNkRDL27bjIPNqOEMGnU5MACSWHtT6WLeD11LI1UEVeoS6dbZ/IG8up4k5J
|
||||
OKGp9ysAIH9hiFg5f3i8xcQtAgMBAAGjUDBOMB0GA1UdDgQWBBQPfkS5ehpstmSb
|
||||
8CGJE7GxSCxl2DAfBgNVHSMEGDAWgBQPfkS5ehpstmSb8CGJE7GxSCxl2DAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA99A+itS9ImdGRGgHZ5fSusiEq
|
||||
wkK5XxGyagL1S0f3VM8e78VabSvC0o/xdD7DHVg6Az8FWxkkksH6Yd7IKfZZUzvs
|
||||
kXQhlOwWpxgmguSmAs4uZTymIoMFRVj3nG664BcXkKu4Yd9UXKNOWP59zgvrCJMM
|
||||
oIsmYiq5u0MFpM31BwfmmW3erqIcfBI9OJrmr1XDzlykPZNWtUSSfVuNQ8d4bim9
|
||||
XH8RfVLeFbqDydSTCHIFvYthH/ESbpRCiGJHoJ8QLfOkhD1k2fI0oJZn5RVtG2W8
|
||||
bZME3gHPYCk1QFZUptriMCJ5fMjCgxeOTR+FAkstb/lTRuCc4UyILJguIMar
|
||||
-----END CERTIFICATE-----
|
||||
`),
|
||||
PrivateKey: foo1Key,
|
||||
Certificate: foo1Cert,
|
||||
}
|
||||
|
||||
err := domainsCertificates.renewCertificates(
|
||||
@@ -256,3 +123,157 @@ bZME3gHPYCk1QFZUptriMCJ5fMjCgxeOTR+FAkstb/lTRuCc4UyILJguIMar
|
||||
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo24Key,
|
||||
Certificate: foo24Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo48Key,
|
||||
Certificate: foo48Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: fooKey,
|
||||
Certificate: fooCert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "bar.com",
|
||||
SANs: []string{}},
|
||||
Certificate: &Certificate{
|
||||
Domain: "bar.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: barKey,
|
||||
Certificate: barCert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo48Key,
|
||||
Certificate: foo48Cert,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
domainsCertificates.Init()
|
||||
|
||||
if len(domainsCertificates.Certs) != 2 {
|
||||
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
|
||||
}
|
||||
|
||||
for _, cert := range domainsCertificates.Certs {
|
||||
switch cert.Domains.Main {
|
||||
case "bar.com":
|
||||
continue
|
||||
case "foo.com":
|
||||
if !cert.tlsCert.Leaf.NotAfter.Equal(now.Add(48 * time.Hour).Truncate(1 * time.Second)) {
|
||||
t.Errorf("Bad expiration %s date for domain %+v, now %s", cert.tlsCert.Leaf.NotAfter.String(), cert, now.Add(48*time.Hour).Truncate(1*time.Second).String())
|
||||
}
|
||||
default:
|
||||
t.Errorf("Unknown domain %+v", cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoPreCheckOverride(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
err := dnsOverrideDelay(0)
|
||||
if err != nil {
|
||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||
}
|
||||
if acme.PreCheckDNS != nil {
|
||||
t.Errorf("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSillyPreCheckOverride(t *testing.T) {
|
||||
err := dnsOverrideDelay(-5)
|
||||
if err == nil {
|
||||
t.Errorf("Missing expected error in dnsOverrideDelay!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreCheckOverride(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
err := dnsOverrideDelay(5)
|
||||
if err != nil {
|
||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||
}
|
||||
if acme.PreCheckDNS == nil {
|
||||
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcmeClientCreation(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
// Lengthy setup to avoid external web requests - oh for easier golang testing!
|
||||
account := &Account{Email: "f@f"}
|
||||
account.PrivateKey, _ = base64.StdEncoding.DecodeString(`
|
||||
MIIBPAIBAAJBAMp2Ni92FfEur+CAvFkgC12LT4l9D53ApbBpDaXaJkzzks+KsLw9zyAxvlrfAyTCQ
|
||||
7tDnEnIltAXyQ0uOFUUdcMCAwEAAQJAK1FbipATZcT9cGVa5x7KD7usytftLW14heQUPXYNV80r/3
|
||||
lmnpvjL06dffRpwkYeN8DATQF/QOcy3NNNGDw/4QIhAPAKmiZFxA/qmRXsuU8Zhlzf16WrNZ68K64
|
||||
asn/h3qZrAiEA1+wFR3WXCPIolOvd7AHjfgcTKQNkoMPywU4FYUNQ1AkCIQDv8yk0qPjckD6HVCPJ
|
||||
llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
|
||||
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{
|
||||
"new-authz": "https://foo/acme/new-authz",
|
||||
"new-cert": "https://foo/acme/new-cert",
|
||||
"new-reg": "https://foo/acme/new-reg",
|
||||
"revoke-cert": "https://foo/acme/revoke-cert"
|
||||
}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
a := ACME{DNSProvider: "manual", DelayDontCheckDNS: 10, CAServer: ts.URL}
|
||||
|
||||
client, err := a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
t.Errorf("Error in buildACMEClient: %v", err)
|
||||
}
|
||||
if client == nil {
|
||||
t.Errorf("No client from buildACMEClient!")
|
||||
}
|
||||
if acme.PreCheckDNS == nil {
|
||||
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||
}
|
||||
}
|
||||
|
@@ -2,55 +2,96 @@ package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"crypto/x509"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
type wrapperChallengeProvider struct {
|
||||
challengeCerts map[string]*tls.Certificate
|
||||
lock sync.RWMutex
|
||||
var _ acme.ChallengeProviderTimeout = (*challengeProvider)(nil)
|
||||
|
||||
type challengeProvider struct {
|
||||
store cluster.Store
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func newWrapperChallengeProvider() *wrapperChallengeProvider {
|
||||
return &wrapperChallengeProvider{
|
||||
challengeCerts: map[string]*tls.Certificate{},
|
||||
func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
|
||||
log.Debugf("Challenge GetCertificate %s", domain)
|
||||
if !strings.HasSuffix(domain, ".acme.invalid") {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wrapperChallengeProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
if cert, ok := c.challengeCerts[domain]; ok {
|
||||
return cert, true
|
||||
account := c.store.Get().(*Account)
|
||||
if account.ChallengeCerts == nil {
|
||||
return nil, false
|
||||
}
|
||||
return nil, false
|
||||
account.Init()
|
||||
var result *tls.Certificate
|
||||
operation := func() error {
|
||||
for _, cert := range account.ChallengeCerts {
|
||||
for _, dns := range cert.certificate.Leaf.DNSNames {
|
||||
if domain == dns {
|
||||
result = cert.certificate
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Cannot find challenge cert for domain %s", domain)
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error getting cert: %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 getting cert: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (c *wrapperChallengeProvider) Present(domain, token, keyAuth string) error {
|
||||
cert, _, err := acme.TLSSNI01ChallengeCert(keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge Present %s", domain)
|
||||
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
for i := range cert.Leaf.DNSNames {
|
||||
c.challengeCerts[cert.Leaf.DNSNames[i]] = &cert
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
account := object.(*Account)
|
||||
if account.ChallengeCerts == nil {
|
||||
account.ChallengeCerts = map[string]*ChallengeCert{}
|
||||
}
|
||||
account.ChallengeCerts[domain] = &cert
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *wrapperChallengeProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
func (c *challengeProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge CleanUp %s", domain)
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
delete(c.challengeCerts, domain)
|
||||
return nil
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
delete(account.ChallengeCerts, domain)
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *challengeProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return 60 * time.Second, 5 * time.Second
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
@@ -15,34 +17,44 @@ import (
|
||||
)
|
||||
|
||||
func generateDefaultCertificate() (*tls.Certificate, error) {
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsaPrivPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
|
||||
|
||||
randomBytes := make([]byte, 100)
|
||||
_, err = rand.Read(randomBytes)
|
||||
_, 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:])
|
||||
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
|
||||
|
||||
certPEM, keyPEM, err := generateKeyPair(domain, time.Time{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
certificate, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &certificate, nil
|
||||
}
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -76,3 +88,48 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin
|
||||
|
||||
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)}
|
||||
break
|
||||
case *x509.CertificateRequest:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
||||
break
|
||||
case []byte:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
|
97
acme/localStore.go
Normal file
97
acme/localStore.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
var _ cluster.Store = (*LocalStore)(nil)
|
||||
|
||||
// LocalStore is a store using a file as storage
|
||||
type LocalStore struct {
|
||||
file string
|
||||
storageLock sync.RWMutex
|
||||
account *Account
|
||||
}
|
||||
|
||||
// NewLocalStore create a LocalStore
|
||||
func NewLocalStore(file string) *LocalStore {
|
||||
return &LocalStore{
|
||||
file: file,
|
||||
}
|
||||
}
|
||||
|
||||
// Get atomically a struct from the file storage
|
||||
func (s *LocalStore) Get() cluster.Object {
|
||||
s.storageLock.RLock()
|
||||
defer s.storageLock.RUnlock()
|
||||
return s.account
|
||||
}
|
||||
|
||||
// Load loads file into store
|
||||
func (s *LocalStore) Load() (cluster.Object, error) {
|
||||
s.storageLock.Lock()
|
||||
defer s.storageLock.Unlock()
|
||||
account := &Account{}
|
||||
|
||||
err := checkPermissions(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(file, &account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Init()
|
||||
s.account = account
|
||||
log.Infof("Loaded ACME config from store %s", s.file)
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// Begin creates a transaction with the KV store.
|
||||
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) {
|
||||
s.storageLock.Lock()
|
||||
return &localTransaction{LocalStore: s}, s.account, nil
|
||||
}
|
||||
|
||||
var _ cluster.Transaction = (*localTransaction)(nil)
|
||||
|
||||
type localTransaction struct {
|
||||
*LocalStore
|
||||
dirty bool
|
||||
}
|
||||
|
||||
// Commit allows to set an object in the file storage
|
||||
func (t *localTransaction) Commit(object cluster.Object) error {
|
||||
t.LocalStore.account = object.(*Account)
|
||||
defer t.storageLock.Unlock()
|
||||
if t.dirty {
|
||||
return fmt.Errorf("transaction already used, please begin a new one")
|
||||
}
|
||||
|
||||
// write account to file
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(t.file, data, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.dirty = true
|
||||
return nil
|
||||
}
|
25
acme/localStore_unix.go
Normal file
25
acme/localStore_unix.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// +build !windows
|
||||
|
||||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Check file permissions
|
||||
func checkPermissions(name string) error {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.Mode().Perm()&0077 != 0 {
|
||||
return fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name)
|
||||
}
|
||||
return nil
|
||||
}
|
6
acme/localStore_windows.go
Normal file
6
acme/localStore_windows.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package acme
|
||||
|
||||
// Do not check file permissions on Windows right now
|
||||
func checkPermissions(name string) error {
|
||||
return nil
|
||||
}
|
@@ -6,7 +6,7 @@ package main
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
// OxyLogger implements oxy Logger interface with logrus.
|
||||
|
@@ -1,25 +1,36 @@
|
||||
FROM golang:1.6.2
|
||||
FROM golang:1.7
|
||||
|
||||
RUN go get github.com/Masterminds/glide \
|
||||
&& go get github.com/jteeuwen/go-bindata/... \
|
||||
RUN go get github.com/jteeuwen/go-bindata/... \
|
||||
&& go get github.com/golang/lint/golint \
|
||||
&& go get github.com/kisielk/errcheck
|
||||
&& go get github.com/kisielk/errcheck \
|
||||
&& go get github.com/client9/misspell/cmd/misspell \
|
||||
&& go get github.com/mattfarina/glide-hash
|
||||
|
||||
# Which docker version to test on
|
||||
ARG DOCKER_VERSION=1.10.1
|
||||
ARG DOCKER_VERSION=1.10.3
|
||||
|
||||
|
||||
# Which glide version to test on
|
||||
ARG GLIDE_VERSION=v0.12.3
|
||||
|
||||
# Download glide
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
&& curl -fL https://github.com/Masterminds/glide/releases/download/${GLIDE_VERSION}/glide-${GLIDE_VERSION}-linux-amd64.tar.gz \
|
||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||
|
||||
# Download docker
|
||||
RUN set -ex; \
|
||||
curl https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION} -o /usr/local/bin/docker-${DOCKER_VERSION}; \
|
||||
chmod +x /usr/local/bin/docker-${DOCKER_VERSION}
|
||||
|
||||
# Set the default Docker to be run
|
||||
RUN ln -s /usr/local/bin/docker-${DOCKER_VERSION} /usr/local/bin/docker
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
&& curl -fL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz \
|
||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||
|
||||
WORKDIR /go/src/github.com/containous/traefik
|
||||
|
||||
COPY glide.yaml glide.yaml
|
||||
COPY glide.lock glide.lock
|
||||
RUN glide install
|
||||
RUN glide install -v
|
||||
|
||||
COPY . /go/src/github.com/containous/traefik
|
||||
COPY integration/glide.yaml integration/glide.yaml
|
||||
COPY integration/glide.lock integration/glide.lock
|
||||
RUN cd integration && glide install
|
||||
|
||||
COPY . /go/src/github.com/containous/traefik
|
||||
|
255
cluster/datastore.go
Normal file
255
cluster/datastore.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// Metadata stores Object plus metadata
|
||||
type Metadata struct {
|
||||
object Object
|
||||
Object []byte
|
||||
Lock string
|
||||
}
|
||||
|
||||
// NewMetadata returns new Metadata
|
||||
func NewMetadata(object Object) *Metadata {
|
||||
return &Metadata{object: object}
|
||||
}
|
||||
|
||||
// Marshall marshalls object
|
||||
func (m *Metadata) Marshall() error {
|
||||
var err error
|
||||
m.Object, err = json.Marshal(m.object)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Metadata) unmarshall() error {
|
||||
if len(m.Object) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(m.Object, m.object)
|
||||
}
|
||||
|
||||
// Listener is called when Object has been changed in KV store
|
||||
type Listener func(Object) error
|
||||
|
||||
var _ Store = (*Datastore)(nil)
|
||||
|
||||
// Datastore holds a struct synced in a KV store
|
||||
type Datastore struct {
|
||||
kv staert.KvSource
|
||||
ctx context.Context
|
||||
localLock *sync.RWMutex
|
||||
meta *Metadata
|
||||
lockKey string
|
||||
listener Listener
|
||||
}
|
||||
|
||||
// NewDataStore creates a Datastore
|
||||
func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object, listener Listener) (*Datastore, error) {
|
||||
datastore := Datastore{
|
||||
kv: kvSource,
|
||||
ctx: ctx,
|
||||
meta: &Metadata{object: object},
|
||||
lockKey: kvSource.Prefix + "/lock",
|
||||
localLock: &sync.RWMutex{},
|
||||
listener: listener,
|
||||
}
|
||||
err := datastore.watchChanges()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &datastore, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) watchChanges() error {
|
||||
stopCh := make(chan struct{})
|
||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
ctx, cancel := context.WithCancel(d.ctx)
|
||||
operation := func() error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stopCh <- struct{}{}
|
||||
return nil
|
||||
case _, ok := <-kvCh:
|
||||
if !ok {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
err = d.reload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// log.Debugf("Datastore object change received: %+v", d.meta)
|
||||
if d.listener != nil {
|
||||
err := d.listener(d.meta.object)
|
||||
if err != nil {
|
||||
log.Errorf("Error calling datastore listener: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error in watch datastore: %+v, retrying in %s", err, time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error in watch datastore: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) reload() error {
|
||||
log.Debugf("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
|
||||
}
|
||||
|
||||
// Begin creates a transaction with the KV store.
|
||||
func (d *Datastore) Begin() (Transaction, Object, error) {
|
||||
id := uuid.NewV4().String()
|
||||
log.Debugf("Transaction %s begins", id)
|
||||
remoteLock, err := d.kv.NewLock(d.lockKey, &store.LockOptions{TTL: 20 * time.Second, Value: []byte(id)})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
ctx, cancel := context.WithCancel(d.ctx)
|
||||
var errLock error
|
||||
go func() {
|
||||
_, errLock = remoteLock.Lock(stopCh)
|
||||
cancel()
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if errLock != nil {
|
||||
return nil, nil, errLock
|
||||
}
|
||||
case <-d.ctx.Done():
|
||||
stopCh <- struct{}{}
|
||||
return nil, nil, d.ctx.Err()
|
||||
}
|
||||
|
||||
// we got the lock! Now make sure we are synced with KV store
|
||||
operation := func() error {
|
||||
meta := d.get()
|
||||
if meta.Lock != id {
|
||||
return fmt.Errorf("Object lock value: expected %s, got %s", id, meta.Lock)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Datastore sync error: %v, retrying in %s", err, time)
|
||||
err = d.reload()
|
||||
if err != nil {
|
||||
log.Errorf("Error reloading: %+v", err)
|
||||
}
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 60 * time.Second
|
||||
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Datastore cannot sync: %v", err)
|
||||
}
|
||||
|
||||
// we synced with KV store, we can now return Setter
|
||||
return &datastoreTransaction{
|
||||
Datastore: d,
|
||||
remoteLock: remoteLock,
|
||||
id: id,
|
||||
}, d.meta.object, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) get() *Metadata {
|
||||
d.localLock.RLock()
|
||||
defer d.localLock.RUnlock()
|
||||
return d.meta
|
||||
}
|
||||
|
||||
// Load load atomically a struct from the KV store
|
||||
func (d *Datastore) Load() (Object, error) {
|
||||
d.localLock.Lock()
|
||||
defer d.localLock.Unlock()
|
||||
err := d.kv.LoadConfig(d.meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = d.meta.unmarshall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.meta.object, nil
|
||||
}
|
||||
|
||||
// Get atomically a struct from the KV store
|
||||
func (d *Datastore) Get() Object {
|
||||
d.localLock.RLock()
|
||||
defer d.localLock.RUnlock()
|
||||
return d.meta.object
|
||||
}
|
||||
|
||||
var _ Transaction = (*datastoreTransaction)(nil)
|
||||
|
||||
type datastoreTransaction struct {
|
||||
*Datastore
|
||||
remoteLock store.Locker
|
||||
dirty bool
|
||||
id string
|
||||
}
|
||||
|
||||
// Commit allows to set an object in the KV store
|
||||
func (s *datastoreTransaction) Commit(object Object) error {
|
||||
s.localLock.Lock()
|
||||
defer s.localLock.Unlock()
|
||||
if s.dirty {
|
||||
return fmt.Errorf("Transaction already used, please begin a new one")
|
||||
}
|
||||
s.Datastore.meta.object = object
|
||||
err := s.Datastore.meta.Marshall()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Marshall error: %s", err)
|
||||
}
|
||||
err = s.kv.StoreConfig(s.Datastore.meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("StoreConfig error: %s", err)
|
||||
}
|
||||
|
||||
err = s.remoteLock.Unlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unlock error: %s", err)
|
||||
}
|
||||
|
||||
s.dirty = true
|
||||
log.Debugf("Transaction committed %s", s.id)
|
||||
return nil
|
||||
}
|
104
cluster/leadership.go
Normal file
104
cluster/leadership.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/leadership"
|
||||
)
|
||||
|
||||
// Leadership allows leadership election using a KV store
|
||||
type Leadership struct {
|
||||
*safe.Pool
|
||||
*types.Cluster
|
||||
candidate *leadership.Candidate
|
||||
leader *safe.Safe
|
||||
listeners []LeaderListener
|
||||
}
|
||||
|
||||
// NewLeadership creates a leadership
|
||||
func NewLeadership(ctx context.Context, cluster *types.Cluster) *Leadership {
|
||||
return &Leadership{
|
||||
Pool: safe.NewPool(ctx),
|
||||
Cluster: cluster,
|
||||
candidate: leadership.NewCandidate(cluster.Store, cluster.Store.Prefix+"/leader", cluster.Node, 20*time.Second),
|
||||
listeners: []LeaderListener{},
|
||||
leader: safe.New(false),
|
||||
}
|
||||
}
|
||||
|
||||
// LeaderListener is called when leadership has changed
|
||||
type LeaderListener func(elected bool) error
|
||||
|
||||
// Participate tries to be a leader
|
||||
func (l *Leadership) Participate(pool *safe.Pool) {
|
||||
pool.GoCtx(func(ctx context.Context) {
|
||||
log.Debugf("Node %s running for election", l.Cluster.Node)
|
||||
defer log.Debugf("Node %s no more running for election", l.Cluster.Node)
|
||||
backOff := backoff.NewExponentialBackOff()
|
||||
operation := func() error {
|
||||
return l.run(ctx, l.candidate)
|
||||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Leadership election error %+v, retrying in %s", err, time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backOff, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot elect leadership %+v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddListener adds a leadership listerner
|
||||
func (l *Leadership) AddListener(listener LeaderListener) {
|
||||
l.listeners = append(l.listeners, listener)
|
||||
}
|
||||
|
||||
// Resign resigns from being a leader
|
||||
func (l *Leadership) Resign() {
|
||||
l.candidate.Resign()
|
||||
log.Infof("Node %s resigned", l.Cluster.Node)
|
||||
}
|
||||
|
||||
func (l *Leadership) run(ctx context.Context, candidate *leadership.Candidate) error {
|
||||
electedCh, errCh := candidate.RunForElection()
|
||||
for {
|
||||
select {
|
||||
case elected := <-electedCh:
|
||||
l.onElection(elected)
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
l.candidate.Resign()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Leadership) onElection(elected bool) {
|
||||
if elected {
|
||||
log.Infof("Node %s elected leader ♚", l.Cluster.Node)
|
||||
l.leader.Set(true)
|
||||
l.Start()
|
||||
} else {
|
||||
log.Infof("Node %s elected slave ♝", l.Cluster.Node)
|
||||
l.leader.Set(false)
|
||||
l.Stop()
|
||||
}
|
||||
for _, listener := range l.listeners {
|
||||
err := listener(elected)
|
||||
if err != nil {
|
||||
log.Errorf("Error calling Leadership listener: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeader returns true if current node is leader
|
||||
func (l *Leadership) IsLeader() bool {
|
||||
return l.leader.Get().(bool)
|
||||
}
|
16
cluster/store.go
Normal file
16
cluster/store.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package cluster
|
||||
|
||||
// Object is the struct to store
|
||||
type Object interface{}
|
||||
|
||||
// Store is a generic interface to represents a storage
|
||||
type Store interface {
|
||||
Load() (Object, error)
|
||||
Get() Object
|
||||
Begin() (Transaction, Object, error)
|
||||
}
|
||||
|
||||
// Transaction allows to set a struct in the KV store
|
||||
type Transaction interface {
|
||||
Commit(object Object) error
|
||||
}
|
111
cmd/bug.go
Normal file
111
cmd/bug.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/mvdan/xurls"
|
||||
)
|
||||
|
||||
var (
|
||||
bugtracker = "https://github.com/containous/traefik/issues/new"
|
||||
bugTemplate = `### What version of Traefik are you using?
|
||||
` + "```" + `
|
||||
{{.Version}}
|
||||
` + "```" + `
|
||||
|
||||
### What is your environment & configuration (arguments, toml...)?
|
||||
` + "```" + `
|
||||
{{.Configuration}}
|
||||
` + "```" + `
|
||||
|
||||
### What did you do?
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
`
|
||||
)
|
||||
|
||||
// NewBugCmd builds a new Bug command
|
||||
func NewBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration interface{}) *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
return &flaeg.Command{
|
||||
Name: "bug",
|
||||
Description: `Report an issue on Traefik bugtracker`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
var version bytes.Buffer
|
||||
if err := getVersionPrint(&version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Parse(bugTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configJSON, err := json.MarshalIndent(traefikConfiguration, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := struct {
|
||||
Version string
|
||||
Configuration string
|
||||
}{
|
||||
Version: version.String(),
|
||||
Configuration: anonymize(string(configJSON)),
|
||||
}
|
||||
|
||||
var bug bytes.Buffer
|
||||
if err := tmpl.Execute(&bug, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body := bug.String()
|
||||
url := bugtracker + "?body=" + url.QueryEscape(body)
|
||||
if err := openBrowser(url); err != nil {
|
||||
fmt.Print("Please file a new issue at " + bugtracker + " using this template:\n\n")
|
||||
fmt.Print(body)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func openBrowser(url string) error {
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func anonymize(input string) string {
|
||||
replace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
mailExp := regexp.MustCompile(`\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}"`)
|
||||
return xurls.Relaxed.ReplaceAllString(mailExp.ReplaceAllString(input, replace), replace)
|
||||
}
|
63
cmd/version.go
Normal file
63
cmd/version.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/version"
|
||||
)
|
||||
|
||||
var versionTemplate = `Version: {{.Version}}
|
||||
Codename: {{.Codename}}
|
||||
Go version: {{.GoVersion}}
|
||||
Built: {{.BuildTime}}
|
||||
OS/Arch: {{.Os}}/{{.Arch}}`
|
||||
|
||||
// NewVersionCmd builds a new Version command
|
||||
func NewVersionCmd() *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
return &flaeg.Command{
|
||||
Name: "version",
|
||||
Description: `Print version`,
|
||||
Config: struct{}{},
|
||||
DefaultPointersConfig: struct{}{},
|
||||
Run: func() error {
|
||||
if err := getVersionPrint(os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getVersionPrint(wr io.Writer) error {
|
||||
tmpl, err := template.New("").Parse(versionTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := struct {
|
||||
Version string
|
||||
Codename string
|
||||
GoVersion string
|
||||
BuildTime string
|
||||
Os string
|
||||
Arch string
|
||||
}{
|
||||
Version: version.Version,
|
||||
Codename: version.Codename,
|
||||
GoVersion: runtime.Version(),
|
||||
BuildTime: version.BuildDate,
|
||||
Os: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
}
|
||||
|
||||
return tmpl.Execute(wr, v)
|
||||
}
|
198
configuration.go
198
configuration.go
@@ -1,20 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/types"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
||||
type TraefikConfiguration struct {
|
||||
GlobalConfiguration
|
||||
ConfigFile string `short:"c" description:"Configuration file to use (TOML)."`
|
||||
GlobalConfiguration `mapstructure:",squash"`
|
||||
ConfigFile string `short:"c" description:"Configuration file to use (TOML)."`
|
||||
}
|
||||
|
||||
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||
@@ -22,15 +25,18 @@ type TraefikConfiguration struct {
|
||||
type GlobalConfiguration struct {
|
||||
GraceTimeOut int64 `short:"g" description:"Duration to give active requests a chance to finish during hot-reload"`
|
||||
Debug bool `short:"d" description:"Enable debug mode"`
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released"`
|
||||
AccessLogsFile string `description:"Access logs file"`
|
||||
TraefikLogsFile string `description:"Traefik logs file"`
|
||||
LogLevel string `short:"l" description:"Log level"`
|
||||
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'"`
|
||||
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags."`
|
||||
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'"`
|
||||
Cluster *types.Cluster `description:"Enable clustering"`
|
||||
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags"`
|
||||
ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL"`
|
||||
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"`
|
||||
ProvidersThrottleDuration time.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."`
|
||||
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"`
|
||||
InsecureSkipVerify bool `description:"Disable SSL certificate verification"`
|
||||
Retry *Retry `description:"Enable retry sending request if network error"`
|
||||
Docker *provider.Docker `description:"Enable Docker backend"`
|
||||
File *provider.File `description:"Enable File backend"`
|
||||
@@ -42,6 +48,10 @@ type GlobalConfiguration struct {
|
||||
Zookeeper *provider.Zookepper `description:"Enable Zookeeper backend"`
|
||||
Boltdb *provider.BoltDb `description:"Enable Boltdb backend"`
|
||||
Kubernetes *provider.Kubernetes `description:"Enable Kubernetes backend"`
|
||||
Mesos *provider.Mesos `description:"Enable Mesos backend"`
|
||||
Eureka *provider.Eureka `description:"Enable Eureka backend"`
|
||||
ECS *provider.ECS `description:"Enable ECS backend"`
|
||||
Rancher *provider.Rancher `description:"Enable Rancher backend"`
|
||||
}
|
||||
|
||||
// DefaultEntryPoints holds default entry points
|
||||
@@ -68,7 +78,9 @@ func (dep *DefaultEntryPoints) Set(value string) error {
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (dep *DefaultEntryPoints) Get() interface{} { return DefaultEntryPoints(*dep) }
|
||||
func (dep *DefaultEntryPoints) Get() interface{} {
|
||||
return DefaultEntryPoints(*dep)
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
|
||||
@@ -93,7 +105,7 @@ 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 {
|
||||
regex := regexp.MustCompile("(?:Name:(?P<Name>\\S*))\\s*(?:Address:(?P<Address>\\S*))?\\s*(?:TLS:(?P<TLS>\\S*))?\\s*((?P<TLSACME>TLS))?\\s*(?:Redirect.EntryPoint:(?P<RedirectEntryPoint>\\S*))?\\s*(?:Redirect.Regex:(?P<RedirectRegex>\\S*))?\\s*(?:Redirect.Replacement:(?P<RedirectReplacement>\\S*))?")
|
||||
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*))?")
|
||||
match := regex.FindAllStringSubmatch(value, -1)
|
||||
if match == nil {
|
||||
return errors.New("Bad EntryPoints format: " + value)
|
||||
@@ -119,6 +131,10 @@ func (ep *EntryPoints) Set(value string) error {
|
||||
Certificates: Certificates{},
|
||||
}
|
||||
}
|
||||
if len(result["CA"]) > 0 {
|
||||
files := strings.Split(result["CA"], ",")
|
||||
tls.ClientCAFiles = files
|
||||
}
|
||||
var redirect *Redirect
|
||||
if len(result["RedirectEntryPoint"]) > 0 || len(result["RedirectRegex"]) > 0 || len(result["RedirectReplacement"]) > 0 {
|
||||
redirect = &Redirect{
|
||||
@@ -128,17 +144,25 @@ func (ep *EntryPoints) Set(value string) error {
|
||||
}
|
||||
}
|
||||
|
||||
compress := false
|
||||
if len(result["Compress"]) > 0 {
|
||||
compress = strings.EqualFold(result["Compress"], "enable") || strings.EqualFold(result["Compress"], "on")
|
||||
}
|
||||
|
||||
(*ep)[result["Name"]] = &EntryPoint{
|
||||
Address: result["Address"],
|
||||
TLS: tls,
|
||||
Redirect: redirect,
|
||||
Compress: compress,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (ep *EntryPoints) Get() interface{} { return EntryPoints(*ep) }
|
||||
func (ep *EntryPoints) Get() interface{} {
|
||||
return EntryPoints(*ep)
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (ep *EntryPoints) SetValue(val interface{}) {
|
||||
@@ -156,6 +180,8 @@ type EntryPoint struct {
|
||||
Address string
|
||||
TLS *TLS
|
||||
Redirect *Redirect
|
||||
Auth *types.Auth
|
||||
Compress bool
|
||||
}
|
||||
|
||||
// Redirect configures a redirection of an entry point to another, or to an URL
|
||||
@@ -167,33 +193,102 @@ type Redirect struct {
|
||||
|
||||
// TLS configures TLS for an entry point
|
||||
type TLS struct {
|
||||
Certificates Certificates
|
||||
MinVersion string
|
||||
CipherSuites []string
|
||||
Certificates Certificates
|
||||
ClientCAFiles []string
|
||||
}
|
||||
|
||||
// Map of allowed TLS minimum versions
|
||||
var minVersion = map[string]uint16{
|
||||
`VersionTLS10`: tls.VersionTLS10,
|
||||
`VersionTLS11`: tls.VersionTLS11,
|
||||
`VersionTLS12`: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
// Map of TLS CipherSuites from crypto/tls
|
||||
var cipherSuites = map[string]uint16{
|
||||
`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
`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_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_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_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
`TLS_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
isAPath := false
|
||||
_, errCert := os.Stat(v.CertFile)
|
||||
_, errKey := os.Stat(v.KeyFile)
|
||||
if errCert == nil {
|
||||
if errKey == nil {
|
||||
isAPath = true
|
||||
} else {
|
||||
return nil, fmt.Errorf("bad TLS Certificate KeyFile format, expected a path")
|
||||
}
|
||||
} else if errKey == nil {
|
||||
return nil, fmt.Errorf("bad TLS Certificate KeyFile format, expected a path")
|
||||
}
|
||||
|
||||
cert := tls.Certificate{}
|
||||
var err error
|
||||
if isAPath {
|
||||
cert, err = tls.LoadX509KeyPair(v.CertFile, v.KeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
cert, err = tls.X509KeyPair([]byte(v.CertFile), []byte(v.KeyFile))
|
||||
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 ""
|
||||
}
|
||||
return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile
|
||||
var result []string
|
||||
for _, certificate := range *certs {
|
||||
result = append(result, certificate.CertFile+","+certificate.KeyFile)
|
||||
}
|
||||
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 {
|
||||
files := strings.Split(value, ",")
|
||||
if len(files) != 2 {
|
||||
return errors.New("Bad certificates format: " + value)
|
||||
certificates := strings.Split(value, ";")
|
||||
for _, certificate := range certificates {
|
||||
files := strings.Split(certificate, ",")
|
||||
if len(files) != 2 {
|
||||
return errors.New("Bad certificates format: " + value)
|
||||
}
|
||||
*certs = append(*certs, Certificate{
|
||||
CertFile: files[0],
|
||||
KeyFile: files[1],
|
||||
})
|
||||
}
|
||||
*certs = append(*certs, Certificate{
|
||||
CertFile: files[0],
|
||||
KeyFile: files[1],
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -203,6 +298,7 @@ func (certs *Certificates) Type() string {
|
||||
}
|
||||
|
||||
// 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 string
|
||||
KeyFile string
|
||||
@@ -218,7 +314,9 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
//default Docker
|
||||
var defaultDocker provider.Docker
|
||||
defaultDocker.Watch = true
|
||||
defaultDocker.ExposedByDefault = true
|
||||
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
|
||||
defaultDocker.SwarmMode = false
|
||||
|
||||
// default File
|
||||
var defaultFile provider.File
|
||||
@@ -228,52 +326,88 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
// default Web
|
||||
var defaultWeb WebProvider
|
||||
defaultWeb.Address = ":8080"
|
||||
defaultWeb.Statistics = &types.Statistics{
|
||||
RecentErrors: 10,
|
||||
}
|
||||
|
||||
// default Metrics
|
||||
defaultWeb.Metrics = &types.Metrics{
|
||||
Prometheus: &types.Prometheus{
|
||||
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
|
||||
},
|
||||
}
|
||||
|
||||
// default Marathon
|
||||
var defaultMarathon provider.Marathon
|
||||
defaultMarathon.Watch = true
|
||||
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
|
||||
defaultMarathon.ExposedByDefault = true
|
||||
defaultMarathon.Constraints = []types.Constraint{}
|
||||
defaultMarathon.Constraints = types.Constraints{}
|
||||
defaultMarathon.DialerTimeout = 60
|
||||
defaultMarathon.KeepAlive = 10
|
||||
|
||||
// default Consul
|
||||
var defaultConsul provider.Consul
|
||||
defaultConsul.Watch = true
|
||||
defaultConsul.Endpoint = "127.0.0.1:8500"
|
||||
defaultConsul.Prefix = "traefik"
|
||||
defaultConsul.Constraints = []types.Constraint{}
|
||||
defaultConsul.Constraints = types.Constraints{}
|
||||
|
||||
// default ConsulCatalog
|
||||
var defaultConsulCatalog provider.ConsulCatalog
|
||||
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
|
||||
defaultConsulCatalog.Constraints = []types.Constraint{}
|
||||
defaultConsulCatalog.Constraints = types.Constraints{}
|
||||
|
||||
// default Etcd
|
||||
var defaultEtcd provider.Etcd
|
||||
defaultEtcd.Watch = true
|
||||
defaultEtcd.Endpoint = "127.0.0.1:2379"
|
||||
defaultEtcd.Prefix = "/traefik"
|
||||
defaultEtcd.Constraints = []types.Constraint{}
|
||||
defaultEtcd.Constraints = types.Constraints{}
|
||||
|
||||
//default Zookeeper
|
||||
var defaultZookeeper provider.Zookepper
|
||||
defaultZookeeper.Watch = true
|
||||
defaultZookeeper.Endpoint = "127.0.0.1:2181"
|
||||
defaultZookeeper.Prefix = "/traefik"
|
||||
defaultZookeeper.Constraints = []types.Constraint{}
|
||||
defaultZookeeper.Constraints = types.Constraints{}
|
||||
|
||||
//default Boltdb
|
||||
var defaultBoltDb provider.BoltDb
|
||||
defaultBoltDb.Watch = true
|
||||
defaultBoltDb.Endpoint = "127.0.0.1:4001"
|
||||
defaultBoltDb.Prefix = "/traefik"
|
||||
defaultBoltDb.Constraints = []types.Constraint{}
|
||||
defaultBoltDb.Constraints = types.Constraints{}
|
||||
|
||||
//default Kubernetes
|
||||
var defaultKubernetes provider.Kubernetes
|
||||
defaultKubernetes.Watch = true
|
||||
defaultKubernetes.Endpoint = "http://127.0.0.1:8080"
|
||||
defaultKubernetes.Constraints = []types.Constraint{}
|
||||
defaultKubernetes.Endpoint = ""
|
||||
defaultKubernetes.LabelSelector = ""
|
||||
defaultKubernetes.Constraints = types.Constraints{}
|
||||
|
||||
// default Mesos
|
||||
var defaultMesos provider.Mesos
|
||||
defaultMesos.Watch = true
|
||||
defaultMesos.Endpoint = "http://127.0.0.1:5050"
|
||||
defaultMesos.ExposedByDefault = true
|
||||
defaultMesos.Constraints = types.Constraints{}
|
||||
defaultMesos.RefreshSeconds = 30
|
||||
defaultMesos.ZkDetectionTimeout = 30
|
||||
defaultMesos.StateTimeoutSecond = 30
|
||||
|
||||
//default ECS
|
||||
var defaultECS provider.ECS
|
||||
defaultECS.Watch = true
|
||||
defaultECS.ExposedByDefault = true
|
||||
defaultECS.RefreshSeconds = 15
|
||||
defaultECS.Cluster = "default"
|
||||
defaultECS.Constraints = types.Constraints{}
|
||||
|
||||
//default Rancher
|
||||
var defaultRancher provider.Rancher
|
||||
defaultRancher.Watch = true
|
||||
defaultRancher.ExposedByDefault = true
|
||||
|
||||
defaultConfiguration := GlobalConfiguration{
|
||||
Docker: &defaultDocker,
|
||||
@@ -286,8 +420,15 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
Zookeeper: &defaultZookeeper,
|
||||
Boltdb: &defaultBoltDb,
|
||||
Kubernetes: &defaultKubernetes,
|
||||
Mesos: &defaultMesos,
|
||||
ECS: &defaultECS,
|
||||
Rancher: &defaultRancher,
|
||||
Retry: &Retry{},
|
||||
}
|
||||
|
||||
//default Rancher
|
||||
//@TODO: ADD
|
||||
|
||||
return &TraefikConfiguration{
|
||||
GlobalConfiguration: defaultConfiguration,
|
||||
}
|
||||
@@ -302,10 +443,11 @@ func NewTraefikConfiguration() *TraefikConfiguration {
|
||||
TraefikLogsFile: "",
|
||||
LogLevel: "ERROR",
|
||||
EntryPoints: map[string]*EntryPoint{},
|
||||
Constraints: []types.Constraint{},
|
||||
Constraints: types.Constraints{},
|
||||
DefaultEntryPoints: []string{},
|
||||
ProvidersThrottleDuration: time.Duration(2 * time.Second),
|
||||
MaxIdleConnsPerHost: 200,
|
||||
CheckNewVersion: true,
|
||||
},
|
||||
ConfigFile: "",
|
||||
}
|
||||
|
@@ -2,5 +2,10 @@
|
||||
Description=Traefik
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/traefik --configFile=/etc/traefik.toml
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
WatchdogSec=1s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
149
docs/basics.md
149
docs/basics.md
@@ -30,7 +30,7 @@ Entrypoints are the network entry points into Træfɪk.
|
||||
They can be defined using:
|
||||
|
||||
- a port (80, 443...)
|
||||
- SSL (Certificates. Keys...)
|
||||
- SSL (Certificates, Keys, authentication with a client certificate signed by a trusted CA...)
|
||||
- redirection to another entrypoint (redirect `HTTP` to `HTTPS`)
|
||||
|
||||
Here is an example of entrypoints definition:
|
||||
@@ -54,6 +54,23 @@ Here is an example of entrypoints definition:
|
||||
- We enable SSL on `https` by giving a certificate and a key.
|
||||
- We also redirect all the traffic from entrypoint `http` to `https`.
|
||||
|
||||
And here is another example with client certificate authentication:
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[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"
|
||||
```
|
||||
|
||||
- We enable SSL on `https` by giving a certificate and a key.
|
||||
- One or several files containing Certificate Authorities in PEM format are added.
|
||||
- It is possible to have multiple CA:s in the same file or keep them in separate files.
|
||||
|
||||
## Frontends
|
||||
|
||||
A frontend is a set of rules that forwards the incoming traffic from an entrypoint to a backend.
|
||||
@@ -68,8 +85,10 @@ Frontends can be defined using the following rules:
|
||||
- `PathStrip`: Same as `Path` but strip the given prefix from the request URL's Path.
|
||||
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefixes. This matches if the given template is a prefix of the full URL path.
|
||||
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
|
||||
- `AddPrefix` : Add prefix from the request URL's Path.
|
||||
|
||||
You can use multiple rules by separating them by `;`
|
||||
You can use multlple values for a rule by separating them with `,`.
|
||||
You can use multiple rules by separating them by `;`.
|
||||
|
||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||
|
||||
@@ -87,7 +106,7 @@ Here is an example of frontends definition:
|
||||
priority = 10
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:localhost,{subdomain:[a-z]+}.localhost"
|
||||
rule = "HostRegexp:localhost,{subdomain:[a-z]+}.localhost"
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
@@ -110,7 +129,7 @@ In TOML file, you can use multiple routes:
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Host:test3.localhost"
|
||||
[frontends.frontend3.routes.test_2]
|
||||
rule = "Host:Path:/test"
|
||||
rule = "Path:/test"
|
||||
```
|
||||
|
||||
Here `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched.
|
||||
@@ -163,16 +182,16 @@ Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
|
||||
## Backends
|
||||
|
||||
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
|
||||
Various methods of load-balancing is supported:
|
||||
Various methods of load-balancing are supported:
|
||||
|
||||
- `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.
|
||||
|
||||
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
|
||||
Initial state is Standby. CB observes the statistics and does not modify the request.
|
||||
In case if condition matches, CB enters Tripped state, where it responds with predefines code or redirects to another frontend.
|
||||
In case the condition matches, CB enters Tripped state, where it responds with predefined code or redirects to another frontend.
|
||||
Once Tripped timer expires, CB enters Recovering state and resets all stats.
|
||||
In case if the condition does not match and recovery timer expires, CB enters Standby state.
|
||||
In case the condition does not match and recovery timer expires, CB enters Standby state.
|
||||
|
||||
It can be configured using:
|
||||
|
||||
@@ -205,6 +224,37 @@ For example:
|
||||
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
|
||||
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
|
||||
|
||||
Sticky sessions are supported with both load balancers. When sticky sessions are enabled, a cookie called `_TRAEFIK_BACKEND` is set on the initial
|
||||
request. 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.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.loadbalancer]
|
||||
sticky = true
|
||||
```
|
||||
|
||||
A health check can be configured in order to remove a backend from LB rotation
|
||||
as long as it keeps returning HTTP status codes other than 200 OK to HTTP GET
|
||||
requests periodically carried out by Traefik. The check is defined by a path
|
||||
appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how
|
||||
often the health check should be executed (the default being 30 seconds). Each
|
||||
backend must respond to the health check within 5 seconds.
|
||||
|
||||
A recovering backend returning 200 OK responses again is being returned to the
|
||||
LB rotation pool.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.healthcheck]
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
```
|
||||
|
||||
## Servers
|
||||
|
||||
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
|
||||
@@ -238,9 +288,30 @@ Here is an example of backends and servers definition:
|
||||
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
|
||||
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
|
||||
|
||||
# Launch
|
||||
# Configuration
|
||||
|
||||
Træfɪk's configuration has two parts:
|
||||
|
||||
- The [static Træfɪk configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
|
||||
- The [dynamic Træfɪk configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
|
||||
|
||||
|
||||
## Static Træfɪk configuration
|
||||
|
||||
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
|
||||
|
||||
Træfɪk can be configured using many configuration sources with the following precedence order.
|
||||
Each item takes precedence over the item below it:
|
||||
|
||||
- [Key-value Store](/basics/#key-value-stores)
|
||||
- [Arguments](/basics/#arguments)
|
||||
- [Configuration file](/basics/#configuration-file)
|
||||
- Default
|
||||
|
||||
It means that arguments override configuration file, and Key-value Store overrides arguments.
|
||||
|
||||
### Configuration file
|
||||
|
||||
Træfɪk can be configured using a TOML file configuration, arguments, or both.
|
||||
By default, Træfɪk will try to find a `traefik.toml` in the following places:
|
||||
|
||||
- `/etc/traefik/`
|
||||
@@ -253,15 +324,63 @@ You can override this by setting a `configFile` argument:
|
||||
$ traefik --configFile=foo/bar/myconfigfile.toml
|
||||
```
|
||||
|
||||
Træfɪk uses the following precedence order. Each item takes precedence over the item below it:
|
||||
Please refer to the [global configuration](/toml/#global-configuration) section to get documentation on it.
|
||||
|
||||
- arguments
|
||||
- configuration file
|
||||
- default
|
||||
### Arguments
|
||||
|
||||
It means that arguments overrides configuration file.
|
||||
Each argument is described in the help section:
|
||||
Each argument (and command) is described in the help section:
|
||||
|
||||
```bash
|
||||
$ traefik --help
|
||||
```
|
||||
|
||||
Note that all default values will be displayed as well.
|
||||
|
||||
### Key-value stores
|
||||
|
||||
Træfɪk supports several Key-value stores:
|
||||
|
||||
- [Consul](https://consul.io)
|
||||
- [etcd](https://coreos.com/etcd/)
|
||||
- [ZooKeeper](https://zookeeper.apache.org/)
|
||||
- [boltdb](https://github.com/boltdb/bolt)
|
||||
|
||||
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
|
||||
|
||||
## Dynamic Træfɪk configuration
|
||||
|
||||
The dynamic configuration concerns :
|
||||
|
||||
- [Frontends](/basics/#frontends)
|
||||
- [Backends](/basics/#backends)
|
||||
- [Servers](/basics/#servers)
|
||||
|
||||
Træfɪk can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends).
|
||||
|
||||
We only need to enable `watch` option to make Træfɪk watch configuration backend changes and generate its configuration automatically.
|
||||
Routes to services will be created and updated instantly at any changes.
|
||||
|
||||
Please refer to the [configuration backends](/toml/#configuration-backends) section to get documentation on it.
|
||||
|
||||
# Commands
|
||||
|
||||
Usage: `traefik [command] [--flag=flag_argument]`
|
||||
|
||||
List of Træfɪk available commands with description :
|
||||
|
||||
- `version` : Print version
|
||||
- `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfɪk configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
|
||||
|
||||
Each command may have related flags.
|
||||
All those related flags will be displayed with :
|
||||
|
||||
```bash
|
||||
$ traefik [command] --help
|
||||
```
|
||||
|
||||
Note that each command is described at the beginning of the help section:
|
||||
|
||||
```bash
|
||||
$ traefik --help
|
||||
```
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
@@ -11,7 +11,7 @@
|
||||
|
||||
|
||||
Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically.
|
||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), [Amazon ECS](https://aws.amazon.com/ecs/), Rest API, file...) to manage its configuration automatically and dynamically.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -34,14 +34,21 @@ Træfɪk can listen to your service registry/orchestrator API, and knows each ti
|
||||
Routes to your services will be created instantly.
|
||||
|
||||
Run it and forget it!
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
Here is a talk (in french) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Lets'Encrypt.
|
||||
## Quickstart
|
||||
|
||||
[](https://www.youtube.com/watch?v=QvAz9mVx5TI)
|
||||
You can have a quick look at Træfɪk in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
|
||||
|
||||
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Kubernetes.
|
||||
|
||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||
|
||||
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
||||
You will learn fundamental Træfɪk features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
|
||||
|
||||
[](http://www.youtube.com/watch?v=QvAz9mVx5TI)
|
||||
|
||||
## Get it
|
||||
|
||||
@@ -63,39 +70,63 @@ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.to
|
||||
|
||||
## Test it
|
||||
|
||||
You can test Træfɪk easily using [Docker compose](https://docs.docker.com/compose), with this `docker-compose.yml` file:
|
||||
You can test Træfɪk easily using [Docker compose](https://docs.docker.com/compose), with this `docker-compose.yml` file in a folder named `traefik`:
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik
|
||||
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /dev/null:/traefik.toml
|
||||
version: '2'
|
||||
|
||||
whoami1:
|
||||
image: emilevauge/whoami
|
||||
labels:
|
||||
- "traefik.backend=whoami"
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
services:
|
||||
proxy:
|
||||
image: traefik
|
||||
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
|
||||
networks:
|
||||
- webgateway
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /dev/null:/traefik.toml
|
||||
|
||||
whoami2:
|
||||
image: emilevauge/whoami
|
||||
labels:
|
||||
- "traefik.backend=whoami"
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
networks:
|
||||
webgateway:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
Then, start it:
|
||||
|
||||
Start it from within the `traefik` folder:
|
||||
|
||||
docker-compose up -d
|
||||
|
||||
In a browser you may open `http://localhost:8080` to access Træfɪk's dashboard and observe the following magic.
|
||||
|
||||
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
whoami:
|
||||
image: emilevauge/whoami
|
||||
networks:
|
||||
- web
|
||||
labels:
|
||||
- "traefik.backend=whoami"
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
|
||||
networks:
|
||||
web:
|
||||
external:
|
||||
name: traefik_webgateway
|
||||
```
|
||||
|
||||
Then, start and scale it in the `test` folder:
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
docker-compose scale whoami=2
|
||||
```
|
||||
|
||||
Finally, test load-balancing between the two servers `whoami1` and `whoami2`:
|
||||
Finally, test load-balancing between the two services `test_whoami_1` and `test_whoami_2`:
|
||||
|
||||
```bash
|
||||
$ curl -H Host:whoami.docker.localhost http://127.0.0.1
|
||||
|
708
docs/toml.md
708
docs/toml.md
@@ -9,6 +9,28 @@
|
||||
# Global configuration
|
||||
################################################################
|
||||
|
||||
# Timeout in seconds.
|
||||
# Duration to give active requests a chance to finish during hot-reloads
|
||||
#
|
||||
# Optional
|
||||
# Default: 10
|
||||
#
|
||||
# graceTimeOut = 10
|
||||
|
||||
# Enable debug mode
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# debug = true
|
||||
|
||||
# Periodically check if a new version has been released
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
# checkNewVersion = false
|
||||
|
||||
# Traefik logs file
|
||||
# If not defined, logs to stdout
|
||||
#
|
||||
@@ -26,26 +48,37 @@
|
||||
#
|
||||
# Optional
|
||||
# Default: "ERROR"
|
||||
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
|
||||
# Messages at and above the selected level will be logged.
|
||||
#
|
||||
# logLevel = "ERROR"
|
||||
|
||||
# Backends throttle duration: minimum duration between 2 events from providers
|
||||
# Backends throttle duration: minimum duration in seconds between 2 events from providers
|
||||
# before applying a new configuration. It avoids unnecessary reloads if multiples events
|
||||
# are sent in a short amount of time.
|
||||
#
|
||||
# Optional
|
||||
# Default: "2s"
|
||||
# Default: "2"
|
||||
#
|
||||
# ProvidersThrottleDuration = "5s"
|
||||
# ProvidersThrottleDuration = "5"
|
||||
|
||||
# If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used.
|
||||
# If you encounter 'too many open files' errors, you can either change this value, or change `ulimit` value.
|
||||
# Controls the maximum idle (keep-alive) connections to keep per-host. If zero, DefaultMaxIdleConnsPerHost
|
||||
# from the Go standard library net/http module is used.
|
||||
# If you encounter 'too many open files' errors, you can either increase this
|
||||
# value or change the `ulimit`.
|
||||
#
|
||||
# Optional
|
||||
# Default: http.DefaultMaxIdleConnsPerHost
|
||||
# Default: 200
|
||||
#
|
||||
# MaxIdleConnsPerHost = 200
|
||||
|
||||
# If set to true invalid SSL certificates are accepted for backends.
|
||||
# Note: This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# InsecureSkipVerify = true
|
||||
|
||||
# Entrypoints to be used by frontends that do not specify any entrypoint.
|
||||
# Each frontend can specify its own entrypoints.
|
||||
#
|
||||
@@ -55,6 +88,53 @@
|
||||
# defaultEntryPoints = ["http", "https"]
|
||||
```
|
||||
|
||||
### Constraints
|
||||
|
||||
In a micro-service architecture, with a central service discovery, setting constraints limits Træfɪk scope to a smaller number of routes.
|
||||
|
||||
Træfɪk filters services according to service attributes/tags set in your configuration backends.
|
||||
|
||||
Supported backends:
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
- BoltDB
|
||||
- Zookeeper
|
||||
- Etcd
|
||||
- Consul Catalog
|
||||
|
||||
Supported filters:
|
||||
|
||||
- ```tag```
|
||||
|
||||
```
|
||||
# Constraints definition
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# Simple matching constraint
|
||||
# constraints = ["tag==api"]
|
||||
#
|
||||
# Simple mismatching constraint
|
||||
# constraints = ["tag!=api"]
|
||||
#
|
||||
# Globbing
|
||||
# constraints = ["tag==us-*"]
|
||||
#
|
||||
# Backend-specific constraint
|
||||
# [consulCatalog]
|
||||
# endpoint = 127.0.0.1:8500
|
||||
# constraints = ["tag==api"]
|
||||
#
|
||||
# Multiple constraints
|
||||
# - "tag==" must match with at least one tag
|
||||
# - "tag!=" must match with none of tags
|
||||
# constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||
# [consulCatalog]
|
||||
# endpoint = 127.0.0.1:8500
|
||||
# constraints = ["tag==api", "tag!=v*-beta"]
|
||||
```
|
||||
|
||||
## Entrypoints definition
|
||||
|
||||
```toml
|
||||
@@ -89,6 +169,64 @@
|
||||
# [entryPoints.http.redirect]
|
||||
# regex = "^http://localhost/(.*)"
|
||||
# replacement = "http://mydomain/$1"
|
||||
#
|
||||
# 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.
|
||||
# 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
|
||||
#
|
||||
# [entryPoints]
|
||||
# [entryPoints.https]
|
||||
# address = ":443"
|
||||
# [entryPoints.https.tls]
|
||||
# ClientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
# [[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"
|
||||
#
|
||||
# To enable basic auth on an entrypoint
|
||||
# with 2 user/pass: test:test and test2:test2
|
||||
# Passwords can be encoded in MD5, SHA1 and BCrypt: you can use htpasswd to generate those ones
|
||||
# [entryPoints]
|
||||
# [entryPoints.http]
|
||||
# address = ":80"
|
||||
# [entryPoints.http.auth.basic]
|
||||
# users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
#
|
||||
# To enable digest auth on an entrypoint
|
||||
# with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
|
||||
# You can use htdigest to generate those ones
|
||||
# [entryPoints]
|
||||
# [entryPoints.http]
|
||||
# address = ":80"
|
||||
# [entryPoints.http.auth.basic]
|
||||
# users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||
#
|
||||
# To specify an https entrypoint with a minimum TLS version, and specifying an array of cipher suites (from crypto/tls):
|
||||
# [entryPoints]
|
||||
# [entryPoints.https]
|
||||
# address = ":443"
|
||||
# [entryPoints.https.tls]
|
||||
# MinVersion = "VersionTLS12"
|
||||
# CipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"]
|
||||
# [[entryPoints.https.tls.certificates]]
|
||||
# CertFile = "integration/fixtures/https/snitest.com.cert"
|
||||
# KeyFile = "integration/fixtures/https/snitest.com.key"
|
||||
# [[entryPoints.https.tls.certificates]]
|
||||
# CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
# KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
|
||||
# To enable compression support using gzip format:
|
||||
# [entryPoints]
|
||||
# [entryPoints.http]
|
||||
# address = ":80"
|
||||
# compress = true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
@@ -133,34 +271,78 @@
|
||||
#
|
||||
email = "test@traefik.io"
|
||||
|
||||
# File used for certificates storage.
|
||||
# File or key used for certificates storage.
|
||||
# WARNING, if you use Traefik in Docker, you have 2 options:
|
||||
# - create a file on your host and mount it has a volume
|
||||
# - create a file on your host and mount it as a volume
|
||||
# storageFile = "acme.json"
|
||||
# $ docker run -v "/my/host/acme.json:acme.json" traefik
|
||||
# - mount the folder containing the file has a volume
|
||||
# - mount the folder containing the file as a volume
|
||||
# storageFile = "/etc/traefik/acme/acme.json"
|
||||
# $ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
#
|
||||
# Required
|
||||
#
|
||||
storageFile = "acme.json"
|
||||
storage = "acme.json" # or "traefik/acme/account" if using KV store
|
||||
|
||||
# Entrypoint to proxy acme challenge to.
|
||||
# Entrypoint to proxy acme challenge/apply certificates to.
|
||||
# WARNING, must point to an entrypoint on port 443
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "https"
|
||||
|
||||
# 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:
|
||||
# - cloudflare: CLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY
|
||||
# - digitalocean: DO_AUTH_TOKEN
|
||||
# - dnsimple: DNSIMPLE_EMAIL, DNSIMPLE_API_KEY
|
||||
# - dnsmadeeasy: DNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET
|
||||
# - exoscale: EXOSCALE_API_KEY, EXOSCALE_API_SECRET
|
||||
# - gandi: GANDI_API_KEY
|
||||
# - linode: LINODE_API_KEY
|
||||
# - manual: none, but run traefik interactively & turn on acmeLogging to see instructions & press Enter
|
||||
# - namecheap: NAMECHEAP_API_USER, NAMECHEAP_API_KEY
|
||||
# - rfc2136: RFC2136_TSIG_KEY, RFC2136_TSIG_SECRET, RFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER
|
||||
# - route53: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, or configured user/instance IAM profile
|
||||
# - dyn: DYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD
|
||||
# - vultr: VULTR_API_KEY
|
||||
# - ovh: OVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY
|
||||
# - pdns: PDNS_API_KEY, PDNS_API_URL
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# dnsProvider = "digitalocean"
|
||||
|
||||
# By default, the dnsProvider will verify the TXT DNS challenge record before letting ACME verify
|
||||
# If delayDontCheckDNS is greater than zero, avoid this & instead just wait so many seconds.
|
||||
# Useful if internal networks block external DNS queries
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# delayDontCheckDNS = 0
|
||||
|
||||
# If true, display debug log messages from the acme client library
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# acmeLogging = true
|
||||
|
||||
# Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate.
|
||||
# WARNING, TLS handshakes will be slow when requesting a hostname certificate for the first time, this can leads to DoS attacks.
|
||||
# WARNING, Take note that Let's Encrypt have rate limiting: https://community.letsencrypt.org/t/quick-start-guide/1631
|
||||
# WARNING, Take note that Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# onDemand = true
|
||||
|
||||
# Enable certificate generation on frontends Host rules. This will request a certificate from Let's Encrypt for each frontend with a Host rule.
|
||||
# For example, a rule Host:test1.traefik.io,test2.traefik.io will request a certificate with main domain test1.traefik.io and SAN test2.traefik.io.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# OnHostRule = true
|
||||
|
||||
# CA server to use
|
||||
# Uncomment the line to run on the staging let's encrypt server
|
||||
# Leave comment to go to prod
|
||||
@@ -172,7 +354,7 @@ entryPoint = "https"
|
||||
# Domains list
|
||||
# You can provide SANs (alternative domains) to each main domain
|
||||
# All domains must have A/AAAA records pointing to Traefik
|
||||
# WARNING, Take note that Let's Encrypt have rate limiting: https://community.letsencrypt.org/t/quick-start-guide/1631
|
||||
# WARNING, Take note that Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
|
||||
# Each domain & SANs will lead to a certificate request.
|
||||
#
|
||||
# [[acme.domains]]
|
||||
@@ -200,7 +382,7 @@ entryPoint = "https"
|
||||
|
||||
Like any other reverse proxy, Træfɪk can be configured with a file. You have two choices:
|
||||
|
||||
- simply add your configuration at the end of the global configuration file `traefik.toml` :
|
||||
- simply add your configuration at the end of the global configuration file `traefik.toml`:
|
||||
|
||||
```toml
|
||||
# traefik.toml
|
||||
@@ -341,7 +523,7 @@ watch = true
|
||||
|
||||
## API backend
|
||||
|
||||
Træfik can be configured using a restful api.
|
||||
Træfik can be configured using a RESTful api.
|
||||
To enable it:
|
||||
|
||||
```toml
|
||||
@@ -359,6 +541,26 @@ address = ":8080"
|
||||
#
|
||||
# Optional
|
||||
# ReadOnly = false
|
||||
#
|
||||
# To enable more detailed statistics
|
||||
# [web.statistics]
|
||||
# RecentErrors = 10
|
||||
#
|
||||
# To enable Traefik to export internal metrics to Prometheus
|
||||
# [web.metrics.prometheus]
|
||||
# Buckets=[0.1,0.3,1.2,5]
|
||||
#
|
||||
# To enable basic auth on the webui
|
||||
# with 2 user/pass: test:test and test2:test2
|
||||
# Passwords can be encoded in MD5, SHA1 and BCrypt: you can use htpasswd to generate those ones
|
||||
# [web.auth.basic]
|
||||
# users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
# To enable digest auth on the webui
|
||||
# with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
|
||||
# You can use htdigest to generate those ones
|
||||
# [web.auth.digest]
|
||||
# users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||
|
||||
```
|
||||
|
||||
- `/`: provides a simple HTML frontend of Træfik
|
||||
@@ -366,6 +568,26 @@ address = ":8080"
|
||||

|
||||

|
||||
|
||||
- `/ping`: `GET` simple endpoint to check for Træfik process liveness.
|
||||
|
||||
```sh
|
||||
$ curl -sv "http://localhost:8080/ping"
|
||||
* 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
|
||||
```
|
||||
|
||||
- `/health`: `GET` json metrics
|
||||
|
||||
```sh
|
||||
@@ -402,7 +624,26 @@ $ curl -s "http://localhost:8080/health" | jq .
|
||||
// average response time (formated time)
|
||||
"average_response_time": "864.8016ms",
|
||||
// average response time in seconds
|
||||
"average_response_time_sec": 0.8648016000000001
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -480,6 +721,11 @@ $ curl -s "http://localhost:8080/api" | jq .
|
||||
- `/api/providers/{provider}/frontends/{frontend}/routes`: `GET` routes in a frontend
|
||||
- `/api/providers/{provider}/frontends/{frontend}/routes/{route}`: `GET` a route in a frontend
|
||||
|
||||
- `/metrics`: You can enable Traefik to export internal metrics to different monitoring systems (Only Prometheus is supported at the moment).
|
||||
|
||||
```bash
|
||||
$ traefik --web.metrics.prometheus --web.metrics.prometheus.buckets="0.1,0.3,1.2,5"
|
||||
```
|
||||
|
||||
## Docker backend
|
||||
|
||||
@@ -521,6 +767,29 @@ watch = true
|
||||
#
|
||||
# filename = "docker.tmpl"
|
||||
|
||||
# Expose containers by default in traefik
|
||||
# If set to false, containers that don't have `traefik.enable=true` will be ignored
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedbydefault = true
|
||||
|
||||
# Use the IP address from the binded port instead of the inner network one. For specific use-case :)
|
||||
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
usebindportip = true
|
||||
# Use Swarm Mode services as data provider
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
swarmmode = false
|
||||
|
||||
|
||||
# Enable docker TLS connection
|
||||
#
|
||||
# [docker.tls]
|
||||
@@ -533,6 +802,12 @@ watch = true
|
||||
Labels can be used on containers to override default behaviour:
|
||||
|
||||
- `traefik.backend=foo`: assign the container to `foo` backend
|
||||
- `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.sticky=true`: enable backend sticky sessions
|
||||
- `traefik.backend.loadbalancer.swarm=true `: use Swarm's inbuilt load balancer (only relevant under Swarm Mode).
|
||||
- `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5`: 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
|
||||
@@ -541,8 +816,9 @@ Labels can be used on containers to override default behaviour:
|
||||
- `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.docker.network`: Set the docker network to use for connections to this container
|
||||
- `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.
|
||||
|
||||
NB: when running inside a container, Træfɪk will need network access through `docker network connect <network> <traefik-container>`
|
||||
|
||||
## Marathon backend
|
||||
|
||||
@@ -589,7 +865,7 @@ domain = "marathon.localhost"
|
||||
# Expose Marathon apps by default in traefik
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
# Default: true
|
||||
#
|
||||
# exposedByDefault = true
|
||||
|
||||
@@ -602,6 +878,13 @@ domain = "marathon.localhost"
|
||||
#
|
||||
# groupsAsSubDomains = true
|
||||
|
||||
# Enable compatibility with marathon-lb labels
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# marathonLBCompatibility = true
|
||||
|
||||
# Enable Marathon basic authentication
|
||||
#
|
||||
# Optional
|
||||
@@ -615,6 +898,9 @@ domain = "marathon.localhost"
|
||||
# Optional
|
||||
#
|
||||
# [marathon.TLS]
|
||||
# CA = "/etc/ssl/ca.crt"
|
||||
# Cert = "/etc/ssl/marathon.cert"
|
||||
# Key = "/etc/ssl/marathon.key"
|
||||
# InsecureSkipVerify = true
|
||||
|
||||
# DCOSToken for DCOS environment, This will override the Authorization header
|
||||
@@ -622,11 +908,31 @@ domain = "marathon.localhost"
|
||||
# Optional
|
||||
#
|
||||
# dcosToken = "xxxxxx"
|
||||
|
||||
# Override DialerTimeout
|
||||
# Amount of time in seconds to allow the Marathon provider to wait to open a TCP
|
||||
# connection to a Marathon master
|
||||
#
|
||||
# Optional
|
||||
# Default: 60
|
||||
# dialerTimeout = 5
|
||||
|
||||
# Set the TCP Keep Alive interval (in seconds) for the Marathon HTTP Client
|
||||
#
|
||||
# Optional
|
||||
# Default: 10
|
||||
#
|
||||
# keepAlive = 10
|
||||
```
|
||||
|
||||
Labels can be used on containers to override default behaviour:
|
||||
|
||||
- `traefik.backend=foo`: assign the application to `foo` backend
|
||||
- `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.sticky=true`: enable backend sticky sessions
|
||||
- `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5`: create a [circuit breaker](/basics/#backends) to be used against the backend
|
||||
- `traefik.portIndex=1`: register port by index in the application's ports array. Useful when the application exposes multiple ports.
|
||||
- `traefik.port=80`: register the explicit application port value. Cannot be used alongside `traefik.portIndex`.
|
||||
- `traefik.protocol=https`: override the default `http` protocol
|
||||
@@ -638,6 +944,92 @@ 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`.
|
||||
|
||||
|
||||
## Mesos generic backend
|
||||
|
||||
Træfɪk can be configured to use Mesos as a backend configuration:
|
||||
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos configuration backend
|
||||
################################################################
|
||||
|
||||
# Enable Mesos configuration backend
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
[mesos]
|
||||
|
||||
# Mesos server endpoint.
|
||||
# You can also specify multiple endpoint for Mesos:
|
||||
# endpoint = "192.168.35.40:5050,192.168.35.41:5050,192.168.35.42:5050"
|
||||
# endpoint = "zk://192.168.35.20:2181,192.168.35.21:2181,192.168.35.22:2181/mesos"
|
||||
#
|
||||
# Required
|
||||
#
|
||||
endpoint = "http://127.0.0.1:8080"
|
||||
|
||||
# Enable watch Mesos changes
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
domain = "mesos.localhost"
|
||||
|
||||
# Override default configuration template. For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "mesos.tmpl"
|
||||
|
||||
# Expose Mesos apps by default in traefik
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# ExposedByDefault = true
|
||||
|
||||
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [mesos.TLS]
|
||||
# InsecureSkipVerify = true
|
||||
|
||||
# Zookeeper timeout (in seconds)
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# ZkDetectionTimeout = 30
|
||||
|
||||
# Polling interval (in seconds)
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# RefreshSeconds = 30
|
||||
|
||||
# IP sources (e.g. host, docker, mesos, rkt)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# IPSources = "host"
|
||||
|
||||
# HTTP Timeout (in seconds)
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# StateTimeoutSecond = "30"
|
||||
```
|
||||
|
||||
## Kubernetes Ingress backend
|
||||
|
||||
|
||||
@@ -665,13 +1057,26 @@ Træfɪk can be configured to use Kubernetes Ingress as a backend configuration:
|
||||
#
|
||||
# endpoint = "http://localhost:8080"
|
||||
# namespaces = ["default","production"]
|
||||
#
|
||||
# See: http://kubernetes.io/docs/user-guide/labels/#list-and-watch-filtering
|
||||
# labelselector = "A and not B"
|
||||
#
|
||||
```
|
||||
|
||||
Annotations can be used on containers to override default behaviour for the whole Ingress resource:
|
||||
|
||||
- `traefik.frontend.rule.type: PathPrefixStrip`: override the default frontend rule type (Default: `PathPrefix`).
|
||||
|
||||
You can find here an example [ingress](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.ingress.yaml) and [replication controller](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.rc.yaml).
|
||||
Annotations can be used on the Kubernetes service to override default behaviour:
|
||||
|
||||
- `traefik.backend.loadbalancer.method=drr`: override the default `wrr` load balancer algorithm
|
||||
- `traefik.backend.loadbalancer.sticky=true`: enable backend sticky sessions
|
||||
|
||||
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](https://docs.traefik.io/basics/#backends) for a backend.
|
||||
|
||||
- `traefik.backend.circuitbreaker: <expression>`: set the circuit breaker expression for the backend (Default: nil).
|
||||
|
||||
## Consul backend
|
||||
|
||||
@@ -723,7 +1128,7 @@ prefix = "traefik"
|
||||
# insecureskipverify = true
|
||||
```
|
||||
|
||||
Please refer to the [Key Value storage structure](#key-value-storage-structure) section to get documentation en traefik KV structure.
|
||||
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
|
||||
|
||||
@@ -769,6 +1174,8 @@ Additional settings can be defined using Consul Catalog tags:
|
||||
- `traefik.backend.weight=10`: assign this weight to the container
|
||||
- `traefik.backend.circuitbreaker=NetworkErrorRatio() > 0.5`
|
||||
- `traefik.backend.loadbalancer=drr`: override the default load balancing mode
|
||||
- `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:{containerName}.{domain}`).
|
||||
- `traefik.frontend.passHostHeader=true`: forward client `Host` header to the backend.
|
||||
- `traefik.frontend.priority=10`: override default frontend priority
|
||||
@@ -824,7 +1231,7 @@ prefix = "/traefik"
|
||||
# insecureskipverify = true
|
||||
```
|
||||
|
||||
Please refer to the [Key Value storage structure](#key-value-storage-structure) section to get documentation en traefik KV structure.
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure.
|
||||
|
||||
|
||||
## Zookeeper backend
|
||||
@@ -858,7 +1265,7 @@ watch = true
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
prefix = "/traefik"
|
||||
prefix = "traefik"
|
||||
|
||||
# Override default configuration template. For advanced users :)
|
||||
#
|
||||
@@ -867,7 +1274,7 @@ prefix = "/traefik"
|
||||
# filename = "zookeeper.tmpl"
|
||||
```
|
||||
|
||||
Please refer to the [Key Value storage structure](#key-value-storage-structure) section to get documentation en traefik KV structure.
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure.
|
||||
|
||||
## BoltDB backend
|
||||
|
||||
@@ -909,85 +1316,214 @@ prefix = "/traefik"
|
||||
# filename = "boltdb.tmpl"
|
||||
```
|
||||
|
||||
Please refer to the [Key Value storage structure](#key-value-storage-structure) section to get documentation en traefik KV structure.
|
||||
## Eureka backend
|
||||
|
||||
## Key-value storage structure
|
||||
Træfɪk can be configured to use Eureka as a backend configuration:
|
||||
|
||||
The Keys-Values structure should look (using `prefix = "/traefik"`):
|
||||
|
||||
- backend 1
|
||||
```toml
|
||||
################################################################
|
||||
# Eureka configuration backend
|
||||
################################################################
|
||||
|
||||
| Key | Value |
|
||||
|--------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/backends/backend1/circuitbreaker/expression` | `NetworkErrorRatio() > 0.5` |
|
||||
| `/traefik/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik/backends/backend1/servers/server1/weight` | `10` |
|
||||
| `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
|
||||
| `/traefik/backends/backend1/servers/server2/weight` | `1` |
|
||||
# Enable Eureka configuration backend
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
[eureka]
|
||||
|
||||
- backend 2
|
||||
# Eureka server endpoint.
|
||||
# endpoint := "http://my.eureka.server/eureka"
|
||||
#
|
||||
# Required
|
||||
#
|
||||
endpoint = "http://my.eureka.server/eureka"
|
||||
|
||||
| Key | Value |
|
||||
|-----------------------------------------------------|------------------------|
|
||||
| `/traefik/backends/backend2/maxconn/amount` | `10` |
|
||||
| `/traefik/backends/backend2/maxconn/extractorfunc` | `request.host` |
|
||||
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
|
||||
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
|
||||
| `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` |
|
||||
| `/traefik/backends/backend2/servers/server2/weight` | `2` |
|
||||
# Override default configuration time between refresh
|
||||
#
|
||||
# Optional
|
||||
# default 30s
|
||||
delay = "1m"
|
||||
|
||||
- frontend 1
|
||||
# Override default configuration template. For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "eureka.tmpl"
|
||||
```
|
||||
|
||||
| Key | Value |
|
||||
|---------------------------------------------------|-----------------------|
|
||||
| `/traefik/frontends/frontend1/backend` | `backend2` |
|
||||
| `/traefik/frontends/frontend1/routes/test_1/rule` | `Host:test.localhost` |
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure.
|
||||
|
||||
- frontend 2
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|--------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passHostHeader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
## ECS backend
|
||||
|
||||
## Atomic configuration changes
|
||||
Træfɪk can be configured to use Amazon ECS as a backend configuration:
|
||||
|
||||
The [Etcd](https://github.com/coreos/etcd/issues/860) and [Consul](https://github.com/hashicorp/consul/issues/886) backends do not support updating multiple keys atomically. As a result, it may be possible for Træfɪk to read an intermediate configuration state despite judicious use of the `--providersThrottleDuration` flag. To solve this problem, Træfɪk supports a special key called `/traefik/alias`. If set, Træfɪk use the value as an alternative key prefix.
|
||||
|
||||
Given the key structure below, Træfɪk will use the `http://172.17.0.2:80` as its only backend (frontend keys have been omitted for brevity).
|
||||
```toml
|
||||
################################################################
|
||||
# ECS configuration backend
|
||||
################################################################
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/1` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
||||
# Enable ECS configuration backend
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
[ecs]
|
||||
|
||||
When an atomic configuration change is required, you may write a new configuration at an alternative prefix. Here, although the `/traefik_configurations/2/...` keys have been set, the old configuration is still active because the `/traefik/alias` key still points to `/traefik_configurations/1`:
|
||||
# ECS Cluster Name
|
||||
#
|
||||
# Optional
|
||||
# Default: "default"
|
||||
#
|
||||
Cluster = "default"
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/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.2:80` |
|
||||
| `/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` |
|
||||
# Enable watch ECS changes
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
Watch = true
|
||||
|
||||
Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` configuration becomes active atomically. Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://172.17.0.4:80` hosts while no traffic is sent to the `172.17.0.2:80` host:
|
||||
# Polling interval (in seconds)
|
||||
#
|
||||
# Optional
|
||||
# Default: 15
|
||||
#
|
||||
RefreshSeconds = 15
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/2` |
|
||||
| `/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/server2/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
# Expose ECS services by default in traefik
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
ExposedByDefault = false
|
||||
|
||||
Note that Træfɪk *will not watch for key changes in the `/traefik_configurations` prefix*. It will only watch for changes in the `/traefik` prefix. Further, if the `/traefik/alias` key is set, all other sibling keys with the `/traefik` prefix are ignored.
|
||||
# Region to use when connecting to AWS
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# Region = "us-east-1"
|
||||
|
||||
# AccessKeyID to use when connecting to AWS
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# AccessKeyID = "abc"
|
||||
|
||||
# SecretAccessKey to use when connecting to AWS
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# SecretAccessKey = "123"
|
||||
|
||||
```
|
||||
|
||||
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æfɪk
|
||||
- `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`.
|
||||
|
||||
If `AccessKeyID`/`SecretAccessKey` is not given credentials will be resolved in the following order:
|
||||
|
||||
- From environment variables; `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`.
|
||||
- Shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to `default` and `~/.aws/credentials`.
|
||||
- EC2 instance role or ECS task role
|
||||
|
||||
Træfɪk needs the following policy to read ECS information:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "Traefik ECS read access",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ecs:ListTasks",
|
||||
"ecs:DescribeTasks",
|
||||
"ecs:DescribeContainerInstances",
|
||||
"ecs:DescribeTaskDefinition",
|
||||
"ec2:DescribeInstances"
|
||||
],
|
||||
"Resource": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Rancher backend
|
||||
|
||||
Træfɪk can be configured to use Rancher as a backend configuration:
|
||||
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher configuration backend
|
||||
################################################################
|
||||
|
||||
# Enable Rancher configuration backend
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
[rancher]
|
||||
|
||||
# Default domain used.
|
||||
# Can be overridden by setting the "traefik.domain" label on an service.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
domain = "rancher.localhost"
|
||||
|
||||
# Enable watch Rancher changes
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
Watch = true
|
||||
|
||||
# Expose Rancher services by default in traefik
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
ExposedByDefault = false
|
||||
|
||||
# Endpoint to use when connecting to Rancher
|
||||
#
|
||||
# Optional
|
||||
# Endpoint = "http://rancherserver.example.com"
|
||||
|
||||
# AccessKey to use when connecting to Rancher
|
||||
#
|
||||
# Optional
|
||||
# AccessKey = "XXXXXXXXX"
|
||||
|
||||
# SecretKey to use when connecting to Rancher
|
||||
#
|
||||
# Optional
|
||||
# SecretKey = "XXXXXXXXXXX"
|
||||
|
||||
```
|
||||
|
||||
If you're deploying traefik as a service within rancher, you can alternatively set these labels on the service to let it only fetch data of its current environment. The settings `endpoint`, `accesskey` and `secretkey` can be omitted then.
|
||||
|
||||
- `io.rancher.container.create_agent=true`
|
||||
- `io.rancher.container.agent.role=environment`
|
||||
|
||||
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æfɪk
|
||||
- `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`.
|
||||
|
19
docs/user-guide/cluster.md
Normal file
19
docs/user-guide/cluster.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Clustering / High Availability
|
||||
|
||||
This guide explains how tu use Træfɪk in high availability mode.
|
||||
In order to deploy and configure multiple Træfɪk instances, without copying the same configuration file on each instance, we will use a distributed Key-Value store.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need a working KV store cluster.
|
||||
|
||||
## File configuration to KV store migration
|
||||
|
||||
We created a special Træfɪk command to help configuring your Key Value store from a Træfɪk TOML configuration file.
|
||||
Please refer to [this section](/user-guide/kv-config/#store-configuration-in-key-value-store) to get more details.
|
||||
|
||||
## Deploy a Træfɪk cluster
|
||||
|
||||
Once your Træfɪk configuration is uploaded on your KV store, you can start each Træfɪk instance.
|
||||
A Træfɪk cluster is based on a master/slave model. When starting, Træfɪk will elect a master. If this instance fails, another master will be automatically elected.
|
||||
|
@@ -29,6 +29,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
||||
```
|
||||
Note that we can either give path to certificate file or directly the file content itself ([like in this TOML example](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store)).
|
||||
|
||||
## HTTP redirect on HTTPS
|
||||
|
||||
@@ -96,3 +97,37 @@ entryPoint = "https"
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
```
|
||||
|
||||
## Enable Basic authentication in an entrypoint
|
||||
|
||||
With two user/pass:
|
||||
|
||||
- `test`:`test`
|
||||
- `test2`:`test2`
|
||||
|
||||
Passwords are encoded in MD5: you can use htpasswd to generate those ones.
|
||||
|
||||
```
|
||||
defaultEntryPoints = ["http"]
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth.basic]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
```
|
||||
|
||||
## Pass Authenticated user to application via headers
|
||||
|
||||
Providing an authentication method as described above, it is possible to pass the user to the application
|
||||
via a configurable header value
|
||||
|
||||
```
|
||||
defaultEntryPoints = ["http"]
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
[entryPoints.http.auth.basic]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
```
|
430
docs/user-guide/kubernetes.md
Normal file
430
docs/user-guide/kubernetes.md
Normal file
@@ -0,0 +1,430 @@
|
||||
# Kubernetes Ingress Controller
|
||||
|
||||
This guide explains how to use Træfɪk as an Ingress controller in a Kubernetes cluster.
|
||||
If you are not familiar with Ingresses in Kubernetes you might want to read the [Kubernetes user guide](http://kubernetes.io/docs/user-guide/ingress/)
|
||||
|
||||
The config files used in this guide can be found in the [examples directory](https://github.com/containous/traefik/tree/master/examples/k8s)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. A working Kubernetes cluster. If you want to follow along with this guide, you should setup [minikube](http://kubernetes.io/docs/getting-started-guides/minikube/)
|
||||
on your machine, as it is the quickest way to get a local Kubernetes cluster setup for experimentation and development.
|
||||
|
||||
2. The `kubectl` binary should be [installed on your workstation](http://kubernetes.io/docs/getting-started-guides/minikube/#download-kubectl).
|
||||
|
||||
## Deploy Træfɪk
|
||||
|
||||
We are going to deploy Træfɪk with a
|
||||
[Deployment](http://kubernetes.io/docs/user-guide/deployments/), as this will
|
||||
allow you to easily roll out config changes or update the image.
|
||||
|
||||
```yaml
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
name: traefik-ingress-lb
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- image: traefik
|
||||
name: traefik-ingress-lb
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 30Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 20Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
- containerPort: 8080
|
||||
args:
|
||||
- --web
|
||||
- --kubernetes
|
||||
```
|
||||
[examples/k8s/traefik.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/traefik.yaml)
|
||||
|
||||
> notice that we binding port 80 on the Træfɪk container to port 80 on the host.
|
||||
> With a multi node cluster we might expose Træfɪk with a NodePort or LoadBalancer service
|
||||
> and run more than 1 replica of Træfɪk for high availability.
|
||||
|
||||
To deploy Træfɪk to your cluster start by submitting the deployment to the cluster with `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/traefik.yaml
|
||||
```
|
||||
|
||||
### Check the deployment
|
||||
|
||||
Now lets check if our deployment was successful.
|
||||
|
||||
Start by listing the pods in the `kube-system` namespace:
|
||||
|
||||
```sh
|
||||
$kubectl --namespace=kube-system get pods
|
||||
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
kube-addon-manager-minikubevm 1/1 Running 0 4h
|
||||
kubernetes-dashboard-s8krj 1/1 Running 0 4h
|
||||
traefik-ingress-controller-678226159-eqseo 1/1 Running 0 7m
|
||||
```
|
||||
|
||||
You should see that after submitting the Deployment to Kubernetes it has launched
|
||||
a pod, and it is now running. _It might take a few moments for kubernetes to pull
|
||||
the Træfɪk image and start the container._
|
||||
|
||||
> You could also check the deployment with the Kubernetes dashboard, run
|
||||
> `minikube dashboard` to open it in your browser, then choose the `kube-system`
|
||||
> namespace from the menu at the top right of the screen.
|
||||
|
||||
You should now be able to access Træfɪk on port 80 of your minikube instance.
|
||||
|
||||
```sh
|
||||
curl $(minikube ip)
|
||||
404 page not found
|
||||
```
|
||||
|
||||
> We expect to see a 404 response here as we haven't yet given Træfɪk any configuration.
|
||||
|
||||
## Submitting An Ingress to the cluster.
|
||||
|
||||
Lets start by creating a Service and an Ingress that will expose the
|
||||
[Træfɪk Web UI](https://github.com/containous/traefik#web-ui).
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: traefik-web-ui
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
k8s-app: traefik-ingress-lb
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: traefik-web-ui
|
||||
namespace: kube-system
|
||||
spec:
|
||||
rules:
|
||||
- host: traefik-ui.local
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: traefik-web-ui
|
||||
servicePort: 80
|
||||
```
|
||||
[examples/k8s/ui.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/ui.yaml)
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/ui.yaml
|
||||
```
|
||||
|
||||
Now lets setup an entry in our /etc/hosts file to route `traefik-ui.local`
|
||||
to our cluster.
|
||||
|
||||
> In production you would want to set up real dns entries.
|
||||
|
||||
> You can get the ip address of your minikube instance by running `minikube ip`
|
||||
|
||||
```
|
||||
echo "$(minikube ip) traefik-ui.local" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
We should now be able to visit [traefik-ui.local](http://traefik-ui.local) in the browser and view the Træfɪk Web UI.
|
||||
|
||||
## 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.
|
||||
|
||||
First lets start by launching the 3 pods for the cheese websites.
|
||||
|
||||
```yaml
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: stilton
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: stilton
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: stilton
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: stilton
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:stilton
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: cheddar
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: cheddar
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:cheddar
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: wensleydale
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: wensleydale
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: wensleydale
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: wensleydale
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:wensleydale
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
[examples/k8s/cheese-deployments.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-deployments.yaml)
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/cheese-deployments.yaml
|
||||
```
|
||||
|
||||
Next we need to setup a service for each of the cheese pods.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: stilton
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: stilton
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cheddar
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: wensleydale
|
||||
annotations:
|
||||
traefik.backend.circuitbreaker: "NetworkErrorRatio() > 0.5"
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: wensleydale
|
||||
```
|
||||
|
||||
> Notice that we also set a [circuit breaker expression](https://docs.traefik.io/basics/#backends) for one of the backends
|
||||
> by setting the `traefik.backend.circuitbreaker` annotation on the service.
|
||||
|
||||
|
||||
[examples/k8s/cheese-services.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-services.yaml)
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/cheese-services.yaml
|
||||
```
|
||||
|
||||
Now we can submit an ingress for the cheese websites.
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cheese
|
||||
spec:
|
||||
rules:
|
||||
- host: stilton.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: stilton
|
||||
servicePort: http
|
||||
- host: cheddar.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: cheddar
|
||||
servicePort: http
|
||||
- host: wensleydale.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: wensleydale
|
||||
servicePort: http
|
||||
```
|
||||
[examples/k8s/cheese-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-ingress.yaml)
|
||||
|
||||
> Notice that we list each hostname, and add a backend service.
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/cheese-ingress.yaml
|
||||
```
|
||||
|
||||
Now visit the [Træfɪk dashboard](http://traefik-ui.local/) and you should
|
||||
see a frontend for each host. Along with a backend listing for each service
|
||||
with a Server set up for each pod.
|
||||
|
||||
If you edit your `/etc/hosts` again you should be able to access the cheese
|
||||
websites in your browser.
|
||||
|
||||
```sh
|
||||
echo "$(minikube ip) stilton.local cheddar.local wensleydale.local" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
* [Stilton](http://stilton.local/)
|
||||
* [Cheddar](http://cheddar.local/)
|
||||
* [Wensleydale](http://wensleydale.local/)
|
||||
|
||||
## Path based routing
|
||||
|
||||
Now lets suppose that our fictional client has decided that while they are
|
||||
super happy about our cheesy web design, when they asked for 3 websites
|
||||
they had not really bargained on having to buy 3 domain names.
|
||||
|
||||
No problem, we say, why don't we reconfigure the sites to host all 3 under one domain.
|
||||
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cheeses
|
||||
annotations:
|
||||
traefik.frontend.rule.type: pathprefixstrip
|
||||
spec:
|
||||
rules:
|
||||
- host: cheeses.local
|
||||
http:
|
||||
paths:
|
||||
- path: /stilton
|
||||
backend:
|
||||
serviceName: stilton
|
||||
servicePort: http
|
||||
- path: /cheddar
|
||||
backend:
|
||||
serviceName: cheddar
|
||||
servicePort: http
|
||||
- path: /wensleydale
|
||||
backend:
|
||||
serviceName: wensleydale
|
||||
servicePort: http
|
||||
```
|
||||
[examples/k8s/cheeses-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheeses-ingress.yaml)
|
||||
|
||||
> Notice that we are configuring Træfɪk to strip the prefix from the url path
|
||||
> with the `traefik.frontend.rule.type` annotation so that we can use
|
||||
> the containers from the previous example without modification.
|
||||
|
||||
```sh
|
||||
kubectl apply -f examples/k8s/cheeses-ingress.yaml
|
||||
```
|
||||
|
||||
```sh
|
||||
echo "$(minikube ip) cheeses.local" | sudo tee -a /etc/hosts
|
||||
```
|
||||
|
||||
You should now be able to visit the websites in your browser.
|
||||
|
||||
* [cheeses.local/stilton](http://cheeses.local/stilton/)
|
||||
* [cheeses.local/cheddar](http://cheeses.local/cheddar/)
|
||||
* [cheeses.local/wensleydale](http://cheeses.local/wensleydale/)
|
328
docs/user-guide/kv-config.md
Normal file
328
docs/user-guide/kv-config.md
Normal file
@@ -0,0 +1,328 @@
|
||||
|
||||
# Key-value store configuration
|
||||
|
||||
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store.
|
||||
|
||||
This section explains how to launch Træfɪk using a configuration loaded from a Key-value store.
|
||||
|
||||
Træfɪk supports several Key-value stores:
|
||||
|
||||
- [Consul](https://consul.io)
|
||||
- [etcd](https://coreos.com/etcd/)
|
||||
- [ZooKeeper](https://zookeeper.apache.org/)
|
||||
- [boltdb](https://github.com/boltdb/bolt)
|
||||
|
||||
# Static configuration in Key-value store
|
||||
|
||||
We will see the steps to set it up with an easy example.
|
||||
Note that we could do the same with any other Key-value Store.
|
||||
|
||||
## docker-compose file for Consul
|
||||
|
||||
The Træfɪk global configuration will be getted from a [Consul](https://consul.io) store.
|
||||
|
||||
First we have to launch Consul in a container.
|
||||
The [docker-compose file](https://docs.docker.com/compose/compose-file/) allows us to launch Consul and four instances of the trivial app [emilevauge/whoamI](https://github.com/emilevauge/whoamI) :
|
||||
|
||||
```yml
|
||||
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"
|
||||
|
||||
whoami1:
|
||||
image: emilevauge/whoami
|
||||
|
||||
whoami2:
|
||||
image: emilevauge/whoami
|
||||
|
||||
whoami3:
|
||||
image: emilevauge/whoami
|
||||
|
||||
whoami4:
|
||||
image: emilevauge/whoami
|
||||
```
|
||||
|
||||
## Upload the configuration in the Key-value store
|
||||
|
||||
We should now fill the store with the Træfɪk global configuration, as we do with a [TOML file configuration](/toml).
|
||||
To do that, we can send the Key-value pairs via [curl commands](https://www.consul.io/intro/getting-started/kv.html) or via the [Web UI](https://www.consul.io/intro/getting-started/ui.html).
|
||||
|
||||
Hopefully, Træfɪk allows automation of this process using the `storeconfig` subcommand.
|
||||
Please refer to the [store Træfɪk configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
||||
|
||||
Here is the toml configuration we would like to store in the Key-value Store :
|
||||
|
||||
```toml
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[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 = """-----BEGIN CERTIFICATE-----
|
||||
<cert file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
KeyFile = """-----BEGIN CERTIFICATE-----
|
||||
<key file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
|
||||
|
||||
[consul]
|
||||
endpoint = "127.0.0.1:8500"
|
||||
watch = true
|
||||
prefix = "traefik"
|
||||
|
||||
[web]
|
||||
address = ":8081"
|
||||
```
|
||||
|
||||
And there, the same global configuration in the Key-value Store (using `prefix = "traefik"`):
|
||||
|
||||
| Key | Value |
|
||||
|-----------------------------------------------------------|---------------------------------------------------------------|
|
||||
| `/traefik/loglevel` | `DEBUG` |
|
||||
| `/traefik/defaultentrypoints/0` | `http` |
|
||||
| `/traefik/defaultentrypoints/1` | `https` |
|
||||
| `/traefik/entrypoints/http/address` | `:80` |
|
||||
| `/traefik/entrypoints/https/address` | `:443` |
|
||||
| `/traefik/entrypoints/https/tls/certificates/0/certfile` | `integration/fixtures/https/snitest.com.cert` |
|
||||
| `/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/consul/endpoint` | `127.0.0.1:8500` |
|
||||
| `/traefik/consul/watch` | `true` |
|
||||
| `/traefik/consul/prefix` | `traefik` |
|
||||
| `/traefik/web/address` | `:8081` |
|
||||
|
||||
In case you are setting key values manually,:
|
||||
- Remember to specify the indexes (`0`,`1`, `2`, ... ) under prefixes `/traefik/defaultentrypoints/` and `/traefik/entrypoints/https/tls/certificates/` in order to match the global configuration structure.
|
||||
- Be careful to give the correct IP address and port on the key `/traefik/consul/endpoint`.
|
||||
|
||||
Note that we can either give path to certificate file or directly the file content itself.
|
||||
|
||||
## Launch Træfɪk
|
||||
|
||||
We will now launch Træfɪk in a container.
|
||||
We use CLI flags to setup the connection between Træfɪk and Consul.
|
||||
All the rest of the global configuration is stored in Consul.
|
||||
|
||||
Here is the [docker-compose file](https://docs.docker.com/compose/compose-file/) :
|
||||
|
||||
```yml
|
||||
traefik:
|
||||
image: traefik
|
||||
command: --consul --consul.endpoint=127.0.0.1:8500
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
```
|
||||
|
||||
NB : Be careful to give the correct IP address and port in the flag `--consul.endpoint`.
|
||||
|
||||
## TLS support
|
||||
|
||||
So far, only [Consul](https://consul.io) and [etcd](https://coreos.com/etcd/) support TLS connections.
|
||||
To set it up, we should enable [consul security](https://www.consul.io/docs/internals/security.html) (or [etcd security](https://coreos.com/etcd/docs/latest/security.html)).
|
||||
|
||||
Then, we have to provide CA, Cert and Key to Træfɪk using `consul` flags :
|
||||
|
||||
- `--consul.tls`
|
||||
- `--consul.tls.ca=path/to/the/file`
|
||||
- `--consul.tls.cert=path/to/the/file`
|
||||
- `--consul.tls.key=path/to/the/file`
|
||||
|
||||
Or etcd flags :
|
||||
|
||||
- `--etcd.tls`
|
||||
- `--etcd.tls.ca=path/to/the/file`
|
||||
- `--etcd.tls.cert=path/to/the/file`
|
||||
- `--etcd.tls.key=path/to/the/file`
|
||||
|
||||
Note that we can either give directly directly the file content itself (instead of the path to certificate) in a TOML file configuration.
|
||||
|
||||
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æfɪk.
|
||||
|
||||
Note that this section is independent of the way Træfɪk got its static configuration.
|
||||
It means that the static configuration can either come from the same Key-value store or from any other sources.
|
||||
|
||||
## Key-value storage structure
|
||||
Here is the toml configuration we would like to store in the store :
|
||||
|
||||
```toml
|
||||
[file]
|
||||
|
||||
# rules
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.circuitbreaker]
|
||||
expression = "NetworkErrorRatio() > 0.5"
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
[backends.backend2.LoadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://172.17.0.4:80"
|
||||
weight = 1
|
||||
[backends.backend2.servers.server2]
|
||||
url = "http://172.17.0.5:80"
|
||||
weight = 2
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend2"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:test.localhost"
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
priority = 10
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
[frontends.frontend3]
|
||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||
backend = "backend2"
|
||||
rule = "Path:/test"
|
||||
```
|
||||
|
||||
And there, the same dynamic configuration in a KV Store (using `prefix = "traefik"`):
|
||||
|
||||
- backend 1
|
||||
|
||||
| Key | Value |
|
||||
|--------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/backends/backend1/circuitbreaker/expression` | `NetworkErrorRatio() > 0.5` |
|
||||
| `/traefik/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik/backends/backend1/servers/server1/weight` | `10` |
|
||||
| `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
|
||||
| `/traefik/backends/backend1/servers/server2/weight` | `1` |
|
||||
| `/traefik/backends/backend1/servers/server2/tags` | `api,helloworld` |
|
||||
|
||||
- backend 2
|
||||
|
||||
| Key | Value |
|
||||
|-----------------------------------------------------|------------------------|
|
||||
| `/traefik/backends/backend2/maxconn/amount` | `10` |
|
||||
| `/traefik/backends/backend2/maxconn/extractorfunc` | `request.host` |
|
||||
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
|
||||
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
|
||||
| `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` |
|
||||
| `/traefik/backends/backend2/servers/server2/weight` | `2` |
|
||||
| `/traefik/backends/backend2/servers/server2/tags` | `web` |
|
||||
|
||||
- frontend 1
|
||||
|
||||
| Key | Value |
|
||||
|---------------------------------------------------|-----------------------|
|
||||
| `/traefik/frontends/frontend1/backend` | `backend2` |
|
||||
| `/traefik/frontends/frontend1/routes/test_1/rule` | `Host:test.localhost` |
|
||||
|
||||
- frontend 2
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|--------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passHostHeader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
|
||||
## Atomic configuration changes
|
||||
|
||||
Træfɪk can watch the backends/frontends configuration changes and generate its configuration automatically.
|
||||
|
||||
Note that only backends/frontends rules are dynamic, the rest of the Træfɪk configuration stay static.
|
||||
|
||||
The [Etcd](https://github.com/coreos/etcd/issues/860) and [Consul](https://github.com/hashicorp/consul/issues/886) backends do not support updating multiple keys atomically. As a result, it may be possible for Træfɪk to read an intermediate configuration state despite judicious use of the `--providersThrottleDuration` flag. To solve this problem, Træfɪk supports a special key called `/traefik/alias`. If set, Træfɪk use the value as an alternative key prefix.
|
||||
|
||||
Given the key structure below, Træfɪk will use the `http://172.17.0.2:80` as its only backend (frontend keys have been omitted for brevity).
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/1` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
||||
|
||||
When an atomic configuration change is required, you may write a new configuration at an alternative prefix. Here, although the `/traefik_configurations/2/...` keys have been set, the old configuration is still active because the `/traefik/alias` key still points to `/traefik_configurations/1`:
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/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.2:80` |
|
||||
| `/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` |
|
||||
|
||||
Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` configuration becomes active atomically. Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://172.17.0.4:80` hosts while no traffic is sent to the `172.17.0.2:80` host:
|
||||
|
||||
| Key | Value |
|
||||
|-------------------------------------------------------------------------|-----------------------------|
|
||||
| `/traefik/alias` | `/traefik_configurations/2` |
|
||||
| `/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/server2/url` | `http://172.17.0.4:80` |
|
||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||
|
||||
Note that Træfɪk *will not watch for key changes in the `/traefik_configurations` prefix*. It will only watch for changes in the `/traefik/alias`.
|
||||
Further, if the `/traefik/alias` key is set, all other configuration with `/traefik/backends` or `/traefik/frontends` prefix are ignored.
|
||||
|
||||
# Store configuration in Key-value store
|
||||
|
||||
Don't forget to [setup the connection between Træfɪk and Key-value store](/user-guide/kv-config/#launch-trfk).
|
||||
The static Træfɪk configuration in a key-value store can be automatically created and updated, using the [`storeconfig` subcommand](/basics/#commands).
|
||||
|
||||
```bash
|
||||
$ 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æfɪk 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.
|
||||
|
||||
To upload your ACME certificates to the KV store, get your traefik TOML file and add the new `storage` option in the `acme` section:
|
||||
|
||||
```
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "traefik/acme/account" # the key where to store your certificates in the KV store
|
||||
storageFile = "acme.json" # your old certificates store
|
||||
```
|
||||
|
||||
Call `traefik storeconfig` to upload your config in the KV store.
|
||||
Then remove the line `storageFile = "acme.json"` from your TOML config file.
|
||||
That's it!
|
||||
|
||||
|
307
docs/user-guide/swarm-mode.md
Normal file
307
docs/user-guide/swarm-mode.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Docker Swarm (mode) cluster
|
||||
|
||||
This section explains how to create a multi-host docker cluster with
|
||||
swarm mode using [docker-machine](https://docs.docker.com/machine) and
|
||||
how to deploy Træfɪk on it.
|
||||
|
||||
The cluster consists of:
|
||||
|
||||
- 3 servers
|
||||
- 1 manager
|
||||
- 2 workers
|
||||
- 1 [overlay](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network) network
|
||||
(multi-host networking)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
|
||||
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
|
||||
|
||||
## Cluster provisioning
|
||||
|
||||
First, let's create all the required nodes. It's a shorter version of
|
||||
the [swarm tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).
|
||||
|
||||
```sh
|
||||
docker-machine create -d virtualbox manager
|
||||
docker-machine create -d virtualbox worker1
|
||||
docker-machine create -d virtualbox worker2
|
||||
```
|
||||
|
||||
Then, let's setup the cluster, in order :
|
||||
|
||||
1. initialize the cluster
|
||||
2. get the token for other host to join
|
||||
3. on both workers, join the cluster with the token
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker swarm init \
|
||||
--listen-addr $(docker-machine ip manager) \
|
||||
--advertise-addr $(docker-machine ip manager)"
|
||||
|
||||
export worker_token=$(docker-machine ssh manager "docker swarm \
|
||||
join-token worker -q")
|
||||
|
||||
docker-machine ssh worker1 "docker swarm join \
|
||||
--token=${worker_token} \
|
||||
--listen-addr $(docker-machine ip worker1) \
|
||||
--advertise-addr $(docker-machine ip worker1) \
|
||||
$(docker-machine ip manager)"
|
||||
|
||||
docker-machine ssh worker2 "docker swarm join \
|
||||
--token=${worker_token} \
|
||||
--listen-addr $(docker-machine ip worker2) \
|
||||
--advertise-addr $(docker-machine ip worker2) \
|
||||
$(docker-machine ip manager)"
|
||||
```
|
||||
|
||||
Let's validate the cluster is up and running.
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager docker node ls
|
||||
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
||||
2a770ov9vixeadep674265u1n worker1 Ready Active
|
||||
dbi3or4q8ii8elbws70g4hkdh * manager Ready Active Leader
|
||||
esbhhy6vnqv90xomjaomdgy46 worker2 Ready Active
|
||||
```
|
||||
|
||||
Finally, let's create a network for Træfik to use.
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker network create --driver=overlay traefik-net"
|
||||
```
|
||||
|
||||
## Deploy Træfik
|
||||
|
||||
Let's deploy Træfik as a docker service in our cluster. The only
|
||||
requirement for Træfik to work with swarm mode is that it needs to run
|
||||
on a manager node — we are going to use a
|
||||
[constraint](https://docs.docker.com/engine/reference/commandline/service_create/#/specify-service-constraints-constraint) for
|
||||
that.
|
||||
|
||||
```
|
||||
docker-machine ssh manager "docker service create \
|
||||
--name traefik \
|
||||
--constraint=node.role==manager \
|
||||
--publish 80:80 --publish 8080:8080 \
|
||||
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
|
||||
--network traefik-net \
|
||||
traefik \
|
||||
--docker \
|
||||
--docker.swarmmode \
|
||||
--docker.domain=traefik \
|
||||
--docker.watch \
|
||||
--web"
|
||||
```
|
||||
|
||||
Let's explain this command:
|
||||
|
||||
- `--publish 80:80 --publish 8080:8080`: we publish port `80` and
|
||||
`8080` on the cluster.
|
||||
- `--constraint=node.role==manager`: we ask docker to schedule Træfik
|
||||
on a manager node.
|
||||
- `--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock`:
|
||||
we bind mount the docker socket where Træfik is scheduled to be able
|
||||
to speak to the daemon.
|
||||
- `--network traefik-net`: we attach the Træfik service (and thus
|
||||
the underlying container) to the `traefik-net` network.
|
||||
- `--docker`: enable docker backend, and `--docker.swarmmode` to
|
||||
enable the swarm mode on Træfik.
|
||||
- `--web`: activate the webUI on port 8080
|
||||
|
||||
## Deploy your apps
|
||||
|
||||
We can now deploy our app on the cluster,
|
||||
here [whoami](https://github.com/emilevauge/whoami), a simple web
|
||||
server in Go. We start 2 services, on the `traefik-net` network.
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker service create \
|
||||
--name whoami0 \
|
||||
--label traefik.port=80 \
|
||||
--network traefik-net \
|
||||
emilevauge/whoami"
|
||||
|
||||
docker-machine ssh manager "docker service create \
|
||||
--name whoami1 \
|
||||
--label traefik.port=80 \
|
||||
--network traefik-net \
|
||||
--label traefik.backend.loadbalancer.sticky=true \
|
||||
emilevauge/whoami"
|
||||
```
|
||||
|
||||
Note that we set whoami1 to use sticky sessions (`--label traefik.backend.loadbalancer.sticky=true`). We'll demonstrate that later.
|
||||
If using `docker stack deploy`, there is [a specific way that the labels must be defined in the docker-compose file](https://github.com/containous/traefik/issues/994#issuecomment-269095109).
|
||||
|
||||
Check that everything is scheduled and started:
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker service ls"
|
||||
ID NAME REPLICAS IMAGE COMMAND
|
||||
ab046gpaqtln whoami0 1/1 emilevauge/whoami
|
||||
cgfg5ifzrpgm whoami1 1/1 emilevauge/whoami
|
||||
dtpl249tfghc traefik 1/1 traefik --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
|
||||
```
|
||||
|
||||
## Access to your apps through Træfɪk
|
||||
|
||||
```sh
|
||||
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
||||
Hostname: 8147a7746e7a
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.3
|
||||
IP: fe80::42:aff:fe00:903
|
||||
IP: 172.18.0.3
|
||||
IP: fe80::42:acff:fe12:3
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.3:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.3:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
|
||||
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
||||
Hostname: ba2c21488299
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.4
|
||||
IP: fe80::42:aff:fe00:904
|
||||
IP: 172.18.0.2
|
||||
IP: fe80::42:acff:fe12:2
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.4:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.4:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
```
|
||||
|
||||
Note that as Træfik is published, you can access it from any machine
|
||||
and not only the manager.
|
||||
|
||||
```sh
|
||||
curl -H Host:whoami0.traefik http://$(docker-machine ip worker1)
|
||||
Hostname: 8147a7746e7a
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.3
|
||||
IP: fe80::42:aff:fe00:903
|
||||
IP: 172.18.0.3
|
||||
IP: fe80::42:acff:fe12:3
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.3:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.3:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
|
||||
curl -H Host:whoami1.traefik http://$(docker-machine ip worker2)
|
||||
Hostname: ba2c21488299
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.4
|
||||
IP: fe80::42:aff:fe00:904
|
||||
IP: 172.18.0.2
|
||||
IP: fe80::42:acff:fe12:2
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.4:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.4:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
```
|
||||
|
||||
## Scale both services
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker service scale whoami0=5"
|
||||
|
||||
docker-machine ssh manager "docker service scale whoami1=5"
|
||||
```
|
||||
|
||||
|
||||
Check that we now have 5 replicas of each `whoami` service:
|
||||
|
||||
```sh
|
||||
docker-machine ssh manager "docker service ls"
|
||||
ID NAME REPLICAS IMAGE COMMAND
|
||||
ab046gpaqtln whoami0 5/5 emilevauge/whoami
|
||||
cgfg5ifzrpgm whoami1 5/5 emilevauge/whoami
|
||||
dtpl249tfghc traefik 1/1 traefik --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
|
||||
```
|
||||
## Access to your whoami0 through Træfɪk multiple times.
|
||||
|
||||
Repeat the following command multiple times and note that the Hostname changes each time as Traefik load balances each request against the 5 tasks.
|
||||
```sh
|
||||
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
||||
Hostname: 8147a7746e7a
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.3
|
||||
IP: fe80::42:aff:fe00:903
|
||||
IP: 172.18.0.3
|
||||
IP: fe80::42:acff:fe12:3
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.3:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.3:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
```
|
||||
|
||||
Do the same against whoami1.
|
||||
```sh
|
||||
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
||||
Hostname: ba2c21488299
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.9.4
|
||||
IP: fe80::42:aff:fe00:904
|
||||
IP: 172.18.0.2
|
||||
IP: fe80::42:acff:fe12:2
|
||||
GET / HTTP/1.1
|
||||
Host: 10.0.9.4:80
|
||||
User-Agent: curl/7.35.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.99.1
|
||||
X-Forwarded-Host: 10.0.9.4:80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
```
|
||||
Wait, I thought we added the sticky flag to whoami1? Traefik relies on a cookie to maintain stickyness so you'll need to test this with a browser.
|
||||
|
||||
First you need to add whoami1.traefik to your hosts file:
|
||||
```ssh
|
||||
if [ -n "$(grep whoami1.traefik /etc/hosts)" ];
|
||||
then
|
||||
echo "whoami1.traefik already exists (make sure the ip is current)";
|
||||
else
|
||||
sudo -- sh -c -e "echo '$(docker-machine ip manager)\twhoami1.traefik'
|
||||
>> /etc/hosts";
|
||||
fi
|
||||
```
|
||||
|
||||
Now open your browser and go to http://whoami1.traefik/
|
||||
|
||||
You will now see that stickyness is maintained.
|
||||
|
||||

|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Swarm cluster
|
||||
|
||||
This section explains how to create a multi-host [swarm](https://docs.docker.com/swarm) cluster using [docker-machine](https://docs.docker.com/machine/) and how to deploy Træfɪk on it.
|
||||
The cluster will be made of:
|
||||
The cluster consists of:
|
||||
|
||||
- 2 servers
|
||||
- 1 swarm master
|
||||
@@ -10,16 +10,16 @@ The cluster will be made of:
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
|
||||
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
|
||||
1. You need to install [docker-machine](https://docs.docker.com/machine/)
|
||||
2. You need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
|
||||
|
||||
## Cluster provisioning
|
||||
|
||||
We will first follow [this guide](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) to create the cluster.
|
||||
We first follow [this guide](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) to create the cluster.
|
||||
|
||||
### Create machine `mh-keystore`
|
||||
|
||||
This machine will be the service registry of our cluster.
|
||||
This machine is the service registry of our cluster.
|
||||
|
||||
```sh
|
||||
docker-machine create -d virtualbox mh-keystore
|
||||
@@ -37,7 +37,7 @@ docker run -d \
|
||||
|
||||
### Create machine `mhs-demo0`
|
||||
|
||||
This machine will have a swarm master and a swarm agent on it.
|
||||
This machine is a swarm master and a swarm agent on it.
|
||||
|
||||
```sh
|
||||
docker-machine create -d virtualbox \
|
||||
@@ -50,7 +50,7 @@ docker-machine create -d virtualbox \
|
||||
|
||||
### Create machine `mhs-demo1`
|
||||
|
||||
This machine will have a swarm agent on it.
|
||||
This machine have a swarm agent on it.
|
||||
|
||||
```sh
|
||||
docker-machine create -d virtualbox \
|
||||
@@ -84,14 +84,14 @@ docker $(docker-machine config mhs-demo0) run \
|
||||
-l DEBUG \
|
||||
-c /dev/null \
|
||||
--docker \
|
||||
--docker.domain traefik \
|
||||
--docker.endpoint tcp://$(docker-machine ip mhs-demo0):3376 \
|
||||
--docker.domain=traefik \
|
||||
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):3376 \
|
||||
--docker.tls \
|
||||
--docker.tls.ca /ssl/ca.pem \
|
||||
--docker.tls.cert /ssl/server.pem \
|
||||
--docker.tls.key /ssl/server-key.pem \
|
||||
--docker.tls.ca=/ssl/ca.pem \
|
||||
--docker.tls.cert=/ssl/server.pem \
|
||||
--docker.tls.key=/ssl/server-key.pem \
|
||||
--docker.tls.insecureSkipVerify \
|
||||
--docker.watch \
|
||||
--docker.watch \
|
||||
--web
|
||||
```
|
||||
|
||||
@@ -102,7 +102,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:3376`: 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
|
||||
|
||||
|
@@ -1,12 +1,11 @@
|
||||
kubelet:
|
||||
image: gcr.io/google_containers/hyperkube-amd64:v1.2.2
|
||||
image: gcr.io/google_containers/hyperkube-amd64:v1.5.2
|
||||
privileged: true
|
||||
pid: host
|
||||
net : host
|
||||
volumes:
|
||||
- /:/rootfs:ro
|
||||
- /sys:/sys:ro
|
||||
- /sys:/sys:rw
|
||||
- /var/lib/docker/:/var/lib/docker:rw
|
||||
- /var/lib/kubelet/:/var/lib/kubelet:rw
|
||||
- /var/lib/kubelet/:/var/lib/kubelet:rw,shared
|
||||
- /var/run:/var/run:rw
|
||||
command: ['/hyperkube', 'kubelet', '--containerized', '--hostname-override=127.0.0.1', '--address=0.0.0.0', '--api-servers=http://localhost:8080', '--config=/etc/kubernetes/manifests', '--allow-privileged=true', '--v=2']
|
||||
command: ['/hyperkube', 'kubelet', '--hostname-override=127.0.0.1', '--api-servers=http://localhost:8080', '--config=/etc/kubernetes/manifests', '--allow-privileged=true', '--v=2', '--cluster-dns=10.0.0.10', '--cluster-domain=cluster.local']
|
||||
|
@@ -1,43 +1,59 @@
|
||||
zk:
|
||||
image: bobrik/zookeeper
|
||||
net: host
|
||||
environment:
|
||||
ZK_CONFIG: tickTime=2000,initLimit=10,syncLimit=5,maxClientCnxns=128,forceSync=no,clientPort=2181
|
||||
ZK_ID: 1
|
||||
|
||||
master:
|
||||
image: mesosphere/mesos-master:0.28.1-2.0.20.ubuntu1404
|
||||
net: host
|
||||
environment:
|
||||
MESOS_ZK: zk://127.0.0.1:2181/mesos
|
||||
MESOS_HOSTNAME: 127.0.0.1
|
||||
MESOS_IP: 127.0.0.1
|
||||
MESOS_QUORUM: 1
|
||||
MESOS_CLUSTER: docker-compose
|
||||
MESOS_WORK_DIR: /var/lib/mesos
|
||||
|
||||
slave:
|
||||
image: mesosphere/mesos-slave:0.28.1-2.0.20.ubuntu1404
|
||||
net: host
|
||||
pid: host
|
||||
privileged: true
|
||||
environment:
|
||||
MESOS_MASTER: zk://127.0.0.1:2181/mesos
|
||||
MESOS_HOSTNAME: 127.0.0.1
|
||||
MESOS_IP: 127.0.0.1
|
||||
MESOS_CONTAINERIZERS: docker,mesos
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup
|
||||
- /usr/bin/docker:/usr/bin/docker:ro
|
||||
- /usr/lib/x86_64-linux-gnu/libapparmor.so.1:/usr/lib/x86_64-linux-gnu/libapparmor.so.1:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /lib/x86_64-linux-gnu/libsystemd-journal.so.0:/lib/x86_64-linux-gnu/libsystemd-journal.so.0
|
||||
|
||||
marathon:
|
||||
image: mesosphere/marathon:v1.1.1
|
||||
net: host
|
||||
environment:
|
||||
MARATHON_MASTER: zk://127.0.0.1:2181/mesos
|
||||
MARATHON_ZK: zk://127.0.0.1:2181/marathon
|
||||
MARATHON_HOSTNAME: 127.0.0.1
|
||||
command: --event_subscriber http_callback
|
||||
version: '2'
|
||||
services:
|
||||
zookeeper:
|
||||
image: netflixoss/exhibitor:1.5.2
|
||||
hostname: zookeeper
|
||||
ports:
|
||||
- "2181:2181"
|
||||
mesos-master:
|
||||
image: mesosphere/marathon:v1.2.0-RC6
|
||||
hostname: mesos-master
|
||||
entrypoint: [ "mesos-master" ]
|
||||
ports:
|
||||
- "5050:5050"
|
||||
links:
|
||||
- zookeeper
|
||||
environment:
|
||||
- MESOS_CLUSTER=local
|
||||
- MESOS_HOSTNAME=mesos-master.docker
|
||||
- MESOS_LOG_DIR=/var/log
|
||||
- MESOS_WORK_DIR=/var/lib/mesos
|
||||
- MESOS_QUORUM=1
|
||||
- MESOS_ZK=zk://zookeeper:2181/mesos
|
||||
mesos-slave:
|
||||
image: mesosphere/mesos-slave-dind:0.2.4_mesos-0.27.2_docker-1.8.2_ubuntu-14.04.4
|
||||
entrypoint:
|
||||
- mesos-slave
|
||||
privileged: true
|
||||
hostname: mesos-slave
|
||||
ports:
|
||||
- "5051:5051"
|
||||
links:
|
||||
- zookeeper
|
||||
- mesos-master
|
||||
environment:
|
||||
- MESOS_CONTAINERIZERS=docker,mesos
|
||||
- MESOS_ISOLATOR=cgroups/cpu,cgroups/mem
|
||||
- MESOS_LOG_DIR=/var/log
|
||||
- MESOS_MASTER=zk://zookeeper:2181/mesos
|
||||
- MESOS_PORT=5051
|
||||
- MESOS_WORK_DIR=/var/lib/mesos
|
||||
- MESOS_EXECUTOR_REGISTRATION_TIMEOUT=5mins
|
||||
- MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD=90secs
|
||||
- MESOS_DOCKER_STOP_TIMEOUT=60secs
|
||||
- MESOS_RESOURCES=cpus:2;mem:2048;disk:20480;ports(*):[12000-12999]
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
marathon:
|
||||
image: mesosphere/marathon:v1.2.0-RC6
|
||||
ports:
|
||||
- "8080:8080"
|
||||
links:
|
||||
- zookeeper
|
||||
- mesos-master
|
||||
extra_hosts:
|
||||
- "mesos-slave:172.17.0.1"
|
||||
environment:
|
||||
- MARATHON_ZK=zk://zookeeper:2181/marathon
|
||||
- MARATHON_MASTER=zk://zookeeper:2181/mesos
|
10
examples/compose-rancher.yml
Normal file
10
examples/compose-rancher.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
traefik:
|
||||
image: traefik
|
||||
command: --web --rancher --rancher.domain=rancher.localhost --logLevel=DEBUG
|
||||
labels:
|
||||
io.rancher.container.agent.role: environment
|
||||
io.rancher.container.create_agent: 'true'
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080"
|
@@ -1,111 +0,0 @@
|
||||
# 3 Services for the 3 endpoints of the Ingress
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service1
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30283
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: https
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service2
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30284
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service3
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30285
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
# A single RC matching all Services
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: whoami
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: emilevauge/whoami
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
# An Ingress with 2 hosts and 3 endpoints
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoami-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /bar
|
||||
backend:
|
||||
serviceName: service1
|
||||
servicePort: 80
|
||||
- host: bar.localhost
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: service2
|
||||
servicePort: 80
|
||||
- backend:
|
||||
serviceName: service3
|
||||
servicePort: 80
|
||||
|
||||
---
|
||||
# Another Ingress with PathPrefixStrip
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoami-ingress-stripped
|
||||
annotations:
|
||||
traefik.frontend.rule.type: "PathPrefixStrip"
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /prefixWillBeStripped
|
||||
backend:
|
||||
serviceName: service1
|
||||
servicePort: 80
|
@@ -1,10 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
kubectl create -f - << EOF
|
||||
kind: Namespace
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: kube-system
|
||||
labels:
|
||||
name: kube-system
|
||||
EOF
|
||||
name: kube-system
|
99
examples/k8s/cheese-deployments.yaml
Normal file
99
examples/k8s/cheese-deployments.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: stilton
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: stilton
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: stilton
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: stilton
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:stilton
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: cheddar
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: cheddar
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:cheddar
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: wensleydale
|
||||
labels:
|
||||
app: cheese
|
||||
cheese: wensleydale
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cheese
|
||||
task: wensleydale
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cheese
|
||||
task: wensleydale
|
||||
version: v0.0.1
|
||||
spec:
|
||||
containers:
|
||||
- name: cheese
|
||||
image: errm/cheese:wensleydale
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 50Mi
|
||||
ports:
|
||||
- containerPort: 80
|
27
examples/k8s/cheese-ingress.yaml
Normal file
27
examples/k8s/cheese-ingress.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cheese
|
||||
spec:
|
||||
rules:
|
||||
- host: stilton.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: stilton
|
||||
servicePort: http
|
||||
- host: cheddar.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: cheddar
|
||||
servicePort: http
|
||||
- host: wensleydale.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: wensleydale
|
||||
servicePort: http
|
39
examples/k8s/cheese-services.yaml
Normal file
39
examples/k8s/cheese-services.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: stilton
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: stilton
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cheddar
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: cheddar
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: wensleydale
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: cheese
|
||||
task: wensleydale
|
23
examples/k8s/cheeses-ingress.yaml
Normal file
23
examples/k8s/cheeses-ingress.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cheeses
|
||||
annotations:
|
||||
traefik.frontend.rule.type: pathprefixstrip
|
||||
spec:
|
||||
rules:
|
||||
- host: cheeses.local
|
||||
http:
|
||||
paths:
|
||||
- path: /stilton
|
||||
backend:
|
||||
serviceName: stilton
|
||||
servicePort: http
|
||||
- path: /cheddar
|
||||
backend:
|
||||
serviceName: cheddar
|
||||
servicePort: http
|
||||
- path: /wensleydale
|
||||
backend:
|
||||
serviceName: wensleydale
|
||||
servicePort: http
|
@@ -1,13 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
k8s-app: traefik-ingress-lb
|
||||
matchLabels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@@ -15,17 +18,25 @@ spec:
|
||||
name: traefik-ingress-lb
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- image: traefik
|
||||
name: traefik-ingress-lb
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 30Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 20Mi
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- name: http
|
||||
containerPort: 80
|
||||
hostPort: 80
|
||||
- containerPort: 443
|
||||
hostPort: 443
|
||||
- containerPort: 8080
|
||||
- name: admin
|
||||
containerPort: 8081
|
||||
args:
|
||||
- -d
|
||||
- --web
|
||||
- --web.address=:8081
|
||||
- --kubernetes
|
||||
- --logLevel=DEBUG
|
28
examples/k8s/ui.yaml
Normal file
28
examples/k8s/ui.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: traefik-web-ui
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
k8s-app: traefik-ingress-lb
|
||||
ports:
|
||||
- name: web
|
||||
port: 80
|
||||
targetPort: 8081
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: traefik-web-ui
|
||||
namespace: kube-system
|
||||
spec:
|
||||
rules:
|
||||
- host: traefik-ui.local
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: traefik-web-ui
|
||||
servicePort: web
|
@@ -6,7 +6,4 @@ Copyright
|
||||
//go:generate mkdir -p static
|
||||
//go:generate go-bindata -pkg autogen -o autogen/gen.go ./static/... ./templates/...
|
||||
|
||||
//go:generate mkdir -p vendor/github.com/docker/docker/autogen/dockerversion
|
||||
//go:generate cp script/dockerversion vendor/github.com/docker/docker/autogen/dockerversion/dockerversion.go
|
||||
|
||||
package main
|
||||
|
705
glide.lock
generated
705
glide.lock
generated
@@ -1,141 +1,237 @@
|
||||
hash: 234c57cf3696be155463b9a92cd8d104bb52c16c821b64dd24d8d88097d80dd8
|
||||
updated: 2016-07-18T17:58:15.732937572+02:00
|
||||
hash: b689cb0faed68086641d9e3504ee29498e5bf06b088ad4fcd1e76543446d4d9a
|
||||
updated: 2017-03-27T14:29:54.009570184+02:00
|
||||
imports:
|
||||
- name: bitbucket.org/ww/goautoneg
|
||||
version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675
|
||||
- name: cloud.google.com/go
|
||||
version: c116c7972ec94f148459a304d07a67ecbc770d4b
|
||||
subpackages:
|
||||
- compute/metadata
|
||||
- name: github.com/abbot/go-http-auth
|
||||
version: cb4372376e1e00e9f6ab9ec142e029302c9e7140
|
||||
- name: github.com/ArthurHlt/go-eureka-client
|
||||
version: ba361cd0f9f571b4e871421423d2f02f5689c3d2
|
||||
subpackages:
|
||||
- eureka
|
||||
- name: github.com/ArthurHlt/gominlog
|
||||
version: 068c01ce147ad68fca25ef3fa29ae5395ae273ab
|
||||
- name: github.com/aws/aws-sdk-go
|
||||
version: 3f8f870ec9939e32b3372abf74d24e468bcd285d
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/awserr
|
||||
- aws/awsutil
|
||||
- aws/client
|
||||
- aws/client/metadata
|
||||
- aws/corehandlers
|
||||
- aws/credentials
|
||||
- aws/credentials/ec2rolecreds
|
||||
- aws/credentials/endpointcreds
|
||||
- aws/credentials/stscreds
|
||||
- aws/defaults
|
||||
- aws/ec2metadata
|
||||
- aws/endpoints
|
||||
- aws/request
|
||||
- aws/session
|
||||
- aws/signer/v4
|
||||
- private/protocol
|
||||
- private/protocol/ec2query
|
||||
- private/protocol/json/jsonutil
|
||||
- private/protocol/jsonrpc
|
||||
- private/protocol/query
|
||||
- private/protocol/query/queryutil
|
||||
- private/protocol/rest
|
||||
- private/protocol/restxml
|
||||
- private/protocol/xml/xmlutil
|
||||
- private/waiter
|
||||
- service/ec2
|
||||
- service/ecs
|
||||
- service/route53
|
||||
- service/sts
|
||||
- name: github.com/Azure/azure-sdk-for-go
|
||||
version: 1620af6b32398bfc91827ceae54a8cc1f55df04d
|
||||
subpackages:
|
||||
- arm/dns
|
||||
- name: github.com/Azure/go-autorest
|
||||
version: 32cc2321122a649b7ba4e323527bcb145134fd47
|
||||
subpackages:
|
||||
- autorest
|
||||
- autorest/azure
|
||||
- autorest/date
|
||||
- autorest/to
|
||||
- autorest/validation
|
||||
- name: github.com/beorn7/perks
|
||||
version: b965b613227fddccbfffe13eae360ed3fa822f8d
|
||||
subpackages:
|
||||
- quantile
|
||||
- name: github.com/blang/semver
|
||||
version: 3a37c301dda64cbe17f16f661b4c976803c0e2d2
|
||||
- name: github.com/boltdb/bolt
|
||||
version: acc803f0ced151102ed51bf824f8709ebd6602bc
|
||||
version: 5cc10bbbc5c141029940133bb33c9e969512a698
|
||||
- name: github.com/BurntSushi/toml
|
||||
version: 99064174e013895bbd9b025c31100bd1d9b590ca
|
||||
- name: github.com/BurntSushi/ty
|
||||
version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
|
||||
subpackages:
|
||||
- fun
|
||||
- name: github.com/cenkalti/backoff
|
||||
version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
|
||||
- name: github.com/cenk/backoff
|
||||
version: 8edc80b07f38c27352fb186d971c628a6c32552b
|
||||
- name: github.com/codahale/hdrhistogram
|
||||
version: f8ad88b59a584afeee9d334eff879b104439117b
|
||||
version: 9208b142303c12d8899bae836fd524ac9338b4fd
|
||||
- name: github.com/codegangsta/cli
|
||||
version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e
|
||||
version: bf4a526f48af7badd25d2cb02d587e1b01be3b50
|
||||
- name: github.com/codegangsta/negroni
|
||||
version: dc6b9d037e8dab60cbfc09c61d6932537829be8b
|
||||
- name: github.com/containous/flaeg
|
||||
version: b98687da5c323650f4513fda6b6203fcbdec9313
|
||||
version: a731c034dda967333efce5f8d276aeff11f8ff87
|
||||
- name: github.com/containous/mux
|
||||
version: a819b77bba13f0c0cbe36e437bc2e948411b3996
|
||||
- name: github.com/containous/staert
|
||||
version: e2aa88e235a02dd52aa1d5d9de75f9d9139d1602
|
||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||
- name: github.com/coreos/etcd
|
||||
version: 1c9e0a0e33051fed6c05c141e6fcbfe5c7f2a899
|
||||
version: c400d05d0aa73e21e431c16145e558d624098018
|
||||
subpackages:
|
||||
- Godeps/_workspace/src/github.com/ugorji/go/codec
|
||||
- Godeps/_workspace/src/golang.org/x/net/context
|
||||
- client
|
||||
- pkg/pathutil
|
||||
- pkg/types
|
||||
- name: github.com/coreos/go-oidc
|
||||
version: 9e117111587506b9dc83b7b38263268bf48352ea
|
||||
subpackages:
|
||||
- http
|
||||
- jose
|
||||
- key
|
||||
- oauth2
|
||||
- oidc
|
||||
- name: github.com/coreos/go-systemd
|
||||
version: 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
||||
subpackages:
|
||||
- daemon
|
||||
- name: github.com/coreos/pkg
|
||||
version: 2c77715c4df99b5420ffcae14ead08f52104065d
|
||||
subpackages:
|
||||
- capnslog
|
||||
- health
|
||||
- httputil
|
||||
- timeutil
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/docker/distribution
|
||||
version: f8083b7ff32b224921c5f66c0f4df3e58dab49f5
|
||||
- name: github.com/daviddengcn/go-colortext
|
||||
version: 3b18c8575a432453d41fdafb340099fff5bba2f7
|
||||
- name: github.com/decker502/dnspod-go
|
||||
version: f6b1d56f1c048bd94d7e42ac36efb4d57b069b6f
|
||||
- name: github.com/dgrijalva/jwt-go
|
||||
version: 9ed569b5d1ac936e6494082958d63a6aa4fff99a
|
||||
- name: github.com/dnsimple/dnsimple-go
|
||||
version: eeb343928d9a3de357a650c8c25d8f1318330d57
|
||||
subpackages:
|
||||
- dnsimple
|
||||
- name: github.com/docker/distribution
|
||||
version: 325b0804fef3a66309d962357aac3c2ce3f4d329
|
||||
subpackages:
|
||||
- reference
|
||||
- digest
|
||||
- registry/api/errcode
|
||||
- registry/client/auth
|
||||
- registry/client/transport
|
||||
- registry/client
|
||||
- context
|
||||
- digest
|
||||
- reference
|
||||
- registry/api/errcode
|
||||
- registry/api/v2
|
||||
- registry/client
|
||||
- registry/client/auth
|
||||
- registry/client/auth/challenge
|
||||
- registry/client/transport
|
||||
- registry/storage/cache
|
||||
- registry/storage/cache/memory
|
||||
- uuid
|
||||
- name: github.com/docker/docker
|
||||
version: 9837ec4da53f15f9120d53a6e1517491ba8b0261
|
||||
version: 49bf474f9ed7ce7143a59d1964ff7b7fd9b52178
|
||||
subpackages:
|
||||
- namesgenerator
|
||||
- pkg/namesgenerator
|
||||
- pkg/random
|
||||
- api/types
|
||||
- api/types/backend
|
||||
- api/types/blkiodev
|
||||
- api/types/container
|
||||
- api/types/filters
|
||||
- api/types/mount
|
||||
- api/types/network
|
||||
- api/types/registry
|
||||
- api/types/strslice
|
||||
- api/types/swarm
|
||||
- api/types/versions
|
||||
- builder
|
||||
- builder/dockerignore
|
||||
- cliconfig
|
||||
- cliconfig/configfile
|
||||
- daemon/graphdriver
|
||||
- image
|
||||
- image/v1
|
||||
- layer
|
||||
- namesgenerator
|
||||
- oci
|
||||
- opts
|
||||
- pkg/archive
|
||||
- pkg/chrootarchive
|
||||
- pkg/fileutils
|
||||
- pkg/gitutils
|
||||
- pkg/homedir
|
||||
- pkg/httputils
|
||||
- pkg/idtools
|
||||
- pkg/ioutils
|
||||
- pkg/jsonlog
|
||||
- pkg/jsonmessage
|
||||
- pkg/longpath
|
||||
- pkg/mount
|
||||
- pkg/namesgenerator
|
||||
- pkg/plugingetter
|
||||
- pkg/plugins
|
||||
- pkg/plugins/transport
|
||||
- pkg/pools
|
||||
- pkg/progress
|
||||
- pkg/promise
|
||||
- pkg/random
|
||||
- pkg/reexec
|
||||
- pkg/signal
|
||||
- pkg/stdcopy
|
||||
- pkg/streamformatter
|
||||
- pkg/stringid
|
||||
- pkg/symlink
|
||||
- pkg/system
|
||||
- pkg/tarsum
|
||||
- pkg/term
|
||||
- pkg/term/windows
|
||||
- pkg/urlutil
|
||||
- plugin/v2
|
||||
- reference
|
||||
- registry
|
||||
- runconfig/opts
|
||||
- pkg/homedir
|
||||
- pkg/jsonlog
|
||||
- pkg/system
|
||||
- pkg/term/windows
|
||||
- image
|
||||
- image/v1
|
||||
- pkg/ioutils
|
||||
- opts
|
||||
- pkg/httputils
|
||||
- pkg/mflag
|
||||
- pkg/stringid
|
||||
- pkg/tarsum
|
||||
- pkg/mount
|
||||
- pkg/signal
|
||||
- pkg/urlutil
|
||||
- builder
|
||||
- builder/dockerignore
|
||||
- pkg/archive
|
||||
- pkg/fileutils
|
||||
- pkg/progress
|
||||
- pkg/streamformatter
|
||||
- layer
|
||||
- pkg/longpath
|
||||
- api/types/backend
|
||||
- pkg/chrootarchive
|
||||
- pkg/gitutils
|
||||
- pkg/symlink
|
||||
- pkg/idtools
|
||||
- pkg/pools
|
||||
- daemon/graphdriver
|
||||
- pkg/reexec
|
||||
- pkg/plugins
|
||||
- pkg/plugins/transport
|
||||
- name: github.com/docker/engine-api
|
||||
version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb
|
||||
version: 3d1601b9d2436a70b0dfc045a23f6503d19195df
|
||||
subpackages:
|
||||
- client
|
||||
- types
|
||||
- types/events
|
||||
- types/filters
|
||||
- types/container
|
||||
- types/network
|
||||
- client/transport
|
||||
- client/transport/cancellable
|
||||
- types
|
||||
- types/blkiodev
|
||||
- types/container
|
||||
- types/events
|
||||
- types/filters
|
||||
- types/network
|
||||
- types/reference
|
||||
- types/registry
|
||||
- types/strslice
|
||||
- types/swarm
|
||||
- types/time
|
||||
- types/versions
|
||||
- types/blkiodev
|
||||
- types/strslice
|
||||
- name: github.com/docker/go-connections
|
||||
version: 990a1a1a70b0da4c4cb70e117971a4f0babfbf1a
|
||||
subpackages:
|
||||
- nat
|
||||
- sockets
|
||||
- tlsconfig
|
||||
- nat
|
||||
- name: github.com/docker/go-units
|
||||
version: f2d77a61e3c169b43402a0a1e84f06daf29b8190
|
||||
- name: github.com/docker/libcompose
|
||||
version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873
|
||||
subpackages:
|
||||
- docker
|
||||
- project
|
||||
- project/events
|
||||
- project/options
|
||||
- config
|
||||
- docker/builder
|
||||
- docker/client
|
||||
- labels
|
||||
- logger
|
||||
- lookup
|
||||
- utils
|
||||
- yaml
|
||||
- version
|
||||
version: 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||
- name: github.com/docker/leadership
|
||||
version: bfc7753dd48af19513b29deec23c364bf0f274eb
|
||||
- name: github.com/docker/libkv
|
||||
version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff
|
||||
subpackages:
|
||||
@@ -146,143 +242,469 @@ imports:
|
||||
- store/zookeeper
|
||||
- name: github.com/donovanhide/eventsource
|
||||
version: fd1de70867126402be23c306e1ce32828455d85b
|
||||
- name: github.com/eapache/channels
|
||||
version: 47238d5aae8c0fefd518ef2bee46290909cf8263
|
||||
- name: github.com/eapache/queue
|
||||
version: 44cc805cf13205b55f69e14bcb69867d1ae92f98
|
||||
- name: github.com/edeckers/auroradnsclient
|
||||
version: 8b777c170cfd377aa16bb4368f093017dddef3f9
|
||||
subpackages:
|
||||
- records
|
||||
- requests
|
||||
- requests/errors
|
||||
- tokens
|
||||
- zones
|
||||
- name: github.com/elazarl/go-bindata-assetfs
|
||||
version: 57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2
|
||||
- name: github.com/emicklei/go-restful
|
||||
version: 892402ba11a2e2fd5e1295dd633481f27365f14d
|
||||
subpackages:
|
||||
- log
|
||||
- swagger
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: a558128c87724cd7430060ef5aedf39f83937f55
|
||||
- name: github.com/go-check/check
|
||||
version: 4f90aeace3a26ad7021961c297b22c42160c7b25
|
||||
version: 6b00a5b651b1beb2c6821863f7c60df490bd46c8
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 04f313413ffd65ce25f2541bfd2b2ceec5c0908c
|
||||
- name: github.com/go-ini/ini
|
||||
version: 6f66b0e091edb3c7b380f7c4f0f884274d550b67
|
||||
- name: github.com/go-kit/kit
|
||||
version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41
|
||||
subpackages:
|
||||
- metrics
|
||||
- metrics/internal/lv
|
||||
- metrics/prometheus
|
||||
- name: github.com/go-openapi/jsonpointer
|
||||
version: 8d96a2dc61536b690bd36b2e9df0b3c0b62825b2
|
||||
- name: github.com/go-openapi/jsonreference
|
||||
version: 36d33bfe519efae5632669801b180bf1a245da3b
|
||||
- name: github.com/go-openapi/spec
|
||||
version: 34b5ffff717ab4535aef76e3dd90818bddde571b
|
||||
- name: github.com/go-openapi/swag
|
||||
version: 96d7b9ebd181a1735a1c9ac87914f2b32fbf56c9
|
||||
- name: github.com/gogo/protobuf
|
||||
version: 909568be09de550ed094403c2bf8a261b5bb730a
|
||||
subpackages:
|
||||
- proto
|
||||
- sortkeys
|
||||
- name: github.com/golang/glog
|
||||
version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
|
||||
- name: github.com/golang/protobuf
|
||||
version: 5677a0e3d5e89854c9974e1256839ee23f8233ca
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/google/go-github
|
||||
version: c8ebe3a4d7f0791a6315b7410353d4084c58805d
|
||||
subpackages:
|
||||
- github
|
||||
- name: github.com/google/go-querystring
|
||||
version: 9235644dd9e52eeae6fa48efd539fdc351a0af53
|
||||
subpackages:
|
||||
- query
|
||||
- name: github.com/google/gofuzz
|
||||
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
|
||||
- name: github.com/gorilla/context
|
||||
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
|
||||
version: 1ea25387ff6f684839d82767c1733ff4d4d15d0a
|
||||
- name: github.com/gorilla/websocket
|
||||
version: 4873052237e4eeda85cf50c071ef33836fe8e139
|
||||
- name: github.com/hashicorp/consul
|
||||
version: 548fb6eb3f407147e20d923521296e0500f57ef0
|
||||
version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d
|
||||
subpackages:
|
||||
- api
|
||||
- name: github.com/hashicorp/go-cleanhttp
|
||||
version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d
|
||||
- name: github.com/hashicorp/go-version
|
||||
version: e96d3840402619007766590ecea8dd7af1292276
|
||||
- name: github.com/hashicorp/serf
|
||||
version: 6c4672d66fc6312ddde18399262943e21175d831
|
||||
subpackages:
|
||||
- coordinate
|
||||
- serf
|
||||
- name: github.com/libkermit/docker
|
||||
version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02
|
||||
- name: github.com/JamesClonk/vultr
|
||||
version: 9ec0427d51411407c0402b093a1771cb75af9679
|
||||
subpackages:
|
||||
- compose
|
||||
- name: github.com/libkermit/docker-check
|
||||
version: bb75a86b169c6c5d22c0ee98278124036f272d7b
|
||||
subpackages:
|
||||
- compose
|
||||
- lib
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jonboulle/clockwork
|
||||
version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982
|
||||
- name: github.com/juju/ratelimit
|
||||
version: 77ed1c8a01217656d2080ad51981f6e99adaa177
|
||||
- name: github.com/mailgun/manners
|
||||
version: fada45142db3f93097ca917da107aa3fad0ffcb5
|
||||
version: a585afd9d65c0e05f6c003f921e71ebc05074f4f
|
||||
- name: github.com/mailgun/timetools
|
||||
version: fd192d755b00c968d312d23f521eb0cdc6f66bd0
|
||||
- name: github.com/mailru/easyjson
|
||||
version: 9d6630dc8c577b56cb9687a9cf9e8578aca7298a
|
||||
subpackages:
|
||||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/mattn/go-shellwords
|
||||
version: 525bedee691b5a8df547cb5cf9f86b7fb1883e24
|
||||
- name: github.com/matttproud/golang_protobuf_extensions
|
||||
version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
|
||||
subpackages:
|
||||
- pbutil
|
||||
- name: github.com/mesos/mesos-go
|
||||
version: 068d5470506e3780189fe607af40892814197c5e
|
||||
subpackages:
|
||||
- detector
|
||||
- detector/zoo
|
||||
- mesos
|
||||
- mesosproto
|
||||
- mesosutil
|
||||
- upid
|
||||
- name: github.com/mesosphere/mesos-dns
|
||||
version: b47dc4c19f215e98da687b15b4c64e70f629bea5
|
||||
repo: https://github.com/containous/mesos-dns.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- detect
|
||||
- errorutil
|
||||
- logging
|
||||
- models
|
||||
- records
|
||||
- records/labels
|
||||
- records/state
|
||||
- util
|
||||
- name: github.com/Microsoft/go-winio
|
||||
version: ce2922f643c8fd76b46cadc7f404a06282678b34
|
||||
- name: github.com/miekg/dns
|
||||
version: 5d001d020961ae1c184f9f8152fdc73810481677
|
||||
- name: github.com/moul/http2curl
|
||||
version: b1479103caacaa39319f75e7f57fc545287fca0d
|
||||
version: 8060d9f51305bbe024b99679454e62f552cd0b0b
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: f3009df150dadf309fdee4a54ed65c124afad715
|
||||
- name: github.com/mvdan/xurls
|
||||
version: fa08908f19eca8c491d68c6bd8b4b44faea6daf8
|
||||
- name: github.com/NYTimes/gziphandler
|
||||
version: 6710af535839f57c687b62c4c23d649f9545d885
|
||||
- name: github.com/ogier/pflag
|
||||
version: 45c278ab3607870051a2ea9040bb85fcb8557481
|
||||
- name: github.com/opencontainers/runc
|
||||
version: 1b49d9b4db3fe7ffbe53698a79124e7b4aa78180
|
||||
version: 1a81e9ab1f138c091fe5c86d0883f87716088527
|
||||
subpackages:
|
||||
- libcontainer/configs
|
||||
- libcontainer/devices
|
||||
- libcontainer/system
|
||||
- libcontainer/user
|
||||
- name: github.com/parnurzeal/gorequest
|
||||
version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
|
||||
- name: github.com/ovh/go-ovh
|
||||
version: a8a4c0bc40e56322142649bda7b2b4bb15145b6e
|
||||
subpackages:
|
||||
- ovh
|
||||
- name: github.com/pborman/uuid
|
||||
version: 5007efa264d92316c43112bc573e754bc889b7b1
|
||||
- name: github.com/pkg/errors
|
||||
version: bfd5150e4e41705ded2129ec33379de1cb90b513
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/prometheus/client_golang
|
||||
version: c5b7fccd204277076155f10851dad72b76a49317
|
||||
subpackages:
|
||||
- prometheus
|
||||
- prometheus/promhttp
|
||||
- name: github.com/prometheus/client_model
|
||||
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||
subpackages:
|
||||
- go
|
||||
- name: github.com/prometheus/common
|
||||
version: ffe929a3f4c4faeaa10f2b9535c2b1be3ad15650
|
||||
subpackages:
|
||||
- expfmt
|
||||
- model
|
||||
- name: github.com/prometheus/procfs
|
||||
version: 454a56f35412459b5e684fd5ec0f9211b94f002a
|
||||
- name: github.com/PuerkitoBio/purell
|
||||
version: 0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4
|
||||
- name: github.com/PuerkitoBio/urlesc
|
||||
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
|
||||
- name: github.com/pyr/egoscale
|
||||
version: ab4b0d7ff424c462da486aef27f354cdeb29a319
|
||||
subpackages:
|
||||
- src/egoscale
|
||||
- name: github.com/rancher/go-rancher
|
||||
version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b
|
||||
subpackages:
|
||||
- client
|
||||
- name: github.com/ryanuber/go-glob
|
||||
version: 572520ed46dbddaed19ea3d9541bdd0494163693
|
||||
- name: github.com/samuel/go-zookeeper
|
||||
version: e64db453f3512cade908163702045e0f31137843
|
||||
subpackages:
|
||||
- zk
|
||||
- name: github.com/satori/go.uuid
|
||||
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: a283a10442df8dc09befd873fab202bf8a253d6a
|
||||
- name: github.com/spf13/pflag
|
||||
version: 5644820622454e71517561946e3d94b9f9db6842
|
||||
- name: github.com/streamrail/concurrent-map
|
||||
version: 65a174a3a4188c0b7099acbc6cfa0c53628d3287
|
||||
- name: github.com/stretchr/objx
|
||||
version: cbeaeb16a013161a98496fad62933b1d21786672
|
||||
- name: github.com/stretchr/testify
|
||||
version: d77da356e56a7428ad25149ca77381849a6a5232
|
||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||
subpackages:
|
||||
- mock
|
||||
- assert
|
||||
- mock
|
||||
- name: github.com/thoas/stats
|
||||
version: 69e3c072eec2df2df41afe6214f62eb940e4cd80
|
||||
version: 152b5d051953fdb6e45f14b6826962aadc032324
|
||||
- name: github.com/timewasted/linode
|
||||
version: 37e84520dcf74488f67654f9c775b9752c232dc1
|
||||
subpackages:
|
||||
- dns
|
||||
- name: github.com/tv42/zbase32
|
||||
version: 03389da7e0bf9844767f82690f4d68fc097a1306
|
||||
- name: github.com/ugorji/go
|
||||
version: b94837a2404ab90efe9289e77a70694c355739cb
|
||||
version: ea9cd21fa0bc41ee4bdd50ac7ed8cbc7ea2ed960
|
||||
subpackages:
|
||||
- codec
|
||||
- name: github.com/unrolled/render
|
||||
version: 198ad4d8b8a4612176b804ca10555b222a086b40
|
||||
- name: github.com/vdemeester/docker-events
|
||||
version: 20e6d2db238723e68197a9e3c6c34c99a9893a9c
|
||||
- name: github.com/vdemeester/shakers
|
||||
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||
version: be74d4929ec1ad118df54349fda4b0cba60f849b
|
||||
- name: github.com/vulcand/oxy
|
||||
version: ab7796d7036b425fbc945853cd1b7e7adf43b0d6
|
||||
version: f88530866c561d24a6b5aac49f76d6351b788b9f
|
||||
repo: https://github.com/containous/oxy.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- cbreaker
|
||||
- connlimit
|
||||
- forward
|
||||
- memmetrics
|
||||
- roundrobin
|
||||
- stream
|
||||
- utils
|
||||
- memmetrics
|
||||
- name: github.com/vulcand/predicate
|
||||
version: 19b9dde14240d94c804ae5736ad0e1de10bf8fe6
|
||||
- name: github.com/vulcand/route
|
||||
version: cb89d787ddbb1c5849a7ac9f79004c1fd12a4a32
|
||||
- name: github.com/vulcand/vulcand
|
||||
version: 28a4e5c0892167589737b95ceecbcef00295be50
|
||||
version: 42492a3a85e294bdbdd1bcabb8c12769a81ea284
|
||||
subpackages:
|
||||
- plugin/rewrite
|
||||
- plugin
|
||||
- conntracker
|
||||
- plugin
|
||||
- plugin/rewrite
|
||||
- router
|
||||
- name: github.com/xenolf/lego
|
||||
version: b2fad6198110326662e9e356a97199078a4a775c
|
||||
version: 0e2937900b224325f4476745a9b53aef246b7410
|
||||
subpackages:
|
||||
- acme
|
||||
- providers/dns
|
||||
- providers/dns/auroradns
|
||||
- providers/dns/azure
|
||||
- providers/dns/cloudflare
|
||||
- providers/dns/digitalocean
|
||||
- providers/dns/dnsimple
|
||||
- providers/dns/dnsmadeeasy
|
||||
- providers/dns/dnspod
|
||||
- providers/dns/dyn
|
||||
- providers/dns/exoscale
|
||||
- providers/dns/gandi
|
||||
- providers/dns/googlecloud
|
||||
- providers/dns/linode
|
||||
- providers/dns/namecheap
|
||||
- providers/dns/ns1
|
||||
- providers/dns/ovh
|
||||
- providers/dns/pdns
|
||||
- providers/dns/rackspace
|
||||
- providers/dns/rfc2136
|
||||
- providers/dns/route53
|
||||
- providers/dns/vultr
|
||||
- name: golang.org/x/crypto
|
||||
version: f28b56427a527c2e35c0bcac123f0a6a8a943cd3
|
||||
version: 4ed45ec682102c643324fae5dff8dab085b6c300
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- ocsp
|
||||
- name: golang.org/x/net
|
||||
version: b400c2eff1badec7022a8c8f5bea058b6315eed7
|
||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||
subpackages:
|
||||
- context
|
||||
- publicsuffix
|
||||
- context/ctxhttp
|
||||
- http2
|
||||
- http2/hpack
|
||||
- idna
|
||||
- lex/httplex
|
||||
- proxy
|
||||
- publicsuffix
|
||||
- name: golang.org/x/oauth2
|
||||
version: 3046bc76d6dfd7d3707f6640f85e42d9c4050f50
|
||||
subpackages:
|
||||
- google
|
||||
- internal
|
||||
- jws
|
||||
- jwt
|
||||
- name: golang.org/x/sys
|
||||
version: 62bee037599929a6e9146f29d10dd5208c43507d
|
||||
version: eb2c74142fd19a79b3f237334c7384d5167b1b46
|
||||
subpackages:
|
||||
- unix
|
||||
- windows
|
||||
- name: golang.org/x/text
|
||||
version: a49bea13b776691cb1b49873e5d8df96ec74831a
|
||||
repo: https://github.com/golang/text.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- .
|
||||
- transform
|
||||
- unicode/norm
|
||||
- width
|
||||
- name: google.golang.org/api
|
||||
version: 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
||||
subpackages:
|
||||
- dns/v1
|
||||
- gensupport
|
||||
- googleapi
|
||||
- googleapi/internal/uritemplates
|
||||
- name: google.golang.org/appengine
|
||||
version: 12d5545dc1cfa6047a286d5e853841b6471f4c19
|
||||
subpackages:
|
||||
- internal
|
||||
- internal/app_identity
|
||||
- internal/base
|
||||
- internal/datastore
|
||||
- internal/log
|
||||
- internal/modules
|
||||
- internal/remote_api
|
||||
- internal/urlfetch
|
||||
- urlfetch
|
||||
- name: google.golang.org/cloud
|
||||
version: f20d6dcccb44ed49de45ae3703312cb46e627db1
|
||||
subpackages:
|
||||
- compute/metadata
|
||||
- internal
|
||||
- name: gopkg.in/fsnotify.v1
|
||||
version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb
|
||||
- name: gopkg.in/inf.v0
|
||||
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
|
||||
- name: gopkg.in/ini.v1
|
||||
version: 6f66b0e091edb3c7b380f7c4f0f884274d550b67
|
||||
- name: gopkg.in/mgo.v2
|
||||
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
|
||||
subpackages:
|
||||
- bson
|
||||
- name: gopkg.in/ns1/ns1-go.v2
|
||||
version: d8d10b7f448291ddbdce48d4594fb1b667014c8b
|
||||
subpackages:
|
||||
- rest
|
||||
- rest/model/account
|
||||
- rest/model/data
|
||||
- rest/model/dns
|
||||
- rest/model/filter
|
||||
- rest/model/monitor
|
||||
- name: gopkg.in/square/go-jose.v1
|
||||
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
|
||||
subpackages:
|
||||
- cipher
|
||||
- json
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: bef53efd0c76e49e6de55ead051f886bea7e9420
|
||||
- name: k8s.io/client-go
|
||||
version: 1195e3a8ee1a529d53eed7c624527a68555ddf1f
|
||||
subpackages:
|
||||
- 1.5/discovery
|
||||
- 1.5/kubernetes
|
||||
- 1.5/kubernetes/typed/apps/v1alpha1
|
||||
- 1.5/kubernetes/typed/authentication/v1beta1
|
||||
- 1.5/kubernetes/typed/authorization/v1beta1
|
||||
- 1.5/kubernetes/typed/autoscaling/v1
|
||||
- 1.5/kubernetes/typed/batch/v1
|
||||
- 1.5/kubernetes/typed/certificates/v1alpha1
|
||||
- 1.5/kubernetes/typed/core/v1
|
||||
- 1.5/kubernetes/typed/extensions/v1beta1
|
||||
- 1.5/kubernetes/typed/policy/v1alpha1
|
||||
- 1.5/kubernetes/typed/rbac/v1alpha1
|
||||
- 1.5/kubernetes/typed/storage/v1beta1
|
||||
- 1.5/pkg/api
|
||||
- 1.5/pkg/api/errors
|
||||
- 1.5/pkg/api/install
|
||||
- 1.5/pkg/api/meta
|
||||
- 1.5/pkg/api/meta/metatypes
|
||||
- 1.5/pkg/api/resource
|
||||
- 1.5/pkg/api/unversioned
|
||||
- 1.5/pkg/api/v1
|
||||
- 1.5/pkg/api/validation/path
|
||||
- 1.5/pkg/apimachinery
|
||||
- 1.5/pkg/apimachinery/announced
|
||||
- 1.5/pkg/apimachinery/registered
|
||||
- 1.5/pkg/apis/apps
|
||||
- 1.5/pkg/apis/apps/install
|
||||
- 1.5/pkg/apis/apps/v1alpha1
|
||||
- 1.5/pkg/apis/authentication
|
||||
- 1.5/pkg/apis/authentication/install
|
||||
- 1.5/pkg/apis/authentication/v1beta1
|
||||
- 1.5/pkg/apis/authorization
|
||||
- 1.5/pkg/apis/authorization/install
|
||||
- 1.5/pkg/apis/authorization/v1beta1
|
||||
- 1.5/pkg/apis/autoscaling
|
||||
- 1.5/pkg/apis/autoscaling/install
|
||||
- 1.5/pkg/apis/autoscaling/v1
|
||||
- 1.5/pkg/apis/batch
|
||||
- 1.5/pkg/apis/batch/install
|
||||
- 1.5/pkg/apis/batch/v1
|
||||
- 1.5/pkg/apis/batch/v2alpha1
|
||||
- 1.5/pkg/apis/certificates
|
||||
- 1.5/pkg/apis/certificates/install
|
||||
- 1.5/pkg/apis/certificates/v1alpha1
|
||||
- 1.5/pkg/apis/extensions
|
||||
- 1.5/pkg/apis/extensions/install
|
||||
- 1.5/pkg/apis/extensions/v1beta1
|
||||
- 1.5/pkg/apis/policy
|
||||
- 1.5/pkg/apis/policy/install
|
||||
- 1.5/pkg/apis/policy/v1alpha1
|
||||
- 1.5/pkg/apis/rbac
|
||||
- 1.5/pkg/apis/rbac/install
|
||||
- 1.5/pkg/apis/rbac/v1alpha1
|
||||
- 1.5/pkg/apis/storage
|
||||
- 1.5/pkg/apis/storage/install
|
||||
- 1.5/pkg/apis/storage/v1beta1
|
||||
- 1.5/pkg/auth/user
|
||||
- 1.5/pkg/conversion
|
||||
- 1.5/pkg/conversion/queryparams
|
||||
- 1.5/pkg/fields
|
||||
- 1.5/pkg/genericapiserver/openapi/common
|
||||
- 1.5/pkg/labels
|
||||
- 1.5/pkg/runtime
|
||||
- 1.5/pkg/runtime/serializer
|
||||
- 1.5/pkg/runtime/serializer/json
|
||||
- 1.5/pkg/runtime/serializer/protobuf
|
||||
- 1.5/pkg/runtime/serializer/recognizer
|
||||
- 1.5/pkg/runtime/serializer/streaming
|
||||
- 1.5/pkg/runtime/serializer/versioning
|
||||
- 1.5/pkg/selection
|
||||
- 1.5/pkg/third_party/forked/golang/reflect
|
||||
- 1.5/pkg/types
|
||||
- 1.5/pkg/util
|
||||
- 1.5/pkg/util/cert
|
||||
- 1.5/pkg/util/clock
|
||||
- 1.5/pkg/util/errors
|
||||
- 1.5/pkg/util/flowcontrol
|
||||
- 1.5/pkg/util/framer
|
||||
- 1.5/pkg/util/integer
|
||||
- 1.5/pkg/util/intstr
|
||||
- 1.5/pkg/util/json
|
||||
- 1.5/pkg/util/labels
|
||||
- 1.5/pkg/util/net
|
||||
- 1.5/pkg/util/parsers
|
||||
- 1.5/pkg/util/rand
|
||||
- 1.5/pkg/util/runtime
|
||||
- 1.5/pkg/util/sets
|
||||
- 1.5/pkg/util/uuid
|
||||
- 1.5/pkg/util/validation
|
||||
- 1.5/pkg/util/validation/field
|
||||
- 1.5/pkg/util/wait
|
||||
- 1.5/pkg/util/yaml
|
||||
- 1.5/pkg/version
|
||||
- 1.5/pkg/watch
|
||||
- 1.5/pkg/watch/versioned
|
||||
- 1.5/plugin/pkg/client/auth
|
||||
- 1.5/plugin/pkg/client/auth/gcp
|
||||
- 1.5/plugin/pkg/client/auth/oidc
|
||||
- 1.5/rest
|
||||
- 1.5/tools/cache
|
||||
- 1.5/tools/clientcmd/api
|
||||
- 1.5/tools/metrics
|
||||
- 1.5/transport
|
||||
testImports:
|
||||
- name: github.com/Azure/go-ansiterm
|
||||
version: fa152c58bc15761d0200cb75fe958b89a9d4888e
|
||||
@@ -290,19 +712,56 @@ testImports:
|
||||
- winterm
|
||||
- name: github.com/cloudfoundry-incubator/candiedyaml
|
||||
version: 99c3df83b51532e3615f851d8c2dbb638f5313bf
|
||||
- name: github.com/docker/libcompose
|
||||
version: d1876c1d68527a49c0aac22a0b161acc7296b740
|
||||
subpackages:
|
||||
- config
|
||||
- docker
|
||||
- docker/builder
|
||||
- docker/client
|
||||
- docker/network
|
||||
- labels
|
||||
- logger
|
||||
- lookup
|
||||
- project
|
||||
- project/events
|
||||
- project/options
|
||||
- utils
|
||||
- version
|
||||
- yaml
|
||||
- name: github.com/flynn/go-shlex
|
||||
version: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||
- name: github.com/go-check/check
|
||||
version: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
||||
- name: github.com/gorilla/mux
|
||||
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
|
||||
- name: github.com/vbatts/tar-split
|
||||
version: 28bc4c32f9fa9725118a685c9ddd7ffdbdbfe2c8
|
||||
version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf
|
||||
- name: github.com/libkermit/compose
|
||||
version: cadc5a3b83a15790174bd7fbc75ea2529785e772
|
||||
subpackages:
|
||||
- check
|
||||
- name: github.com/libkermit/docker
|
||||
version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7
|
||||
- name: github.com/libkermit/docker-check
|
||||
version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3
|
||||
- name: github.com/opencontainers/runtime-spec
|
||||
version: 06479209bdc0d4135911688c18157bd39bd99c22
|
||||
subpackages:
|
||||
- specs-go
|
||||
- name: github.com/vbatts/tar-split
|
||||
version: 6810cedb21b2c3d0b9bb8f9af12ff2dc7a2f14df
|
||||
subpackages:
|
||||
- archive/tar
|
||||
- tar/asm
|
||||
- tar/storage
|
||||
- archive/tar
|
||||
- name: github.com/vdemeester/shakers
|
||||
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||
- name: github.com/xeipuuv/gojsonpointer
|
||||
version: e0fe6f68307607d540ed8eac07a342c33fa1b54a
|
||||
- name: github.com/xeipuuv/gojsonreference
|
||||
version: e02fc20de94c78484cd5ffb007f8af96be030a45
|
||||
- name: github.com/xeipuuv/gojsonschema
|
||||
version: 66a3de92def23708184148ae337750915875e7c1
|
||||
version: 00f9fafb54d2244d291b86ab63d12c38bd5c3886
|
||||
- name: golang.org/x/time
|
||||
version: a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||
subpackages:
|
||||
- rate
|
||||
|
103
glide.yaml
103
glide.yaml
@@ -5,12 +5,11 @@ import:
|
||||
subpackages:
|
||||
- fun
|
||||
- package: github.com/Sirupsen/logrus
|
||||
- package: github.com/cenkalti/backoff
|
||||
- package: github.com/codegangsta/negroni
|
||||
- package: github.com/cenk/backoff
|
||||
- package: github.com/containous/flaeg
|
||||
version: b98687da5c323650f4513fda6b6203fcbdec9313
|
||||
version: a731c034dda967333efce5f8d276aeff11f8ff87
|
||||
- package: github.com/vulcand/oxy
|
||||
version: ab7796d7036b425fbc945853cd1b7e7adf43b0d6
|
||||
version: f88530866c561d24a6b5aac49f76d6351b788b9f
|
||||
repo: https://github.com/containous/oxy.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
@@ -21,18 +20,21 @@ import:
|
||||
- stream
|
||||
- utils
|
||||
- package: github.com/containous/staert
|
||||
version: e2aa88e235a02dd52aa1d5d9de75f9d9139d1602
|
||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||
- package: github.com/docker/engine-api
|
||||
version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb
|
||||
version: v0.4.0
|
||||
subpackages:
|
||||
- client
|
||||
- types
|
||||
- types/events
|
||||
- types/filters
|
||||
- package: github.com/docker/go-connections
|
||||
version: v0.2.1
|
||||
subpackages:
|
||||
- sockets
|
||||
- tlsconfig
|
||||
- package: github.com/docker/go-units
|
||||
version: 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||
- package: github.com/docker/libkv
|
||||
subpackages:
|
||||
- store
|
||||
@@ -41,44 +43,99 @@ import:
|
||||
- store/etcd
|
||||
- store/zookeeper
|
||||
- package: github.com/elazarl/go-bindata-assetfs
|
||||
- package: github.com/gambol99/go-marathon
|
||||
version: a558128c87724cd7430060ef5aedf39f83937f55
|
||||
- package: github.com/containous/mux
|
||||
- package: github.com/hashicorp/consul
|
||||
subpackages:
|
||||
- api
|
||||
- package: github.com/mailgun/manners
|
||||
- package: github.com/parnurzeal/gorequest
|
||||
- package: github.com/streamrail/concurrent-map
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- mock
|
||||
- package: github.com/thoas/stats
|
||||
version: 152b5d051953fdb6e45f14b6826962aadc032324
|
||||
- package: github.com/unrolled/render
|
||||
- package: github.com/vdemeester/docker-events
|
||||
version: 20e6d2db238723e68197a9e3c6c34c99a9893a9c
|
||||
version: be74d4929ec1ad118df54349fda4b0cba60f849b
|
||||
- package: github.com/vulcand/vulcand
|
||||
version: 42492a3a85e294bdbdd1bcabb8c12769a81ea284
|
||||
subpackages:
|
||||
- plugin/rewrite
|
||||
- package: github.com/xenolf/lego
|
||||
version: b2fad6198110326662e9e356a97199078a4a775c
|
||||
version: 0e2937900b224325f4476745a9b53aef246b7410
|
||||
subpackages:
|
||||
- acme
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- context
|
||||
- package: gopkg.in/fsnotify.v1
|
||||
- package: github.com/libkermit/docker-check
|
||||
version: bb75a86b169c6c5d22c0ee98278124036f272d7b
|
||||
- package: github.com/libkermit/docker
|
||||
version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02
|
||||
- package: github.com/docker/docker
|
||||
version: 9837ec4da53f15f9120d53a6e1517491ba8b0261
|
||||
version: v1.13.0
|
||||
subpackages:
|
||||
- namesgenerator
|
||||
- package: github.com/go-check/check
|
||||
- package: github.com/docker/libcompose
|
||||
version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873
|
||||
- package: github.com/mattn/go-shellwords
|
||||
- package: github.com/vdemeester/shakers
|
||||
- package: github.com/ryanuber/go-glob
|
||||
- package: github.com/mesos/mesos-go
|
||||
subpackages:
|
||||
- mesosproto
|
||||
- mesos
|
||||
- upid
|
||||
- mesosutil
|
||||
- detector
|
||||
- package: github.com/miekg/dns
|
||||
version: 8060d9f51305bbe024b99679454e62f552cd0b0b
|
||||
- package: github.com/mesosphere/mesos-dns
|
||||
version: b47dc4c19f215e98da687b15b4c64e70f629bea5
|
||||
repo: https://github.com/containous/mesos-dns.git
|
||||
vcs: git
|
||||
- package: github.com/abbot/go-http-auth
|
||||
- package: github.com/NYTimes/gziphandler
|
||||
- package: github.com/docker/leadership
|
||||
- package: github.com/satori/go.uuid
|
||||
version: ^1.1.0
|
||||
- package: k8s.io/client-go
|
||||
version: ^v1.5.0
|
||||
- package: github.com/gambol99/go-marathon
|
||||
version: ^0.5.1
|
||||
- package: github.com/ArthurHlt/go-eureka-client
|
||||
subpackages:
|
||||
- eureka
|
||||
- package: github.com/coreos/go-systemd
|
||||
version: v14
|
||||
subpackages:
|
||||
- daemon
|
||||
- package: github.com/google/go-github
|
||||
- package: github.com/hashicorp/go-version
|
||||
- package: github.com/mvdan/xurls
|
||||
- package: github.com/go-kit/kit
|
||||
version: v0.3.0
|
||||
subpackages:
|
||||
- metrics
|
||||
- package: github.com/eapache/channels
|
||||
version: v1.1.0
|
||||
- package: golang.org/x/net
|
||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||
subpackages:
|
||||
- http2
|
||||
- context
|
||||
- package: github.com/docker/distribution
|
||||
version: v2.6.0
|
||||
- package: github.com/aws/aws-sdk-go
|
||||
version: v1.6.18
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/credentials
|
||||
- aws/defaults
|
||||
- aws/ec2metadata
|
||||
- aws/endpoints
|
||||
- aws/request
|
||||
- aws/session
|
||||
- service/ec2
|
||||
- service/ecs
|
||||
- package: cloud.google.com/go
|
||||
version: v0.6.0
|
||||
subpackages:
|
||||
- compute/metadata
|
||||
- package: github.com/gogo/protobuf
|
||||
version: v0.3
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/rancher/go-rancher
|
||||
version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b
|
||||
|
117
healthcheck/healthcheck.go
Normal file
117
healthcheck/healthcheck.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
var singleton *HealthCheck
|
||||
var once sync.Once
|
||||
|
||||
// GetHealthCheck Get HealtchCheck Singleton
|
||||
func GetHealthCheck() *HealthCheck {
|
||||
once.Do(func() {
|
||||
singleton = newHealthCheck()
|
||||
})
|
||||
return singleton
|
||||
}
|
||||
|
||||
// BackendHealthCheck HealthCheck configuration for a backend
|
||||
type BackendHealthCheck struct {
|
||||
Path string
|
||||
Interval time.Duration
|
||||
DisabledURLs []*url.URL
|
||||
lb loadBalancer
|
||||
}
|
||||
|
||||
var launch = false
|
||||
|
||||
//HealthCheck struct
|
||||
type HealthCheck struct {
|
||||
Backends map[string]*BackendHealthCheck
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
type loadBalancer interface {
|
||||
RemoveServer(u *url.URL) error
|
||||
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error
|
||||
Servers() []*url.URL
|
||||
}
|
||||
|
||||
func newHealthCheck() *HealthCheck {
|
||||
return &HealthCheck{make(map[string]*BackendHealthCheck), nil}
|
||||
}
|
||||
|
||||
// NewBackendHealthCheck Instantiate a new BackendHealthCheck
|
||||
func NewBackendHealthCheck(URL string, interval time.Duration, lb loadBalancer) *BackendHealthCheck {
|
||||
return &BackendHealthCheck{URL, interval, nil, lb}
|
||||
}
|
||||
|
||||
//SetBackendsConfiguration set backends configuration
|
||||
func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backends map[string]*BackendHealthCheck) {
|
||||
hc.Backends = backends
|
||||
if hc.cancel != nil {
|
||||
hc.cancel()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(parentCtx)
|
||||
hc.cancel = cancel
|
||||
hc.execute(ctx)
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) execute(ctx context.Context) {
|
||||
for backendID, backend := range hc.Backends {
|
||||
currentBackend := backend
|
||||
currentBackendID := backendID
|
||||
safe.Go(func() {
|
||||
for {
|
||||
ticker := time.NewTicker(currentBackend.Interval)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Debugf("Stopping all current Healthcheck goroutines")
|
||||
return
|
||||
case <-ticker.C:
|
||||
log.Debugf("Refreshing Healthcheck for currentBackend %s ", currentBackendID)
|
||||
enabledURLs := currentBackend.lb.Servers()
|
||||
var newDisabledURLs []*url.URL
|
||||
for _, url := range currentBackend.DisabledURLs {
|
||||
if checkHealth(url, currentBackend.Path) {
|
||||
log.Debugf("HealthCheck is up [%s]: Upsert in server list", url.String())
|
||||
currentBackend.lb.UpsertServer(url, roundrobin.Weight(1))
|
||||
} else {
|
||||
newDisabledURLs = append(newDisabledURLs, url)
|
||||
}
|
||||
}
|
||||
currentBackend.DisabledURLs = newDisabledURLs
|
||||
|
||||
for _, url := range enabledURLs {
|
||||
if !checkHealth(url, currentBackend.Path) {
|
||||
log.Debugf("HealthCheck has failed [%s]: Remove from server list", url.String())
|
||||
currentBackend.lb.RemoveServer(url)
|
||||
currentBackend.DisabledURLs = append(currentBackend.DisabledURLs, url)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func checkHealth(serverURL *url.URL, path string) bool {
|
||||
timeout := time.Duration(5 * time.Second)
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Get(serverURL.String() + path)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
@@ -75,7 +75,7 @@ func (s *AccessLogSuite) TestAccessLog(c *check.C) {
|
||||
c.Assert(tokens[9], checker.Equals, fmt.Sprintf("%d", i+1))
|
||||
c.Assert(strings.HasPrefix(tokens[10], "frontend"), checker.True)
|
||||
c.Assert(strings.HasPrefix(tokens[11], "http://127.0.0.1:808"), checker.True)
|
||||
c.Assert(regexp.MustCompile("^\\d+\\.\\d+.*s$").MatchString(tokens[12]), checker.True)
|
||||
c.Assert(regexp.MustCompile("^\\d+ms$").MatchString(tokens[12]), checker.True)
|
||||
}
|
||||
}
|
||||
c.Assert(count, checker.Equals, 3)
|
||||
|
92
integration/acme_test.go
Normal file
92
integration/acme_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/go-check/check"
|
||||
|
||||
"errors"
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
// ACME test suites (using libcompose)
|
||||
type AcmeSuite struct {
|
||||
BaseSuite
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "boulder")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
boulderHost := s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress
|
||||
|
||||
// wait for boulder
|
||||
err := utils.Try(120*time.Second, func() error {
|
||||
resp, err := http.Get("http://" + boulderHost + ":4000/directory")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return errors.New("Expected http 200 from boulder")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TearDownSuite(c *check.C) {
|
||||
// shutdown and delete compose project
|
||||
if s.composeProject != nil {
|
||||
s.composeProject.Stop(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestRetrieveAcmeCertificate(c *check.C) {
|
||||
boulderHost := s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress
|
||||
file := s.adaptFile(c, "fixtures/acme/acme.toml", struct{ BoulderHost string }{boulderHost})
|
||||
defer os.Remove(file)
|
||||
cmd := exec.Command(traefikBinary, "--configFile="+file)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
backend := startTestServer("9010", 200)
|
||||
defer backend.Close()
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
// wait for traefik (generating acme account take some seconds)
|
||||
err = utils.Try(30*time.Second, func() error {
|
||||
_, err := client.Get("https://127.0.0.1:5001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "traefik.acme.wtf",
|
||||
},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
req, _ := http.NewRequest("GET", "https://127.0.0.1:5001/", nil)
|
||||
req.Host = "traefik.acme.wtf"
|
||||
req.Header.Set("Host", "traefik.acme.wtf")
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
// Expected a 200
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
}
|
@@ -5,17 +5,22 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
"github.com/containous/staert"
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/go-check/check"
|
||||
|
||||
"errors"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
"github.com/containous/traefik/provider"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Consul test suites (using libcompose)
|
||||
@@ -24,7 +29,7 @@ type ConsulSuite struct {
|
||||
kv store.Store
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) SetUpSuite(c *check.C) {
|
||||
func (s *ConsulSuite) setupConsul(c *check.C) {
|
||||
s.createComposeProject(c, "consul")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
@@ -52,7 +57,56 @@ func (s *ConsulSuite) SetUpSuite(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) setupConsulTLS(c *check.C) {
|
||||
s.createComposeProject(c, "consul_tls")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
consul.Register()
|
||||
clientTLS := &provider.ClientTLS{
|
||||
CA: "resources/tls/ca.cert",
|
||||
Cert: "resources/tls/consul.cert",
|
||||
Key: "resources/tls/consul.key",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
TLSConfig, err := clientTLS.CreateTLSConfig()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
kv, err := libkv.NewStore(
|
||||
store.CONSUL,
|
||||
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8585"},
|
||||
&store.Config{
|
||||
ConnectionTimeout: 10 * time.Second,
|
||||
TLS: TLSConfig,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
c.Fatal("Cannot create store consul")
|
||||
}
|
||||
s.kv = kv
|
||||
|
||||
// wait for consul
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
_, err := kv.Exists("test")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TearDownTest(c *check.C) {
|
||||
// shutdown and delete compose project
|
||||
if s.composeProject != nil {
|
||||
s.composeProject.Stop(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TearDownSuite(c *check.C) {}
|
||||
|
||||
func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulHost string }{consulHost})
|
||||
defer os.Remove(file)
|
||||
@@ -70,6 +124,7 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TestNominalConfiguration(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulHost string }{consulHost})
|
||||
defer os.Remove(file)
|
||||
@@ -190,3 +245,279 @@ func (s *ConsulSuite) TestNominalConfiguration(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 404)
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TestGlobalConfiguration(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
err := s.kv.Put("traefik/entrypoints/http/address", []byte(":8001"), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for consul
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("traefik/entrypoints/http/address")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start traefik
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml", "--consul", "--consul.endpoint="+consulHost+":8500")
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
whoami1 := s.composeProject.Container(c, "whoami1")
|
||||
whoami2 := s.composeProject.Container(c, "whoami2")
|
||||
whoami3 := s.composeProject.Container(c, "whoami3")
|
||||
whoami4 := s.composeProject.Container(c, "whoami4")
|
||||
|
||||
backend1 := map[string]string{
|
||||
"traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"traefik/backends/backend1/servers/server1/url": "http://" + whoami1.NetworkSettings.IPAddress + ":80",
|
||||
"traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"traefik/backends/backend1/servers/server2/url": "http://" + whoami2.NetworkSettings.IPAddress + ":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://" + whoami3.NetworkSettings.IPAddress + ":80",
|
||||
"traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"traefik/backends/backend2/servers/server2/url": "http://" + whoami4.NetworkSettings.IPAddress + ":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 consul
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("traefik/frontends/frontend2/routes/test_2/rule")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(string(body), "Path:/test") {
|
||||
return errors.New("Incorrect traefik config")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://127.0.0.1:8001/", 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, 200)
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) skipTestGlobalConfigurationWithClientTLS(c *check.C) {
|
||||
c.Skip("wait for relative path issue in the composefile")
|
||||
s.setupConsulTLS(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
|
||||
err := s.kv.Put("traefik/web/address", []byte(":8081"), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for consul
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("traefik/web/address")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start traefik
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml",
|
||||
"--consul", "--consul.endpoint="+consulHost+":8585",
|
||||
"--consul.tls.ca=resources/tls/ca.cert",
|
||||
"--consul.tls.cert=resources/tls/consul.cert",
|
||||
"--consul.tls.key=resources/tls/consul.key",
|
||||
"--consul.tls.insecureskipverify")
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
_, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
}
|
||||
func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
|
||||
cmd := exec.Command(traefikBinary, "storeconfig", "--configFile=fixtures/simple_web.toml", "--consul.endpoint="+consulHost+":8500")
|
||||
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/consul/endpoint": (consulHost + ":8500"),
|
||||
}
|
||||
|
||||
for key, value := range checkmap {
|
||||
var p *store.KVPair
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
p, err = s.kv.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(string(p.Value), checker.Equals, value)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
String string
|
||||
Int int
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TestDatastore(c *check.C) {
|
||||
s.setupConsul(c)
|
||||
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
|
||||
kvSource, err := staert.NewKvSource(store.CONSUL, []string{consulHost + ":8500"}, &store.Config{
|
||||
ConnectionTimeout: 10 * time.Second,
|
||||
}, "traefik")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
ctx := context.Background()
|
||||
datastore1, err := cluster.NewDataStore(ctx, *kvSource, &TestStruct{}, nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
datastore2, err := cluster.NewDataStore(ctx, *kvSource, &TestStruct{}, nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
setter1, _, err := datastore1.Begin()
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = setter1.Commit(&TestStruct{
|
||||
String: "foo",
|
||||
Int: 1,
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
time.Sleep(2 * time.Second)
|
||||
test1 := datastore1.Get().(*TestStruct)
|
||||
c.Assert(test1.String, checker.Equals, "foo")
|
||||
|
||||
test2 := datastore2.Get().(*TestStruct)
|
||||
c.Assert(test2.String, checker.Equals, "foo")
|
||||
|
||||
setter2, _, err := datastore2.Begin()
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = setter2.Commit(&TestStruct{
|
||||
String: "bar",
|
||||
Int: 2,
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
time.Sleep(2 * time.Second)
|
||||
test1 = datastore1.Get().(*TestStruct)
|
||||
c.Assert(test1.String, checker.Equals, "bar")
|
||||
|
||||
test2 = datastore2.Get().(*TestStruct)
|
||||
c.Assert(test2.String, checker.Equals, "bar")
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(4)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
setter1, _, err := datastore1.Begin()
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = setter1.Commit(&TestStruct{
|
||||
String: "datastore1",
|
||||
Int: i,
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
setter2, _, err := datastore2.Begin()
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = setter2.Commit(&TestStruct{
|
||||
String: "datastore2",
|
||||
Int: i,
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
test1 := datastore1.Get().(*TestStruct)
|
||||
c.Assert(test1, checker.NotNil)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
test2 := datastore2.Get().(*TestStruct)
|
||||
c.Assert(test2, checker.NotNil)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ func (s *DockerSuite) TestDefaultDockerContainers(c *check.C) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/version", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = fmt.Sprintf("%s.docker.localhost", name)
|
||||
req.Host = fmt.Sprintf("%s.docker.localhost", strings.Replace(name, "_", "-", -1))
|
||||
resp, err := client.Do(req)
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
@@ -25,7 +26,7 @@ type EtcdSuite struct {
|
||||
kv store.Store
|
||||
}
|
||||
|
||||
func (s *EtcdSuite) SetUpSuite(c *check.C) {
|
||||
func (s *EtcdSuite) SetUpTest(c *check.C) {
|
||||
s.createComposeProject(c, "etcd")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
@@ -54,6 +55,15 @@ func (s *EtcdSuite) SetUpSuite(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *EtcdSuite) TearDownTest(c *check.C) {
|
||||
// shutdown and delete compose project
|
||||
if s.composeProject != nil {
|
||||
s.composeProject.Stop(c)
|
||||
}
|
||||
}
|
||||
|
||||
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})
|
||||
@@ -193,3 +203,266 @@ func (s *EtcdSuite) TestNominalConfiguration(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 404)
|
||||
}
|
||||
|
||||
func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
err := s.kv.Put("/traefik/entrypoints/http/address", []byte(":8001"), nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for etcd
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/entrypoints/http/address")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// start traefik
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml", "--etcd", "--etcd.endpoint="+etcdHost+":4001")
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
whoami1 := s.composeProject.Container(c, "whoami1")
|
||||
whoami2 := s.composeProject.Container(c, "whoami2")
|
||||
whoami3 := s.composeProject.Container(c, "whoami3")
|
||||
whoami4 := s.composeProject.Container(c, "whoami4")
|
||||
|
||||
backend1 := map[string]string{
|
||||
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
|
||||
"/traefik/backends/backend1/servers/server1/url": "http://" + whoami1.NetworkSettings.IPAddress + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + whoami2.NetworkSettings.IPAddress + ":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://" + whoami3.NetworkSettings.IPAddress + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + whoami4.NetworkSettings.IPAddress + ":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 = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(string(body), "Path:/test") {
|
||||
return errors.New("Incorrect traefik config")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//check
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://127.0.0.1:8001/", 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, 200)
|
||||
}
|
||||
|
||||
func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
// start traefik
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml", "--etcd", "--etcd.endpoint="+etcdHost+":4001")
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
|
||||
whoami1 := s.composeProject.Container(c, "whoami1")
|
||||
whoami2 := s.composeProject.Container(c, "whoami2")
|
||||
whoami3 := s.composeProject.Container(c, "whoami3")
|
||||
whoami4 := s.composeProject.Container(c, "whoami4")
|
||||
|
||||
//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://" + whoami1.NetworkSettings.IPAddress + ":80",
|
||||
"/traefik/backends/backend1/servers/server1/weight": "10",
|
||||
"/traefik/backends/backend1/servers/server2/url": "http://" + whoami2.NetworkSettings.IPAddress + ":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://" + whoami3.NetworkSettings.IPAddress + ":80",
|
||||
"/traefik/backends/backend2/servers/server1/weight": "1",
|
||||
"/traefik/backends/backend2/servers/server2/url": "http://" + whoami4.NetworkSettings.IPAddress + ":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 = utils.Try(60*time.Second, func() error {
|
||||
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(string(body), "Host:snitest.org") {
|
||||
return errors.New("Incorrect traefik config")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
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 *EtcdSuite) TestCommandStoreConfig(c *check.C) {
|
||||
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
|
||||
|
||||
cmd := exec.Command(traefikBinary, "storeconfig", "--configFile=fixtures/simple_web.toml", "--etcd.endpoint="+etcdHost+":4001")
|
||||
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": (etcdHost + ":4001"),
|
||||
}
|
||||
|
||||
for key, value := range checkmap {
|
||||
var p *store.KVPair
|
||||
err = utils.Try(60*time.Second, func() error {
|
||||
p, err = s.kv.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(string(p.Value), checker.Equals, value)
|
||||
|
||||
}
|
||||
}
|
||||
|
111
integration/eureka_test.go
Normal file
111
integration/eureka_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
"github.com/go-check/check"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
// Eureka test suites (using libcompose)
|
||||
type EurekaSuite struct{ BaseSuite }
|
||||
|
||||
func (s *EurekaSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "eureka")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
}
|
||||
|
||||
func (s *EurekaSuite) TestSimpleConfiguration(c *check.C) {
|
||||
|
||||
eurekaHost := s.composeProject.Container(c, "eureka").NetworkSettings.IPAddress
|
||||
whoami1Host := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
||||
|
||||
file := s.adaptFile(c, "fixtures/eureka/simple.toml", struct{ EurekaHost string }{eurekaHost})
|
||||
defer os.Remove(file)
|
||||
cmd := exec.Command(traefikBinary, "--configFile="+file)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
eurekaURL := "http://" + eurekaHost + ":8761/eureka/apps"
|
||||
|
||||
// wait for eureka
|
||||
err = utils.TryRequest(eurekaURL, 60*time.Second, func(res *http.Response) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
eurekaTemplate := `
|
||||
{
|
||||
"instance": {
|
||||
"hostName": "{{ .IP }}",
|
||||
"app": "{{ .ID }}",
|
||||
"ipAddr": "{{ .IP }}",
|
||||
"status": "UP",
|
||||
"port": {
|
||||
"$": {{ .Port }},
|
||||
"@enabled": "true"
|
||||
},
|
||||
"dataCenterInfo": {
|
||||
"name": "MyOwn"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
tmpl, err := template.New("eurekaTemlate").Parse(eurekaTemplate)
|
||||
c.Assert(err, checker.IsNil)
|
||||
buf := new(bytes.Buffer)
|
||||
templateVars := map[string]string{
|
||||
"ID": "tests-integration-traefik",
|
||||
"IP": whoami1Host,
|
||||
"Port": "80",
|
||||
}
|
||||
// add in eureka
|
||||
err = tmpl.Execute(buf, templateVars)
|
||||
resp, err := http.Post(eurekaURL+"/tests-integration-traefik", "application/json", strings.NewReader(buf.String()))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 204)
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(string(body), "Host:tests-integration-traefik") {
|
||||
return errors.New("Incorrect traefik config")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "tests-integration-traefik"
|
||||
resp, err = client.Do(req)
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
|
||||
// TODO validate : run on 80
|
||||
resp, err = http.Get("http://127.0.0.1:8000/")
|
||||
|
||||
// Expected a 404 as we did not configure anything
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 404)
|
||||
}
|
32
integration/fixtures/acme/acme.toml
Normal file
32
integration/fixtures/acme/acme.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
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 = true
|
||||
caServer = "http://{{.BoulderHost}}:4000/directory"
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend]
|
||||
[backends.backend.servers.server1]
|
||||
url = "http://127.0.0.1:9010"
|
||||
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend]
|
||||
backend = "backend"
|
||||
[frontends.frontend.routes.test]
|
||||
rule = "Host:traefik.acme.wtf"
|
@@ -12,3 +12,4 @@ logLevel = "DEBUG"
|
||||
endpoint = "{{.DockerHost}}"
|
||||
|
||||
domain = "docker.localhost"
|
||||
exposedbydefault = true
|
14
integration/fixtures/eureka/simple.toml
Normal file
14
integration/fixtures/eureka/simple.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
|
||||
[eureka]
|
||||
endpoint = "http://{{.EurekaHost}}:8761/eureka"
|
||||
delay = "1s"
|
||||
[web]
|
||||
address = ":8080"
|
27
integration/fixtures/healthcheck/simple.toml
Normal file
27
integration/fixtures/healthcheck/simple.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[web]
|
||||
address = ":8080"
|
||||
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.healthcheck]
|
||||
url = "/health"
|
||||
interval = "1s"
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://{{.Server1}}:80"
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://{{.Server2}}:80"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:test.localhost"
|
25
integration/fixtures/https/clientca/README.md
Normal file
25
integration/fixtures/https/clientca/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# This is how the certs were created
|
||||
|
||||
```bash
|
||||
openssl req -new -newkey rsa:2048 -x509 -days 3650 -extensions v3_ca -keyout ca1.pem -out ca1.crt
|
||||
openssl req -new -newkey rsa:2048 -x509 -days 3650 -extensions v3_ca -keyout ca2.pem -out ca2.crt
|
||||
openssl req -new -newkey rsa:2048 -x509 -days 3650 -extensions v3_ca -keyout ca3.pem -out ca3.crt
|
||||
openssl rsa -in ca1.pem -out ca1.key
|
||||
openssl rsa -in ca2.pem -out ca2.key
|
||||
openssl rsa -in ca3.pem -out ca3.key
|
||||
cat ca1.crt ca2.crt > ca1and2.crt
|
||||
rm ca1.pem ca2.pem ca3.pem
|
||||
|
||||
openssl genrsa -out client1.key 2048
|
||||
openssl genrsa -out client2.key 2048
|
||||
openssl genrsa -out client3.key 2048
|
||||
|
||||
openssl req -key client1.key -new -out client1.csr
|
||||
openssl req -key client2.key -new -out client2.csr
|
||||
openssl req -key client3.key -new -out client3.csr
|
||||
|
||||
openssl x509 -req -days 3650 -in client1.csr -CA ca1.crt -CAkey ca1.key -CAcreateserial -out client1.crt
|
||||
openssl x509 -req -days 3650 -in client2.csr -CA ca2.crt -CAkey ca2.key -CAcreateserial -out client2.crt
|
||||
openssl x509 -req -days 3650 -in client3.csr -CA ca3.crt -CAkey ca3.key -CAcreateserial -out client3.crt
|
||||
|
||||
```
|
20
integration/fixtures/https/clientca/ca1.crt
Normal file
20
integration/fixtures/https/clientca/ca1.crt
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMjCCAhqgAwIBAgIJAKXHiSnQw6LqMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||
BAMTD2NhMS5leGFtcGxlLmNvbTAeFw0xNjA2MTgxMzAyNDdaFw0yNjA2MTYxMzAy
|
||||
NDdaMBoxGDAWBgNVBAMTD2NhMS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAL9ZNf1Pqu30i/DUyAAbEVFfCvGEmN9hfGAK44IrBqfC
|
||||
1ziW2Lfg2AkswNIC/T6M+lcoN0ftPhJpnP2Cdz9U/gF9FMd/XAGY/SOiun7wC8so
|
||||
qdab7CMDlHP1c/XiL7lGEdm9RfynLcJ5JJn2X7mXwEZTviFtiJVmaoAl3TVNy3MZ
|
||||
ZyfjNac9sA5idpX66TpVO9tE1gu71nRkBvTEzO/IYv8rcWQmogvH7DN3UurP3RUK
|
||||
weij01rekG3OOOXUlQgZO6mhuvrKes9Xoc901bmTkOgTq7wIFf2AZozU4wy6kZfM
|
||||
0sdzmjMpuEr7oROepvtzFiVyNIEGDJ3QvEEY4QJaFvcCAwEAAaN7MHkwHQYDVR0O
|
||||
BBYEFFyJ/cSOOvcsfu+WLZbi/u3t8W/uMEoGA1UdIwRDMEGAFFyJ/cSOOvcsfu+W
|
||||
LZbi/u3t8W/uoR6kHDAaMRgwFgYDVQQDEw9jYTEuZXhhbXBsZS5jb22CCQClx4kp
|
||||
0MOi6jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCOBLJJF0esBVLX
|
||||
xmj0xa0TREXTxco40e/fmUU1cGYgl1UCCZI7MLDcl6k6Km9Sbp/LCpZx88mtLwGY
|
||||
wUss2mQ058kqiUrpb/U8xEbglLrRtsp1y8z7lood/8ru39zj1/9X4MFyqNi6390I
|
||||
zxZNf2QauUS1TMxgv6UhVE52JaAL+sn2hqA6IaSYeT9NFzFsulCr29mxlIC9SzUr
|
||||
Mbqri9LKX5aciy78+hQBKdXoJ5raRwttBvULabOrLhZdyvvL6QfcdgRV+JOT7vKn
|
||||
htQahWSKoqhdpM6Q2pXP42/MyuKXFB5Nk8fnFiIoXH0Bs9vlPLOvToM2jYJ+LlDd
|
||||
85qbL4eP
|
||||
-----END CERTIFICATE-----
|
27
integration/fixtures/https/clientca/ca1.key
Normal file
27
integration/fixtures/https/clientca/ca1.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAv1k1/U+q7fSL8NTIABsRUV8K8YSY32F8YArjgisGp8LXOJbY
|
||||
t+DYCSzA0gL9Poz6Vyg3R+0+Emmc/YJ3P1T+AX0Ux39cAZj9I6K6fvALyyip1pvs
|
||||
IwOUc/Vz9eIvuUYR2b1F/KctwnkkmfZfuZfARlO+IW2IlWZqgCXdNU3LcxlnJ+M1
|
||||
pz2wDmJ2lfrpOlU720TWC7vWdGQG9MTM78hi/ytxZCaiC8fsM3dS6s/dFQrB6KPT
|
||||
Wt6Qbc445dSVCBk7qaG6+sp6z1ehz3TVuZOQ6BOrvAgV/YBmjNTjDLqRl8zSx3Oa
|
||||
Mym4SvuhE56m+3MWJXI0gQYMndC8QRjhAloW9wIDAQABAoIBAGJ9g8mn6R5kImfK
|
||||
zksno4lTt2lLS/im0AMLd8E3bkyJgIgTNOeopupKC9HNUhaRMAYOoC24kpudmv3t
|
||||
2n1RvRB9FmX9SxlTavCdwQq3egqPGqRpS2lWXWI2dAKa8t+VjniZ8N00G9yeyFUr
|
||||
OGhqEMDiN9oy6/uiZK0jUDIwocjS5FZMBh+epM7/CnKj3uvqarmFXKcJ4ni28ww4
|
||||
RPrXDm+VvXa30/hK8q8Eo3C3u39TMvNEaRqMP/zqRY89fbpd1+Okno79dugFhz7D
|
||||
r/Jae9z4ChFBXegDmA/OkWOdLY5LyvwvpJpONjD/5wImY1OAJlFTg7S+2FcSVvCF
|
||||
diUJ7/ECgYEA9pHYlJsWAo/izRUVhKRtBAVVjnlidxExuvOGNXpyPjZd5ruXochu
|
||||
J6tAKA0rSE4RsISFVCrkQmjDgjyKa2D+o/hsTTlW3yrD4TSLI8/MrDtfCw9XRqeE
|
||||
KqfeqT79Hh0icnsUVYH4eoND9CKuJ/B9NcdyUqRPm7Pnrx07SnhGHd8CgYEAxqqy
|
||||
MPIDO2dadRqUIhWwMPIBegkZC1eeuv4pNEyukZc4+pXRshKXhvhmvz5NgsaSsKxZ
|
||||
O6FgqzgTceLEubVYF4hvy1TC+3Fc/PFvh4Fo3SKjtiJRJjRREDWBu6hl16Cw/83j
|
||||
k6Im//8WD1ri9iFf8RjrBwYH1xHqGTkNEUHl+ekCgYEAzlIWD6uCDFzIJGGLIvXP
|
||||
fvjTsadivE039r7Fw8QVCnfFtUetxyOHAUysH5d9a0BgTvtk8Zv+ao9tYXI1RUrh
|
||||
aOV8AlaDmbQYOj8UWsAL/OalTgTlO+r6jhLwH2DkvqkUZQUWa8KY4DMszoGihysW
|
||||
KsUcpYh2UMyGhqKINXVU/rMCgYEAqJxbG9trDtHLHjRuoPcTUJc01aQ/EzdMSpxH
|
||||
0FF8n6he/Z6GGMJaxHyyh4GTO3jZKwU7vrZaWzb+mdvC53KXz3FGoKXRzqIKL8uh
|
||||
wrn8jCJIG97ITMp+OmmPL/veY8HIN3NAwR4QR5jx2hpjIk51JSTm5FEj+k8EBmA7
|
||||
TPhG/XECgYA9e9B0jgR2aFSAWzpGMZYPW+NdGQlySv94AJmfF8U5J7PmU2BojvVn
|
||||
bhWNSQk2LI/mTjLgB+liYtLqFGkgIrJdbBOQ8hKSBPGQltSR0Dvf0ZK/0F1hqDTW
|
||||
m3AUvPZthNMNJIYkTav5a246tyKkmg11nUQsgoqdxCrEiLyv48PFnw==
|
||||
-----END RSA PRIVATE KEY-----
|
1
integration/fixtures/https/clientca/ca1.srl
Normal file
1
integration/fixtures/https/clientca/ca1.srl
Normal file
@@ -0,0 +1 @@
|
||||
83E81F36599F4400
|
40
integration/fixtures/https/clientca/ca1and2.crt
Normal file
40
integration/fixtures/https/clientca/ca1and2.crt
Normal file
@@ -0,0 +1,40 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMjCCAhqgAwIBAgIJAKXHiSnQw6LqMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||
BAMTD2NhMS5leGFtcGxlLmNvbTAeFw0xNjA2MTgxMzAyNDdaFw0yNjA2MTYxMzAy
|
||||
NDdaMBoxGDAWBgNVBAMTD2NhMS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAL9ZNf1Pqu30i/DUyAAbEVFfCvGEmN9hfGAK44IrBqfC
|
||||
1ziW2Lfg2AkswNIC/T6M+lcoN0ftPhJpnP2Cdz9U/gF9FMd/XAGY/SOiun7wC8so
|
||||
qdab7CMDlHP1c/XiL7lGEdm9RfynLcJ5JJn2X7mXwEZTviFtiJVmaoAl3TVNy3MZ
|
||||
ZyfjNac9sA5idpX66TpVO9tE1gu71nRkBvTEzO/IYv8rcWQmogvH7DN3UurP3RUK
|
||||
weij01rekG3OOOXUlQgZO6mhuvrKes9Xoc901bmTkOgTq7wIFf2AZozU4wy6kZfM
|
||||
0sdzmjMpuEr7oROepvtzFiVyNIEGDJ3QvEEY4QJaFvcCAwEAAaN7MHkwHQYDVR0O
|
||||
BBYEFFyJ/cSOOvcsfu+WLZbi/u3t8W/uMEoGA1UdIwRDMEGAFFyJ/cSOOvcsfu+W
|
||||
LZbi/u3t8W/uoR6kHDAaMRgwFgYDVQQDEw9jYTEuZXhhbXBsZS5jb22CCQClx4kp
|
||||
0MOi6jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCOBLJJF0esBVLX
|
||||
xmj0xa0TREXTxco40e/fmUU1cGYgl1UCCZI7MLDcl6k6Km9Sbp/LCpZx88mtLwGY
|
||||
wUss2mQ058kqiUrpb/U8xEbglLrRtsp1y8z7lood/8ru39zj1/9X4MFyqNi6390I
|
||||
zxZNf2QauUS1TMxgv6UhVE52JaAL+sn2hqA6IaSYeT9NFzFsulCr29mxlIC9SzUr
|
||||
Mbqri9LKX5aciy78+hQBKdXoJ5raRwttBvULabOrLhZdyvvL6QfcdgRV+JOT7vKn
|
||||
htQahWSKoqhdpM6Q2pXP42/MyuKXFB5Nk8fnFiIoXH0Bs9vlPLOvToM2jYJ+LlDd
|
||||
85qbL4eP
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMjCCAhqgAwIBAgIJAKjhXgiuPQexMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||
BAMTD2NhMi5leGFtcGxlLmNvbTAeFw0xNjA2MTgxMzAzMjJaFw0yNjA2MTYxMzAz
|
||||
MjJaMBoxGDAWBgNVBAMTD2NhMi5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAMx8S4U3tdeMGn1NEUNWCmD7pIYUCUhtORrn2rqF5b2M
|
||||
ZQJZXAIfWJ7KrGjn8W7KPx8/V2FREHF1Z6v1fpB2rfCIFo97HszhQEt6lduKup2j
|
||||
09ItpFjec7RahwaMksYDwl4PaxgKe2OYdLFJ/QIv8+I01vWPXFmHgZkBHQWhR5nV
|
||||
TvGM6MU834e+PXxCXfcaC8VYpbHYKYxHmM5Sxa5V9WlppBBshB0OL+KrCPXwPqHl
|
||||
StZPkG2p2qJUjCZ38uDx605RYaORZ0eDhrKj4M3lJzOTTcC4I77BzTb74+GcRT+R
|
||||
lJMrWrS22jNZONnawBdbTWIFM4PzaqVvE7qVwZK1M5UCAwEAAaN7MHkwHQYDVR0O
|
||||
BBYEFPooSq3ZvoyIzRQ96/dwUC0LDBvRMEoGA1UdIwRDMEGAFPooSq3ZvoyIzRQ9
|
||||
6/dwUC0LDBvRoR6kHDAaMRgwFgYDVQQDEw9jYTIuZXhhbXBsZS5jb22CCQCo4V4I
|
||||
rj0HsTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCvRgu11LrF7G9X
|
||||
yuvUwBZJ8FgjAMPwXQIAYg47tlvD9ZDiZgXVulWOm6aHpT520MjNO9f0oKpsrSsh
|
||||
7bsO4GSkbTPgGekbw4P3JtXAvlBEB5uabpdmF37Pg9s7dU/MeXCElzWF+yLVAo7o
|
||||
Hj1UlENxh08FzlErNw6Djy2FZAADeSZ3LmHUl+50rrp5/DxrEhkHFm8dTTjFVPnK
|
||||
KrnYLM8R7+v2Ysk6hTy4kwyiTKVZurK7ELRvS0RxWhtbVCXJ2HS1lv/LgEH1hyIP
|
||||
SwvyZ25JhcGrBAL/jpzTxdDEGsPfUSVfrUhrhDWxg0dzY+ptwdTWHqxyR2YKmOgU
|
||||
dKYIz/nK
|
||||
-----END CERTIFICATE-----
|
20
integration/fixtures/https/clientca/ca2.crt
Normal file
20
integration/fixtures/https/clientca/ca2.crt
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMjCCAhqgAwIBAgIJAKjhXgiuPQexMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||
BAMTD2NhMi5leGFtcGxlLmNvbTAeFw0xNjA2MTgxMzAzMjJaFw0yNjA2MTYxMzAz
|
||||
MjJaMBoxGDAWBgNVBAMTD2NhMi5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAMx8S4U3tdeMGn1NEUNWCmD7pIYUCUhtORrn2rqF5b2M
|
||||
ZQJZXAIfWJ7KrGjn8W7KPx8/V2FREHF1Z6v1fpB2rfCIFo97HszhQEt6lduKup2j
|
||||
09ItpFjec7RahwaMksYDwl4PaxgKe2OYdLFJ/QIv8+I01vWPXFmHgZkBHQWhR5nV
|
||||
TvGM6MU834e+PXxCXfcaC8VYpbHYKYxHmM5Sxa5V9WlppBBshB0OL+KrCPXwPqHl
|
||||
StZPkG2p2qJUjCZ38uDx605RYaORZ0eDhrKj4M3lJzOTTcC4I77BzTb74+GcRT+R
|
||||
lJMrWrS22jNZONnawBdbTWIFM4PzaqVvE7qVwZK1M5UCAwEAAaN7MHkwHQYDVR0O
|
||||
BBYEFPooSq3ZvoyIzRQ96/dwUC0LDBvRMEoGA1UdIwRDMEGAFPooSq3ZvoyIzRQ9
|
||||
6/dwUC0LDBvRoR6kHDAaMRgwFgYDVQQDEw9jYTIuZXhhbXBsZS5jb22CCQCo4V4I
|
||||
rj0HsTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCvRgu11LrF7G9X
|
||||
yuvUwBZJ8FgjAMPwXQIAYg47tlvD9ZDiZgXVulWOm6aHpT520MjNO9f0oKpsrSsh
|
||||
7bsO4GSkbTPgGekbw4P3JtXAvlBEB5uabpdmF37Pg9s7dU/MeXCElzWF+yLVAo7o
|
||||
Hj1UlENxh08FzlErNw6Djy2FZAADeSZ3LmHUl+50rrp5/DxrEhkHFm8dTTjFVPnK
|
||||
KrnYLM8R7+v2Ysk6hTy4kwyiTKVZurK7ELRvS0RxWhtbVCXJ2HS1lv/LgEH1hyIP
|
||||
SwvyZ25JhcGrBAL/jpzTxdDEGsPfUSVfrUhrhDWxg0dzY+ptwdTWHqxyR2YKmOgU
|
||||
dKYIz/nK
|
||||
-----END CERTIFICATE-----
|
27
integration/fixtures/https/clientca/ca2.key
Normal file
27
integration/fixtures/https/clientca/ca2.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAzHxLhTe114wafU0RQ1YKYPukhhQJSG05GufauoXlvYxlAllc
|
||||
Ah9YnsqsaOfxbso/Hz9XYVEQcXVnq/V+kHat8IgWj3sezOFAS3qV24q6naPT0i2k
|
||||
WN5ztFqHBoySxgPCXg9rGAp7Y5h0sUn9Ai/z4jTW9Y9cWYeBmQEdBaFHmdVO8Yzo
|
||||
xTzfh749fEJd9xoLxVilsdgpjEeYzlLFrlX1aWmkEGyEHQ4v4qsI9fA+oeVK1k+Q
|
||||
banaolSMJnfy4PHrTlFho5FnR4OGsqPgzeUnM5NNwLgjvsHNNvvj4ZxFP5GUkyta
|
||||
tLbaM1k42drAF1tNYgUzg/NqpW8TupXBkrUzlQIDAQABAoIBAGFMg2LQL2Zw8+nL
|
||||
UfuIZUfgdViXEBO2ZQW4bQtzyu12cFm9y1n3MGPebEs+klL1STPFH/7eY8SY6MuZ
|
||||
9K8oyXs6RgHfw7gZNk6z9bqROFrqKVBJB3qB3uxiZv1mxjASednn3D2EP1IUqPHz
|
||||
EsCHsLRiECaoIHk5USFMtlKHe1pmmsvQrQX7EV9Qg0VSGvQlgxc/Pcg/WeB6uT6u
|
||||
CS2serWpUE2dBUTJisnUuL7F5/3JbPEPbUG4eeTcO8IafvgdOgFEc5qUlYCFFai0
|
||||
fvjSabXrJO9QE1Huw0gyC/5FHlVr5x4aJ8NzPKcMRYqn7jpdwA0eyLyBo/KtPIbJ
|
||||
6s0PFAECgYEA98cKuyaBXpPyG7/Y0C89Mzlt5+Qr0fpPksH6GEelPJVdhrdXP32W
|
||||
66ROgCVZpf2pQeCCHfXyWdZQwEdSf+8ee1DJMSNgIm4Usqp6yIDS0iZ7pPWz0KSI
|
||||
un/dm3lRE7hFMIQfbNf3rA0WD8Ani3c76eZruwQ5DNdXNOM+z1DN38UCgYEA00V4
|
||||
6UOCcA3romkXuIyeyh/tuJ6K1J3ApUxA+E42f4raSMSMgnlAwpL0Wmt11bBOmToi
|
||||
UAtwFcTfJRJSOvfmM/nd66592FAV/D4xcDIiNGh4xNDi8LSKmSj0WRYPU3YjkdFN
|
||||
SwI48LmQKMfj3P8fClazKsdcDccfO4pyhEK98ZECgYEAt8QZw1/1hw22/Lm2tgCz
|
||||
JTCswNXLYjqBldjkAenxNROaf/WucdpVeoMr7YLGEIQnakJ2fn4QtmxrC5BaMaRJ
|
||||
OTBbZ2RTQnXeR/yEf/x7X31HKrtIF7BP7/Ixi8PYTAXY2vjCzdkHScWS3S+opJlU
|
||||
CE/rCpNBNLLpbMI1rVDCv/kCgYEAkP3/sg67yQ00prx7JBOVsl/hNK/R1YMCQC8p
|
||||
838x1axEjGYfjDeM4zwZaKiRMPsTpgMIo2iGHtqCzh1Zw9B38znLPMD+6uJjhD5m
|
||||
jXpKkS8VmvVEmi89Y0mBEFacZAoS9TLwWccHruWa8vHkBror4luIEJbLLUV3wNQO
|
||||
LYjkdJECgYBcIjZ1iQiOmFL8lm/JlPOs2JcT33fjnubreHkiG42dZFN2S8D5MdU5
|
||||
JBP6IVVllPmbptw9T4wcw+bjVa0LQtQMGZLMxdx5nJp5dmFE0Pj8MjLpLy641Vlx
|
||||
5sv2O+eRpt4yCiuHcuvDrKPGTyM2YqF7ilQwSC5Cfki155InnU2QUg==
|
||||
-----END RSA PRIVATE KEY-----
|
1
integration/fixtures/https/clientca/ca2.srl
Normal file
1
integration/fixtures/https/clientca/ca2.srl
Normal file
@@ -0,0 +1 @@
|
||||
9EAC6D05226C2216
|
20
integration/fixtures/https/clientca/ca3.crt
Normal file
20
integration/fixtures/https/clientca/ca3.crt
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMjCCAhqgAwIBAgIJAK/JGxwwmv1jMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
|
||||
BAMTD2NhMy5leGFtcGxlLmNvbTAeFw0xNjA2MTgxMzAzNTNaFw0yNjA2MTYxMzAz
|
||||
NTNaMBoxGDAWBgNVBAMTD2NhMy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAO0B9iUp5w0m1NWC9QYWhxSE/emmmKcx99DWnzKZoIbj
|
||||
TSRQtyhx+9c2z1dYAFZQpdVRSKQFn1IO8s51wlIc01KLFflz4EvSfAKZiAnkOOez
|
||||
wzVQ8JWgKfOJV/ZctFPo4xtdhQmO1+U+YgSfU0ASEhHvHbIPTUJNRTfkJsGygq4q
|
||||
/p9uA1TsjM4bh6AkiD1OlGjp0lbkzn3LLYpXWvgGsuejsdVkJS5pn2NKjkqVhhEg
|
||||
g7hKKqm8Nc3mb+vGhw/fNppN/xeOswpMPaW77LppyFoDd/OmqqWrbzn2Fqw1nELh
|
||||
zfo7AkKPyRm8eU3wSTIdmaXx1R5qPjqEmYrrDZ2HXa8CAwEAAaN7MHkwHQYDVR0O
|
||||
BBYEFMR6dBZAeGgkxwSC/62xGwLEdXCdMEoGA1UdIwRDMEGAFMR6dBZAeGgkxwSC
|
||||
/62xGwLEdXCdoR6kHDAaMRgwFgYDVQQDEw9jYTMuZXhhbXBsZS5jb22CCQCvyRsc
|
||||
MJr9YzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB3VgvPnLEEfbj4
|
||||
Z61q8oKneklZV+WpDyWSodI6M1l/0pXJCTDRROJ37KaQHLJRQo+rMJiYKvQkCU+y
|
||||
9JhLdRdMEzy++9hIWiNbDiy3BNMUiQOS1234WVFBosQ6uXNhXbL/Anl4xgiFFRZG
|
||||
FehjPo0XRvxmBHnrnE1Rce0EmU/1bwVglu8e7mG5bs0gQrXTRlTkxvucyi+B6npF
|
||||
2vuzxj4q+KgeEYURxCt95JoULtMY2c0VifcdweYDO/2sYEhOVi1N+PhPvZxJD6vR
|
||||
CxIuT6K3nRe58b1J/f7TH/dvURIb1mVG8+EDQVqa1bzH3JfytsIVG5VL1hppQlgZ
|
||||
Y0G4haMn
|
||||
-----END CERTIFICATE-----
|
27
integration/fixtures/https/clientca/ca3.key
Normal file
27
integration/fixtures/https/clientca/ca3.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA7QH2JSnnDSbU1YL1BhaHFIT96aaYpzH30NafMpmghuNNJFC3
|
||||
KHH71zbPV1gAVlCl1VFIpAWfUg7yznXCUhzTUosV+XPgS9J8ApmICeQ457PDNVDw
|
||||
laAp84lX9ly0U+jjG12FCY7X5T5iBJ9TQBISEe8dsg9NQk1FN+QmwbKCrir+n24D
|
||||
VOyMzhuHoCSIPU6UaOnSVuTOfcstilda+Aay56Ox1WQlLmmfY0qOSpWGESCDuEoq
|
||||
qbw1zeZv68aHD982mk3/F46zCkw9pbvsumnIWgN386aqpatvOfYWrDWcQuHN+jsC
|
||||
Qo/JGbx5TfBJMh2ZpfHVHmo+OoSZiusNnYddrwIDAQABAoIBAD87j71YkaFro8sX
|
||||
NmIabo2l8cx9uyqYZUKdkDnCzRZP3Iv80PEEgClqISVvgB+HQsdH+XZxXZFaFaPJ
|
||||
vT+FG0hhfUphhQ0VqipTZf0lm50N094MqzNwWOD12rcLAr2EW9s4Nz9WkflCjIop
|
||||
K9/jMlkAj86q0HUJApen0kNJah4nLPnkqKC9BQipGe2goERHA5N8MS/k/ODJrOzI
|
||||
qdD77wE5oov5sIePsGp3zCKNw89qoVTfkH8eYos6lPsAibYfgm5z7LwEtfe0ZizG
|
||||
myQfAYZx3Orl2eNxAb0c1dw+hNYKfeNAwn6h4J8AKuBHawZMb2ztlTj0ZludrhQC
|
||||
VuwAcrkCgYEA9sFsszjoSO8pXDnbaQ8UNGwy+C1t0fcZIOxIebKPcfipGio0R9vr
|
||||
SXEEfRQb+YdIFkQpe4hwAHt1Q75zh8z+oOTq8EHprxAwI9bzgyaEIHtGibvs99XT
|
||||
iWSPtL274CISiwSL8NzMl/orD6sDhmJqiXhwtf2SDubUJu3gz13CeRsCgYEA9eMM
|
||||
CYiOc4wLxKqyCqe3R86vnBFVauxp9eq9XTLvD+XoGqOksXupP8rE0jx26ILmKiQZ
|
||||
z99MGJoQicEpo+BW3L9wr6OJQZSrs+NqWCxlmFRJL+p3sw53B4zjgYaimNl5KH4G
|
||||
8pn7XbyRXtqhSBQ2kuNrkVI4SNxdEi1K+PoZ6v0CgYEAkwVcRsy5WftloVW3rTkW
|
||||
yMVO+R/YNyoLBtrBtAD4BugpmTVcQRR/dBqqmfvJTzuTb/Dc5oW8dg0ZKWvoWhmB
|
||||
/Utn0A71tSDoDfKc1J+2ScQpmxclceUtTMdl+EK0Fi827S2gU7q7DDI6RfOW/hLV
|
||||
d2MThNu4krhl32wMboFmxdECgYACwAhZbvKQ7kcPaw1Uuy18mx4xs6vt5zkELBz0
|
||||
Fua/mcWvzpa/+W8aLI1pAI4f6Z7jZ8X2Ijw6pjZ7I/LwR0kRbP64qC6X0i7dczS0
|
||||
ScLVIlQzOf8evJGuPvAoebYF2aDWSBqRyhEaqkpB8lYNdVRq7io81NuWTQipdGI7
|
||||
SKjTjQKBgBNSbDUWS2CAc+fsM/fBvYHKgrigVcKyvWwvb5LRXpWgPQH4LbqhG4uA
|
||||
g/mFTB5B1UBg9exN/dX6uegREdRA1/X+jRAzCqXYTFESo0/UrJhJQZ3waFKJ5PZK
|
||||
WChrSl6Lg5IMF2jYP5W0HwzbPPgRGibyELYBS5gZdAZpHgZToXeT
|
||||
-----END RSA PRIVATE KEY-----
|
1
integration/fixtures/https/clientca/ca3.srl
Normal file
1
integration/fixtures/https/clientca/ca3.srl
Normal file
@@ -0,0 +1 @@
|
||||
813218563E2DA0DE
|
17
integration/fixtures/https/clientca/client1.crt
Normal file
17
integration/fixtures/https/clientca/client1.crt
Normal file
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICszCCAZsCCQCD6B82WZ9D/jANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDEw9j
|
||||
YTEuZXhhbXBsZS5jb20wHhcNMTYwNjE4MTMzODQ0WhcNMjYwNjE2MTMzODQ0WjAd
|
||||
MRswGQYDVQQDExJjbGllbjEuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
|
||||
A4IBDwAwggEKAoIBAQDH75aclHoZkQfmeH3XpapxyF2/K73SpesY8Y8I3B33WnQc
|
||||
vIy5y554pPJMtGH3ZwiN6ifo3TBEs/2WjSOWYwxfXh3utllYArApelSgUrI7SBkw
|
||||
0MqVm9NG+X9cCTeWsCf+nldHOCnCARuyBEpLeRDPVlNmfgdNK2ar0KqqEPnN5UV+
|
||||
k968nAuqSDtRL7Yl7R/uxEq4MglM/ocxOpGIrLTFh1eclPVaQ/dNsEJpkrnYQlFZ
|
||||
aI1sWDzWoqtpAO15PgBBNnkW9EJGrF8dAds64U2jYBZLMKuHwvuERkEgOKEdUrB3
|
||||
uu1dWJxS5BCumWM1C3xs6qsLeonWxZ5GXjjWObZNAgMBAAEwDQYJKoZIhvcNAQEF
|
||||
BQADggEBAJKME0zm/0eokmXMCLJhKYgm8hDKOHKRFRZl7vwy9SC9cwhdlhcPEeeP
|
||||
5M+dXQCtEQWgo7phoJX8nBipZ/Y0lsvDD/I3XucIkUlbOW4rk18L83nBIN4paKzW
|
||||
I4CMJ6FQ72thP7L7wC/lzp3+qUCxmcpGjw9pkU3b1pQPkxBfOvfGtRFMG6E5+xj/
|
||||
MtL3owJzpIH2f7vtmIszBPcgFWpvB0Sq0eJ+TwuC1huvcnmP+YZ7Iz0JhsSRw+pU
|
||||
yiO9ByItBbGfK8x+DfUwCVsCL7vNscpjvTCgT3x2FNvS+XmiHZmZtpRGJPzvdI0m
|
||||
Bd615VD5z+SoG/SiemqDGmt2Ank/zcI=
|
||||
-----END CERTIFICATE-----
|
15
integration/fixtures/https/clientca/client1.csr
Normal file
15
integration/fixtures/https/clientca/client1.csr
Normal file
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICYjCCAUoCAQAwHTEbMBkGA1UEAxMSY2xpZW4xLmV4YW1wbGUuY29tMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx++WnJR6GZEH5nh916Wqcchdvyu9
|
||||
0qXrGPGPCNwd91p0HLyMucueeKTyTLRh92cIjeon6N0wRLP9lo0jlmMMX14d7rZZ
|
||||
WAKwKXpUoFKyO0gZMNDKlZvTRvl/XAk3lrAn/p5XRzgpwgEbsgRKS3kQz1ZTZn4H
|
||||
TStmq9CqqhD5zeVFfpPevJwLqkg7US+2Je0f7sRKuDIJTP6HMTqRiKy0xYdXnJT1
|
||||
WkP3TbBCaZK52EJRWWiNbFg81qKraQDteT4AQTZ5FvRCRqxfHQHbOuFNo2AWSzCr
|
||||
h8L7hEZBIDihHVKwd7rtXVicUuQQrpljNQt8bOqrC3qJ1sWeRl441jm2TQIDAQAB
|
||||
oAAwDQYJKoZIhvcNAQEFBQADggEBAEZ67vahAVydtW6LTXFI0cVY88vqunCWpOzz
|
||||
UgJAzUnWG84CGDiyezj/llv/Nq3YbEEpBuxp/prOEwrJXAi/+tjx7wCh2iLJDqo2
|
||||
aNRUiAvR/XZgafxq4NUrAze70u7BWR3QX+XSaxmIEEX1z1KJDGTfY6tYpCZNlUr+
|
||||
/Hl6MXwlpWX0WR26zIrjx5u0dEsY4pviN6NxTZRQJxbQO1H1wHr6poVngOhIdErp
|
||||
h2ZcqvTcASTkIEdKR6R8E2iYklgxIHNLWKaHZ6aBqW7lW17WKNSiGPfPVAtFhUTk
|
||||
tBmgdVreAwMj+AdaweBVt0uBqb/9UKhqNThEnh4kJn1I0pMJzP4=
|
||||
-----END CERTIFICATE REQUEST-----
|
27
integration/fixtures/https/clientca/client1.key
Normal file
27
integration/fixtures/https/clientca/client1.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAx++WnJR6GZEH5nh916Wqcchdvyu90qXrGPGPCNwd91p0HLyM
|
||||
ucueeKTyTLRh92cIjeon6N0wRLP9lo0jlmMMX14d7rZZWAKwKXpUoFKyO0gZMNDK
|
||||
lZvTRvl/XAk3lrAn/p5XRzgpwgEbsgRKS3kQz1ZTZn4HTStmq9CqqhD5zeVFfpPe
|
||||
vJwLqkg7US+2Je0f7sRKuDIJTP6HMTqRiKy0xYdXnJT1WkP3TbBCaZK52EJRWWiN
|
||||
bFg81qKraQDteT4AQTZ5FvRCRqxfHQHbOuFNo2AWSzCrh8L7hEZBIDihHVKwd7rt
|
||||
XVicUuQQrpljNQt8bOqrC3qJ1sWeRl441jm2TQIDAQABAoIBAQCtD942uu7VooxM
|
||||
GpATUfsvclhzWdF9vNC7TpyY9q+ZpFpNZYgKaw5JL73sV1dVZ4IoFT9mec+GKKag
|
||||
4pqjWikjg7w1HPJJFEqYHKOUAwDz/3yOnKw+xBslnGF5sSDE9sYnx7eUljDPFVZ7
|
||||
yOrmWW0Li5W1afG4ApFkt8KCYx9X8E0Mren2nfqoobM2l2LKFcF1Xs+M0iUAOoeN
|
||||
ojS/NTvxjZibm92CMblp7x6e51y+oq3TJFoUwFSAj3U26jyubL5sYpJeAeTxyZg6
|
||||
Y+UcEGmCpW5gsZSvRxvNxzCS4bCl9KOZXvyFtcVswHppfTynba6x8hDF7LkfJS7H
|
||||
z/Ut+e+hAoGBAO2d2M2316eBpwP7x99DQSFpg7E/emdcfdRuHDEonBeJr2X2Fw0a
|
||||
O/ZtUxcccovy4wrJcqiZsmjqetRUZ6ymsOaaASsPG21ChegFjm9D6+ZtejHpbuo3
|
||||
8HQ//LW7hqoiRQh3ODihfYCTwwxIIuwAdUzoxpM9Yu57Zg7reYuNh48LAoGBANdn
|
||||
c003ay1cq1fuuDJKENj10UZGotRdBxt+X1A7MhpMAqSaYJ+V2XOXpuMbIiX/qDfF
|
||||
M4hcQhJygoCozNzsynztyIjpGtl57AG95igOi2Hah0OOMt/1Z9UwaukIaHHo8Tyk
|
||||
sPZYoxBTstZcdsyHnGdU6n90SA9oYLBB89E3AocHAoGBAITR27M6FTCLl2jxn0qc
|
||||
FFbx3OwB2JDYMXnBxr5vvbimfMWYpk/rnyLi/zQG8bxqmyCXdCDsML7WeqwfNgha
|
||||
8L0lzotcGW+cZK9KE9D7/WvDPC+UFSyU8jJ45fBLjz2ghEf0JBf7pORvM/K0i9ix
|
||||
dN/1qbH5+Ufm8Chc1Yb9KI37AoGAAbxDoYugwWzNtJenxD/0gsr4NKi9Bxj4xa/u
|
||||
9KaFcNDL9KeJv79lURkXrxy42bWFlW1xTNfxcFSb2I2DmQQPXZJM202FedsRm7H7
|
||||
+LalSNSJ4nFy13sSqxUIx3fZ35EQ4HwzMMjmB2ulNTTpgBxXlj2I5h35tqYQoVrm
|
||||
q/jVfGECgYEAkU0L9bp1NPMzXgVJ2Os1VPSzoOywUQfx4NCJhTA1oZR+20JFsQBN
|
||||
b6g0q6xean0xDuXjDRrjPET5V/GPOQ7stAPTLtqN42XPuRcFzSNj7Skh+ALTP5JS
|
||||
bNZgBMwxQsbS89bUjRTDlRK/isuNIyUn0Zn7QlEsZEvJw0cNR3wPOc4=
|
||||
-----END RSA PRIVATE KEY-----
|
17
integration/fixtures/https/clientca/client2.crt
Normal file
17
integration/fixtures/https/clientca/client2.crt
Normal file
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtDCCAZwCCQCerG0FImwiFjANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDEw9j
|
||||
YTIuZXhhbXBsZS5jb20wHhcNMTYwNjE4MTgxNDUxWhcNMjYwNjE2MTgxNDUxWjAe
|
||||
MRwwGgYDVQQDExNjbGllbnQyLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEA9LaFxeiG1qpBTUIEl2vxvojOSWR1xkayaGtGsXBuqucf
|
||||
wifulsVC7dw+t7lW42pWutIuR98iflAZ9+tFX7TsEITNIyV64ePn9TF935LW2DFm
|
||||
AFkqYdZcTJ4qOkdsbbiHznlaIDPbMhIZFd9L8NEhrDTHuTtCav3g5B5V4okJfeNh
|
||||
iSpKm3WLHP6lFwG1RISLhHTCeIMFxer49iHiQ+A33TV2l0bQGcv4e1+OoRsXGKGs
|
||||
3oY6RJZ0GqzjeYqoybsLGBvZPPd2e8nH3RZac66XHMexsHHTV7L2tpWZm+JunMRg
|
||||
hMXONc0b3V9mbUdrjHY/aGDPADEevZA4LLztGUc3VQIDAQABMA0GCSqGSIb3DQEB
|
||||
BQUAA4IBAQCFo6IXUznH/iSuJtWrMtMkJTEg7o2qKRDgFApzw1J2URdvyYery15w
|
||||
6FddKFvkYhNLFl9Nb3Z8HLxruZrrItqwjR2kIG9lW00uxnwIcgwTibmwDQL5nr7m
|
||||
1cWzelhY/TVwBpLXRMg1YOXU8NRkT1VjkTUCpyIETI8b+wed67MkrofOadaY+FUL
|
||||
gk1F3yDKz35UYIKnlxKwvrdySE8WFza0PmiXQDtTG1moTpe1BDEK1b60vhfudMBK
|
||||
9vhE8kTooF01+su9gLUcrjVknI9H5PHtXID7FDiZ/disIAaWqSQLuvg/Kvb/cAFd
|
||||
PwTKgnJQVcTKXkz2leJ6fsvzYwlANob1
|
||||
-----END CERTIFICATE-----
|
15
integration/fixtures/https/clientca/client2.csr
Normal file
15
integration/fixtures/https/clientca/client2.csr
Normal file
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICYzCCAUsCAQAwHjEcMBoGA1UEAxMTY2xpZW50Mi5leGFtcGxlLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPS2hcXohtaqQU1CBJdr8b6Izklk
|
||||
dcZGsmhrRrFwbqrnH8In7pbFQu3cPre5VuNqVrrSLkffIn5QGffrRV+07BCEzSMl
|
||||
euHj5/Uxfd+S1tgxZgBZKmHWXEyeKjpHbG24h855WiAz2zISGRXfS/DRIaw0x7k7
|
||||
Qmr94OQeVeKJCX3jYYkqSpt1ixz+pRcBtUSEi4R0wniDBcXq+PYh4kPgN901dpdG
|
||||
0BnL+HtfjqEbFxihrN6GOkSWdBqs43mKqMm7Cxgb2Tz3dnvJx90WWnOulxzHsbBx
|
||||
01ey9raVmZvibpzEYITFzjXNG91fZm1Ha4x2P2hgzwAxHr2QOCy87RlHN1UCAwEA
|
||||
AaAAMA0GCSqGSIb3DQEBBQUAA4IBAQDHvJVKkKIqCWrJ9sZWQEYBaki76woJMjFW
|
||||
Ihyd12mzNfUW25hqfk7stablqu+CM/DhwOqLkxQleGAlp0BFo1wBOUDOgfrH5NVS
|
||||
9lAl7L/roEyRGH6V5/Hsbwi8zDsGOzWCuZk/gNGIZpB1c3TRXBUHsdqpz9FReDZf
|
||||
0HRD/7CH8hl96ZQTqhHE6+ysHzBB/4CuqbXVtTEhH52FdzCOpt5X0D6Pl/3lNlVd
|
||||
gMHAssoEa5E00XtjeJdxXuIKYbGLgldj6v+hHFX7k9UNveAXgYBbGtUQ9gA+uEf/
|
||||
qosVPEyvULj3aCJ8BSBulzPlhl9rNa/8Q1qUmzyCj28j3E4I22Oo
|
||||
-----END CERTIFICATE REQUEST-----
|
27
integration/fixtures/https/clientca/client2.key
Normal file
27
integration/fixtures/https/clientca/client2.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA9LaFxeiG1qpBTUIEl2vxvojOSWR1xkayaGtGsXBuqucfwifu
|
||||
lsVC7dw+t7lW42pWutIuR98iflAZ9+tFX7TsEITNIyV64ePn9TF935LW2DFmAFkq
|
||||
YdZcTJ4qOkdsbbiHznlaIDPbMhIZFd9L8NEhrDTHuTtCav3g5B5V4okJfeNhiSpK
|
||||
m3WLHP6lFwG1RISLhHTCeIMFxer49iHiQ+A33TV2l0bQGcv4e1+OoRsXGKGs3oY6
|
||||
RJZ0GqzjeYqoybsLGBvZPPd2e8nH3RZac66XHMexsHHTV7L2tpWZm+JunMRghMXO
|
||||
Nc0b3V9mbUdrjHY/aGDPADEevZA4LLztGUc3VQIDAQABAoIBAQDRxjVetjoAgup/
|
||||
w/wToeEVqEjN+WRMmAYQJQXwzaTQtFgxI/IPJQJ+zLKm5CZrxJichdhOnCUBisD4
|
||||
GaLarElAz9baLiLsyWXqdoakxUePBKmf2s/OFugAdgVU+C0m0Wz5vmVX/ZwFjCYc
|
||||
7dI3mc73xDcBvp7tAL1sT+Tn0PlmA3xURssiqC0J+4EtYzfHl1MvcQuU8JsVQjO3
|
||||
GvGWMr9EBO3oPa6yx3oWD4dn7xHLcCkuSJ6arIvASEaTyPg0Iu7roPrC7AXA+oGq
|
||||
+fbzJMqYZW6pMb8HZmxMt7X/srEq1kiyMYFy5fr+aun/vQ6596xjfFroEENJQY96
|
||||
+jir0biBAoGBAPutA9/2yo/fchRWLgpsWZ1SLXRWewFYqxlA7DluYXvqciYCXuKe
|
||||
S/+gLqHklHsc8YUwbEgW2oI9GPJ3iQps6XVNBaF9GvGjSrA+R6Ha3IT9ZUgvN4/d
|
||||
WOYiNRw5+eZ5PfxTufNK6EwXNwKR/siGEnWJ8AH0oNibTVzJHvMYSP2ZAoGBAPjq
|
||||
4a9MV6X7eShKHJtkqp33WWQWa3bidlmthhxjhPFlVnjJDj70oKGT5b/YcEFGBxPN
|
||||
JvTFJplQe0kLaeC49fPaEefARJe+HuCfUc1C73/q2o0pzvWf6Ut+W8ZZLKSC7aHH
|
||||
ZFAiZeMzzbCiqAbAAQFIgDp6m2U9mRYPTxKskoUdAoGBAMPEJzl1XMdhBfnvt0yA
|
||||
T4ziOV0/T9sSP7UbHSTnSYj8KuKKAYjBnVgwH1Xq2dyR/QSfT/sbW8jnAZrJhJ3J
|
||||
bifCS9j72ZOQcy54o3uxJMuF19y4bb3IbbhFb46PQmYWdTLrZb9ryxo1DKNBMTCF
|
||||
qaIoM7sxsFQNKbY884ggodYxAoGBAIbKopXL0HbIe65um5kmZSIPjK+fWGhTb+VW
|
||||
CxaaaaZSywWzUmyTCd0VesdtjDQ8mJ6HbH4FuMYzB9/hN1+CqWV4hFOsETjeslfO
|
||||
znxJr+nkIp9osXLfOnUwIsCBD6SyZb5CfDbMucHUDqGFI1osZR7txMpmFHo5ZgnF
|
||||
Fnu1Sc6dAoGAbgA9Cf6y8JGwr4/zGPDtaemBTLYMD8m/emdqGPgR7yVWXP/jTMqi
|
||||
o1EEWtpehALZMVZOsmSg7C/1J+nlHbuxPKsjjYK+V7aGsqXGx95lPyemd2cGcJN4
|
||||
fgoCyCahp2BnVCp7Gm3B/AzeZlH7n23qvbkJOsKGuocDycR9TIud+fk=
|
||||
-----END RSA PRIVATE KEY-----
|
17
integration/fixtures/https/clientca/client3.crt
Normal file
17
integration/fixtures/https/clientca/client3.crt
Normal file
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtDCCAZwCCQCBMhhWPi2g3jANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDEw9j
|
||||
YTMuZXhhbXBsZS5jb20wHhcNMTYwNjE4MTgxNTAwWhcNMjYwNjE2MTgxNTAwWjAe
|
||||
MRwwGgYDVQQDExNjbGllbnQzLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAyW1BnimfqeE2TFVCVaFSOnKucZMezOUs5CiNAECbgBPU
|
||||
ehAYNZCpKlD0ejZjc8/x2m0fnfHnmRdmCDprpI1gZV/dUMQCgmsq83pccnk3qFyn
|
||||
tdDTo0vxTKZhusihipKmVEpvtQP0hMH2De7QjwOpjxnIZwFH8anLr8EyUFNyF8fK
|
||||
k5emkMh8Xe5ppOTof36v9N/WPBW2/gxM9aj0l47CUSXjAUD8Fy8DeRtq/COywlnG
|
||||
DK25tnrQcX4RBwU9s8pHrXVrvmgLUEc3pWuxrwGJzQ/iY8l1mDDmhqjmcg1uGYOe
|
||||
hs/Olnx7pttUbd36mNXSSkjPeTabgpZDFtljMcTJwwIDAQABMA0GCSqGSIb3DQEB
|
||||
BQUAA4IBAQBUSxHFcGKaTBBj9peCgzr+buhPQ7F72uNe0uYZhcCn91KXECiFM+rh
|
||||
W13qcfsHDM/PPWN+TXHKzTxCHYv3fGkcAR/bUD206dXbO/T1Oc8UTciJFWXCxMK9
|
||||
zKlZgn48TcAIEhJodVcqWXr8hZ5Grxw4wB2DnTUTr5FuFS/f4gtlflPJzirxZGe8
|
||||
LPZb7QZ+LHxGK39QVY/g9LJxlWzbCytPBR0enb8ijjVj2+Sc+NntvQHqXedNFIql
|
||||
ns6X98nQtwFn9/MgLGbqOYNN36b15HddyDRgfZ6zMO3Aeve7GM5GqnpqhyprN91t
|
||||
gVaVUIxZCUNmcmtWu+a1QtK/MgLIpX4I
|
||||
-----END CERTIFICATE-----
|
15
integration/fixtures/https/clientca/client3.csr
Normal file
15
integration/fixtures/https/clientca/client3.csr
Normal file
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICYzCCAUsCAQAwHjEcMBoGA1UEAxMTY2xpZW50My5leGFtcGxlLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMltQZ4pn6nhNkxVQlWhUjpyrnGT
|
||||
HszlLOQojQBAm4AT1HoQGDWQqSpQ9Ho2Y3PP8dptH53x55kXZgg6a6SNYGVf3VDE
|
||||
AoJrKvN6XHJ5N6hcp7XQ06NL8UymYbrIoYqSplRKb7UD9ITB9g3u0I8DqY8ZyGcB
|
||||
R/Gpy6/BMlBTchfHypOXppDIfF3uaaTk6H9+r/Tf1jwVtv4MTPWo9JeOwlEl4wFA
|
||||
/BcvA3kbavwjssJZxgytubZ60HF+EQcFPbPKR611a75oC1BHN6Vrsa8Bic0P4mPJ
|
||||
dZgw5oao5nINbhmDnobPzpZ8e6bbVG3d+pjV0kpIz3k2m4KWQxbZYzHEycMCAwEA
|
||||
AaAAMA0GCSqGSIb3DQEBBQUAA4IBAQB3ApcHgGHXXYqkNHDp3xaXBrsYNnGSoqQq
|
||||
PoFqNh2SVVh9D25hcfTrCXbv8Ng+rTEZqb4BMrSPxl5vNRQL78M70NMNE1bXcdW3
|
||||
XWSh7vLxCAmHx7DKNxQI/96o5lG6FFkmZNYZ4CllqXaW0hV3CTuy4ixGwz4JJ6vI
|
||||
caS4OEMB/r+kEm0jReGjalS/KWk61+bZnHWKAkvpPxIFKp8YMi84I+GlE1YfbXiC
|
||||
kYtwCFmEd5T6Ztz/f/DtCF0JuH+S/2+R+7APD1RbaU8lCMDA292zlVP2mro2WRnZ
|
||||
GAbynaqxaPQIn+2LBD5ytRx9aHALj58vq/PVpUUKb20RwIf74+t1
|
||||
-----END CERTIFICATE REQUEST-----
|
27
integration/fixtures/https/clientca/client3.key
Normal file
27
integration/fixtures/https/clientca/client3.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAyW1BnimfqeE2TFVCVaFSOnKucZMezOUs5CiNAECbgBPUehAY
|
||||
NZCpKlD0ejZjc8/x2m0fnfHnmRdmCDprpI1gZV/dUMQCgmsq83pccnk3qFyntdDT
|
||||
o0vxTKZhusihipKmVEpvtQP0hMH2De7QjwOpjxnIZwFH8anLr8EyUFNyF8fKk5em
|
||||
kMh8Xe5ppOTof36v9N/WPBW2/gxM9aj0l47CUSXjAUD8Fy8DeRtq/COywlnGDK25
|
||||
tnrQcX4RBwU9s8pHrXVrvmgLUEc3pWuxrwGJzQ/iY8l1mDDmhqjmcg1uGYOehs/O
|
||||
lnx7pttUbd36mNXSSkjPeTabgpZDFtljMcTJwwIDAQABAoIBAFYbTqG+SXLlw8B9
|
||||
8g2JGQ3DWK9UpSYSEk62xxAEjnUCBSLpHnBHlwlv8hMMjRdFHa6yV4G9l7PqPMPn
|
||||
tXxys3KiuIl+QVRfW80Z0ctd5l0ivs8Kpm54WH7b4YtnmScT6ea+q2JGfpECGZ17
|
||||
Kcz5U9LIwtLFyWuVmm1XuZp9EZj4HI7XgaktPom2f97k7oyQgKvgHVMUU+KYjS0X
|
||||
KTTf+PkSR/laV9TXNTRlFOwblh8tjrb/CohR/REQ5yTM04oRHhD+TkIV2qIUuM5Z
|
||||
P2hmIrMGhoJt01eJlZz73vXBBFAd/rzEIuKBroEG+culxje56qBbMKvfhmVJNuMz
|
||||
6AvkPYECgYEA+yXVRcU7Zqz7rqq7Mw3s3yC6LzE5SNO1hRl9J2N4ldHf4bUeHEuh
|
||||
ztW8Q7XiiftLcCGJPc3QxWRWwC8omhwefJzEmmVgpDeTMVjI/nNpgdgKSQfM77LX
|
||||
jZtH8q5JH4cMvJbFMV3JN24LSTrctryt+p/Jps3x0FzbgNGEDik7/y0CgYEAzVGB
|
||||
UESd09YvBjjW+1CJXHwgTh9Qc4L4VhAt7+O83SxGJEeVMYeTi/YHM2X5ytFBq/1w
|
||||
M+j9CWpNi7XoHOCVWZfQjOvsrX5R0s7iBrL9ikkmaEy95VEgywsXTaoJE37/l5GR
|
||||
j9s8P6o3KeCyce++2Qi+dHvAUEspLS1nW8KvAq8CgYAKwrw4mRLKe27tNPOAZIBZ
|
||||
rxVLIFjL/gYxBb6PCXwJL0zgZto7bCIqso22ePyT3OiGjWlL9J2VV48//MVIlRvZ
|
||||
Sv5Bf0Z8wsTTwHIcNOW4YoFOT79AJfGGZ7jVdRI8/5RUIEGis9oDPfvNz2/VhJAP
|
||||
xPjm5LwPqWreQhveX3XqoQKBgQC86JANVYTNotTTabrLspcf5AkpOACit09ciDhr
|
||||
7uMXsKO8v6wSzUZBUZXggaQqKwy8fUweRvGCFy/QKwesgiqIK3m0H2I9YutQBg/K
|
||||
0CcddB6Feo6CDnoYt1SynY8KRCBQyZvfe3zcqvVkb5xf3pF/SV9K943DktQJACyI
|
||||
LgEuewKBgF48AjNyi7zMgH0h5rsVimtjS7p8PhwZXyN70B/poykR3sxhCnChuNmt
|
||||
47MNOQrNgXCQivrZk1uh4v/itoVCDX/GYz+bqHh8MihALuSX3eRnVPk8oDjoM1lN
|
||||
Hks5wU7RK8gy14DPtTxfr0ca65v7kzu3nY1UwdFuTjcDwIv8EdGj
|
||||
-----END RSA PRIVATE KEY-----
|
35
integration/fixtures/https/clientca/https_1ca1config.toml
Normal file
35
integration/fixtures/https/clientca/https_1ca1config.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1.crt"]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
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"
|
||||
|
||||
[file]
|
||||
|
||||
[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"
|
35
integration/fixtures/https/clientca/https_2ca1config.toml
Normal file
35
integration/fixtures/https/clientca/https_2ca1config.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1and2.crt"]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
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"
|
||||
|
||||
[file]
|
||||
|
||||
[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"
|
35
integration/fixtures/https/clientca/https_2ca2config.toml
Normal file
35
integration/fixtures/https/clientca/https_2ca2config.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":4443"
|
||||
[entryPoints.https.tls]
|
||||
ClientCAFiles = ["fixtures/https/clientca/ca1.crt", "fixtures/https/clientca/ca2.crt"]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
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"
|
||||
|
||||
[file]
|
||||
|
||||
[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"
|
9
integration/fixtures/mesos/simple.toml
Normal file
9
integration/fixtures/mesos/simple.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
logLevel = "DEBUG"
|
||||
|
||||
[mesos]
|
295
integration/glide.lock
generated
Normal file
295
integration/glide.lock
generated
Normal file
@@ -0,0 +1,295 @@
|
||||
hash: c53f57a45247b08a91f127ece494d49f1b7fee8c5f75be87ab12e27aa92d065f
|
||||
updated: 2016-11-17T16:23:56.727970904Z
|
||||
imports:
|
||||
- name: github.com/cenk/backoff
|
||||
version: 8edc80b07f38c27352fb186d971c628a6c32552b
|
||||
testImports:
|
||||
- name: github.com/ArthurHlt/go-eureka-client
|
||||
version: ba361cd0f9f571b4e871421423d2f02f5689c3d2
|
||||
subpackages:
|
||||
- eureka
|
||||
- name: github.com/ArthurHlt/gominlog
|
||||
version: 068c01ce147ad68fca25ef3fa29ae5395ae273ab
|
||||
- name: github.com/Azure/go-ansiterm
|
||||
version: fa152c58bc15761d0200cb75fe958b89a9d4888e
|
||||
subpackages:
|
||||
- winterm
|
||||
- name: github.com/boltdb/bolt
|
||||
version: f4c032d907f61f08dba2d719c58f108a1abb8e81
|
||||
- name: github.com/BurntSushi/toml
|
||||
version: 99064174e013895bbd9b025c31100bd1d9b590ca
|
||||
- name: github.com/BurntSushi/ty
|
||||
version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
|
||||
subpackages:
|
||||
- fun
|
||||
- name: github.com/cloudfoundry-incubator/candiedyaml
|
||||
version: 99c3df83b51532e3615f851d8c2dbb638f5313bf
|
||||
- name: github.com/containous/flaeg
|
||||
version: a731c034dda967333efce5f8d276aeff11f8ff87
|
||||
- name: github.com/containous/staert
|
||||
version: 92329254783dc01174f03302d51d7cf2c9ff84cf
|
||||
- name: github.com/containous/traefik
|
||||
version: 15732269da23c35524bf7cabea5857e4c5f63881
|
||||
subpackages:
|
||||
- autogen
|
||||
- cluster
|
||||
- job
|
||||
- log
|
||||
- provider
|
||||
- provider/k8s
|
||||
- safe
|
||||
- types
|
||||
- version
|
||||
- name: github.com/coreos/etcd
|
||||
version: c400d05d0aa73e21e431c16145e558d624098018
|
||||
subpackages:
|
||||
- Godeps/_workspace/src/github.com/ugorji/go/codec
|
||||
- Godeps/_workspace/src/golang.org/x/net/context
|
||||
- client
|
||||
- pkg/pathutil
|
||||
- pkg/types
|
||||
- name: github.com/daviddengcn/go-colortext
|
||||
version: 3b18c8575a432453d41fdafb340099fff5bba2f7
|
||||
- name: github.com/docker/distribution
|
||||
version: 99cb7c0946d2f5a38015443e515dc916295064d7
|
||||
subpackages:
|
||||
- context
|
||||
- digest
|
||||
- reference
|
||||
- registry/api/errcode
|
||||
- registry/api/v2
|
||||
- registry/client
|
||||
- registry/client/auth
|
||||
- registry/client/transport
|
||||
- registry/storage/cache
|
||||
- registry/storage/cache/memory
|
||||
- uuid
|
||||
- name: github.com/docker/docker
|
||||
version: 534753663161334baba06f13b8efa4cad22b5bc5
|
||||
subpackages:
|
||||
- api/types/backend
|
||||
- builder
|
||||
- builder/dockerignore
|
||||
- cliconfig
|
||||
- cliconfig/configfile
|
||||
- daemon/graphdriver
|
||||
- image
|
||||
- image/v1
|
||||
- layer
|
||||
- opts
|
||||
- pkg/archive
|
||||
- pkg/chrootarchive
|
||||
- pkg/fileutils
|
||||
- pkg/gitutils
|
||||
- pkg/homedir
|
||||
- pkg/httputils
|
||||
- pkg/idtools
|
||||
- pkg/ioutils
|
||||
- pkg/jsonlog
|
||||
- pkg/jsonmessage
|
||||
- pkg/longpath
|
||||
- pkg/mflag
|
||||
- pkg/mount
|
||||
- pkg/namesgenerator
|
||||
- pkg/plugins
|
||||
- pkg/plugins/transport
|
||||
- pkg/pools
|
||||
- pkg/progress
|
||||
- pkg/promise
|
||||
- pkg/random
|
||||
- pkg/reexec
|
||||
- pkg/signal
|
||||
- pkg/stdcopy
|
||||
- pkg/streamformatter
|
||||
- pkg/stringid
|
||||
- pkg/symlink
|
||||
- pkg/system
|
||||
- pkg/tarsum
|
||||
- pkg/term
|
||||
- pkg/term/windows
|
||||
- pkg/urlutil
|
||||
- reference
|
||||
- registry
|
||||
- runconfig/opts
|
||||
- name: github.com/docker/engine-api
|
||||
version: 62043eb79d581a32ea849645277023c550732e52
|
||||
subpackages:
|
||||
- client
|
||||
- client/transport
|
||||
- client/transport/cancellable
|
||||
- types
|
||||
- types/blkiodev
|
||||
- types/container
|
||||
- types/events
|
||||
- types/filters
|
||||
- types/network
|
||||
- types/reference
|
||||
- types/registry
|
||||
- types/strslice
|
||||
- types/swarm
|
||||
- types/time
|
||||
- types/versions
|
||||
- name: github.com/docker/go-connections
|
||||
version: 988efe982fdecb46f01d53465878ff1f2ff411ce
|
||||
subpackages:
|
||||
- nat
|
||||
- sockets
|
||||
- tlsconfig
|
||||
- name: github.com/docker/go-units
|
||||
version: f2145db703495b2e525c59662db69a7344b00bb8
|
||||
- name: github.com/docker/leadership
|
||||
version: 0a913e2d71a12fd14a028452435cb71ac8d82cb6
|
||||
- name: github.com/docker/libcompose
|
||||
version: d1876c1d68527a49c0aac22a0b161acc7296b740
|
||||
subpackages:
|
||||
- config
|
||||
- docker
|
||||
- docker/builder
|
||||
- docker/client
|
||||
- docker/network
|
||||
- labels
|
||||
- logger
|
||||
- lookup
|
||||
- project
|
||||
- project/events
|
||||
- project/options
|
||||
- utils
|
||||
- version
|
||||
- yaml
|
||||
- name: github.com/docker/libkv
|
||||
version: 3fce6a0f26e07da3eac45796a8e255547a47a750
|
||||
subpackages:
|
||||
- store
|
||||
- store/boltdb
|
||||
- store/consul
|
||||
- store/etcd
|
||||
- store/zookeeper
|
||||
- name: github.com/donovanhide/eventsource
|
||||
version: fd1de70867126402be23c306e1ce32828455d85b
|
||||
- name: github.com/flynn/go-shlex
|
||||
version: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||
- name: github.com/gambol99/go-marathon
|
||||
version: a558128c87724cd7430060ef5aedf39f83937f55
|
||||
- name: github.com/go-check/check
|
||||
version: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
||||
- name: github.com/gogo/protobuf
|
||||
version: 43ab7f0ec7b6d072e0368bd537ffefe74ed30198
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/golang/glog
|
||||
version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
|
||||
- name: github.com/google/go-querystring
|
||||
version: 9235644dd9e52eeae6fa48efd539fdc351a0af53
|
||||
subpackages:
|
||||
- query
|
||||
- name: github.com/gorilla/context
|
||||
version: 14f550f51af52180c2eefed15e5fd18d63c0a64a
|
||||
- name: github.com/gorilla/mux
|
||||
version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf
|
||||
- name: github.com/hashicorp/consul
|
||||
version: d8e2fb7dd594163e25a89bc52c1a4613f5c5bfb8
|
||||
subpackages:
|
||||
- api
|
||||
- name: github.com/hashicorp/go-cleanhttp
|
||||
version: ad28ea4487f05916463e2423a55166280e8254b5
|
||||
- name: github.com/hashicorp/serf
|
||||
version: 598c54895cc5a7b1a24a398d635e8c0ea0959870
|
||||
subpackages:
|
||||
- coordinate
|
||||
- name: github.com/libkermit/compose
|
||||
version: cadc5a3b83a15790174bd7fbc75ea2529785e772
|
||||
subpackages:
|
||||
- check
|
||||
- name: github.com/libkermit/docker
|
||||
version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7
|
||||
- name: github.com/libkermit/docker-check
|
||||
version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3
|
||||
- name: github.com/mattn/go-shellwords
|
||||
version: 525bedee691b5a8df547cb5cf9f86b7fb1883e24
|
||||
- name: github.com/mesos/mesos-go
|
||||
version: 068d5470506e3780189fe607af40892814197c5e
|
||||
subpackages:
|
||||
- detector
|
||||
- detector/zoo
|
||||
- mesosproto
|
||||
- mesosutil
|
||||
- upid
|
||||
- name: github.com/mesosphere/mesos-dns
|
||||
version: b47dc4c19f215e98da687b15b4c64e70f629bea5
|
||||
repo: https://github.com/containous/mesos-dns.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- detect
|
||||
- errorutil
|
||||
- logging
|
||||
- models
|
||||
- records
|
||||
- records/labels
|
||||
- records/state
|
||||
- util
|
||||
- name: github.com/Microsoft/go-winio
|
||||
version: ce2922f643c8fd76b46cadc7f404a06282678b34
|
||||
- name: github.com/miekg/dns
|
||||
version: 5d001d020961ae1c184f9f8152fdc73810481677
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1
|
||||
- name: github.com/moul/http2curl
|
||||
version: b1479103caacaa39319f75e7f57fc545287fca0d
|
||||
- name: github.com/ogier/pflag
|
||||
version: 45c278ab3607870051a2ea9040bb85fcb8557481
|
||||
- name: github.com/opencontainers/runc
|
||||
version: ba1568de399395774ad84c2ace65937814c542ed
|
||||
subpackages:
|
||||
- libcontainer/user
|
||||
- name: github.com/parnurzeal/gorequest
|
||||
version: e30af16d4e485943aab0b0885ad6bdbb8c0d3dc7
|
||||
- name: github.com/ryanuber/go-glob
|
||||
version: 572520ed46dbddaed19ea3d9541bdd0494163693
|
||||
- name: github.com/samuel/go-zookeeper
|
||||
version: 87e1bca4477a3cc767ca71be023ced183d74e538
|
||||
subpackages:
|
||||
- zk
|
||||
- name: github.com/satori/go.uuid
|
||||
version: 879c5887cd475cd7864858769793b2ceb0d44feb
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
|
||||
- name: github.com/spf13/pflag
|
||||
version: 5644820622454e71517561946e3d94b9f9db6842
|
||||
- name: github.com/stretchr/objx
|
||||
version: cbeaeb16a013161a98496fad62933b1d21786672
|
||||
- name: github.com/stretchr/testify
|
||||
version: b8dc1cecf15bdaf1988d9e87aa7cd98d899a06d6
|
||||
subpackages:
|
||||
- assert
|
||||
- mock
|
||||
- name: github.com/tv42/zbase32
|
||||
version: 03389da7e0bf9844767f82690f4d68fc097a1306
|
||||
- name: github.com/vbatts/tar-split
|
||||
version: bd4c5d64c3e9297f410025a3b1bd0c58f659e721
|
||||
subpackages:
|
||||
- archive/tar
|
||||
- tar/asm
|
||||
- tar/storage
|
||||
- name: github.com/vdemeester/docker-events
|
||||
version: be74d4929ec1ad118df54349fda4b0cba60f849b
|
||||
- name: github.com/vdemeester/shakers
|
||||
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||
- name: github.com/xeipuuv/gojsonpointer
|
||||
version: e0fe6f68307607d540ed8eac07a342c33fa1b54a
|
||||
- name: github.com/xeipuuv/gojsonreference
|
||||
version: e02fc20de94c78484cd5ffb007f8af96be030a45
|
||||
- name: github.com/xeipuuv/gojsonschema
|
||||
version: 00f9fafb54d2244d291b86ab63d12c38bd5c3886
|
||||
- name: golang.org/x/net
|
||||
version: db8e4de5b2d6653f66aea53094624468caad15d2
|
||||
subpackages:
|
||||
- context
|
||||
- proxy
|
||||
- publicsuffix
|
||||
- name: golang.org/x/sys
|
||||
version: 9c60d1c508f5134d1ca726b4641db998f2523357
|
||||
subpackages:
|
||||
- unix
|
||||
- windows
|
||||
- name: gopkg.in/fsnotify.v1
|
||||
version: 944cff21b3baf3ced9a880365682152ba577d348
|
33
integration/glide.yaml
Normal file
33
integration/glide.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
package: github.com/containous/traefik/integration
|
||||
import:
|
||||
- package: github.com/cenk/backoff
|
||||
testImport:
|
||||
- package: github.com/containous/staert
|
||||
version: 92329254783dc01174f03302d51d7cf2c9ff84cf
|
||||
- package: github.com/docker/docker
|
||||
version: 534753663161334baba06f13b8efa4cad22b5bc5
|
||||
subpackages:
|
||||
- pkg/namesgenerator
|
||||
- package: github.com/docker/libkv
|
||||
subpackages:
|
||||
- store
|
||||
- store/consul
|
||||
- store/etcd
|
||||
- package: github.com/go-check/check
|
||||
- package: github.com/hashicorp/consul
|
||||
subpackages:
|
||||
- api
|
||||
- package: github.com/libkermit/compose
|
||||
version: cadc5a3b83a15790174bd7fbc75ea2529785e772
|
||||
subpackages:
|
||||
- check
|
||||
- package: github.com/libkermit/docker
|
||||
version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7
|
||||
- package: github.com/libkermit/docker-check
|
||||
- package: github.com/mattn/go-shellwords
|
||||
- package: github.com/vdemeester/shakers
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- context
|
||||
- package: github.com/spf13/pflag
|
||||
version: 5644820622454e71517561946e3d94b9f9db6842
|
91
integration/healthcheck_test.go
Normal file
91
integration/healthcheck_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
"github.com/go-check/check"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
// HealchCheck test suites (using libcompose)
|
||||
type HealchCheckSuite struct{ BaseSuite }
|
||||
|
||||
func (s *HealchCheckSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "healthcheck")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
}
|
||||
|
||||
func (s *HealchCheckSuite) TestSimpleConfiguration(c *check.C) {
|
||||
|
||||
whoami1Host := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
||||
whoami2Host := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
|
||||
|
||||
file := s.adaptFile(c, "fixtures/healthcheck/simple.toml", struct {
|
||||
Server1 string
|
||||
Server2 string
|
||||
}{whoami1Host, whoami2Host})
|
||||
defer os.Remove(file)
|
||||
cmd := exec.Command(traefikBinary, "--configFile="+file)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(string(body), "Host:test.localhost") {
|
||||
return errors.New("Incorrect traefik config: " + string(body))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/health", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.localhost"
|
||||
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
|
||||
healthReq, err := http.NewRequest("POST", "http://"+whoami1Host+"/health", bytes.NewBuffer([]byte("500")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
_, err = client.Do(healthReq)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 200)
|
||||
|
||||
// TODO validate : run on 80
|
||||
resp, err = http.Get("http://127.0.0.1:8000/")
|
||||
|
||||
// Expected a 404 as we did not configure anything
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 404)
|
||||
}
|
@@ -30,6 +30,7 @@ func (s *HTTPSSuite) TestWithSNIConfigHandshake(c *check.C) {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
}
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
@@ -41,6 +42,9 @@ func (s *HTTPSSuite) TestWithSNIConfigHandshake(c *check.C) {
|
||||
cs := conn.ConnectionState()
|
||||
err = cs.PeerCertificates[0].VerifyHostname("snitest.com")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername"))
|
||||
|
||||
proto := conn.ConnectionState().NegotiatedProtocol
|
||||
c.Assert(proto, checker.Equals, "h2")
|
||||
}
|
||||
|
||||
// TestWithSNIConfigRoute involves a client sending HTTPS requests with
|
||||
@@ -93,6 +97,164 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
||||
c.Assert(resp.StatusCode, checker.Equals, 205)
|
||||
}
|
||||
|
||||
// TestWithClientCertificateAuthentication
|
||||
// The client has to send a certificate signed by a CA trusted by the server
|
||||
func (s *HTTPSSuite) TestWithClientCertificateAuthentication(c *check.C) {
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/https/clientca/https_1ca1config.toml")
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
// Connection without client certificate should fail
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
|
||||
// Connect with client certificate signed by ca1
|
||||
cert, err := tls.LoadX509KeyPair("fixtures/https/clientca/client1.crt", "fixtures/https/clientca/client1.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
|
||||
conn.Close()
|
||||
|
||||
// Connect with client signed by ca2 should fail
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
cert, err = tls.LoadX509KeyPair("fixtures/https/clientca/client2.crt", "fixtures/https/clientca/client2.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
|
||||
}
|
||||
|
||||
// TestWithClientCertificateAuthentication
|
||||
// Use two CA:s and test that clients with client signed by either of them can connect
|
||||
func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipeCAs(c *check.C) {
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/https/clientca/https_2ca1config.toml")
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
// Connection without client certificate should fail
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
|
||||
// Connect with client signed by ca1
|
||||
cert, err := tls.LoadX509KeyPair("fixtures/https/clientca/client1.crt", "fixtures/https/clientca/client1.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
|
||||
conn.Close()
|
||||
|
||||
// Connect with client signed by ca2
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
cert, err = tls.LoadX509KeyPair("fixtures/https/clientca/client2.crt", "fixtures/https/clientca/client2.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
conn.Close()
|
||||
|
||||
// Connect with client signed by ca3 should fail
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
cert, err = tls.LoadX509KeyPair("fixtures/https/clientca/client3.crt", "fixtures/https/clientca/client3.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
}
|
||||
|
||||
// TestWithClientCertificateAuthentication
|
||||
// Use two CA:s in two different files and test that clients with client signed by either of them can connect
|
||||
func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipeCAsMultipleFiles(c *check.C) {
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/https/clientca/https_2ca2config.toml")
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
// Connection without client certificate should fail
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
|
||||
// Connect with client signed by ca1
|
||||
cert, err := tls.LoadX509KeyPair("fixtures/https/clientca/client1.crt", "fixtures/https/clientca/client1.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
|
||||
conn.Close()
|
||||
|
||||
// Connect with client signed by ca2
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
cert, err = tls.LoadX509KeyPair("fixtures/https/clientca/client2.crt", "fixtures/https/clientca/client2.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
|
||||
conn.Close()
|
||||
|
||||
// Connect with client signed by ca3 should fail
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: "snitest.com",
|
||||
Certificates: []tls.Certificate{},
|
||||
}
|
||||
cert, err = tls.LoadX509KeyPair("fixtures/https/clientca/client3.crt", "fixtures/https/clientca/client3.key")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("unable to load client certificate and key"))
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
|
||||
conn, err = tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("should not be allowed to connect to server"))
|
||||
}
|
||||
|
||||
func startTestServer(port string, statusCode int) (ts *httptest.Server) {
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(statusCode)
|
||||
|
@@ -4,6 +4,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
"github.com/containous/traefik/integration/utils"
|
||||
"github.com/go-check/check"
|
||||
|
||||
"github.com/libkermit/docker-check/compose"
|
||||
compose "github.com/libkermit/compose/check"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
@@ -32,6 +33,9 @@ func init() {
|
||||
check.Suite(&EtcdSuite{})
|
||||
check.Suite(&MarathonSuite{})
|
||||
check.Suite(&ConstraintSuite{})
|
||||
check.Suite(&MesosSuite{})
|
||||
check.Suite(&EurekaSuite{})
|
||||
check.Suite(&AcmeSuite{})
|
||||
}
|
||||
|
||||
var traefikBinary = "../dist/traefik"
|
||||
@@ -50,6 +54,18 @@ func (s *BaseSuite) TearDownSuite(c *check.C) {
|
||||
func (s *BaseSuite) createComposeProject(c *check.C, name string) {
|
||||
projectName := fmt.Sprintf("integration-test-%s", name)
|
||||
composeFile := fmt.Sprintf("resources/compose/%s.yml", name)
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
c.Assert(err, checker.IsNil)
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := net.ParseCIDR(addr.String())
|
||||
c.Assert(err, checker.IsNil)
|
||||
if !ip.IsLoopback() && ip.To4() != nil {
|
||||
os.Setenv("DOCKER_HOST_IP", ip.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
s.composeProject = compose.CreateProject(c, projectName, composeFile)
|
||||
}
|
||||
|
||||
|
33
integration/mesos_test.go
Normal file
33
integration/mesos_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/go-check/check"
|
||||
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
// Mesos test suites (using libcompose)
|
||||
type MesosSuite struct{ BaseSuite }
|
||||
|
||||
func (s *MesosSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "mesos")
|
||||
}
|
||||
|
||||
func (s *MesosSuite) TestSimpleConfiguration(c *check.C) {
|
||||
cmd := exec.Command(traefikBinary, "--configFile=fixtures/mesos/simple.toml")
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
// TODO validate : run on 80
|
||||
resp, err := http.Get("http://127.0.0.1:8000/")
|
||||
|
||||
// Expected a 404 as we did not configure anything
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, 404)
|
||||
}
|
44
integration/resources/compose/boulder.yml
Normal file
44
integration/resources/compose/boulder.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
boulder:
|
||||
image: containous/boulder:release
|
||||
environment:
|
||||
FAKE_DNS: ${DOCKER_HOST_IP}
|
||||
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
|
||||
links:
|
||||
- bhsm:boulder-hsm
|
||||
- bmysql:boulder-mysql
|
||||
- brabbitmq:boulder-rabbitmq
|
||||
|
||||
bhsm:
|
||||
# To minimize the fetching of various layers this should match
|
||||
# the FROM image and tag in boulder/Dockerfile
|
||||
image: letsencrypt/boulder-tools:2016-11-02
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so
|
||||
expose:
|
||||
- 5657
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
log_driver: none
|
||||
brabbitmq:
|
||||
image: rabbitmq:3
|
||||
environment:
|
||||
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
|
14
integration/resources/compose/consul_tls.yml
Normal file
14
integration/resources/compose/consul_tls.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
consul:
|
||||
image: progrium/consul
|
||||
command: -server -bootstrap -log-level debug -ui-dir /ui -config-dir /configs
|
||||
ports:
|
||||
- "8500:8500"
|
||||
- "8585:8585"
|
||||
expose:
|
||||
- "8300"
|
||||
- "8301"
|
||||
- "8301/udp"
|
||||
- "8302"
|
||||
- "8302/udp"
|
||||
volumes:
|
||||
- ../tls:/configs
|
5
integration/resources/compose/eureka.yml
Normal file
5
integration/resources/compose/eureka.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
eureka:
|
||||
image: springcloud/eureka
|
||||
|
||||
whoami1:
|
||||
image: emilevauge/whoami
|
5
integration/resources/compose/healthcheck.yml
Normal file
5
integration/resources/compose/healthcheck.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
whoami1:
|
||||
image: emilevauge/whoami
|
||||
|
||||
whoami2:
|
||||
image: emilevauge/whoami
|
34
integration/resources/compose/mesos.yml
Normal file
34
integration/resources/compose/mesos.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
zk:
|
||||
image: bobrik/zookeeper
|
||||
net: host
|
||||
environment:
|
||||
ZK_CONFIG: tickTime=2000,initLimit=10,syncLimit=5,maxClientCnxns=128,forceSync=no,clientPort=2181
|
||||
ZK_ID: " 1"
|
||||
|
||||
master:
|
||||
image: mesosphere/mesos-master:0.28.1-2.0.20.ubuntu1404
|
||||
net: host
|
||||
environment:
|
||||
MESOS_ZK: zk://127.0.0.1:2181/mesos
|
||||
MESOS_HOSTNAME: 127.0.0.1
|
||||
MESOS_IP: 127.0.0.1
|
||||
MESOS_QUORUM: " 1"
|
||||
MESOS_CLUSTER: docker-compose
|
||||
MESOS_WORK_DIR: /var/lib/mesos
|
||||
|
||||
slave:
|
||||
image: mesosphere/mesos-slave:0.28.1-2.0.20.ubuntu1404
|
||||
net: host
|
||||
pid: host
|
||||
privileged: true
|
||||
environment:
|
||||
MESOS_MASTER: zk://127.0.0.1:2181/mesos
|
||||
MESOS_HOSTNAME: 127.0.0.1
|
||||
MESOS_IP: 127.0.0.1
|
||||
MESOS_CONTAINERIZERS: docker,mesos
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup
|
||||
- /usr/bin/docker:/usr/bin/docker:ro
|
||||
- /usr/lib/x86_64-linux-gnu/libapparmor.so.1:/usr/lib/x86_64-linux-gnu/libapparmor.so.1:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /lib/x86_64-linux-gnu/libsystemd-journal.so.0:/lib/x86_64-linux-gnu/libsystemd-journal.so.0
|
22
integration/resources/tls/ca.cert
Normal file
22
integration/resources/tls/ca.cert
Normal file
@@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrTCCApWgAwIBAgIJAO8QudN/gvGqMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV
|
||||
BAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxDTALBgNVBAcMBEx5b24xDzANBgNVBAoM
|
||||
BlplbmlrYTENMAsGA1UEAwwEdGVzdDEeMBwGCSqGSIb3DQEJARYPdGVzdEB6ZW5p
|
||||
a2EuY29tMB4XDTE2MDcwNjA5MTA1MloXDTI2MDcwNDA5MTA1MlowbTELMAkGA1UE
|
||||
BhMCRlIxDzANBgNVBAgMBkZyYW5jZTENMAsGA1UEBwwETHlvbjEPMA0GA1UECgwG
|
||||
WmVuaWthMQ0wCwYDVQQDDAR0ZXN0MR4wHAYJKoZIhvcNAQkBFg90ZXN0QHplbmlr
|
||||
YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKwrXlde3J8hFY
|
||||
hEvH1GH5SfA64/yb7pjXOwI3kvdS2dkbLglOL/jsolARAfFWxhhnnyJUy/BBzZtS
|
||||
rZN/IukuLCypzjnF6I9koVwILU2EkhPcBUzPZWD6gDU42XZH/lgglZyTyLA/pi24
|
||||
eAag5xVuTBMmBGbRsJJEq8MYgzSOAQLu2K8vFPARZdnvOMXVpfrC5+RxDj1AzyxU
|
||||
5s7olWWG13cWkkh2PUNdb1gCXsz34ALG3EmD2S92tovkKHUZS5zHnOvFl8bF7bKC
|
||||
MoXBi4bL2cUQXq815uFl0gfRrBgN4U+uT2UjzhIV9ax/xnkGueXi9wGPYP3Yanu8
|
||||
dguEtevRAgMBAAGjUDBOMB0GA1UdDgQWBBSxdmZrC6APPhMg73JGRa1sKPB2CDAf
|
||||
BgNVHSMEGDAWgBSxdmZrC6APPhMg73JGRa1sKPB2CDAMBgNVHRMEBTADAQH/MA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQBEZNfJxlKr/hv/cyfbJX6yUKDRG/sIFVD4G9uNEKak
|
||||
N9Dm5/FZ3pzosq/mBuMjXyY/5kYfiBPpyJfUK7CpWfa/U1RP76dDPm+3aaTNK0XS
|
||||
rWWxP/n5plfb6bt53cfKrnk9ud9ZqY6jX0vQzbVp6F4+jN3ZZfl4SEwlbK0jnrYV
|
||||
pbjPKbDS5o0RNgLuk/KN9x/KLb9FdgTYxVrB4orDUzpxx54sjfHRGodUAO9VIlbZ
|
||||
WteavUhCqbVWvYBB64vxKY695PeX79nmwCMVmsy8luquJYgIn27Czexuei3+2mxX
|
||||
f1rPZL+iCzi8cuShXqhrxH2dNyxsmYFjiPwFHSVgYtL2
|
||||
-----END CERTIFICATE-----
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user