forked from Ivasoft/traefik
Compare commits
16 Commits
v1.0.0-bet
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f47434833 | ||
|
|
6f13a2c0c7 | ||
|
|
b7a150bc64 | ||
|
|
4d22c45b76 | ||
|
|
b3b658a955 | ||
|
|
06d2f343dd | ||
|
|
a6c5e85ae7 | ||
|
|
45d6a326cd | ||
|
|
0332e32293 | ||
|
|
2a3a34a80c | ||
|
|
68da47b59a | ||
|
|
b1f0f048cd | ||
|
|
ee60adc45a | ||
|
|
36338b4928 | ||
|
|
23d3c512c2 | ||
|
|
4144638be4 |
@@ -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
|
||||
|
||||
@@ -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...")
|
||||
|
||||
@@ -19,7 +19,7 @@ Let's zoom on Træfɪk and have an overview of its internal architecture:
|
||||

|
||||
|
||||
- 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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
2
rules.go
2
rules.go
@@ -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 {
|
||||
|
||||
@@ -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"`
|
||||
|
||||
Reference in New Issue
Block a user