1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-24 21:44:26 +03:00

Compare commits

...

86 Commits

Author SHA1 Message Date
Emile Vauge
a9f9894f29 Merge pull request #422 from containous/fix-travis-tag
Fix travis tag check
2016-05-31 00:15:58 +02:00
Emile Vauge
a6c360eeda Fix travis hangs on docker version
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-30 23:59:27 +02:00
Emile Vauge
01a4002169 Fix travis tag check
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-30 23:44:53 +02:00
Emile Vauge
8caaf317ae Merge pull request #412 from containous/prepare-release-candidate
Prepare release candidate
2016-05-30 18:27:48 +02:00
Emile Vauge
0e3c2ef10f Fix log config file
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-30 17:57:57 +02:00
Emile Vauge
db6c85d3d7 Prepare release candidate
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-30 17:48:50 +02:00
Vincent Demeester
2bd95620a5 Merge pull request #420 from cocap10/fixes-log
log info about TOML configuration file using
2016-05-30 17:48:00 +02:00
Martin
d8ad30f38a log info about TOML configuration file using
+glide update flaeg & staert
+fix README.md
+fix configFile flag description

Signed-off-by: Martin <martin.piegay@zenika.com>
2016-05-30 17:04:26 +02:00
Vincent Demeester
aad5f52968 Merge pull request #416 from download13/no_require_expose
Don't filter out containers with no exposed port if they have a traefik.port label set
2016-05-30 11:21:49 +02:00
Erin Dachtler
f5d49f6657 Merge branch 'master' into no_require_expose 2016-05-28 16:36:59 -07:00
Erin Dachtler
53ae64e578 Filter containers with no exposed ports unless they have a traefik.port label 2016-05-28 15:16:57 -07:00
Vincent Demeester
1a936b6aca Merge pull request #403 from cocap10/migrate-to-staert
Migrate to staert
2016-05-27 16:41:44 +02:00
Martin
4776fa1361 add parsers tests
Signed-off-by: Martin <martin.piegay@zenika.com>
2016-05-27 13:23:38 +02:00
Martin
c5084fd025 update staert + glide pin version 2016-05-27 10:06:19 +02:00
Martin
cc2735f733 add Debug StructTag
Signed-off-by: Martin <martin.piegay@zenika.com>
2016-05-27 10:06:19 +02:00
Martin
7f6b2b80f8 rm useless TestNoOrInexistentConfigShouldNotFail
Signed-off-by: Martin <martin.piegay@zenika.com>
2016-05-27 10:06:19 +02:00
Martin
f64c2bc065 add flag on ACME
add flag on Retry

set Retry.MaxMem to 2 by default

rm useless import

rm useless structtag

add custom parser on []acme.Domain type

add commants + refactor
2016-05-27 10:06:19 +02:00
Martin
6752b49536 rm useless StrucTag 2016-05-27 10:06:19 +02:00
Martin
ab138e7df1 update to new version go-bindata-assetfs 2016-05-27 10:06:19 +02:00
Martin
059da90a96 clean glide dependancies 2016-05-27 10:06:19 +02:00
Martin
0821c7bdd9 Add version in logs 2016-05-27 10:06:19 +02:00
Martin
89e00eb5a4 add staert & fleag 2016-05-27 10:06:19 +02:00
Martin
1a0f347023 update default value 2016-05-27 10:06:19 +02:00
Martin
1e27c2dabe fix TestNoOrInexistentConfigShouldNotFail 2016-05-27 10:06:19 +02:00
Martin
629be45c4a fix DisablePassHostHeaders 2016-05-27 10:06:19 +02:00
Martin
e115e3c4e7 fix default value 2016-05-27 10:06:19 +02:00
Martin
414fb1f406 add kubernetes.Namespaces parser 2016-05-27 10:06:19 +02:00
Martin
fe0a8f3363 Flaeg integration 2016-05-27 10:06:19 +02:00
Erin Dachtler
45589d5133 Reminder 2016-05-26 11:03:40 -07:00
Vincent Demeester
7804787e9e Merge pull request #408 from errm/k8s-endpoints
Build backend config using the K8S endpoint resource.
2016-05-26 17:12:23 +02:00
Ed Robinson
2e735f622f Adds some more coverage of the endpoint port selection logic. 2016-05-26 12:09:36 +01:00
Ed Robinson
6accb90c47 Simplify Service Lookup
Since we already know the name and namespace
of the service(s) we want we can just get the
correct one back from the API without filtering
the results.
2016-05-26 11:17:38 +01:00
Ed Robinson
e948a013cd Build backend config using the K8S endpoint resource.
* Potentialy saves a network hop
* Ability to configure LB algothim (given some work to expose an
anotation etc...)
* K8s config Watch is triggered far less often
2016-05-26 10:52:30 +01:00
Ed Robinson
b79535f369 Support ingresses without a host (#406)
fixes #370
2016-05-25 14:16:19 +02:00
Vincent Demeester
ed3bcc6d9a Merge pull request #387 from containous/fix-k8s-memory-leak
Fix k8s memory leak
2016-05-25 09:57:42 +02:00
Emile Vauge
0f23581f64 Fix k8s memory leak
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-24 23:37:17 +02:00
Emile Vauge
2af1e4b192 Fix k8s compose file
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-24 23:31:35 +02:00
Emile Vauge
dc404b365f Add expvar endpoint
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-24 23:31:35 +02:00
Emile Vauge
86f3891a2b Add debug flag
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-24 23:31:35 +02:00
Vincent Demeester
86053ea54b Update Makefile to fix local builds (#397)
Use --build-arg only if DOCKER_VERSION is specified

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2016-05-23 16:07:35 +02:00
Vincent Demeester
938600ba95 Merge pull request #398 from containous/fix-safari-http2
Fix safari http2
2016-05-23 14:37:19 +02:00
Emile Vauge
80ab967d39 Fix benchmarks doc
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-23 14:02:25 +02:00
Emile Vauge
43acbaa702 Fix safari error with http2
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-23 14:00:14 +02:00
Vincent Demeester
5d6492e6f5 Merge pull request #395 from vdemeester/carry-pr-382
Carry: http protocol should not use TLS
2016-05-23 13:11:56 +02:00
Jonas Falck
aeb9cc1732 http protocol should not use TLS
I need this in order to run kubectl proxy and then
make traefik use http://localhost to get to my cluster
when developing
2016-05-23 09:51:05 +02:00
Vincent Demeester
fa25c8ef22 Merge pull request #394 from vdemeester/carry-pr-312
Carry: Updating Toml to sure PathPrefix instead of Path
2016-05-23 09:50:38 +02:00
Russell Clare
77a9613c3a Updating Toml to sure PathPrefix instead of Path 2016-05-23 09:35:18 +02:00
Vincent Demeester
153ab8f0fa Update engine-api to fix versions issues (#383)
Updating an engine-api that has fixed some versioning issues (filters
json marshalling)

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2016-05-19 21:11:30 +02:00
Vincent Demeester
f6c860afc0 Merge pull request #380 from errm/k8s-ref-ports-by-name
Allow k8s ports to be referenced by name as well as number
2016-05-19 15:00:12 +02:00
Ed Robinson
d13b755df2 Allow k8s ports to be referenced by name as well as number 2016-05-19 13:36:19 +01:00
Vincent Demeester
6bacbf6cac Merge pull request #379 from errm/k8s-services-from-same-namespace
The referenced k8s service(s) must be in namespace
2016-05-19 14:08:53 +02:00
Ed Robinson
5923d22379 The referenced k8s service(s) must be in namespace
By design k8s ingress is only designed to ballance services from within
the namespace of the ingress.

This is disscuessed a little in
https://github.com/kubernetes/kubernetes/issues/17088.

For now traefik should only reference the services in the current
namespace. For me this was a confusing change of behaviour
from the reference implimentations, as I have services
with the same name in each namespace.
2016-05-18 17:38:47 +01:00
Vincent Demeester
70494117d1 Merge pull request #373 from AlmogBaku/master
K8s: add an option to configure "PathPrefixStrip" for the ingress-resource using annotation
2016-05-18 17:19:11 +02:00
AlmogBaku
8210743dad change log error of "ruleType" to warning 2016-05-17 16:54:40 +03:00
AlmogBaku
895f3cc109 fixes golint error 2016-05-17 16:22:37 +03:00
AlmogBaku
71f160dddc Following up to the conversation on Slack & GitHub:
- Change the annotation to define the rule type in `traefik.frontend.rule.type`
 - Update tests
 - Add documentation
 - Add example
2016-05-17 13:50:06 +03:00
AlmogBaku
92abaa0d47 fixes wrong actual on unit-test 2016-05-16 01:24:23 +03:00
AlmogBaku
47710c1385 fixes wrong names on tests - make it pass 2016-05-16 01:06:30 +03:00
AlmogBaku
df3abcbc9a gofmt to tests.. 2016-05-15 20:01:26 +03:00
AlmogBaku
dbb7ad41e5 Add tests for k8s PathPrefixStrip annotation 2016-05-15 12:16:27 +03:00
AlmogBaku
9773d4e409 K8s: add an option to configure "PathPrefixStrip" for the ingress-resource using annotation 2016-05-15 12:00:20 +03:00
Emile Vauge
993165fa66 Added Devoxx Video & support (#371)
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-13 17:32:39 +02:00
Emile Vauge
c49f5dad05 Merge pull request #368 from vdemeester/update-docker-deps
Update docker dependencies to fix issues
2016-05-13 08:21:09 +02:00
Vincent Demeester
c0bdedfed3 Add travis matrix for 1.9.1 and 1.10.1
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2016-05-12 17:18:58 +02:00
Vincent Demeester
061107b65f Update docker dependencies to fix issues
Update engine-api, libcompose and related dependencies (including
libkermit), to fix issues with docker v1.9.1 and lower.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2016-05-12 17:10:42 +02:00
Vincent Demeester
7bf421f847 Merge pull request #369 from containous/fix-hijack-logger
fix hijack logger middleware
2016-05-12 17:06:15 +02:00
Emile Vauge
cb0c1d34a2 fix hijack logger middleware
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-12 16:32:12 +02:00
Vincent Demeester
749b381f26 Merge pull request #355 from containous/change-PathPrefixStrip-k8s
Replace PathPrefixStrip by PathPrefix in k8s
2016-05-12 13:56:43 +02:00
Emile Vauge
d89279d708 Replace PathPrefixStrip by PathPrefix in k8s
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-12 12:44:26 +02:00
Marcus Stong
be209ed30c #216: sets passHostHeader to true by default (#351) 2016-05-10 13:43:24 +02:00
Emile Vauge
4a4ba2791d Merge pull request #357 from s7anley/generate-fix
Create static folder on first generate
2016-05-09 23:47:23 +02:00
Ján Koščo
c61d9776e7 Create static folder on first generate 2016-05-09 23:13:22 +02:00
Vincent Demeester
b5716abd3e Merge pull request #347 from keis/consul-catalog-replace-space-in-tags
Replace whitespace in tags in consul_catalog
2016-05-07 17:26:17 +02:00
David Keijser
b9bb78d04b Normalise tags in backend name of consul_catalog
Another fun thing consul lets you do is use spaces in your tags. This
means when including tags in backend name it's possible to generate
invalid names.
2016-05-07 16:30:38 +02:00
Vincent Demeester
8a39ee65cd Merge pull request #341 from errm/k8s-namespacing
Adds option to namespace k8s ingresses
2016-05-07 13:30:35 +02:00
Ed Robinson
301a463aeb Adds option to namespace k8s ingresses
If the flag kubernetes.namespaces is set...
Then we only select ingresses from that/those namespace(s)

This allows multiple instances of traefik to
independently load balance for each namespace.
This could be for logical or security reasons.

Addresses #336
2016-05-06 11:36:53 +01:00
Vincent Demeester
d1b0bece47 Merge pull request #356 from errm/documentation-documentation
Adds some documentation about adding documentation
2016-05-05 23:33:35 +02:00
Ed Robinson
63fd7d1d63 Adds some documentation about adding documentation 2016-05-05 21:45:57 +01:00
Vincent Demeester
f4fb2518a1 Merge pull request #352 from containous/remove-alpine-from-build
Go back to standard golang image
2016-05-05 19:45:12 +02:00
Emile Vauge
ee486de947 Go back to standard golang image
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-05 18:31:55 +02:00
Vincent Demeester
c1a12a58eb Merge pull request #340 from containous/fix-etcd-backend
Fix etcd backend with prefix /
2016-05-03 16:16:10 +02:00
Emile Vauge
c3aadab615 Add Consul integration tests
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-03 15:41:09 +02:00
Emile Vauge
26774d2317 Add Etcd integration tests
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-03 15:41:09 +02:00
Emile Vauge
61def880db Fix etcd backend with prefix /
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-05-03 15:41:09 +02:00
Vincent Demeester
11a6331185 Merge pull request #349 from stongo/acme-bundle
#345: bundle intermediate certificates
2016-05-03 11:49:35 +02:00
Marcus Stong
378509cef4 #345: bundle intermediate certificates
fixes warnings and gives generated certs an A rating on ssl labs
2016-05-02 20:01:10 -04:00
58 changed files with 2610 additions and 1006 deletions

View File

@@ -76,3 +76,34 @@ ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
Test success
```
### Documentation
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
First make sure you have python and pip installed
```
$ python --version
Python 2.7.2
$ pip --version
pip 1.5.2
```
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.
```
$ 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
[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
```

View File

@@ -1,19 +1,22 @@
branches:
except:
- /^v\d\.\d\.\d.*$/
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: v1.0.0-beta.$TRAVIS_BUILD_NUMBER
- VERSION: $TRAVIS_TAG
matrix:
- DOCKER_VERSION=1.9.1
- DOCKER_VERSION=1.10.1
sudo: required
services:
- docker
install:
- sudo service docker stop
- sudo curl https://get.docker.com/builds/Linux/x86_64/docker-1.10.1 -o /usr/bin/docker
- 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
before_script:

View File

@@ -18,6 +18,7 @@ REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
print-%: ; @echo $*=$($*)
@@ -46,7 +47,7 @@ validate: build ## validate gofmt, golint and go vet
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint
build: dist
docker build -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
build-webui:
docker build -t traefik-webui -f webui/Dockerfile webui

View File

@@ -63,9 +63,11 @@ Run it and forget it!
## Demo
Here is a demo of Træfɪk using Docker backend, showing a load-balancing between two servers, hot reloading of configuration, and graceful shutdown.
[![asciicast](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko.png)](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko)
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.
[![Traefik Devoxx France](http://img.youtube.com/vi/QvAz9mVx5TI/0.jpg)](http://www.youtube.com/watch?v=QvAz9mVx5TI)
## Web UI
@@ -87,7 +89,7 @@ You can access to a simple HTML frontend of Træfik.
- The simple way: grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
./traefik -c traefik.toml
./traefik --configFile=traefik.toml
```
- Use the tiny Docker image:
@@ -110,6 +112,11 @@ You can find the complete documentation [here](https://docs.traefik.io).
Please refer to [this section](.github/CONTRIBUTING.md).
## Support
You can join [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) to get basic support.
If you prefer a commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
## Træfɪk here and there
These projects use Træfɪk internally. If your company uses Træfɪk, we would be glad to get your feedback :) Contact us on [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com)
@@ -140,4 +147,4 @@ Founded in 2014, Asteris creates next-generation infrastructure software for the
## Credits
Thanks you [Peka](http://peka.byethost11.com/photoblog/) for your awesome work on the logo ![logo](docs/img/traefik.icon.png)
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo ![logo](docs/img/traefik.icon.png)

View File

@@ -16,6 +16,7 @@ import (
fmtlog "log"
"os"
"reflect"
"strings"
"sync"
"time"
)
@@ -161,15 +162,50 @@ func (dc *DomainsCertificate) needRenew() bool {
// ACME allows to connect to lets encrypt and retrieve certs
type ACME struct {
Email string
Domains []Domain
StorageFile string
OnDemand bool
CAServer string
EntryPoint string
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
}
//Domains parse []Domain
type Domains []Domain
//Set []Domain
func (ds *Domains) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
if len(slice) < 1 {
return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str)
}
d := Domain{
Main: slice[0],
SANs: []string{},
}
if len(slice) > 1 {
d.SANs = slice[1:]
}
*ds = append(*ds, d)
return nil
}
//Get []Domain
func (ds *Domains) Get() interface{} { return []Domain(*ds) }
//String returns []Domain in string
func (ds *Domains) String() string { return fmt.Sprintf("%+v", *ds) }
//SetValue sets []Domain into the parser
func (ds *Domains) SetValue(val interface{}) {
*ds = Domains(val.([]Domain))
}
// Domain holds a domain name with SANs
type Domain struct {
Main string
@@ -406,7 +442,7 @@ func (a *ACME) saveAccount(Account *Account) error {
func (a *ACME) getDomainsCertificates(client *acme.Client, domains []string) (*Certificate, error) {
log.Debugf("Loading ACME certificates %s...", domains)
bundle := false
bundle := true
certificate, failures := client.ObtainCertificate(domains, bundle, nil)
if len(failures) > 0 {
log.Error(failures)

61
acme/acme_test.go Normal file
View File

@@ -0,0 +1,61 @@
package acme
import (
"reflect"
"testing"
)
func TestDomainsSet(t *testing.T) {
checkMap := map[string]Domains{
"": {},
"foo.com": {Domain{Main: "foo.com", SANs: []string{}}},
"foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}},
"foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
}
for in, check := range checkMap {
ds := Domains{}
ds.Set(in)
if !reflect.DeepEqual(check, ds) {
t.Errorf("Expected %+v\nGo %+v", check, ds)
}
}
}
func TestDomainsSetAppend(t *testing.T) {
inSlice := []string{
"",
"foo1.com",
"foo2.com,bar.net",
"foo3.com,bar1.net,bar2.net,bar3.net",
}
checkSlice := []Domains{
{},
{
Domain{
Main: "foo1.com",
SANs: []string{}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}},
Domain{Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
}
ds := Domains{}
for i, in := range inSlice {
ds.Set(in)
if !reflect.DeepEqual(checkSlice[i], ds) {
t.Errorf("Expected %s %+v\nGo %+v", in, checkSlice[i], ds)
}
}
}

View File

@@ -1,17 +1,12 @@
FROM golang:1.6.1-alpine
FROM golang:1.6.2
RUN apk update && apk add git bash gcc musl-dev \
&& go get github.com/Masterminds/glide \
&& go get github.com/mitchellh/gox \
RUN go get github.com/Masterminds/glide \
&& go get github.com/jteeuwen/go-bindata/... \
&& go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck
# Which docker version to test on
ENV DOCKER_VERSION 1.10.1
# enable GO15VENDOREXPERIMENT
ENV GO15VENDOREXPERIMENT 1
ARG DOCKER_VERSION=1.10.1
# Download docker
RUN set -ex; \

226
cmd.go
View File

@@ -1,226 +0,0 @@
/*
Copyright
*/
package main
import (
"encoding/json"
fmtlog "log"
"os"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"net/http"
)
var traefikCmd = &cobra.Command{
Use: "traefik",
Short: "traefik, a modern reverse proxy",
Long: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at http://traefik.io`,
Run: func(cmd *cobra.Command, args []string) {
run()
},
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version",
Long: `Print version`,
Run: func(cmd *cobra.Command, args []string) {
fmtlog.Println(Version + " built on the " + BuildDate)
os.Exit(0)
},
}
var arguments = struct {
GlobalConfiguration
web bool
file bool
docker bool
dockerTLS bool
marathon bool
consul bool
consulTLS bool
consulCatalog bool
zookeeper bool
etcd bool
etcdTLS bool
boltdb bool
kubernetes bool
}{
GlobalConfiguration{
EntryPoints: make(EntryPoints),
Docker: &provider.Docker{
TLS: &provider.DockerTLS{},
},
File: &provider.File{},
Web: &WebProvider{},
Marathon: &provider.Marathon{},
Consul: &provider.Consul{
Kv: provider.Kv{
TLS: &provider.KvTLS{},
},
},
ConsulCatalog: &provider.ConsulCatalog{},
Zookeeper: &provider.Zookepper{},
Etcd: &provider.Etcd{
Kv: provider.Kv{
TLS: &provider.KvTLS{},
},
},
Boltdb: &provider.BoltDb{},
Kubernetes: &provider.Kubernetes{},
},
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
}
func init() {
traefikCmd.AddCommand(versionCmd)
traefikCmd.PersistentFlags().StringP("configFile", "c", "", "Configuration file to use (TOML, JSON, YAML, HCL).")
traefikCmd.PersistentFlags().StringP("graceTimeOut", "g", "10", "Timeout in seconds. Duration to give active requests a chance to finish during hot-reloads")
traefikCmd.PersistentFlags().String("accessLogsFile", "log/access.log", "Access logs file")
traefikCmd.PersistentFlags().String("traefikLogsFile", "log/traefik.log", "Traefik logs file")
traefikCmd.PersistentFlags().Var(&arguments.EntryPoints, "entryPoints", "Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'")
traefikCmd.PersistentFlags().Var(&arguments.DefaultEntryPoints, "defaultEntryPoints", "Entrypoints to be used by frontends that do not specify any entrypoint")
traefikCmd.PersistentFlags().StringP("logLevel", "l", "ERROR", "Log level")
traefikCmd.PersistentFlags().DurationVar(&arguments.ProvidersThrottleDuration, "providersThrottleDuration", time.Duration(2*time.Second), "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.")
traefikCmd.PersistentFlags().Int("maxIdleConnsPerHost", 0, "If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used")
traefikCmd.PersistentFlags().BoolVar(&arguments.web, "web", false, "Enable Web backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.Address, "web.address", ":8080", "Web administration port")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.CertFile, "web.cerFile", "", "SSL certificate")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.KeyFile, "web.keyFile", "", "SSL certificate")
traefikCmd.PersistentFlags().BoolVar(&arguments.Web.ReadOnly, "web.readOnly", false, "Enable read only API")
traefikCmd.PersistentFlags().BoolVar(&arguments.file, "file", false, "Enable File backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.File.Watch, "file.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.File.Filename, "file.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().BoolVar(&arguments.docker, "docker", false, "Enable Docker backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.Watch, "docker.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Filename, "docker.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Endpoint, "docker.endpoint", "unix:///var/run/docker.sock", "Docker server endpoint. Can be a tcp or a unix socket endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Domain, "docker.domain", "", "Default domain used")
traefikCmd.PersistentFlags().BoolVar(&arguments.dockerTLS, "docker.tls", false, "Enable Docker TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.CA, "docker.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Cert, "docker.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Key, "docker.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.TLS.InsecureSkipVerify, "docker.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.marathon, "marathon", false, "Enable Marathon backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.Watch, "marathon.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Filename, "marathon.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Endpoint, "marathon.endpoint", "http://127.0.0.1:8080", "Marathon server endpoint. You can also specify multiple endpoint for Marathon")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Domain, "marathon.domain", "", "Default domain used")
traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.ExposedByDefault, "marathon.exposedByDefault", true, "Expose Marathon apps by default")
traefikCmd.PersistentFlags().BoolVar(&arguments.consul, "consul", false, "Enable Consul backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.Watch, "consul.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Filename, "consul.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Comma sepparated Consul server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Prefix, "consul.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.consulTLS, "consul.tls", false, "Enable Consul TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.CA, "consul.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Cert, "consul.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Key, "consul.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.TLS.InsecureSkipVerify, "consul.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.consulCatalog, "consulCatalog", false, "Enable Consul catalog backend")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Domain, "consulCatalog.domain", "", "Default domain used")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Endpoint, "consulCatalog.endpoint", "127.0.0.1:8500", "Consul server endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Prefix, "consulCatalog.prefix", "traefik", "Consul catalog tag prefix")
traefikCmd.PersistentFlags().BoolVar(&arguments.zookeeper, "zookeeper", false, "Enable Zookeeper backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Zookeeper.Watch, "zookeeper.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Filename, "zookeeper.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Endpoint, "zookeeper.endpoint", "127.0.0.1:2181", "Comma sepparated Zookeeper server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Prefix, "zookeeper.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.etcd, "etcd", false, "Enable Etcd backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.Watch, "etcd.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Filename, "etcd.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Comma sepparated Etcd server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Prefix, "etcd.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.etcdTLS, "etcd.tls", false, "Enable Etcd TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.CA, "etcd.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Cert, "etcd.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Key, "etcd.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.TLS.InsecureSkipVerify, "etcd.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.boltdb, "boltdb", false, "Enable Boltdb backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Boltdb.Watch, "boltdb.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Filename, "boltdb.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Endpoint, "boltdb.endpoint", "127.0.0.1:4001", "Boltdb server endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Prefix, "boltdb.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.kubernetes, "kubernetes", false, "Enable Kubernetes backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Kubernetes.Endpoint, "kubernetes.endpoint", "127.0.0.1:8080", "Kubernetes server endpoint")
_ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile"))
_ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut"))
_ = viper.BindPFlag("logLevel", traefikCmd.PersistentFlags().Lookup("logLevel"))
// TODO: wait for this issue to be corrected: https://github.com/spf13/viper/issues/105
_ = viper.BindPFlag("providersThrottleDuration", traefikCmd.PersistentFlags().Lookup("providersThrottleDuration"))
_ = viper.BindPFlag("maxIdleConnsPerHost", traefikCmd.PersistentFlags().Lookup("maxIdleConnsPerHost"))
viper.SetDefault("providersThrottleDuration", time.Duration(2*time.Second))
viper.SetDefault("logLevel", "ERROR")
viper.SetDefault("MaxIdleConnsPerHost", 200)
}
func run() {
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
// load global configuration
globalConfiguration := LoadConfiguration()
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
defer loggerMiddleware.Close()
// logging
level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
log.Fatal("Error getting level", err)
}
log.SetLevel(level)
if len(globalConfiguration.TraefikLogsFile) > 0 {
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
defer func() {
if err := fi.Close(); err != nil {
log.Error("Error closinf file", err)
}
}()
if err != nil {
log.Fatal("Error opening file", err)
} else {
log.SetOutput(fi)
log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
}
} else {
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Debugf("Global configuration loaded %s", string(jsonConf))
server := NewServer(*globalConfiguration)
server.Start()
defer server.Close()
log.Info("Shutting down")
}

View File

@@ -3,41 +3,44 @@ package main
import (
"errors"
"fmt"
fmtlog "log"
"regexp"
"strings"
"time"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/types"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"regexp"
"strings"
"time"
)
// TraefikConfiguration holds GlobalConfiguration and other stuff
type TraefikConfiguration struct {
GlobalConfiguration
ConfigFile string `short:"c" description:"Configuration file to use (TOML)."`
}
// GlobalConfiguration holds global configuration (with providers, etc.).
// It's populated from the traefik configuration file passed as an argument to the binary.
type GlobalConfiguration struct {
GraceTimeOut int64
AccessLogsFile string
TraefikLogsFile string
LogLevel string
EntryPoints EntryPoints
ACME *acme.ACME
DefaultEntryPoints DefaultEntryPoints
ProvidersThrottleDuration time.Duration
MaxIdleConnsPerHost int
Retry *Retry
Docker *provider.Docker
File *provider.File
Web *WebProvider
Marathon *provider.Marathon
Consul *provider.Consul
ConsulCatalog *provider.ConsulCatalog
Etcd *provider.Etcd
Zookeeper *provider.Zookepper
Boltdb *provider.BoltDb
Kubernetes *provider.Kubernetes
GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."`
Debug bool `short:"d" description:"Enable debug mode"`
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'"`
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"`
Retry *Retry `description:"Enable retry sending request if network error"`
Docker *provider.Docker `description:"Enable Docker backend"`
File *provider.File `description:"Enable File backend"`
Web *WebProvider `description:"Enable Web backend"`
Marathon *provider.Marathon `description:"Enable Marathon backend"`
Consul *provider.Consul `description:"Enable Consul backend"`
ConsulCatalog *provider.ConsulCatalog `description:"Enable Consul catalog backend"`
Etcd *provider.Etcd `description:"Enable Etcd backend"`
Zookeeper *provider.Zookepper `description:"Enable Zookeeper backend"`
Boltdb *provider.BoltDb `description:"Enable Boltdb backend"`
Kubernetes *provider.Kubernetes `description:"Enable Kubernetes backend"`
}
// DefaultEntryPoints holds default entry points
@@ -46,7 +49,7 @@ type DefaultEntryPoints []string
// 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 (dep *DefaultEntryPoints) String() string {
return fmt.Sprintf("%#v", dep)
return strings.Join(*dep, ",")
}
// Set is the method to set the flag value, part of the flag.Value interface.
@@ -63,9 +66,17 @@ func (dep *DefaultEntryPoints) Set(value string) error {
return nil
}
// Get return the EntryPoints map
func (dep *DefaultEntryPoints) Get() interface{} { return DefaultEntryPoints(*dep) }
// SetValue sets the EntryPoints map with val
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
*dep = DefaultEntryPoints(val.(DefaultEntryPoints))
}
// Type is type of the struct
func (dep *DefaultEntryPoints) Type() string {
return fmt.Sprint("defaultentrypoints²")
return fmt.Sprint("defaultentrypoints")
}
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
@@ -74,7 +85,7 @@ type EntryPoints map[string]*EntryPoint
// 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 (ep *EntryPoints) String() string {
return ""
return fmt.Sprintf("%+v", *ep)
}
// Set is the method to set the flag value, part of the flag.Value interface.
@@ -121,6 +132,14 @@ func (ep *EntryPoints) Set(value string) error {
return nil
}
// Get return the EntryPoints map
func (ep *EntryPoints) Get() interface{} { return EntryPoints(*ep) }
// SetValue sets the EntryPoints map with val
func (ep *EntryPoints) SetValue(val interface{}) {
*ep = EntryPoints(val.(EntryPoints))
}
// Type is type of the struct
func (ep *EntryPoints) Type() string {
return fmt.Sprint("entrypoints²")
@@ -186,121 +205,101 @@ type Certificate struct {
// Retry contains request retry config
type Retry struct {
Attempts int
MaxMem int64
Attempts int `description:"Number of attempts"`
MaxMem int64 `description:"Maximum request body to be stored in memory in Mo"`
}
// NewGlobalConfiguration returns a GlobalConfiguration with default values.
func NewGlobalConfiguration() *GlobalConfiguration {
return new(GlobalConfiguration)
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
//default Docker
var defaultDocker provider.Docker
defaultDocker.Watch = true
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
defaultDocker.TLS = &provider.DockerTLS{}
// default File
var defaultFile provider.File
defaultFile.Watch = true
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
// default Web
var defaultWeb WebProvider
defaultWeb.Address = ":8080"
// default Marathon
var defaultMarathon provider.Marathon
defaultMarathon.Watch = true
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
defaultMarathon.ExposedByDefault = true
// default Consul
var defaultConsul provider.Consul
defaultConsul.Watch = true
defaultConsul.Endpoint = "127.0.0.1:8500"
defaultConsul.Prefix = "/traefik"
defaultConsul.TLS = &provider.KvTLS{}
// default ConsulCatalog
var defaultConsulCatalog provider.ConsulCatalog
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
// default Etcd
var defaultEtcd provider.Etcd
defaultEtcd.Watch = true
defaultEtcd.Endpoint = "127.0.0.1:400"
defaultEtcd.Prefix = "/traefik"
defaultEtcd.TLS = &provider.KvTLS{}
//default Zookeeper
var defaultZookeeper provider.Zookepper
defaultZookeeper.Watch = true
defaultZookeeper.Endpoint = "127.0.0.1:2181"
defaultZookeeper.Prefix = "/traefik"
//default Boltdb
var defaultBoltDb provider.BoltDb
defaultBoltDb.Watch = true
defaultBoltDb.Endpoint = "127.0.0.1:4001"
defaultBoltDb.Prefix = "/traefik"
//default Kubernetes
var defaultKubernetes provider.Kubernetes
defaultKubernetes.Watch = true
defaultKubernetes.Endpoint = "127.0.0.1:8080"
defaultConfiguration := GlobalConfiguration{
Docker: &defaultDocker,
File: &defaultFile,
Web: &defaultWeb,
Marathon: &defaultMarathon,
Consul: &defaultConsul,
ConsulCatalog: &defaultConsulCatalog,
Etcd: &defaultEtcd,
Zookeeper: &defaultZookeeper,
Boltdb: &defaultBoltDb,
Kubernetes: &defaultKubernetes,
Retry: &Retry{MaxMem: 2},
}
return &TraefikConfiguration{
GlobalConfiguration: defaultConfiguration,
}
}
// LoadConfiguration returns a GlobalConfiguration.
func LoadConfiguration() *GlobalConfiguration {
configuration := NewGlobalConfiguration()
viper.SetEnvPrefix("traefik")
viper.SetConfigType("toml")
viper.AutomaticEnv()
if len(viper.GetString("configFile")) > 0 {
viper.SetConfigFile(viper.GetString("configFile"))
} else {
viper.SetConfigName("traefik") // name of config file (without extension)
// NewTraefikConfiguration creates a TraefikConfiguration with default values
func NewTraefikConfiguration() *TraefikConfiguration {
return &TraefikConfiguration{
GlobalConfiguration: GlobalConfiguration{
GraceTimeOut: 10,
AccessLogsFile: "",
TraefikLogsFile: "",
LogLevel: "ERROR",
EntryPoints: map[string]*EntryPoint{},
DefaultEntryPoints: []string{},
ProvidersThrottleDuration: time.Duration(2 * time.Second),
MaxIdleConnsPerHost: 200,
},
ConfigFile: "",
}
viper.AddConfigPath("/etc/traefik/") // path to look for the config file in
viper.AddConfigPath("$HOME/.traefik/") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory
if err := viper.ReadInConfig(); err != nil {
if len(viper.ConfigFileUsed()) > 0 {
fmtlog.Printf("Error reading configuration file: %s", err)
} else {
fmtlog.Printf("No configuration file found")
}
}
if len(arguments.EntryPoints) > 0 {
viper.Set("entryPoints", arguments.EntryPoints)
}
if len(arguments.DefaultEntryPoints) > 0 {
viper.Set("defaultEntryPoints", arguments.DefaultEntryPoints)
}
if arguments.web {
viper.Set("web", arguments.Web)
}
if arguments.file {
viper.Set("file", arguments.File)
}
if !arguments.dockerTLS {
arguments.Docker.TLS = nil
}
if arguments.docker {
viper.Set("docker", arguments.Docker)
}
if arguments.marathon {
viper.Set("marathon", arguments.Marathon)
}
if !arguments.consulTLS {
arguments.Consul.TLS = nil
}
if arguments.consul {
viper.Set("consul", arguments.Consul)
}
if arguments.consulCatalog {
viper.Set("consulCatalog", arguments.ConsulCatalog)
}
if arguments.zookeeper {
viper.Set("zookeeper", arguments.Zookeeper)
}
if !arguments.etcdTLS {
arguments.Etcd.TLS = nil
}
if arguments.etcd {
viper.Set("etcd", arguments.Etcd)
}
if arguments.boltdb {
viper.Set("boltdb", arguments.Boltdb)
}
if arguments.kubernetes {
viper.Set("kubernetes", arguments.Kubernetes)
}
if err := unmarshal(&configuration); err != nil {
fmtlog.Fatalf("Error reading file: %s", err)
}
if len(configuration.EntryPoints) == 0 {
configuration.EntryPoints = make(map[string]*EntryPoint)
configuration.EntryPoints["http"] = &EntryPoint{
Address: ":80",
}
configuration.DefaultEntryPoints = []string{"http"}
}
if configuration.File != nil && len(configuration.File.Filename) == 0 {
// no filename, setting to global config file
configuration.File.Filename = viper.ConfigFileUsed()
}
return configuration
}
func unmarshal(rawVal interface{}) error {
config := &mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
Metadata: nil,
Result: rawVal,
WeaklyTypedInput: true,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
err = decoder.Decode(viper.AllSettings())
if err != nil {
return err
}
return nil
}
type configs map[string]*types.Configuration

View File

@@ -146,7 +146,7 @@ defaultEntryPoints = ["http"]
### whoami:
```
wrk -t8 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-whoami:80/bench
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-whoami:80/bench
Running 1m test @ http://IP-whoami:80/bench
20 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
@@ -184,7 +184,7 @@ Transfer/sec: 4.97MB
### traefik:
```
wrk -t8 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
Running 1m test @ http://IP-traefik:8000/bench
20 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev

View File

@@ -38,9 +38,10 @@ Run it and forget it!
## Demo
Here is a demo of Træfɪk using Docker backend, showing a load-balancing between two servers, hot reloading of configuration, and graceful shutdown.
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.
[![asciicast](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko.png)](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko)
[![Traefik Devoxx France](https://img.youtube.com/vi/QvAz9mVx5TI/0.jpg)](https://www.youtube.com/watch?v=QvAz9mVx5TI)
## Get it

View File

@@ -619,7 +619,7 @@ Labels can be used on containers to override default behaviour:
- `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.entryPoints=http,https`: assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`.
* `traefik.domain=traefik.localhost`: override the default domain
- `traefik.domain=traefik.localhost`: override the default domain
## Kubernetes Ingress backend
@@ -648,8 +648,13 @@ Træfɪk can be configured to use Kubernetes Ingress as a backend configuration:
# Optional
#
# endpoint = "http://localhost:8080"
# namespaces = ["default","production"]
```
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 (Default: `Host:{containerName}.{domain}`).
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).
## Consul backend
@@ -929,7 +934,7 @@ The Keys-Values structure should look (using `prefix = "/traefik"`):
| `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/passHostHeader` | `true` |
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `Path:/test` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
## Atomic configuration changes

View File

@@ -0,0 +1,4 @@
etcd:
image: gcr.io/google_containers/etcd:2.2.1
net: host
command: ['/usr/local/bin/etcd', '--addr=127.0.0.1:4001', '--bind-addr=0.0.0.0:4001', '--data-dir=/var/etcd/data']

View File

@@ -1,8 +1,3 @@
# etcd:
# image: gcr.io/google_containers/etcd:2.2.1
# net: host
# command: ['/usr/local/bin/etcd', '--addr=127.0.0.1:4001', '--bind-addr=0.0.0.0:4001', '--data-dir=/var/etcd/data']
kubelet:
image: gcr.io/google_containers/hyperkube-amd64:v1.2.2
privileged: true

25
examples/etcd-config.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/sh
# backend 1
curl -i -H "Accept: application/json" -X PUT -d value="NetworkErrorRatio() > 0.5" http://localhost:4001/v2/keys/traefik/backends/backend1/circuitbreaker/expression
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.2:80" http://localhost:4001/v2/keys/traefik/backends/backend1/servers/server1/url
curl -i -H "Accept: application/json" -X PUT -d value="10" http://localhost:4001/v2/keys/traefik/backends/backend1/servers/server1/weight
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.3:80" http://localhost:4001/v2/keys/traefik/backends/backend1/servers/server2/url
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:4001/v2/keys/traefik/backends/backend1/servers/server2/weight
# backend 2
curl -i -H "Accept: application/json" -X PUT -d value="drr" http://localhost:4001/v2/keys/traefik/backends/backend2/loadbalancer/method
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.4:80" http://localhost:4001/v2/keys/traefik/backends/backend2/servers/server1/url
curl -i -H "Accept: application/json" -X PUT -d value="1" http://localhost:4001/v2/keys/traefik/backends/backend2/servers/server1/weight
curl -i -H "Accept: application/json" -X PUT -d value="http://172.17.0.5:80" http://localhost:4001/v2/keys/traefik/backends/backend2/servers/server2/url
curl -i -H "Accept: application/json" -X PUT -d value="2" http://localhost:4001/v2/keys/traefik/backends/backend2/servers/server2/weight
# frontend 1
curl -i -H "Accept: application/json" -X PUT -d value="backend2" http://localhost:4001/v2/keys/traefik/frontends/frontend1/backend
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:4001/v2/keys/traefik/frontends/frontend1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="Host:test.localhost" http://localhost:4001/v2/keys/traefik/frontends/frontend1/routes/test_1/rule
# frontend 2
curl -i -H "Accept: application/json" -X PUT -d value="backend1" http://localhost:4001/v2/keys/traefik/frontends/frontend2/backend
curl -i -H "Accept: application/json" -X PUT -d value="http" http://localhost:4001/v2/keys/traefik/frontends/frontend2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d value="Path:/test" http://localhost:4001/v2/keys/traefik/frontends/frontend2/routes/test_2/rule

View File

@@ -91,3 +91,21 @@ spec:
- 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

View File

@@ -3,6 +3,7 @@ Copyright
*/
//go:generate rm -vf autogen/gen.go
//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

222
glide.lock generated
View File

@@ -1,39 +1,40 @@
hash: a9f41b9fe89ac3028da27ac9cbe31db9a79ae89082f42507d4d0c58290517ee2
updated: 2016-04-27T17:14:45.61228359Z
hash: 1d42530971835a5a1e593e8efc54f28f2714cb8d1dfcb333e3b9ba92ef1917cc
updated: 2016-05-30T16:53:17.058435869+02:00
imports:
- name: github.com/alecthomas/template
version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
- name: github.com/alecthomas/units
version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
- name: github.com/boltdb/bolt
version: 51f99c862475898df9773747d3accd05a7ca33c1
version: dfb21201d9270c1082d5fb0f07f500311ff72f18
- name: github.com/BurntSushi/toml
version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
version: f0aeabca5a127c4078abb8c8d64298b147264b55
- name: github.com/BurntSushi/ty
version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
subpackages:
- fun
- name: github.com/cenkalti/backoff
version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99
version: c29158af31815ccc31ca29c86c121bc39e00d3d8
- name: github.com/codahale/hdrhistogram
version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17
version: 9208b142303c12d8899bae836fd524ac9338b4fd
- name: github.com/codegangsta/cli
version: bf4a526f48af7badd25d2cb02d587e1b01be3b50
- name: github.com/codegangsta/negroni
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
version: ffbc66b612ee3eac2eba29aedce4c3a65e4dd0a1
- name: github.com/containous/flaeg
version: a208db24c0e580be76efed167736fe3204c7c954
- name: github.com/containous/oxy
version: 021f82bd8260ba15f5862a9fe62018437720dff5
version: 183212964e13e7b8afe01a08b193d04300554a68
subpackages:
- cbreaker
- forward
- memmetrics
- roundrobin
- utils
- connlimit
- forward
- roundrobin
- stream
- utils
- name: github.com/containous/staert
version: 9f815ef829b66de3519fea6c0b8d4708eb9472d4
- name: github.com/coreos/etcd
version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637
version: c400d05d0aa73e21e431c16145e558d624098018
subpackages:
- Godeps/_workspace/src/github.com/ugorji/go/codec
- Godeps/_workspace/src/golang.org/x/net/context
- client
- pkg/pathutil
- pkg/types
@@ -42,73 +43,41 @@ imports:
subpackages:
- spew
- name: github.com/docker/distribution
version: ff6f38ccb69afa96214c7ee955359465d1fc767a
version: 5bbf65499960b184fe8e0f045397375e1a6722b8
subpackages:
- reference
- digest
- name: github.com/docker/docker
version: f39987afe8d611407887b3094c03d6ba6a766a67
version: 9837ec4da53f15f9120d53a6e1517491ba8b0261
subpackages:
- autogen
- api
- cliconfig
- daemon/network
- graph/tags
- image
- opts
- pkg/archive
- pkg/fileutils
- pkg/homedir
- pkg/httputils
- pkg/ioutils
- pkg/jsonmessage
- pkg/mflag
- pkg/nat
- pkg/parsers
- pkg/pools
- pkg/promise
- pkg/random
- pkg/stdcopy
- pkg/stringid
- pkg/symlink
- pkg/system
- pkg/tarsum
- pkg/term
- pkg/timeutils
- pkg/tlsconfig
- pkg/ulimit
- pkg/units
- pkg/urlutil
- pkg/useragent
- pkg/version
- registry
- runconfig
- utils
- volume
- namesgenerator
- name: github.com/docker/engine-api
version: 8924d6900370b4c7e7984be5adc61f50a80d7537
version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb
subpackages:
- client
- types
- types/container
- types/filters
- types/strslice
- types/events
- types/filters
- client/transport
- client/transport/cancellable
- types/container
- types/network
- types/reference
- types/registry
- types/time
- types/versions
- types/blkiodev
- types/strslice
- name: github.com/docker/go-connections
version: f549a9393d05688dff0992ef3efd8bbe6c628aeb
version: c7838b258fbfa3fe88eecfb2a0e08ea0dbd6a646
subpackages:
- nat
- sockets
- tlsconfig
- nat
- name: github.com/docker/go-units
version: 5d2041e26a699eaca682e2ea41c8f891e1060444
- name: github.com/docker/libcompose
version: e290a513ba909ca3afefd5cd611f3a3fe56f6a3a
version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873
- name: github.com/docker/libkv
version: 7283ef27ed32fe267388510a91709b307bb9942c
subpackages:
@@ -117,67 +86,39 @@ imports:
- store/consul
- store/etcd
- store/zookeeper
- name: github.com/docker/libtrust
version: 9cbd2a1374f46905c68a4eb3694a130610adc62a
- name: github.com/donovanhide/eventsource
version: d8a3071799b98cacd30b6da92f536050ccfe6da4
- name: github.com/eapache/go-resiliency
version: b86b1ec0dd4209a588dc1285cdd471e73525c0b3
subpackages:
- breaker
- name: github.com/eapache/queue
version: ded5959c0d4e360646dc9e9908cff48666781367
version: c3f57f280ec708df24886d9e62f2fd178d69d8e8
- name: github.com/elazarl/go-bindata-assetfs
version: d5cac425555ca5cf00694df246e04f05e6a55150
- name: github.com/flynn/go-shlex
version: 3f9db97f856818214da2e1057f8ad84803971cff
version: 57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2
- name: github.com/gambol99/go-marathon
version: ade11d1dc2884ee1f387078fc28509559b6235d1
- name: github.com/go-check/check
version: 11d3bc7aa68e238947792f30573146a3231fc0f1
- name: github.com/golang/glog
version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
- name: github.com/golang/snappy
version: ec642410cd033af63620b66a91ccbd3c69c2c59a
version: 4f90aeace3a26ad7021961c297b22c42160c7b25
- name: github.com/google/go-querystring
version: 9235644dd9e52eeae6fa48efd539fdc351a0af53
subpackages:
- query
- name: github.com/gorilla/context
version: 215affda49addc4c8ef7e2534915df2c8c35c6cd
- name: github.com/gorilla/handlers
version: 40694b40f4a928c062f56849989d3e9cd0570e5f
version: a8d44e7d8e4d532b6a27a02dd82abb31cc1b01bd
- name: github.com/gorilla/mux
version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a
- name: github.com/gorilla/websocket
version: e2e3d8414d0fbae04004f151979f4e27c6747fe7
version: 9c19ed558d5df4da88e2ade9c8940d742aef0e7e
- name: github.com/hashicorp/consul
version: de080672fee9e6104572eeea89eccdca135bb918
version: f6fef66e1bf17be4f3c9855fbec6de802ca6bd7d
subpackages:
- api
- name: github.com/hashicorp/hcl
version: 2604f3bda7e8960c1be1063709e7d7f0765048d0
- name: github.com/hashicorp/go-cleanhttp
version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d
- name: github.com/hashicorp/serf
version: e4ec8cc423bbe20d26584b96efbeb9102e16d05f
subpackages:
- hcl/ast
- hcl/parser
- hcl/token
- json/parser
- hcl/scanner
- hcl/strconv
- json/scanner
- json/token
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/klauspost/crc32
version: 19b0b332c9e4516a6370a0456e6182c3b5036720
- name: github.com/kr/pretty
version: add1dbc86daf0f983cd4a48ceb39deb95c729b67
- name: github.com/kr/text
version: bb797dc4fb8320488f47bf11de07a733d7233e1f
- name: github.com/magiconair/properties
version: c265cfa48dda6474e208715ca93e987829f572f8
- coordinate
- serf
- name: github.com/libkermit/docker
version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02
- name: github.com/libkermit/docker-check
version: bb75a86b169c6c5d22c0ee98278124036f272d7b
- name: github.com/mailgun/log
version: 44874009257d4d47ba9806f1b7f72a32a015e4d8
version: 2f35a4607f1abf71f97f77f99b0de8493ef6f4ef
- name: github.com/mailgun/manners
version: fada45142db3f93097ca917da107aa3fad0ffcb5
- name: github.com/mailgun/multibuf
@@ -187,64 +128,44 @@ imports:
- name: github.com/mattn/go-shellwords
version: 525bedee691b5a8df547cb5cf9f86b7fb1883e24
- name: github.com/Microsoft/go-winio
version: 862b6557927a5c5c81e411c12aa6de7e566cbb7a
version: 4f1a71750d95a5a8a46c40a67ffbed8129c2f138
- name: github.com/miekg/dns
version: dd83d5cbcfd986f334b2747feeb907e281318fdf
- name: github.com/mitchellh/mapstructure
version: d2dd0262208475919e1a362f675cfc0e7c10e905
version: 48ab6605c66ac797e07f615101c3e9e10e932b66
- name: github.com/moul/http2curl
version: 1812aee76a1ce98d604a44200c6a23c689b17a89
version: b1479103caacaa39319f75e7f57fc545287fca0d
- name: github.com/ogier/pflag
version: 45c278ab3607870051a2ea9040bb85fcb8557481
- name: github.com/opencontainers/runc
version: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
version: d2d09b9bcd0573c58d7cd94e57bd7555af0c2072
subpackages:
- libcontainer/user
- name: github.com/parnurzeal/gorequest
version: 91b42fce877cc6af96c45818665a4c615cc5f4ee
version: 2169dfca686cfcbefc983a98a25e9c22a2815be4
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
- difflib
- name: github.com/samuel/go-zookeeper
version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b
version: 6eb1b09c6ce23f305f4c81bf748b22fbc6f3f9e9
subpackages:
- zk
- name: github.com/Shopify/sarama
version: 92a286e4dde1688175cff3d2ec9b49a02838b447
- name: github.com/Sirupsen/logrus
version: 418b41d23a1bf978c06faea5313ba194650ac088
- name: github.com/spf13/cast
version: ee7b3e0353166ab1f3a605294ac8cd2b77953778
- name: github.com/spf13/cobra
version: 4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f
subpackages:
- cobra
- name: github.com/spf13/jwalterweatherman
version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
- name: github.com/spf13/pflag
version: 1f296710f879815ad9e6d39d947c828c3e4b4c3d
- name: github.com/spf13/viper
version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325
version: 6d9ae300aaf85d6acd2e5424081c7fcddb21dab8
- name: github.com/streamrail/concurrent-map
version: 788b276dc7eabf20890ea3fa280956664d58b329
version: 65a174a3a4188c0b7099acbc6cfa0c53628d3287
- name: github.com/stretchr/objx
version: cbeaeb16a013161a98496fad62933b1d21786672
- name: github.com/stretchr/testify
version: bcd9e3389dd03b0b668d11f4d462a6af6c2dfd60
version: 6cb3b85ef5a0efef77caef88363ec4d4b5c0976d
subpackages:
- mock
- assert
- name: github.com/thoas/stats
version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
- name: github.com/ugorji/go
version: ea9cd21fa0bc41ee4bdd50ac7ed8cbc7ea2ed960
subpackages:
- codec
version: 69e3c072eec2df2df41afe6214f62eb940e4cd80
- name: github.com/unrolled/render
version: 26b4e3aac686940fe29521545afad9966ddfc80c
version: 198ad4d8b8a4612176b804ca10555b222a086b40
- name: github.com/vdemeester/docker-events
version: 6ea3f28df37f29a47498bc8b32b36ad8491dbd37
- name: github.com/vdemeester/libkermit
version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec
version: b308d2e8d639d928c882913bcb4f85b3a84c7a07
- name: github.com/vdemeester/shakers
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
- name: github.com/vulcand/oxy
@@ -262,18 +183,16 @@ imports:
- plugin/rewrite
- plugin
- router
- name: github.com/wendal/errors
version: f66c77a7882b399795a8987ebf87ef64a427417e
- name: github.com/xenolf/lego
version: 23e88185c255e95a106835d80e76e5a3a66d7c54
version: b119bc45fbd1cc71348003541aac9d3a7da63654
subpackages:
- acme
- name: golang.org/x/crypto
version: d68c3ecb62c850b645dc072a8d78006286bf81ca
version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f
subpackages:
- ocsp
- name: golang.org/x/net
version: d9558e5c97f85372afee28cf2b6059d7d3818919
version: 6460565bec1e8891e29ff478184c71b9e443ac36
subpackages:
- context
- publicsuffix
@@ -282,19 +201,16 @@ imports:
version: eb2c74142fd19a79b3f237334c7384d5167b1b46
subpackages:
- unix
- name: gopkg.in/alecthomas/kingpin.v2
version: 639879d6110b1b0409410c7b737ef0bb18325038
- windows
- name: gopkg.in/fsnotify.v1
version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0
version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
- name: gopkg.in/mgo.v2
version: 22287bab4379e1fbf6002fb4eb769888f3fb224c
version: b6e2fa371e64216a45e61072a96d4e3859f169da
subpackages:
- bson
- name: gopkg.in/square/go-jose.v1
version: 40d457b439244b546f023d056628e5184136899b
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
subpackages:
- cipher
- json
- name: gopkg.in/yaml.v2
version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40
devImports: []

View File

@@ -1,187 +1,78 @@
package: main
package: github.com/containous/traefik
import:
- package: github.com/coreos/etcd
version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637
- package: github.com/BurntSushi/toml
- package: github.com/BurntSushi/ty
subpackages:
- client
- package: github.com/mailgun/log
version: 44874009257d4d47ba9806f1b7f72a32a015e4d8
- fun
- package: github.com/Sirupsen/logrus
- package: github.com/cenkalti/backoff
- package: github.com/codegangsta/negroni
- package: github.com/containous/flaeg
version: a208db24c0e580be76efed167736fe3204c7c954
- package: github.com/containous/oxy
version: 021f82bd8260ba15f5862a9fe62018437720dff5
subpackages:
- cbreaker
- connlimit
- forward
- memmetrics
- roundrobin
- stream
- utils
- package: github.com/hashicorp/consul
version: de080672fee9e6104572eeea89eccdca135bb918
subpackages:
- api
- package: github.com/samuel/go-zookeeper
version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b
subpackages:
- zk
- package: github.com/docker/libtrust
version: 9cbd2a1374f46905c68a4eb3694a130610adc62a
- package: github.com/go-check/check
version: 11d3bc7aa68e238947792f30573146a3231fc0f1
- package: golang.org/x/net
version: d9558e5c97f85372afee28cf2b6059d7d3818919
subpackages:
- context
- package: github.com/gorilla/handlers
version: 40694b40f4a928c062f56849989d3e9cd0570e5f
- package: github.com/docker/libkv
version: 7283ef27ed32fe267388510a91709b307bb9942c
- package: github.com/alecthomas/template
version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
- package: github.com/vdemeester/shakers
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
- package: github.com/alecthomas/units
version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
- package: github.com/gambol99/go-marathon
version: ade11d1dc2884ee1f387078fc28509559b6235d1
- package: github.com/vulcand/predicate
version: cb0bff91a7ab7cf7571e661ff883fc997bc554a3
- package: github.com/thoas/stats
version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
- package: github.com/Sirupsen/logrus
version: 418b41d23a1bf978c06faea5313ba194650ac088
- package: github.com/unrolled/render
version: 26b4e3aac686940fe29521545afad9966ddfc80c
- package: github.com/flynn/go-shlex
version: 3f9db97f856818214da2e1057f8ad84803971cff
- package: github.com/boltdb/bolt
version: 51f99c862475898df9773747d3accd05a7ca33c1
- package: gopkg.in/mgo.v2
version: 22287bab4379e1fbf6002fb4eb769888f3fb224c
subpackages:
- bson
- package: github.com/docker/docker
version: f39987afe8d611407887b3094c03d6ba6a766a67
subpackages:
- autogen
- api
- cliconfig
- daemon/network
- graph/tags
- image
- opts
- pkg/archive
- pkg/fileutils
- pkg/homedir
- pkg/httputils
- pkg/ioutils
- pkg/jsonmessage
- pkg/mflag
- pkg/nat
- pkg/parsers
- pkg/pools
- pkg/promise
- pkg/random
- pkg/stdcopy
- pkg/stringid
- pkg/symlink
- pkg/system
- pkg/tarsum
- pkg/term
- pkg/timeutils
- pkg/tlsconfig
- pkg/ulimit
- pkg/units
- pkg/urlutil
- pkg/useragent
- pkg/version
- registry
- runconfig
- utils
- volume
- package: github.com/mailgun/timetools
version: fd192d755b00c968d312d23f521eb0cdc6f66bd0
- package: github.com/codegangsta/negroni
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
- package: gopkg.in/yaml.v2
version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf
- package: github.com/opencontainers/runc
version: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
subpackages:
- libcontainer/user
- package: github.com/gorilla/mux
version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a
- package: github.com/BurntSushi/ty
version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
- package: github.com/elazarl/go-bindata-assetfs
version: d5cac425555ca5cf00694df246e04f05e6a55150
- package: github.com/BurntSushi/toml
version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- package: gopkg.in/alecthomas/kingpin.v2
version: 639879d6110b1b0409410c7b737ef0bb18325038
- package: github.com/cenkalti/backoff
version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99
- package: gopkg.in/fsnotify.v1
version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0
- package: github.com/mailgun/manners
version: fada45142db3f93097ca917da107aa3fad0ffcb5
- package: github.com/gorilla/context
version: 215affda49addc4c8ef7e2534915df2c8c35c6cd
- package: github.com/codahale/hdrhistogram
version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17
- package: github.com/gorilla/websocket
- package: github.com/donovanhide/eventsource
version: d8a3071799b98cacd30b6da92f536050ccfe6da4
- package: github.com/golang/glog
version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
- package: github.com/spf13/cast
version: ee7b3e0353166ab1f3a605294ac8cd2b77953778
- package: github.com/mitchellh/mapstructure
- package: github.com/spf13/jwalterweatherman
- package: github.com/spf13/pflag
- package: github.com/wendal/errors
- package: github.com/hashicorp/hcl
- package: github.com/kr/pretty
- package: github.com/magiconair/properties
- package: github.com/kr/text
- package: github.com/spf13/viper
version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325
- package: github.com/spf13/cobra
subpackages:
- cobra
- package: github.com/google/go-querystring
subpackages:
- query
- package: github.com/vulcand/vulcand
subpackages:
- plugin/rewrite
- package: github.com/stretchr/testify
subpackages:
- mock
- package: github.com/xenolf/lego
- package: github.com/vdemeester/libkermit
version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec
- package: github.com/docker/libcompose
version: e290a513ba909ca3afefd5cd611f3a3fe56f6a3a
- package: github.com/docker/distribution
version: ff6f38ccb69afa96214c7ee955359465d1fc767a
subpackages:
- reference
- package: github.com/containous/staert
version: 9f815ef829b66de3519fea6c0b8d4708eb9472d4
- package: github.com/docker/engine-api
version: 8924d6900370b4c7e7984be5adc61f50a80d7537
version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb
subpackages:
- client
- types
- types/container
- types/events
- types/filters
- types/strslice
- package: github.com/vdemeester/docker-events
- package: github.com/docker/go-connections
subpackages:
- nat
- sockets
- tlsconfig
- package: github.com/docker/go-units
- package: github.com/mailgun/multibuf
- package: github.com/streamrail/concurrent-map
- package: github.com/docker/libkv
subpackages:
- store
- store/boltdb
- store/consul
- store/etcd
- store/zookeeper
- package: github.com/elazarl/go-bindata-assetfs
- package: github.com/gambol99/go-marathon
version: ade11d1dc2884ee1f387078fc28509559b6235d1
- package: github.com/gorilla/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
- package: github.com/unrolled/render
- package: github.com/vdemeester/docker-events
- package: github.com/vulcand/vulcand
subpackages:
- plugin/rewrite
- package: github.com/xenolf/lego
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
subpackages:
- namesgenerator
- package: github.com/go-check/check
- package: github.com/docker/libcompose
version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873
- package: github.com/mattn/go-shellwords
- package: github.com/moul/http2curl
- package: github.com/vdemeester/shakers

View File

@@ -5,7 +5,6 @@ import (
"os/exec"
"time"
"fmt"
"github.com/go-check/check"
"bytes"
@@ -15,34 +14,6 @@ import (
// SimpleSuite
type SimpleSuite struct{ BaseSuite }
func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) {
cmd := exec.Command(traefikBinary)
var b bytes.Buffer
cmd.Stdout = &b
cmd.Stderr = &b
cmd.Start()
time.Sleep(500 * time.Millisecond)
output := b.Bytes()
c.Assert(string(output), checker.Contains, "No configuration file found")
cmd.Process.Kill()
nonExistentFile := "non/existent/file.toml"
cmd = exec.Command(traefikBinary, "--configFile="+nonExistentFile)
cmd.Stdout = &b
cmd.Stderr = &b
cmd.Start()
time.Sleep(500 * time.Millisecond)
output = b.Bytes()
c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile))
cmd.Process.Kill()
}
func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
cmd := exec.Command(traefikBinary, "--configFile=fixtures/invalid_configuration.toml")
@@ -55,7 +26,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
defer cmd.Process.Kill()
output := b.Bytes()
c.Assert(string(output), checker.Contains, "While parsing config: Near line 0 (last key parsed ''): Bare keys cannot contain '{'")
c.Assert(string(output), checker.Contains, "Near line 0 (last key parsed ''): Bare keys cannot contain '{'")
}
func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {

View File

@@ -5,29 +5,186 @@ import (
"os/exec"
"time"
"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/integration/utils"
checker "github.com/vdemeester/shakers"
"io/ioutil"
"os"
"strings"
)
// Consul test suites (using libcompose)
type ConsulSuite struct{ BaseSuite }
type ConsulSuite struct {
BaseSuite
kv store.Store
}
func (s *ConsulSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "consul")
s.composeProject.Start(c)
consul.Register()
kv, err := libkv.NewStore(
store.CONSUL,
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
)
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) TestSimpleConfiguration(c *check.C) {
cmd := exec.Command(traefikBinary, "--configFile=fixtures/consul/simple.toml")
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulHost string }{consulHost})
defer os.Remove(file)
cmd := exec.Command(traefikBinary, "--configFile="+file)
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)
}
func (s *ConsulSuite) TestNominalConfiguration(c *check.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)
cmd := exec.Command(traefikBinary, "--configFile="+file)
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/routes/test_1/rule": "Host:test.localhost",
}
frontend2 := map[string]string{
"traefik/frontends/frontend2/backend": "backend1",
"traefik/frontends/frontend2/entrypoints": "http",
"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:8081/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)
client := &http.Client{}
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/", 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)
body, err := ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if !strings.Contains(string(body), whoami3.NetworkSettings.IPAddress) &&
!strings.Contains(string(body), whoami4.NetworkSettings.IPAddress) {
c.Fail()
}
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/test", nil)
c.Assert(err, checker.IsNil)
response, err = client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, 200)
body, err = ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if !strings.Contains(string(body), whoami1.NetworkSettings.IPAddress) &&
!strings.Contains(string(body), whoami2.NetworkSettings.IPAddress) {
c.Fail()
}
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/test2", nil)
resp, err := client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, 404)
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/", nil)
req.Host = "test2.localhost"
resp, err = client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, 404)
}

View File

@@ -13,8 +13,8 @@ import (
"github.com/docker/docker/pkg/namesgenerator"
"github.com/go-check/check"
d "github.com/vdemeester/libkermit/docker"
docker "github.com/vdemeester/libkermit/docker/check"
d "github.com/libkermit/docker"
docker "github.com/libkermit/docker-check"
checker "github.com/vdemeester/shakers"
)

View File

@@ -8,17 +8,58 @@ import (
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
"errors"
"fmt"
"github.com/containous/traefik/integration/utils"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/etcd"
"io/ioutil"
"os"
"strings"
)
// Etcd test suites (using libcompose)
type EtcdSuite struct{ BaseSuite }
type EtcdSuite struct {
BaseSuite
kv store.Store
}
func (s *EtcdSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "etcd")
s.composeProject.Start(c)
etcd.Register()
url := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":4001"
kv, err := libkv.NewStore(
store.ETCD,
[]string{url},
&store.Config{
ConnectionTimeout: 10 * time.Second,
},
)
if err != nil {
c.Fatal("Cannot create store etcd")
}
s.kv = kv
// wait for etcd
err = utils.Try(60*time.Second, func() error {
_, err := kv.Exists("test")
if err != nil {
return fmt.Errorf("Etcd connection error to %s: %v", url, err)
}
return nil
})
c.Assert(err, checker.IsNil)
}
func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
cmd := exec.Command(traefikBinary, "--configFile=fixtures/etcd/simple.toml")
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdHost string }{etcdHost})
defer os.Remove(file)
cmd := exec.Command(traefikBinary, "--configFile="+file)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
@@ -31,3 +72,123 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) {
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, 404)
}
func (s *EtcdSuite) TestNominalConfiguration(c *check.C) {
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/etcd/simple.toml", struct{ EtcdHost string }{etcdHost})
defer os.Remove(file)
cmd := exec.Command(traefikBinary, "--configFile="+file)
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/routes/test_1/rule": "Host:test.localhost",
}
frontend2 := map[string]string{
"/traefik/frontends/frontend2/backend": "backend1",
"/traefik/frontends/frontend2/entrypoints": "http",
"/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:8081/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)
client := &http.Client{}
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/", 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)
body, err := ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if !strings.Contains(string(body), whoami3.NetworkSettings.IPAddress) &&
!strings.Contains(string(body), whoami4.NetworkSettings.IPAddress) {
c.Fail()
}
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/test", nil)
c.Assert(err, checker.IsNil)
response, err = client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, 200)
body, err = ioutil.ReadAll(response.Body)
c.Assert(err, checker.IsNil)
if !strings.Contains(string(body), whoami1.NetworkSettings.IPAddress) &&
!strings.Contains(string(body), whoami2.NetworkSettings.IPAddress) {
c.Fail()
}
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/test2", nil)
req.Host = "test2.localhost"
resp, err := client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, 404)
req, err = http.NewRequest("GET", "http://127.0.0.1:8000/", nil)
resp, err = client.Do(req)
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, 404)
}

View File

@@ -1,9 +1,16 @@
defaultEntryPoints = ["http"]
logLevel = "DEBUG"
[entryPoints]
[entryPoints.http]
address = ":8000"
logLevel = "DEBUG"
[consul]
endpoint = "{{.ConsulHost}}:8500"
watch = true
prefix = "traefik"
[web]
address = ":8081"

View File

@@ -1,11 +1,11 @@
defaultEntryPoints = ["http"]
logLevel = "DEBUG"
[entryPoints]
[entryPoints.http]
address = ":8000"
logLevel = "DEBUG"
[docker]
# It's dynamagic !

View File

@@ -1,10 +1,16 @@
defaultEntryPoints = ["http"]
logLevel = "DEBUG"
[entryPoints]
[entryPoints.http]
address = ":8000"
logLevel = "DEBUG"
[etcd]
endpoint = "127.0.0.1:4003,127.0.0.1:4002,127.0.0.1:4001"
endpoint = "{{.EtcdHost}}:4001"
prefix = "/traefik"
watch = true
[web]
address = ":8081"

View File

@@ -13,7 +13,7 @@ import (
"github.com/containous/traefik/integration/utils"
"github.com/go-check/check"
compose "github.com/vdemeester/libkermit/compose/check"
"github.com/libkermit/docker-check/compose"
checker "github.com/vdemeester/shakers"
)
@@ -64,7 +64,11 @@ func (s *BaseSuite) adaptFileForHost(c *check.C, path string) string {
// Default docker socket
dockerHost = "unix:///var/run/docker.sock"
}
tempObjects := struct{ DockerHost string }{dockerHost}
return s.adaptFile(c, path, tempObjects)
}
func (s *BaseSuite) adaptFile(c *check.C, path string, tempObjects interface{}) string {
// Load file
tmpl, err := template.ParseFiles(path)
c.Assert(err, checker.IsNil)
@@ -74,7 +78,7 @@ func (s *BaseSuite) adaptFileForHost(c *check.C, path string) string {
c.Assert(err, checker.IsNil)
defer tmpFile.Close()
err = tmpl.ExecuteTemplate(tmpFile, prefix, struct{ DockerHost string }{dockerHost})
err = tmpl.ExecuteTemplate(tmpFile, prefix, tempObjects)
c.Assert(err, checker.IsNil)
err = tmpFile.Sync()

View File

@@ -1,6 +1,6 @@
consul:
image: progrium/consul
command: -server -bootstrap -advertise 12.0.0.254 -log-level debug -ui-dir /ui
command: -server -bootstrap -log-level debug -ui-dir /ui
ports:
- "8400:8400"
- "8500:8500"
@@ -10,4 +10,16 @@ consul:
- "8301"
- "8301/udp"
- "8302"
- "8302/udp"
- "8302/udp"
whoami1:
image: emilevauge/whoami
whoami2:
image: emilevauge/whoami
whoami3:
image: emilevauge/whoami
whoami4:
image: emilevauge/whoami

View File

@@ -1,30 +1,14 @@
etcd1:
image: quay.io/coreos/etcd:v2.2.0
net: "host"
command: >
--name etcd1
--listen-peer-urls http://localhost:7001
--listen-client-urls http://localhost:4001
--initial-advertise-peer-urls http://localhost:7001
--advertise-client-urls http://localhost:4001
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
etcd2:
image: quay.io/coreos/etcd:v2.2.0
net: "host"
command: >
--name etcd2
--listen-peer-urls http://localhost:7002
--listen-client-urls http://localhost:4002
--initial-advertise-peer-urls http://localhost:7002
--advertise-client-urls http://localhost:4002
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
etcd3:
image: quay.io/coreos/etcd:v2.2.0
net: "host"
command: >
--name etcd3
--listen-peer-urls http://localhost:7003
--listen-client-urls http://localhost:4003
--initial-advertise-peer-urls http://localhost:7003
--advertise-client-urls http://localhost:4003
--initial-cluster etcd1=http://localhost:7001,etcd2=http://localhost:7002,etcd3=http://localhost:7003
etcd:
image: containous/docker-etcd
whoami1:
image: emilevauge/whoami
whoami2:
image: emilevauge/whoami
whoami3:
image: emilevauge/whoami
whoami4:
image: emilevauge/whoami

50
integration/utils/try.go Normal file
View File

@@ -0,0 +1,50 @@
package utils
import (
"errors"
"github.com/cenkalti/backoff"
"net/http"
"strconv"
"time"
)
// TryRequest try operation timeout, and retry backoff
func TryRequest(url string, timeout time.Duration, condition Condition) error {
exponentialBackOff := backoff.NewExponentialBackOff()
exponentialBackOff.MaxElapsedTime = timeout
var res *http.Response
err := backoff.Retry(func() error {
var err error
res, err = http.Get(url)
if err != nil {
return err
}
return condition(res)
}, exponentialBackOff)
return err
}
// Try try operation timeout, and retry backoff
func Try(timeout time.Duration, operation func() error) error {
exponentialBackOff := backoff.NewExponentialBackOff()
exponentialBackOff.MaxElapsedTime = timeout
err := backoff.Retry(operation, exponentialBackOff)
return err
}
// Condition is a retry condition function.
// It receives a response, and returns an error
// if the response failed the condition.
type Condition func(*http.Response) error
// ErrorIfStatusCodeIsNot returns a retry condition function.
// The condition returns an error
// if the given response's status code is not the given HTTP status code.
func ErrorIfStatusCodeIsNot(status int) Condition {
return func(res *http.Response) error {
if res.StatusCode != status {
return errors.New("Bad status. Got: " + res.Status + ", expected:" + strconv.Itoa(status))
}
return nil
}
}

View File

@@ -1,6 +1,7 @@
package middlewares
import (
"bufio"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/streamrail/concurrent-map"
@@ -167,6 +168,10 @@ func (lirw *logInfoResponseWriter) Flush() {
}
}
func (lirw *logInfoResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return lirw.rw.(http.Hijacker).Hijack()
}
func (lirw *logInfoResponseWriter) GetStatus() int {
return lirw.status
}

View File

@@ -9,7 +9,7 @@ import (
// BoltDb holds configurations of the BoltDb provider.
type BoltDb struct {
Kv `mapstructure:",squash"`
Kv
}
// Provide allows the provider to provide configurations to traefik

View File

@@ -9,7 +9,7 @@ import (
// Consul holds configurations of the Consul provider.
type Consul struct {
Kv `mapstructure:",squash"`
Kv
}
// Provide allows the provider to provide configurations to traefik

View File

@@ -23,11 +23,11 @@ const (
// ConsulCatalog holds configurations of the Consul catalog provider.
type ConsulCatalog struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
Domain string
client *api.Client
Prefix string
BaseProvider
Endpoint string `description:"Consul server endpoint"`
Domain string `description:"Default domain used"`
client *api.Client
Prefix string
}
type serviceUpdate struct {
@@ -133,9 +133,11 @@ func (provider *ConsulCatalog) getBackendAddress(node *api.ServiceEntry) string
func (provider *ConsulCatalog) getBackendName(node *api.ServiceEntry, index int) string {
serviceName := node.Service.Service + "--" + node.Service.Address + "--" + strconv.Itoa(node.Service.Port)
if len(node.Service.Tags) > 0 {
serviceName += "--" + strings.Join(node.Service.Tags, "--")
for _, tag := range node.Service.Tags {
serviceName += "--" + normalize(tag)
}
serviceName = strings.Replace(serviceName, ".", "-", -1)
serviceName = strings.Replace(serviceName, "=", "-", -1)

View File

@@ -154,6 +154,17 @@ func TestConsulCatalogGetBackendName(t *testing.T) {
},
expected: "api--10-0-0-1--80--traefik-weight-42--traefik-enable-true--1",
},
{
node: &api.ServiceEntry{
Service: &api.AgentService{
Service: "api",
Address: "10.0.0.1",
Port: 80,
Tags: []string{"a funny looking tag"},
},
},
expected: "api--10-0-0-1--80--a-funny-looking-tag--2",
},
}
for i, e := range services {
@@ -225,7 +236,8 @@ func TestConsulCatalogBuildConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-test": {
Backend: "backend-test",
Backend: "backend-test",
PassHostHeader: true,
Routes: map[string]types.Route{
"route-host-test": {
Rule: "Host:test.localhost",

View File

@@ -29,18 +29,18 @@ const DockerAPIVersion string = "1.21"
// Docker holds configurations of the Docker provider.
type Docker struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
Domain string
TLS *DockerTLS
BaseProvider
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"`
Domain string `description:"Default domain used"`
TLS *DockerTLS `description:"Enable Docker TLS support"`
}
// DockerTLS holds TLS specific configurations
type DockerTLS struct {
CA string
Cert string
Key string
InsecureSkipVerify bool
CA string `description:"TLS CA"`
Cert string `description:"TLS cert"`
Key string `description:"TLS key"`
InsecureSkipVerify bool `description:"TLS insecure skip verify"`
}
func (provider *Docker) createClient() (client.APIClient, error) {
@@ -196,11 +196,11 @@ func (provider *Docker) loadDockerConfig(containersInspected []dockertypes.Conta
}
func containerFilter(container dockertypes.ContainerJSON) bool {
if len(container.NetworkSettings.Ports) == 0 {
log.Debugf("Filtering container without port %s", container.Name)
_, err := strconv.Atoi(container.Config.Labels["traefik.port"])
if len(container.NetworkSettings.Ports) == 0 && err != nil {
log.Debugf("Filtering container without port and no traefik.port label %s", container.Name)
return false
}
_, err := strconv.Atoi(container.Config.Labels["traefik.port"])
if len(container.NetworkSettings.Ports) > 1 && err != nil {
log.Debugf("Filtering container with more than 1 port and no traefik.port label %s", container.Name)
return false
@@ -279,7 +279,7 @@ func (provider *Docker) getPassHostHeader(container dockertypes.ContainerJSON) s
if passHostHeader, err := getLabel(container, "traefik.frontend.passHostHeader"); err == nil {
return passHostHeader
}
return "false"
return "true"
}
func (provider *Docker) getEntryPoints(container dockertypes.ContainerJSON) []string {

View File

@@ -250,6 +250,20 @@ func TestDockerGetPort(t *testing.T) {
// },
// expected: "80",
// },
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "test",
},
Config: &container.Config{
Labels: map[string]string{
"traefik.port": "8080",
},
},
NetworkSettings: &docker.NetworkSettings{},
},
expected: "8080",
},
{
container: docker.ContainerJSON{
ContainerJSONBase: &docker.ContainerJSONBase{
@@ -401,7 +415,6 @@ func TestDockerGetProtocol(t *testing.T) {
func TestDockerGetPassHostHeader(t *testing.T) {
provider := &Docker{}
containers := []struct {
container docker.ContainerJSON
expected string
@@ -413,7 +426,7 @@ func TestDockerGetPassHostHeader(t *testing.T) {
},
Config: &container.Config{},
},
expected: "false",
expected: "true",
},
{
container: docker.ContainerJSON{
@@ -422,11 +435,11 @@ func TestDockerGetPassHostHeader(t *testing.T) {
},
Config: &container.Config{
Labels: map[string]string{
"traefik.frontend.passHostHeader": "true",
"traefik.frontend.passHostHeader": "false",
},
},
},
expected: "true",
expected: "false",
},
}
@@ -744,8 +757,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test-docker-localhost": {
Backend: "backend-test",
EntryPoints: []string{},
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test-docker-localhost": {
Rule: "Host:test.docker.localhost",
@@ -816,8 +830,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
Backend: "backend-foobar",
EntryPoints: []string{"http", "https"},
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
Rule: "Host:test1.docker.localhost",
@@ -825,8 +840,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
},
"frontend-Host-test2-docker-localhost": {
Backend: "backend-foobar",
EntryPoints: []string{},
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test2-docker-localhost": {
Rule: "Host:test2.docker.localhost",

View File

@@ -9,7 +9,7 @@ import (
// Etcd holds configurations of the Etcd provider.
type Etcd struct {
Kv `mapstructure:",squash"`
Kv
}
// Provide allows the provider to provide configurations to traefik

View File

@@ -14,7 +14,7 @@ import (
// File holds configurations of the File provider.
type File struct {
BaseProvider `mapstructure:",squash"`
BaseProvider
}
// Provide allows the provider to provide configurations to traefik

View File

@@ -5,7 +5,6 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"github.com/containous/traefik/safe"
"github.com/parnurzeal/gorequest"
"net/http"
"net/url"
@@ -17,12 +16,14 @@ const (
APIEndpoint = "/api/v1"
extentionsEndpoint = "/apis/extensions/v1beta1"
defaultIngress = "/ingresses"
namespaces = "/namespaces/"
)
// Client is a client for the Kubernetes master.
type Client interface {
GetIngresses(predicate func(Ingress) bool) ([]Ingress, error)
GetServices(predicate func(Service) bool) ([]Service, error)
GetService(name, namespace string) (Service, error)
GetEndpoints(name, namespace string) (Endpoints, error)
WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error)
}
@@ -49,7 +50,7 @@ func NewClient(baseURL string, caCert []byte, token string) (Client, error) {
}, nil
}
// GetIngresses returns all services in the cluster
// GetIngresses returns all ingresses in the cluster
func (c *clientImpl) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
@@ -77,26 +78,20 @@ func (c *clientImpl) WatchIngresses(stopCh <-chan bool) (chan interface{}, chan
return c.watch(getURL, stopCh)
}
// GetServices returns all services in the cluster
func (c *clientImpl) GetServices(predicate func(Service) bool) ([]Service, error) {
getURL := c.endpointURL + APIEndpoint + "/services"
// GetService returns the named service from the named namespace
func (c *clientImpl) GetService(name, namespace string) (Service, error) {
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name
body, err := c.do(c.request(getURL))
if err != nil {
return nil, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err)
return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err)
}
var serviceList ServiceList
if err := json.Unmarshal(body, &serviceList); err != nil {
return nil, fmt.Errorf("failed to decode list of services resources: %v", err)
var service Service
if err := json.Unmarshal(body, &service); err != nil {
return Service{}, fmt.Errorf("failed to decode service resource: %v", err)
}
services := serviceList.Items[:0]
for _, service := range serviceList.Items {
if predicate(service) {
services = append(services, service)
}
}
return services, nil
return service, nil
}
// WatchServices returns all services in the cluster
@@ -105,28 +100,33 @@ func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan e
return c.watch(getURL, stopCh)
}
// WatchEvents returns events in the cluster
func (c *clientImpl) WatchEvents(stopCh <-chan bool) (chan interface{}, chan error, error) {
getURL := c.endpointURL + APIEndpoint + "/events"
return c.watch(getURL, stopCh)
// GetEndpoints returns the named Endpoints
// Endpoints have the same name as the coresponding service
func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) {
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name
body, err := c.do(c.request(getURL))
if err != nil {
return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err)
}
var endpoints Endpoints
if err := json.Unmarshal(body, &endpoints); err != nil {
return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err)
}
return endpoints, nil
}
// WatchPods returns pods in the cluster
func (c *clientImpl) WatchPods(stopCh <-chan bool) (chan interface{}, chan error, error) {
getURL := c.endpointURL + APIEndpoint + "/pods"
return c.watch(getURL, stopCh)
}
// WatchReplicationControllers returns ReplicationControllers in the cluster
func (c *clientImpl) WatchReplicationControllers(stopCh <-chan bool) (chan interface{}, chan error, error) {
getURL := c.endpointURL + APIEndpoint + "/replicationcontrollers"
// WatchEndpoints returns endpoints in the cluster
func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) {
getURL := c.endpointURL + APIEndpoint + "/endpoints"
return c.watch(getURL, stopCh)
}
// WatchAll returns events in the cluster
func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) {
watchCh := make(chan interface{})
errCh := make(chan error)
watchCh := make(chan interface{}, 10)
errCh := make(chan error, 10)
stopIngresses := make(chan bool)
chanIngresses, chanIngressesErr, err := c.WatchIngresses(stopIngresses)
@@ -138,13 +138,8 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error,
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
}
stopPods := make(chan bool)
chanPods, chanPodsErr, err := c.WatchPods(stopPods)
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
}
stopReplicationControllers := make(chan bool)
chanReplicationControllers, chanReplicationControllersErr, err := c.WatchReplicationControllers(stopReplicationControllers)
stopEndpoints := make(chan bool)
chanEndpoints, chanEndpointsErr, err := c.WatchEndpoints(stopEndpoints)
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
}
@@ -153,32 +148,26 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error,
defer close(errCh)
defer close(stopIngresses)
defer close(stopServices)
defer close(stopPods)
defer close(stopReplicationControllers)
defer close(stopEndpoints)
for {
select {
case <-stopCh:
stopIngresses <- true
stopServices <- true
stopPods <- true
stopReplicationControllers <- true
break
stopEndpoints <- true
return
case err := <-chanIngressesErr:
errCh <- err
case err := <-chanServicesErr:
errCh <- err
case err := <-chanPodsErr:
errCh <- err
case err := <-chanReplicationControllersErr:
case err := <-chanEndpointsErr:
errCh <- err
case event := <-chanIngresses:
watchCh <- event
case event := <-chanServices:
watchCh <- event
case event := <-chanPods:
watchCh <- event
case event := <-chanReplicationControllers:
case event := <-chanEndpoints:
watchCh <- event
}
}
@@ -192,6 +181,7 @@ func (c *clientImpl) do(request *gorequest.SuperAgent) ([]byte, error) {
if errs != nil {
return nil, fmt.Errorf("failed to create request: GET %q : %v", request.Url, errs)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, request.Url, string(body))
}
@@ -201,6 +191,12 @@ func (c *clientImpl) do(request *gorequest.SuperAgent) ([]byte, error) {
func (c *clientImpl) request(url string) *gorequest.SuperAgent {
// Make request to Kubernetes API
request := gorequest.New().Get(url)
request.Transport.DisableKeepAlives = true
if strings.HasPrefix(url, "http://") {
return request
}
if len(c.token) > 0 {
request.Header["Authorization"] = "Bearer " + c.token
pool := x509.NewCertPool()
@@ -217,8 +213,8 @@ type GenericObject struct {
}
func (c *clientImpl) watch(url string, stopCh <-chan bool) (chan interface{}, chan error, error) {
watchCh := make(chan interface{})
errCh := make(chan error)
watchCh := make(chan interface{}, 10)
errCh := make(chan error, 10)
// get version
body, err := c.do(c.request(url))
@@ -240,34 +236,38 @@ func (c *clientImpl) watch(url string, stopCh <-chan bool) (chan interface{}, ch
return watchCh, errCh, fmt.Errorf("failed to make watch request: GET %q : %v", url, err)
}
request.Client.Transport = request.Transport
res, err := request.Client.Do(req)
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to do watch request: GET %q: %v", url, err)
}
shouldStop := safe.New(false)
go func() {
select {
case <-stopCh:
shouldStop.Set(true)
res.Body.Close()
return
}
}()
go func() {
finishCh := make(chan bool)
defer close(finishCh)
defer close(watchCh)
defer close(errCh)
for {
var eventList interface{}
if err := json.NewDecoder(res.Body).Decode(&eventList); err != nil {
if !shouldStop.Get().(bool) {
errCh <- fmt.Errorf("failed to decode watch event: %v", err)
go func() {
defer res.Body.Close()
for {
var eventList interface{}
if err := json.NewDecoder(res.Body).Decode(&eventList); err != nil {
if !strings.Contains(err.Error(), "net/http: request canceled") {
errCh <- fmt.Errorf("failed to decode watch event: GET %q : %v", url, err)
}
finishCh <- true
return
}
return
watchCh <- eventList
}
watchCh <- eventList
}()
select {
case <-stopCh:
go func() {
request.Transport.CancelRequest(req)
}()
<-finishCh
return
}
}()
return watchCh, errCh, nil

84
provider/k8s/endpoints.go Normal file
View File

@@ -0,0 +1,84 @@
package k8s
// Endpoints is a collection of endpoints that implement the actual service. Example:
// Name: "mysvc",
// Subsets: [
// {
// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],
// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]
// },
// {
// Addresses: [{"ip": "10.10.3.3"}],
// Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}]
// },
// ]
type Endpoints struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"`
// The set of all endpoints is the union of all subsets.
Subsets []EndpointSubset
}
// EndpointSubset is a group of addresses with a common set of ports. The
// expanded set of endpoints is the Cartesian product of Addresses x Ports.
// For example, given:
// {
// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],
// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]
// }
// The resulting set of endpoints can be viewed as:
// a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],
// b: [ 10.10.1.1:309, 10.10.2.2:309 ]
type EndpointSubset struct {
Addresses []EndpointAddress
NotReadyAddresses []EndpointAddress
Ports []EndpointPort
}
// EndpointAddress is a tuple that describes single IP address.
type EndpointAddress struct {
// The IP of this endpoint.
// IPv6 is also accepted but not fully supported on all platforms. Also, certain
// kubernetes components, like kube-proxy, are not IPv6 ready.
// TODO: This should allow hostname or IP, see #4447.
IP string
// Optional: Hostname of this endpoint
// Meant to be used by DNS servers etc.
Hostname string `json:"hostname,omitempty"`
// Optional: The kubernetes object related to the entry point.
TargetRef *ObjectReference
}
// EndpointPort is a tuple that describes a single port.
type EndpointPort struct {
// The name of this port (corresponds to ServicePort.Name). Optional
// if only one port is defined. Must be a DNS_LABEL.
Name string
// The port number.
Port int32
// The IP protocol for this port.
Protocol Protocol
}
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
UID UID `json:"uid,omitempty"`
APIVersion string `json:"apiVersion,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
// Optional. If referring to a piece of an object instead of an entire object, this string
// should contain information to identify the sub-object. For example, if the object
// reference is to a container within a pod, this would take on a value like:
// "spec.containers{name}" (where "name" refers to the name of the container that triggered
// the event) or if no container name is specified "spec.containers[2]" (container with
// index 2 in this pod). This syntax is chosen only to have some well-defined way of
// referencing a part of an object.
// TODO: this design is not final and this field is subject to change in the future.
FieldPath string `json:"fieldPath,omitempty"`
}

View File

@@ -1,6 +1,7 @@
package provider
import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/containous/traefik/provider/k8s"
@@ -9,6 +10,7 @@ import (
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"text/template"
"time"
@@ -19,10 +21,38 @@ const (
serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)
// Namespaces holds kubernetes namespaces
type Namespaces []string
//Set adds strings elem into the the parser
//it splits str on , and ;
func (ns *Namespaces) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
*ns = append(*ns, slice...)
return nil
}
//Get []string
func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) }
//String return slice in a string
func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) }
//SetValue sets []string into the parser
func (ns *Namespaces) SetValue(val interface{}) {
*ns = Namespaces(val.(Namespaces))
}
// Kubernetes holds configurations of the Kubernetes provider.
type Kubernetes struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
BaseProvider
Endpoint string `description:"Kubernetes server endpoint"`
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
Namespaces Namespaces `description:"Kubernetes namespaces"`
}
func (provider *Kubernetes) createClient() (k8s.Client, error) {
@@ -59,19 +89,20 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
backOff := backoff.NewExponentialBackOff()
pool.Go(func(stop chan bool) {
stopWatch := make(chan bool)
defer close(stopWatch)
operation := func() error {
select {
case <-stop:
return nil
default:
}
for {
stopWatch := make(chan bool, 5)
defer close(stopWatch)
eventsChan, errEventsChan, err := k8sClient.WatchAll(stopWatch)
if err != nil {
log.Errorf("Error watching kubernetes events: %v", err)
return err
timer := time.NewTimer(1 * time.Second)
select {
case <-timer.C:
return err
case <-stop:
return nil
}
}
Watch:
for {
@@ -79,14 +110,15 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
case <-stop:
stopWatch <- true
return nil
case err := <-errEventsChan:
if strings.Contains(err.Error(), io.EOF.Error()) {
case err, ok := <-errEventsChan:
stopWatch <- true
if ok && strings.Contains(err.Error(), io.EOF.Error()) {
// edge case, kubernetes long-polling disconnection
break Watch
}
return err
case event := <-eventsChan:
log.Debugf("Received event from kubenetes %+v", event)
log.Debugf("Received event from kubernetes %+v", event)
templateObjects, err := provider.loadIngresses(k8sClient)
if err != nil {
return err
@@ -123,7 +155,15 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
ingresses, err := k8sClient.GetIngresses(func(ingress k8s.Ingress) bool {
return true
if len(provider.Namespaces) == 0 {
return true
}
for _, n := range provider.Namespaces {
if ingress.ObjectMeta.Namespace == n {
return true
}
}
return false
})
if err != nil {
log.Errorf("Error retrieving ingresses: %+v", err)
@@ -133,6 +173,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
map[string]*types.Backend{},
map[string]*types.Frontend{},
}
PassHostHeader := provider.getPassHostHeader()
for _, i := range ingresses {
for _, r := range i.Spec.Rules {
for _, pa := range r.HTTP.Paths {
@@ -143,45 +184,75 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
templateObjects.Frontends[r.Host+pa.Path] = &types.Frontend{
Backend: r.Host + pa.Path,
Routes: make(map[string]types.Route),
Backend: r.Host + pa.Path,
PassHostHeader: PassHostHeader,
Routes: make(map[string]types.Route),
}
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host]; !exists {
templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host] = types.Route{
Rule: "Host:" + r.Host,
if len(r.Host) > 0 {
if _, exists := templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host]; !exists {
templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host] = types.Route{
Rule: "Host:" + r.Host,
}
}
}
if len(pa.Path) > 0 {
ruleType := i.Annotations["traefik.frontend.rule.type"]
switch strings.ToLower(ruleType) {
case "pathprefixstrip":
ruleType = "PathPrefixStrip"
case "pathstrip":
ruleType = "PathStrip"
case "path":
ruleType = "Path"
case "pathprefix":
ruleType = "PathPrefix"
default:
log.Warnf("Unknown RuleType `%s`, falling back to `PathPrefix", ruleType)
ruleType = "PathPrefix"
}
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
Rule: "PathPrefixStrip:" + pa.Path,
Rule: ruleType + ":" + pa.Path,
}
}
services, err := k8sClient.GetServices(func(service k8s.Service) bool {
return service.Name == pa.Backend.ServiceName
})
service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace)
if err != nil {
log.Errorf("Error retrieving services: %v", err)
log.Warnf("Error retrieving services: %v", err)
delete(templateObjects.Frontends, r.Host+pa.Path)
log.Warnf("Error retrieving services %s", pa.Backend.ServiceName)
continue
}
if len(services) == 0 {
// no backends found, delete frontend...
delete(templateObjects.Frontends, r.Host+pa.Path)
log.Errorf("Error retrieving services %s", pa.Backend.ServiceName)
}
for _, service := range services {
protocol := "http"
for _, port := range service.Spec.Ports {
if port.Port == pa.Backend.ServicePort.IntValue() {
if port.Port == 443 {
protocol = "https"
}
protocol := "http"
for _, port := range service.Spec.Ports {
if equalPorts(port, pa.Backend.ServicePort) {
if port.Port == 443 {
protocol = "https"
}
endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace)
if err != nil {
log.Errorf("Error retrieving endpoints: %v", err)
continue
}
if len(endpoints.Subsets) == 0 {
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
URL: protocol + "://" + service.Spec.ClusterIP + ":" + pa.Backend.ServicePort.String(),
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port),
Weight: 1,
}
break
} else {
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports))
templateObjects.Backends[r.Host+pa.Path].Servers[url] = types.Server{
URL: url,
Weight: 1,
}
}
}
}
break
}
}
}
@@ -190,6 +261,37 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
return &templateObjects, nil
}
func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.EndpointPort) int {
if len(endpointPorts) > 0 {
//name is optional if there is only one port
port := endpointPorts[0]
for _, endpointPort := range endpointPorts {
if servicePort.Name == endpointPort.Name {
port = endpointPort
}
}
return int(port.Port)
}
return servicePort.Port
}
func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool {
if servicePort.Port == ingressPort.IntValue() {
return true
}
if servicePort.Name != "" && servicePort.Name == ingressPort.String() {
return true
}
return false
}
func (provider *Kubernetes) getPassHostHeader() bool {
if provider.DisablePassHostHeaders {
return false
}
return true
}
func (provider *Kubernetes) loadConfig(templateObjects types.Configuration) *types.Configuration {
var FuncMap = template.FuncMap{}
configuration, err := provider.getConfiguration("templates/kubernetes.tmpl", FuncMap, templateObjects)

File diff suppressed because it is too large Load Diff

View File

@@ -22,28 +22,27 @@ import (
// Kv holds common configurations of key-value providers.
type Kv struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
Prefix string
TLS *KvTLS
storeType store.Backend
kvclient store.Store
BaseProvider
Endpoint string `description:"Comma sepparated server endpoints"`
Prefix string `description:"Prefix used for KV store"`
TLS *KvTLS `description:"Enable TLS support"`
storeType store.Backend
kvclient store.Store
}
// KvTLS holds TLS specific configurations
type KvTLS struct {
CA string
Cert string
Key string
InsecureSkipVerify bool
CA string `description:"TLS CA"`
Cert string `description:"TLS cert"`
Key string `description:"TLS key"`
InsecureSkipVerify bool `description:"TLS insecure skip verify"`
}
func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) {
func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error {
operation := func() error {
events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{}) /* stop chan */)
events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{}))
if err != nil {
log.Errorf("Failed to WatchTree %s", err)
return err
return fmt.Errorf("Failed to KV WatchTree: %v", err)
}
for {
select {
@@ -65,12 +64,13 @@ func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix
}
notify := func(err error, time time.Duration) {
log.Errorf("KV connection error %+v, retrying in %s", err, time)
log.Errorf("KV connection error: %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
if err != nil {
log.Fatalf("Cannot connect to KV server %+v", err)
return fmt.Errorf("Cannot connect to KV server: %v", err)
}
return nil
}
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
@@ -112,15 +112,18 @@ func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *
storeConfig,
)
if err != nil {
return err
return fmt.Errorf("Failed to Connect to KV store: %v", err)
}
if _, err := kv.List(""); err != nil {
return err
if _, err := kv.Exists("qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"); err != nil {
return fmt.Errorf("Failed to test KV store connection: %v", err)
}
provider.kvclient = kv
if provider.Watch {
pool.Go(func(stop chan bool) {
provider.watchKv(configurationChan, provider.Prefix, stop)
err := provider.watchKv(configurationChan, provider.Prefix, stop)
if err != nil {
log.Errorf("Cannot watch KV store: %v", err)
}
})
}
configuration := provider.loadConfig()
@@ -131,11 +134,11 @@ func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *
return nil
}
notify := func(err error, time time.Duration) {
log.Errorf("KV connection error %+v, retrying in %s", err, time)
log.Errorf("KV connection error: %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
if err != nil {
log.Fatalf("Cannot connect to KV server %+v", err)
return fmt.Errorf("Cannot connect to KV server: %v", err)
}
return nil
}
@@ -170,7 +173,7 @@ func (provider *Kv) list(keys ...string) []string {
}
directoryKeys := make(map[string]string)
for _, key := range keysPairs {
directory := strings.Split(strings.TrimPrefix(key.Key, strings.TrimPrefix(joinedKeys, "/")), "/")[0]
directory := strings.Split(strings.TrimPrefix(key.Key, joinedKeys), "/")[0]
directoryKeys[directory] = joinedKeys + directory
}
return fun.Values(directoryKeys).([]string)
@@ -178,7 +181,7 @@ func (provider *Kv) list(keys ...string) []string {
func (provider *Kv) get(defaultValue string, keys ...string) string {
joinedKeys := strings.Join(keys, "")
keyPair, err := provider.kvclient.Get(joinedKeys)
keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/"))
if err != nil {
log.Warnf("Error getting key %s %s, setting default %s", joinedKeys, err, defaultValue)
return defaultValue

View File

@@ -430,7 +430,7 @@ func TestKVLoadConfig(t *testing.T) {
Frontends: map[string]*types.Frontend{
"frontend.with.dot": {
Backend: "backend.with.dot.too",
PassHostHeader: false,
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route.with.dot": {

View File

@@ -20,10 +20,10 @@ import (
// Marathon holds configuration of the Marathon provider.
type Marathon struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
Domain string
ExposedByDefault bool
BaseProvider `mapstructure:",squash" description:"go through"`
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"`
Basic *MarathonBasic
TLS *tls.Config
marathonClient marathon.Marathon
@@ -36,8 +36,8 @@ type MarathonBasic struct {
}
type lightMarathonClient interface {
Applications(url.Values) (*marathon.Applications, error)
AllTasks(v url.Values) (*marathon.Tasks, error)
Applications(url.Values) (*marathon.Applications, error)
}
// Provide allows the provider to provide configurations to traefik
@@ -316,7 +316,7 @@ func (provider *Marathon) getPassHostHeader(application marathon.Application) st
if passHostHeader, err := provider.getLabel(application, "traefik.frontend.passHostHeader"); err == nil {
return passHostHeader
}
return "false"
return "true"
}
func (provider *Marathon) getEntryPoints(application marathon.Application) []string {

View File

@@ -82,8 +82,9 @@ func TestMarathonLoadConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
`frontend-test`: {
Backend: "backend-test",
EntryPoints: []string{},
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-test`: {
Rule: "Host:test.docker.localhost",
@@ -780,15 +781,15 @@ func TestMarathonGetPassHostHeader(t *testing.T) {
}{
{
application: marathon.Application{},
expected: "false",
expected: "true",
},
{
application: marathon.Application{
Labels: map[string]string{
"traefik.frontend.passHostHeader": "true",
"traefik.frontend.passHostHeader": "false",
},
},
expected: "true",
expected: "false",
},
}

View File

@@ -22,8 +22,8 @@ type Provider interface {
// BaseProvider should be inherited by providers
type BaseProvider struct {
Watch bool
Filename string
Watch bool `description:"Watch provider"`
Filename string `description:"Override default configuration template. For advanced users :)"`
}
func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {

View File

@@ -9,13 +9,13 @@ fi
if [ -z "$1" ]; then
# Remove windows platform because of
# https://github.com/mailgun/log/issues/10
OS_PLATFORM_ARG=(-os="darwin linux")
OS_PLATFORM_ARG=(linux)
else
OS_PLATFORM_ARG=($1)
fi
if [ -z "$2" ]; then
OS_ARCH_ARG=(-arch="386 amd64 arm")
OS_ARCH_ARG=(386 amd64 arm)
else
OS_ARCH_ARG=($2)
fi
@@ -32,5 +32,9 @@ fi
rm -f dist/traefik_*
# Build binaries
GOGC=off gox -ldflags "-X main.Version=$VERSION -X main.BuildDate=$DATE" "${OS_PLATFORM_ARG[@]}" "${OS_ARCH_ARG[@]}" \
-output="dist/traefik_{{.OS}}-{{.Arch}}"
for OS in ${OS_PLATFORM_ARG[@]}; do
for ARCH in ${OS_ARCH_ARG[@]}; do
echo "Building binary for $OS/$ARCH..."
GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build -ldflags "-s -w -X main.Version=$VERSION -X main.BuildDate=$DATE" -o "dist/traefik_$OS-$ARCH" .
done
done

View File

@@ -1,7 +1,7 @@
#!/bin/bash
set -e
if ([ "$TRAVIS_BRANCH" = "master" ] || [ ! -z "$TRAVIS_TAG" ]) && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
if [ -n "$TRAVIS_TAG" ] && [ "$DOCKER_VERSION" = "1.10.1" ]; then
echo "Deploying..."
else
echo "Skipping deploy"

View File

@@ -11,4 +11,6 @@ if [ -n "$VERBOSE" ]; then
fi
cd integration
echo "Testing against…"
docker version
CGO_ENABLED=0 go test $TESTFLAGS

View File

@@ -68,8 +68,8 @@ func NewServer(globalConfiguration GlobalConfiguration) *Server {
server := new(Server)
server.serverEntryPoints = make(map[string]*serverEntryPoint)
server.configurationChan = make(chan types.ConfigMessage, 10)
server.configurationValidatedChan = make(chan types.ConfigMessage, 10)
server.configurationChan = make(chan types.ConfigMessage, 100)
server.configurationValidatedChan = make(chan types.ConfigMessage, 100)
server.signals = make(chan os.Signal, 1)
server.stopChan = make(chan bool, 1)
server.providers = []provider.Provider{}

View File

@@ -29,7 +29,7 @@
{{range .Services}}
[frontends.frontend-{{.ServiceName}}]
backend = "backend-{{.ServiceName}}"
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "false"}}
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}}
{{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}}
{{with $entryPoints}}
entrypoints = [{{range getEntryPoints $entryPoints}}

View File

@@ -9,6 +9,7 @@
[frontends]{{range $frontendName, $frontend := .Frontends}}
[frontends."{{$frontendName}}"]
backend = "{{$frontend.Backend}}"
passHostHeader = {{$frontend.PassHostHeader}}
{{range $routeName, $route := $frontend.Routes}}
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
rule = "{{$route.Rule}}"

View File

@@ -39,7 +39,7 @@
{{$entryPoints := SplitGet . "/entrypoints"}}
[frontends."{{$frontend}}"]
backend = "{{Get "" . "/backend"}}"
passHostHeader = {{Get "false" . "/passHostHeader"}}
passHostHeader = {{Get "true" . "/passHostHeader"}}
entryPoints = [{{range $entryPoints}}
"{{.}}",
{{end}}]

View File

@@ -1,16 +1,144 @@
package main
import (
"encoding/json"
log "github.com/Sirupsen/logrus"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
fmtlog "log"
"net/http"
"os"
"reflect"
"runtime"
"strings"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
if err := traefikCmd.Execute(); err != nil {
//traefik config inits
traefikConfiguration := NewTraefikConfiguration()
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
//traefik Command init
traefikCmd := &flaeg.Command{
Name: "traefik",
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at https://traefik.io`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
run(traefikConfiguration)
return nil
},
}
//version Command init
versionCmd := &flaeg.Command{
Name: "version",
Description: `Print version`,
Config: struct{}{},
DefaultPointersConfig: struct{}{},
Run: func() error {
fmtlog.Println(Version + " built on the " + BuildDate)
return nil
},
}
//init flaeg source
f := flaeg.New(traefikCmd, os.Args[1:])
//add custom parsers
f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{})
f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{})
f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{})
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
//add version command
f.AddCommand(versionCmd)
if _, err := f.Parse(traefikCmd); err != nil {
fmtlog.Println(err)
os.Exit(-1)
}
//staert init
s := staert.NewStaert(traefikCmd)
//init toml source
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
//add sources to staert
s.AddSource(toml)
s.AddSource(f)
if _, err := s.LoadConfig(); err != nil {
fmtlog.Println(err)
}
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
if err := s.Run(); err != nil {
fmtlog.Println(err)
os.Exit(-1)
}
os.Exit(0)
}
func run(traefikConfiguration *TraefikConfiguration) {
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
// load global configuration
globalConfiguration := traefikConfiguration.GlobalConfiguration
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
defer loggerMiddleware.Close()
// logging
level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
log.Fatal("Error getting level", err)
}
log.SetLevel(level)
if traefikConfiguration.File != nil && len(traefikConfiguration.File.Filename) == 0 {
// no filename, setting to global config file
if len(traefikConfiguration.ConfigFile) != 0 {
traefikConfiguration.File.Filename = traefikConfiguration.ConfigFile
} else {
log.Errorln("Error using file configuration backend, no filename defined")
}
}
if len(traefikConfiguration.EntryPoints) == 0 {
traefikConfiguration.EntryPoints = map[string]*EntryPoint{"http": {Address: ":80"}}
traefikConfiguration.DefaultEntryPoints = []string{"http"}
}
if len(globalConfiguration.TraefikLogsFile) > 0 {
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
defer func() {
if err := fi.Close(); err != nil {
log.Error("Error closinf file", err)
}
}()
if err != nil {
log.Fatal("Error opening file", err)
} else {
log.SetOutput(fi)
log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
}
} else {
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Infof("Traefik version %s built on %s", Version, BuildDate)
if len(traefikConfiguration.ConfigFile) != 0 {
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
}
log.Debugf("Global configuration loaded %s", string(jsonConf))
server := NewServer(globalConfiguration)
server.Start()
defer server.Close()
log.Info("Shutting down")
}

View File

@@ -343,6 +343,7 @@
# Optional
#
# endpoint = "http://localhost:8080"
# namespaces = ["default"]
################################################################
# Consul KV configuration backend

40
web.go
View File

@@ -2,9 +2,11 @@ package main
import (
"encoding/json"
"expvar"
"fmt"
"io/ioutil"
"net/http"
"runtime"
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/autogen"
@@ -21,10 +23,11 @@ var metrics = stats.New()
// WebProvider is a provider.Provider implementation that provides the UI.
// FIXME to be handled another way.
type WebProvider struct {
Address string
CertFile, KeyFile string
ReadOnly bool
server *Server
Address string `description:"Web administration port"`
CertFile string `description:"SSL certificate"`
KeyFile string `description:"SSL certificate"`
ReadOnly bool `description:"Enable read only API"`
server *Server
}
var (
@@ -33,6 +36,14 @@ var (
})
)
func init() {
expvar.Publish("Goroutines", expvar.Func(goroutines))
}
func goroutines() interface{} {
return runtime.NumGoroutine()
}
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
@@ -82,7 +93,12 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag
systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
http.Redirect(response, request, "/dashboard/", 302)
})
systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetDir: autogen.AssetDir, Prefix: "static"})))
systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetInfo: autogen.AssetInfo, AssetDir: autogen.AssetDir, Prefix: "static"})))
// expvars
if provider.server.globalConfiguration.Debug {
systemRouter.Methods("GET").Path("/debug/vars").HandlerFunc(expvarHandler)
}
go func() {
if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 {
@@ -231,3 +247,17 @@ func (provider *WebProvider) getRouteHandler(response http.ResponseWriter, reque
}
http.NotFound(response, request)
}
func expvarHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, "{\n")
first := true
expvar.Do(func(kv expvar.KeyValue) {
if !first {
fmt.Fprintf(w, ",\n")
}
first = false
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
})
fmt.Fprintf(w, "\n}\n")
}