Compare commits

..

16 Commits

Author SHA1 Message Date
Vincent Demeester
6f47434833 Merge pull request #328 from samber/consul-catalog--remove-disable-frontend
feat(consul-catalog): Remove frontend when backends disabled
2016-04-22 12:09:14 +02:00
Poney baker
6f13a2c0c7 feat(consul-catalog): Remove frontend when backends disabled 2016-04-22 11:55:31 +02:00
Vincent Demeester
b7a150bc64 Merge pull request #327 from pborreli/typos
Fixed typos
2016-04-22 11:38:02 +02:00
Pascal Borreli
4d22c45b76 Fixed typos 2016-04-21 23:38:44 +01:00
Vincent Demeester
b3b658a955 Merge pull request #324 from containous/fix-kv-backend
Fix KV backend
2016-04-20 08:01:32 +02:00
Emile Vauge
06d2f343dd Fix KV backend
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-04-19 23:49:26 +02:00
Vincent Demeester
a6c5e85ae7 Merge pull request #320 from samber/consul-provider-compare-tag-lowercase
fix(consul-provider): Compare consul catalog tag keys with lowered case
2016-04-19 18:23:47 +02:00
Samuel BERTHE
45d6a326cd fix(consul-provider): Compare consul catalog tag keys with lowered case 2016-04-19 17:27:19 +02:00
Vincent Demeester
0332e32293 Merge pull request #322 from containous/fix-marathon-backend
Fix Marathon backend
2016-04-19 12:32:56 +02:00
Emile Vauge
2a3a34a80c Fix Marathon backend
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-04-19 12:05:28 +02:00
Emile Vauge
68da47b59a Merge pull request #321 from samber/minor-doc-fix
fix(doc)
2016-04-19 12:04:13 +02:00
Samuel BERTHE
b1f0f048cd fix(doc) 2016-04-19 10:00:33 +02:00
Vincent Demeester
ee60adc45a Merge pull request #315 from containous/add-backoff-marathon
Add backoff to marathon provider
2016-04-16 17:32:01 +02:00
Emile Vauge
36338b4928 add backoff to marathon provider
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-04-16 17:21:24 +02:00
Emile Vauge
23d3c512c2 Merge pull request #316 from vdemeester/docker-provider-stop-support
Support stop chan on docker provider
2016-04-16 17:20:55 +02:00
Vincent Demeester
4144638be4 Support stop chan on docker provider
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2016-04-16 14:46:35 +02:00
14 changed files with 141 additions and 83 deletions

View File

@@ -76,7 +76,7 @@ You can access to a simple HTML frontend of Træfik.
## Plumbing
- [Oxy](https://github.com/vulcand/oxy): an awsome proxy library made by Mailgun guys
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun guys
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
- [Negroni](https://github.com/codegangsta/negroni): web middlewares made simple
- [Manners](https://github.com/mailgun/manners): graceful shutdown of http.Handler servers

View File

@@ -181,7 +181,7 @@ func (a *ACME) CreateConfig(tlsConfig *tls.Config, CheckOnDemandDomain func(doma
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
if len(a.StorageFile) == 0 {
return errors.New("Empty StorageFile, please provide a filenmae for certs storage")
return errors.New("Empty StorageFile, please provide a filename for certs storage")
}
log.Debugf("Generating default certificate...")

View File

@@ -19,7 +19,7 @@ Let's zoom on Træfɪk and have an overview of its internal architecture:
![Architecture](img/internal.png)
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Træfɪk (listening port, SSL, traffic redirection...).
- Traffic is then forwared to a matching [frontend](#frontends). A frontend defines routes from [entrypoints](#entrypoints) to [backends](#backends).
- Traffic is then forwarded to a matching [frontend](#frontends). A frontend defines routes from [entrypoints](#entrypoints) to [backends](#backends).
Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can match or not a request.
- The [frontend](#frontends) will then send the request to a [backend](#backends). A backend can be composed by one or more [servers](#servers), and by a load-balancing strategy.
- Finally, the [server](#servers) will forward the request to the corresponding microservice in the private network.
@@ -142,7 +142,7 @@ For example:
## Servers
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balacning).
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
Here is an example of backends and servers definition:

View File

@@ -90,7 +90,7 @@
# regex = "^http://localhost/(.*)"
# replacement = "http://mydomain/$1"
entryPoints]
[entryPoints]
[entryPoints.http]
address = ":80"
```
@@ -258,7 +258,7 @@ defaultEntryPoints = ["http", "https"]
rule = "Path:/test"
```
- or put your rules in a separate file, for example `rules.tml`:
- or put your rules in a separate file, for example `rules.toml`:
```toml
# traefik.toml

View File

@@ -6,7 +6,7 @@ zk:
ZK_ID: 1
master:
image: mesosphere/mesos-master:0.26.0-0.2.145.ubuntu1404
image: mesosphere/mesos-master:0.28.1-2.0.20.ubuntu1404
net: host
environment:
MESOS_ZK: zk://127.0.0.1:2181/mesos
@@ -17,7 +17,7 @@ master:
MESOS_WORK_DIR: /var/lib/mesos
slave:
image: mesosphere/mesos-slave:0.26.0-0.2.145.ubuntu1404
image: mesosphere/mesos-slave:0.28.1-2.0.20.ubuntu1404
net: host
pid: host
privileged: true
@@ -34,10 +34,19 @@ slave:
- /lib/x86_64-linux-gnu/libsystemd-journal.so.0:/lib/x86_64-linux-gnu/libsystemd-journal.so.0
marathon:
image: mesosphere/marathon:v0.13.0
image: mesosphere/marathon:v1.1.1
net: host
environment:
MARATHON_MASTER: zk://127.0.0.1:2181/mesos
MARATHON_ZK: zk://127.0.0.1:2181/marathon
MARATHON_HOSTNAME: 127.0.0.1
command: --event_subscriber http_callback
traefik:
image: containous/traefik
command: -c /dev/null --web --logLevel=DEBUG --marathon --marathon.domain marathon.localhost --marathon.endpoint http://172.17.0.1:8080 --marathon.watch
ports:
- "8000:80"
- "8081:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock

View File

@@ -1,12 +1,11 @@
traefik:
image: traefik
command: --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
command: -c /dev/null --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /dev/null:/traefik.toml
whoami1:
image: emilevauge/whoami

View File

@@ -17,11 +17,9 @@ curl -i -H "Accept: application/json" -X PUT -d "2" ht
# frontend 1
curl -i -H "Accept: application/json" -X PUT -d "backend2" http://localhost:8500/v1/kv/traefik/frontends/frontend1/backend
curl -i -H "Accept: application/json" -X PUT -d "http" http://localhost:8500/v1/kv/traefik/frontends/frontend1/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "Host" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/rule
curl -i -H "Accept: application/json" -X PUT -d "test.localhost" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/value
curl -i -H "Accept: application/json" -X PUT -d "Host:test.localhost" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/rule
# frontend 2
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend
curl -i -H "Accept: application/json" -X PUT -d "http,https" http://localhost:8500/v1/kv/traefik/frontends/frontend2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "Path" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule
curl -i -H "Accept: application/json" -X PUT -d "/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/value
curl -i -H "Accept: application/json" -X PUT -d "http" http://localhost:8500/v1/kv/traefik/frontends/frontend2/entrypoints
curl -i -H "Accept: application/json" -X PUT -d "Path:/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule

View File

@@ -25,7 +25,7 @@
],
"labels": {
"traefik.weight": "1",
"traefik.protocole": "http",
"traefik.frontend.rule" : "Headers:Host,test.localhost"
"traefik.protocol": "http",
"traefik.frontend.rule" : "Host:test.marathon.localhost"
}
}

View File

@@ -125,8 +125,8 @@ func (provider *ConsulCatalog) getFrontendRule(service serviceUpdate) string {
func (provider *ConsulCatalog) getAttribute(name string, tags []string, defaultValue string) string {
for _, tag := range tags {
if strings.Index(tag, DefaultConsulCatalogTagPrefix+".") == 0 {
if kv := strings.SplitN(tag[len(DefaultConsulCatalogTagPrefix+"."):], "=", 2); len(kv) == 2 && kv[0] == name {
if strings.Index(strings.ToLower(tag), DefaultConsulCatalogTagPrefix+".") == 0 {
if kv := strings.SplitN(tag[len(DefaultConsulCatalogTagPrefix+"."):], "=", 2); len(kv) == 2 && strings.ToLower(kv[0]) == strings.ToLower(name) {
return kv[1]
}
}
@@ -146,9 +146,14 @@ func (provider *ConsulCatalog) buildConfig(catalog []catalogUpdate) *types.Confi
allNodes := []*api.ServiceEntry{}
services := []*serviceUpdate{}
for _, info := range catalog {
if len(info.Nodes) > 0 {
services = append(services, info.Service)
allNodes = append(allNodes, info.Nodes...)
for _, node := range info.Nodes {
isEnabled := provider.getAttribute("enable", node.Service.Tags, "true")
if isEnabled != "false" && len(info.Nodes) > 0 {
services = append(services, info.Service)
allNodes = append(allNodes, info.Nodes...)
break
}
}
}

View File

@@ -130,6 +130,15 @@ func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, po
eventHandler.Handle("die", startStopHandle)
errChan := events.MonitorWithHandler(ctx, dockerClient, options, eventHandler)
pool.Go(func(stop chan bool) {
for {
select {
case <-stop:
cancel()
return
}
}
})
if err := <-errChan; err != nil {
return err
}

View File

@@ -10,8 +10,10 @@ import (
"text/template"
"time"
"errors"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv"
@@ -37,25 +39,38 @@ type KvTLS struct {
}
func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) {
for {
operation := func() error {
events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{}) /* stop chan */)
if err != nil {
log.Errorf("Failed to WatchTree %s", err)
continue
return err
}
select {
case <-stop:
return
case <-events:
configuration := provider.loadConfig()
if configuration != nil {
configurationChan <- types.ConfigMessage{
ProviderName: string(provider.storeType),
Configuration: configuration,
for {
select {
case <-stop:
return nil
case _, ok := <-events:
if !ok {
return errors.New("watchtree channel closed")
}
configuration := provider.loadConfig()
if configuration != nil {
configurationChan <- types.ConfigMessage{
ProviderName: string(provider.storeType),
Configuration: configuration,
}
}
}
}
}
notify := func(err error, time time.Duration) {
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)
}
}
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
@@ -90,27 +105,37 @@ func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *
}
}
kv, err := libkv.NewStore(
provider.storeType,
strings.Split(provider.Endpoint, ","),
storeConfig,
)
operation := func() error {
kv, err := libkv.NewStore(
provider.storeType,
strings.Split(provider.Endpoint, ","),
storeConfig,
)
if err != nil {
return err
}
if _, err := kv.List(""); err != nil {
return err
}
provider.kvclient = kv
if provider.Watch {
pool.Go(func(stop chan bool) {
provider.watchKv(configurationChan, provider.Prefix, stop)
})
}
configuration := provider.loadConfig()
configurationChan <- types.ConfigMessage{
ProviderName: string(provider.storeType),
Configuration: configuration,
}
return nil
}
notify := func(err error, time time.Duration) {
log.Errorf("KV connection error %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
if err != nil {
return err
}
if _, err := kv.List(""); err != nil {
return err
}
provider.kvclient = kv
if provider.Watch {
pool.Go(func(stop chan bool) {
provider.watchKv(configurationChan, provider.Prefix, stop)
})
}
configuration := provider.loadConfig()
configurationChan <- types.ConfigMessage{
ProviderName: string(provider.storeType),
Configuration: configuration,
log.Fatalf("Cannot connect to KV server %+v", err)
}
return nil
}

View File

@@ -10,10 +10,12 @@ import (
"crypto/tls"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/gambol99/go-marathon"
"net/http"
"time"
)
// Marathon holds configuration of the Marathon provider.
@@ -41,30 +43,33 @@ type lightMarathonClient interface {
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
config := marathon.NewDefaultConfig()
config.URL = provider.Endpoint
config.EventsTransport = marathon.EventsTransportSSE
if provider.Basic != nil {
config.HTTPBasicAuthUser = provider.Basic.HTTPBasicAuthUser
config.HTTPBasicPassword = provider.Basic.HTTPBasicPassword
}
config.HTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: provider.TLS,
},
}
client, err := marathon.NewClient(config)
if err != nil {
log.Errorf("Failed to create a client for marathon, error: %s", err)
return err
}
provider.marathonClient = client
update := make(marathon.EventsChannel, 5)
if provider.Watch {
if err := client.AddEventsListener(update, marathon.EVENTS_APPLICATIONS); err != nil {
log.Errorf("Failed to register for events, %s", err)
} else {
operation := func() error {
config := marathon.NewDefaultConfig()
config.URL = provider.Endpoint
config.EventsTransport = marathon.EventsTransportSSE
if provider.Basic != nil {
config.HTTPBasicAuthUser = provider.Basic.HTTPBasicAuthUser
config.HTTPBasicPassword = provider.Basic.HTTPBasicPassword
}
config.HTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: provider.TLS,
},
}
client, err := marathon.NewClient(config)
if err != nil {
log.Errorf("Failed to create a client for marathon, error: %s", err)
return err
}
provider.marathonClient = client
update := make(marathon.EventsChannel, 5)
if provider.Watch {
if err := client.AddEventsListener(update, marathon.EVENTS_APPLICATIONS); err != nil {
log.Errorf("Failed to register for events, %s", err)
return err
}
pool.Go(func(stop chan bool) {
defer close(update)
for {
select {
case <-stop:
@@ -82,12 +87,20 @@ func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage,
}
})
}
configuration := provider.loadMarathonConfig()
configurationChan <- types.ConfigMessage{
ProviderName: "marathon",
Configuration: configuration,
}
return nil
}
configuration := provider.loadMarathonConfig()
configurationChan <- types.ConfigMessage{
ProviderName: "marathon",
Configuration: configuration,
notify := func(err error, time time.Duration) {
log.Errorf("Marathon connection error %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
if err != nil {
log.Fatalf("Cannot connect to Marathon server %+v", err)
}
return nil
}

View File

@@ -116,7 +116,7 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
}
parsedFunction, ok := functions[parsedFunctions[0]]
if !ok {
return nil, errors.New("Error parsing rule: " + expression + ". Unknow function: " + parsedFunctions[0])
return nil, errors.New("Error parsing rule: " + expression + ". Unknown function: " + parsedFunctions[0])
}
parsedFunctions = append(parsedFunctions[:0], parsedFunctions[1:]...)
fargs := func(c rune) bool {

View File

@@ -13,7 +13,7 @@ type Backend struct {
MaxConn *MaxConn `json:"maxConn,omitempty"`
}
// MaxConn holds maximum connection configuraiton
// MaxConn holds maximum connection configuration
type MaxConn struct {
Amount int64 `json:"amount,omitempty"`
ExtractorFunc string `json:"extractorFunc,omitempty"`