From 46d7cc83c97835110372e96e9fa77c70a2735a54 Mon Sep 17 00:00:00 2001 From: emile Date: Thu, 15 Oct 2015 09:08:16 +0200 Subject: [PATCH 1/8] Better logs http status in websocket --- middlewares/websocketproxy.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/middlewares/websocketproxy.go b/middlewares/websocketproxy.go index abb82688f..b377b9940 100644 --- a/middlewares/websocketproxy.go +++ b/middlewares/websocketproxy.go @@ -73,15 +73,16 @@ func NewProxy(target *url.URL) *WebsocketProxy { // ServeHTTP implements the http.Handler that proxies WebSocket connections. func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if w.Backend == nil { - log.Println("websocketproxy: backend function is not defined") - http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError) + log.Errorf("Websocketproxy: backend function is not defined") + http.Error(rw, "Backend not found", http.StatusInternalServerError) + http.NotFound(rw, req) return } backendURL := w.Backend(req) if backendURL == nil { - log.Println("websocketproxy: backend URL is nil") - http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError) + log.Errorf("Websocketproxy: backend URL is nil") + http.Error(rw, "Backend URL is nil", http.StatusInternalServerError) return } @@ -131,7 +132,8 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01 connBackend, resp, err := dialer.Dial(backendURL.String(), nil) if err != nil { - log.Printf("websocketproxy: couldn't dial to remote backend url %s, %s, %+v", backendURL.String(), err, resp) + log.Errorf("Websocketproxy: couldn't dial to remote backend url %s, %s, %+v", backendURL.String(), err, resp) + http.NotFound(rw, req) return } defer connBackend.Close() @@ -152,7 +154,8 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Also pass the header that we gathered from the Dial handshake. connPub, err := upgrader.Upgrade(rw, req, upgradeHeader) if err != nil { - log.Printf("websocketproxy: couldn't upgrade %s\n", err) + log.Errorf("Websocketproxy: couldn't upgrade %s", err) + http.NotFound(rw, req) return } defer connPub.Close() From 1fdff9dae466514616e76671c7c9018709620088 Mon Sep 17 00:00:00 2001 From: emile Date: Thu, 22 Oct 2015 13:39:32 +0200 Subject: [PATCH 2/8] Move config objects to configuration.go --- configuration.go | 7 +++++++ traefik.go | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/configuration.go b/configuration.go index 8507ab4c9..3778a17f5 100644 --- a/configuration.go +++ b/configuration.go @@ -103,3 +103,10 @@ func NewLoadBalancerMethod(loadBalancer *LoadBalancer) (LoadBalancerMethod, erro } var ErrInvalidLoadBalancerMethod = errors.New("Invalid method, using default") + +type configMessage struct { + providerName string + configuration *Configuration +} + +type configs map[string]*Configuration diff --git a/traefik.go b/traefik.go index a33b23fe1..e32f0a711 100644 --- a/traefik.go +++ b/traefik.go @@ -43,13 +43,6 @@ var ( }) ) -type configMessage struct { - providerName string - configuration *Configuration -} - -type configs map[string]*Configuration - func main() { runtime.GOMAXPROCS(runtime.NumCPU()) kingpin.Version(Version + " built on the " + BuildDate) From 5dea2e79020b94cae5b239bd05b9d696549c023b Mon Sep 17 00:00:00 2001 From: emile Date: Thu, 22 Oct 2015 14:02:14 +0200 Subject: [PATCH 3/8] Remove providerTemplates dir, moved in templates --- docker.go | 2 +- docs/index.md | 2 +- generate.go | 2 +- kv.go | 2 +- marathon.go | 2 +- {providerTemplates => templates}/docker.tmpl | 0 {providerTemplates => templates}/kv.tmpl | 0 {providerTemplates => templates}/marathon-prefix.tmpl | 0 {providerTemplates => templates}/marathon.tmpl | 0 traefik.go | 6 ------ web.go | 7 +++++++ 11 files changed, 12 insertions(+), 11 deletions(-) rename {providerTemplates => templates}/docker.tmpl (100%) rename {providerTemplates => templates}/kv.tmpl (100%) rename {providerTemplates => templates}/marathon-prefix.tmpl (100%) rename {providerTemplates => templates}/marathon.tmpl (100%) diff --git a/docker.go b/docker.go index aefe0efdb..017ef596c 100644 --- a/docker.go +++ b/docker.go @@ -162,7 +162,7 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C return nil } } else { - buf, err := Asset("providerTemplates/docker.tmpl") + buf, err := Asset("templates/docker.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/docs/index.md b/docs/index.md index ffdd05d7b..793e85a20 100644 --- a/docs/index.md +++ b/docs/index.md @@ -469,7 +469,7 @@ Labels can be used on containers to override default behaviour: - `traefik.weight=10`: assign this weight to the application - `traefik.enable=false`: disable this application in Træfɪk - `traefik.host=bar`: override the default routing from `{appName}.{domain}` to `bar.{domain}` -- `traefik.prefixes=pf1,pf2`: use `PathPrefix(es)` instead of hostname for routing, use `filename="providerTemplates/marathon-prefix.tmpl"` with this option +- `traefik.prefixes=pf1,pf2`: use `PathPrefix(es)` instead of hostname for routing, use `filename="templates/marathon-prefix.tmpl"` with this option * `traefik.domain=traefik.localhost`: override the default domain ## Consul backend diff --git a/generate.go b/generate.go index 4757a70bd..f7b6e4ec3 100644 --- a/generate.go +++ b/generate.go @@ -4,6 +4,6 @@ Copyright //go:generate go get github.com/jteeuwen/go-bindata/... //go:generate rm -vf gen.go -//go:generate go-bindata -o gen.go static/... templates/... providerTemplates/... +//go:generate go-bindata -o gen.go static/... templates/... package main diff --git a/kv.go b/kv.go index 4d3f7dd82..afe240d13 100644 --- a/kv.go +++ b/kv.go @@ -166,7 +166,7 @@ func (provider *KvProvider) loadConfig() *Configuration { return nil } } else { - buf, err := Asset("providerTemplates/kv.tmpl") + buf, err := Asset("templates/kv.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/marathon.go b/marathon.go index 46b197007..c852ce8c1 100644 --- a/marathon.go +++ b/marathon.go @@ -167,7 +167,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { return nil } } else { - buf, err := Asset("providerTemplates/marathon.tmpl") + buf, err := Asset("templates/marathon.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/providerTemplates/docker.tmpl b/templates/docker.tmpl similarity index 100% rename from providerTemplates/docker.tmpl rename to templates/docker.tmpl diff --git a/providerTemplates/kv.tmpl b/templates/kv.tmpl similarity index 100% rename from providerTemplates/kv.tmpl rename to templates/kv.tmpl diff --git a/providerTemplates/marathon-prefix.tmpl b/templates/marathon-prefix.tmpl similarity index 100% rename from providerTemplates/marathon-prefix.tmpl rename to templates/marathon-prefix.tmpl diff --git a/providerTemplates/marathon.tmpl b/templates/marathon.tmpl similarity index 100% rename from providerTemplates/marathon.tmpl rename to templates/marathon.tmpl diff --git a/traefik.go b/traefik.go index e32f0a711..63b83e70b 100644 --- a/traefik.go +++ b/traefik.go @@ -23,7 +23,6 @@ import ( "github.com/mailgun/oxy/forward" "github.com/mailgun/oxy/roundrobin" "github.com/thoas/stats" - "github.com/unrolled/render" "gopkg.in/alecthomas/kingpin.v2" "runtime" ) @@ -36,11 +35,6 @@ var ( currentConfigurations = make(configs) metrics = stats.New() oxyLogger = &OxyLogger{} - templatesRenderer = render.New(render.Options{ - Directory: "templates", - Asset: Asset, - AssetNames: AssetNames, - }) ) func main() { diff --git a/web.go b/web.go index ed2d4f70f..1d4db0617 100644 --- a/web.go +++ b/web.go @@ -9,6 +9,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/elazarl/go-bindata-assetfs" "github.com/gorilla/mux" + "github.com/unrolled/render" ) type WebProvider struct { @@ -16,6 +17,12 @@ type WebProvider struct { CertFile, KeyFile string } +var ( + templatesRenderer = render.New(render.Options{ + Directory: "nowhere", + }) +) + func (provider *WebProvider) Provide(configurationChan chan<- configMessage) error { systemRouter := mux.NewRouter() From d671cc382145a1b6c41049ebd52975abb9cb50d1 Mon Sep 17 00:00:00 2001 From: emile Date: Fri, 23 Oct 2015 09:49:19 +0200 Subject: [PATCH 4/8] Adds traefik.frontend. rule and value labels in Docker and Marathon. Fixes #64. Fixes #73 --- docker.go | 84 +++++++++++++++++++++++----------- marathon.go | 74 ++++++++++++++++++++---------- templates/docker.tmpl | 13 +++--- templates/marathon-prefix.tmpl | 27 ----------- templates/marathon.tmpl | 15 ++---- tests/whoami.json | 39 ++++++++++++++++ 6 files changed, 157 insertions(+), 95 deletions(-) delete mode 100644 templates/marathon-prefix.tmpl create mode 100644 tests/whoami.json diff --git a/docker.go b/docker.go index 017ef596c..bef97ba8c 100644 --- a/docker.go +++ b/docker.go @@ -73,18 +73,14 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration { var DockerFuncMap = template.FuncMap{ "getBackend": func(container docker.Container) string { - for key, value := range container.Config.Labels { - if key == "traefik.backend" { - return value - } + if label, err := provider.getLabel(container, "traefik.backend"); err == nil { + return label } - return getHost(container) + return provider.getEscapedName(container.Name) }, "getPort": func(container docker.Container) string { - for key, value := range container.Config.Labels { - if key == "traefik.port" { - return value - } + if label, err := provider.getLabel(container, "traefik.port"); err == nil { + return label } for key := range container.NetworkSettings.Ports { return key.Port() @@ -92,30 +88,33 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C return "" }, "getWeight": func(container docker.Container) string { - for key, value := range container.Config.Labels { - if key == "traefik.weight" { - return value - } + if label, err := provider.getLabel(container, "traefik.weight"); err == nil { + return label } return "0" }, "getDomain": func(container docker.Container) string { - for key, value := range container.Config.Labels { - if key == "traefik.domain" { - return value - } + if label, err := provider.getLabel(container, "traefik.domain"); err == nil { + return label } return provider.Domain }, + "getProtocole": func(container docker.Container) string { + if label, err := provider.getLabel(container, "traefik.protocole"); err == nil { + return label + } + return "http" + }, + "getFrontendValue": provider.GetFrontendValue, + "getFrontendRule": provider.GetFrontendRule, "replace": func(s1 string, s2 string, s3 string) string { return strings.Replace(s3, s1, s2, -1) }, - "getHost": getHost, } configuration := new(Configuration) containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{}) containersInspected := []docker.Container{} - hosts := map[string][]docker.Container{} + frontends := map[string][]docker.Container{} // get inspect containers for _, container := range containerList { @@ -138,20 +137,28 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C log.Debugf("Filtering disabled container %s", container.Name) return false } + _, errRule := provider.getLabel(container, "traefik.frontend.rule") + _, errValue := provider.getLabel(container, "traefik.frontend.value") + + if errRule != nil && errValue == nil || errRule == nil && errValue != nil { + log.Debugf("Filtering bad labeled container %s", container.Name) + return false + } + return true }, containersInspected).([]docker.Container) for _, container := range filteredContainers { - hosts[getHost(container)] = append(hosts[getHost(container)], container) + frontends[provider.getFrontendName(container)] = append(frontends[provider.getFrontendName(container)], container) } templateObjects := struct { Containers []docker.Container - Hosts map[string][]docker.Container + Frontends map[string][]docker.Container Domain string }{ filteredContainers, - hosts, + frontends, provider.Domain, } tmpl := template.New(provider.Filename).Funcs(DockerFuncMap) @@ -181,17 +188,40 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C } if _, err := toml.Decode(buffer.String(), configuration); err != nil { - log.Error("Error creating docker configuration", err) + log.Error("Error creating docker configuration ", err) return nil } return configuration } -func getHost(container docker.Container) string { +func (provider *DockerProvider) getFrontendName(container docker.Container) string { + // Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78 + return strings.Replace(provider.GetFrontendRule(container)+"-"+provider.GetFrontendValue(container), ".", "-", -1) +} + +func (provider *DockerProvider) getEscapedName(name string) string { + return strings.Replace(name, "/", "", -1) +} + +func (provider *DockerProvider) getLabel(container docker.Container, label string) (string, error) { for key, value := range container.Config.Labels { - if key == "traefik.host" { - return value + if key == label { + return value, nil } } - return strings.Replace(strings.Replace(container.Name, "/", "", -1), ".", "-", -1) + return "", errors.New("Label not found:" + label) +} + +func (provider *DockerProvider) GetFrontendValue(container docker.Container) string { + if label, err := provider.getLabel(container, "traefik.frontend.value"); err == nil { + return label + } + return provider.getEscapedName(container.Name) + "." + provider.Domain +} + +func (provider *DockerProvider) GetFrontendRule(container docker.Container) string { + if label, err := provider.getLabel(container, "traefik.frontend.rule"); err == nil { + return label + } + return "Host" } diff --git a/marathon.go b/marathon.go index c852ce8c1..f3e345344 100644 --- a/marathon.go +++ b/marathon.go @@ -6,6 +6,7 @@ import ( "strings" "text/template" + "errors" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" @@ -62,41 +63,39 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { } return "" }, - "getHost": func(application marathon.Application) string { - for key, value := range application.Labels { - if key == "traefik.host" { - return value - } + "getWeight": func(task marathon.Task, applications []marathon.Application) string { + application := getApplication(task, applications) + if application == nil { + log.Errorf("Unable to get marathon application from task %s", task.AppID) + return "0" } - return strings.Replace(application.ID, "/", "", 1) - }, - "getWeight": func(application marathon.Application) string { - for key, value := range application.Labels { - if key == "traefik.weight" { - return value - } + if label, err := provider.getLabel(*application, "traefik.weight"); err == nil { + return label } return "0" }, "getDomain": func(application marathon.Application) string { - for key, value := range application.Labels { - if key == "traefik.domain" { - return value - } + if label, err := provider.getLabel(application, "traefik.domain"); err == nil { + return label } return provider.Domain }, - "getPrefixes": func(application marathon.Application) ([]string, error) { - for key, value := range application.Labels { - if key == "traefik.prefixes" { - return strings.Split(value, ","), nil - } - } - return []string{}, nil - }, "replace": func(s1 string, s2 string, s3 string) string { return strings.Replace(s3, s1, s2, -1) }, + "getProtocole": func(task marathon.Task, applications []marathon.Application) string { + application := getApplication(task, applications) + if application == nil { + log.Errorf("Unable to get marathon application from task %s", task.AppID) + return "http" + } + if label, err := provider.getLabel(*application, "traefik.protocole"); err == nil { + return label + } + return "http" + }, + "getFrontendValue": provider.GetFrontendValue, + "getFrontendRule": provider.GetFrontendRule, } configuration := new(Configuration) @@ -202,3 +201,30 @@ func getApplication(task marathon.Task, apps []marathon.Application) *marathon.A } return nil } + +func (provider *MarathonProvider) getLabel(application marathon.Application, label string) (string, error) { + for key, value := range application.Labels { + if key == label { + return value, nil + } + } + return "", errors.New("Label not found:" + label) +} + +func (provider *MarathonProvider) getEscapedName(name string) string { + return strings.Replace(name, "/", "", -1) +} + +func (provider *MarathonProvider) GetFrontendValue(application marathon.Application) string { + if label, err := provider.getLabel(application, "traefik.frontend.value"); err == nil { + return label + } + return provider.getEscapedName(application.ID) + "." + provider.Domain +} + +func (provider *MarathonProvider) GetFrontendRule(application marathon.Application) string { + if label, err := provider.getLabel(application, "traefik.frontend.rule"); err == nil { + return label + } + return "Host" +} diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 9dfd6eac4..60da39130 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -1,14 +1,13 @@ [backends]{{range .Containers}} [backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/" "" | replace "." "-"}}] - url = "http://{{.NetworkSettings.IPAddress}}:{{getPort .}}" + url = "{{getProtocole .}}://{{.NetworkSettings.IPAddress}}:{{getPort .}}" weight = {{getWeight .}} {{end}} -[frontends]{{range $host, $containers := .Hosts}} - [frontends.frontend-{{$host}}] - {{$container := index $containers 0}} +[frontends]{{range $frontend, $containers := .Frontends}} + [frontends."frontend-{{$frontend}}"]{{$container := index $containers 0}} backend = "backend-{{getBackend $container}}" - [frontends.frontend-{{$host}}.routes.route-host-{{$host}}] - rule = "Host" - value = "{{$host}}.{{getDomain $container}}" + [frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"] + rule = "{{getFrontendRule $container}}" + value = "{{getFrontendValue $container}}" {{end}} \ No newline at end of file diff --git a/templates/marathon-prefix.tmpl b/templates/marathon-prefix.tmpl deleted file mode 100644 index 1a331626a..000000000 --- a/templates/marathon-prefix.tmpl +++ /dev/null @@ -1,27 +0,0 @@ -{{$apps := .Applications}} -[backends]{{range .Tasks}} - [backends.backend{{.AppID | replace "/" "-"}}.servers.server-{{.ID | replace "." "-"}}] - url = "http://{{.Host}}:{{getPort .}}" - {{$appID := .AppID}} - {{range $apps}} - {{if eq $appID .ID}} - weight = {{getWeight .}} - {{end}} - {{end}} -{{end}} - -[frontends]{{ range $app := .Applications}} - {{range $prefix := getPrefixes .}} - [frontends.frontend{{$app.ID | replace "/" "-"}}{{$prefix | replace "/" "-"}}] - backend = "backend{{$app.ID | replace "/" "-"}}" - [frontends.frontend-{{getHost $app | replace "/" "-"}}{{$prefix | replace "/" "-"}}.routes.route-prefix{{$prefix | replace "/" "-"}}] - rule = "PathPrefix" - value = "{{.}}" - {{else}} - [frontends.frontend{{.ID | replace "/" "-"}}] - backend = "backend{{.ID | replace "/" "-"}}" - [frontends.frontend-{{getHost $app | replace "/" "-"}}.routes.route-host-{{getHost $app | replace "/" "-"}}] - rule = "Host" - value = "{{getHost $app | replace "/" "-"}}.{{getDomain .}}" - {{end}} -{{end}} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index 10f1fa249..ba491ac47 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -1,19 +1,14 @@ {{$apps := .Applications}} [backends]{{range .Tasks}} [backends.backend{{.AppID | replace "/" "-"}}.servers.server-{{.ID | replace "." "-"}}] - url = "http://{{.Host}}:{{getPort .}}" - {{$appID := .AppID}} - {{range $apps}} - {{if eq $appID .ID}} - weight = {{getWeight .}} - {{end}} - {{end}} + url = "{{getProtocole . $apps}}://{{.Host}}:{{getPort .}}" + weight = {{getWeight . $apps}} {{end}} [frontends]{{range .Applications}} [frontends.frontend{{.ID | replace "/" "-"}}] backend = "backend{{.ID | replace "/" "-"}}" - [frontends.frontend-{{getHost . | replace "/" "-"}}.routes.route-host-{{getHost . | replace "/" "-"}}] - rule = "Host" - value = "{{getHost . | replace "/" "-"}}.{{getDomain .}}" + [frontends.frontend-{{.ID | replace "/" ""}}.routes.route-host-{{.ID | replace "/" ""}}] + rule = "{{getFrontendRule .}}" + value = "{{getFrontendValue .}}" {{end}} \ No newline at end of file diff --git a/tests/whoami.json b/tests/whoami.json new file mode 100644 index 000000000..741d399d8 --- /dev/null +++ b/tests/whoami.json @@ -0,0 +1,39 @@ +{ + "id": "whoami", + "cpus": 0.1, + "mem": 64.0, + "instances": 3, + "container": { + "type": "DOCKER", + "docker": { + "image": "emilevauge/whoami", + "network": "BRIDGE", + "portMappings": [ + { "containerPort": 80, "hostPort": 0, "protocol": "tcp" } + ], + "parameters": [{ + "key": "log-driver", + "value": "gelf" + }, { + "key": "log-opt", + "value": "gelf-address=udp://172.17.42.1:12201" + }] + } + }, + "healthChecks": [ + { + "protocol": "HTTP", + "portIndex": 0, + "path": "/", + "gracePeriodSeconds": 5, + "intervalSeconds": 20, + "maxConsecutiveFailures": 3 + } + ], + "labels": { + "traefik.weight": "1", + "traefik.protocole": "https", + "traefik.frontend.rule": "Path", + "traefik.frontend.value": "/test" + } +} From 32bfecff83e23a1b5bedef80bad2aae274630462 Mon Sep 17 00:00:00 2001 From: emile Date: Fri, 23 Oct 2015 17:43:10 +0200 Subject: [PATCH 5/8] Docs on traefik.frontend. rule and value labels in Docker and Marathon. --- docs/index.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/index.md b/docs/index.md index 793e85a20..f13ebe1d8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,7 +25,7 @@ It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/ Basically, Træfɪk is a http router, which sends traffic from frontends to http backends, following rules you have configured. -### Frontends +### Frontends Frontends can be defined using the following rules: @@ -409,9 +409,11 @@ Labels can be used on containers to override default behaviour: - `traefik.port=80`: register this port. Useful when the container exposes multiples ports. - `traefik.weight=10`: assign this weight to the container - `traefik.enable=false`: disable this container in Træfɪk -- `traefik.host=bar`: override the default routing from `{containerName}.{domain}` to `bar.{domain}` +- `traefik.frontend.rule=Host`: override the default frontend rule (Default: Host). See [frontends](#frontends). +- `traefik.frontend.value=test.example.com`: override the default frontend value (Default: `{containerName}.{domain}`) See [frontends](#frontends). * `traefik.domain=traefik.localhost`: override the default domain + ## Marathon backend Træfɪk can be configured to use Marathon as a backend configuration: @@ -464,12 +466,11 @@ domain = "marathon.localhost" Labels can be used on containers to override default behaviour: -- `traefik.backend=foo`: assign the application to `foo` backend - `traefik.port=80`: register this port. Useful when the application exposes multiples ports. - `traefik.weight=10`: assign this weight to the application - `traefik.enable=false`: disable this application in Træfɪk -- `traefik.host=bar`: override the default routing from `{appName}.{domain}` to `bar.{domain}` -- `traefik.prefixes=pf1,pf2`: use `PathPrefix(es)` instead of hostname for routing, use `filename="templates/marathon-prefix.tmpl"` with this option +- `traefik.frontend.rule=Host`: override the default frontend rule (Default: Host). See [frontends](#frontends). +- `traefik.frontend.value=test.example.com`: override the default frontend value (Default: `{appName}.{domain}`) See [frontends](#frontends). * `traefik.domain=traefik.localhost`: override the default domain ## Consul backend From aaeb7cdffdfc290de66c7b3dc00eaf8805bf08e1 Mon Sep 17 00:00:00 2001 From: emile Date: Fri, 23 Oct 2015 18:59:08 +0200 Subject: [PATCH 6/8] Correct BoltDB backend. Fixes #68 --- docker.go | 4 ++-- docs/index.md | 3 +++ kv.go | 1 + marathon.go | 4 ++-- templates/docker.tmpl | 4 ++-- templates/marathon.tmpl | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docker.go b/docker.go index bef97ba8c..77a1f6f73 100644 --- a/docker.go +++ b/docker.go @@ -99,8 +99,8 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C } return provider.Domain }, - "getProtocole": func(container docker.Container) string { - if label, err := provider.getLabel(container, "traefik.protocole"); err == nil { + "getProtocol": func(container docker.Container) string { + if label, err := provider.getLabel(container, "traefik.protocol"); err == nil { return label } return "http" diff --git a/docs/index.md b/docs/index.md index f13ebe1d8..743215fc4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -407,6 +407,7 @@ Labels can be used on containers to override default behaviour: - `traefik.backend=foo`: assign the container to `foo` backend - `traefik.port=80`: register this port. Useful when the container exposes multiples ports. +- `traefik.protocol=https`: override the default `http` protocol - `traefik.weight=10`: assign this weight to the container - `traefik.enable=false`: disable this container in Træfɪk - `traefik.frontend.rule=Host`: override the default frontend rule (Default: Host). See [frontends](#frontends). @@ -466,7 +467,9 @@ domain = "marathon.localhost" Labels can be used on containers to override default behaviour: +- `traefik.backend=foo`: assign the application to `foo` backend - `traefik.port=80`: register this port. Useful when the application exposes multiples ports. +- `traefik.protocol=https`: override the default `http` protocol - `traefik.weight=10`: assign this weight to the application - `traefik.enable=false`: disable this application in Træfɪk - `traefik.frontend.rule=Host`: override the default frontend rule (Default: Host). See [frontends](#frontends). diff --git a/kv.go b/kv.go index afe240d13..44ab3401b 100644 --- a/kv.go +++ b/kv.go @@ -88,6 +88,7 @@ func (provider *KvProvider) provide(configurationChan chan<- configMessage) erro []string{provider.Endpoint}, &store.Config{ ConnectionTimeout: 30 * time.Second, + Bucket: "traefik", }, ) if err != nil { diff --git a/marathon.go b/marathon.go index f3e345344..4d9271e69 100644 --- a/marathon.go +++ b/marathon.go @@ -83,13 +83,13 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { "replace": func(s1 string, s2 string, s3 string) string { return strings.Replace(s3, s1, s2, -1) }, - "getProtocole": func(task marathon.Task, applications []marathon.Application) string { + "getProtocol": func(task marathon.Task, applications []marathon.Application) string { application := getApplication(task, applications) if application == nil { log.Errorf("Unable to get marathon application from task %s", task.AppID) return "http" } - if label, err := provider.getLabel(*application, "traefik.protocole"); err == nil { + if label, err := provider.getLabel(*application, "traefik.protocol"); err == nil { return label } return "http" diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 60da39130..4aba4e124 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -1,6 +1,6 @@ [backends]{{range .Containers}} [backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/" "" | replace "." "-"}}] - url = "{{getProtocole .}}://{{.NetworkSettings.IPAddress}}:{{getPort .}}" + url = "{{getProtocol .}}://{{.NetworkSettings.IPAddress}}:{{getPort .}}" weight = {{getWeight .}} {{end}} @@ -10,4 +10,4 @@ [frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"] rule = "{{getFrontendRule $container}}" value = "{{getFrontendValue $container}}" -{{end}} \ No newline at end of file +{{end}} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index ba491ac47..e79d792c9 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -1,7 +1,7 @@ {{$apps := .Applications}} [backends]{{range .Tasks}} [backends.backend{{.AppID | replace "/" "-"}}.servers.server-{{.ID | replace "." "-"}}] - url = "{{getProtocole . $apps}}://{{.Host}}:{{getPort .}}" + url = "{{getProtocol . $apps}}://{{.Host}}:{{getPort .}}" weight = {{getWeight . $apps}} {{end}} From d390f86de26209525b5c8d1b91f5e304c5b27e67 Mon Sep 17 00:00:00 2001 From: emile Date: Tue, 27 Oct 2015 00:26:35 +0100 Subject: [PATCH 7/8] Code review corrections --- docker.go | 20 ++++++++++++++++---- marathon.go | 22 +++++++++++----------- middlewares/websocketproxy.go | 2 +- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/docker.go b/docker.go index 77a1f6f73..45a29b8c6 100644 --- a/docker.go +++ b/docker.go @@ -8,6 +8,7 @@ import ( "text/template" "time" + "fmt" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" @@ -137,10 +138,8 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C log.Debugf("Filtering disabled container %s", container.Name) return false } - _, errRule := provider.getLabel(container, "traefik.frontend.rule") - _, errValue := provider.getLabel(container, "traefik.frontend.value") - if errRule != nil && errValue == nil || errRule == nil && errValue != nil { + if _, err := provider.getLabels(container, []string{"traefik.frontend.rule", "traefik.frontend.value"}); err != nil { log.Debugf("Filtering bad labeled container %s", container.Name) return false } @@ -196,7 +195,8 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C func (provider *DockerProvider) getFrontendName(container docker.Container) string { // Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78 - return strings.Replace(provider.GetFrontendRule(container)+"-"+provider.GetFrontendValue(container), ".", "-", -1) + frontendName := fmt.Sprintf("%s-%s", provider.GetFrontendRule(container), provider.GetFrontendValue(container)) + return strings.Replace(frontendName, ".", "-", -1) } func (provider *DockerProvider) getEscapedName(name string) string { @@ -212,6 +212,18 @@ func (provider *DockerProvider) getLabel(container docker.Container, label strin return "", errors.New("Label not found:" + label) } +func (provider *DockerProvider) getLabels(container docker.Container, labels []string) (map[string]string, error) { + foundLabels := map[string]string{} + for _, label := range labels { + if foundLabel, err := provider.getLabel(container, label); err != nil { + return nil, errors.New("Label not found: " + label) + } else { + foundLabels[label] = foundLabel + } + } + return foundLabels, nil +} + func (provider *DockerProvider) GetFrontendValue(container docker.Container) string { if label, err := provider.getLabel(container, "traefik.frontend.value"); err == nil { return label diff --git a/marathon.go b/marathon.go index 4d9271e69..f79558d0a 100644 --- a/marathon.go +++ b/marathon.go @@ -64,12 +64,12 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { return "" }, "getWeight": func(task marathon.Task, applications []marathon.Application) string { - application := getApplication(task, applications) - if application == nil { + application, errApp := getApplication(task, applications) + if errApp != nil { log.Errorf("Unable to get marathon application from task %s", task.AppID) return "0" } - if label, err := provider.getLabel(*application, "traefik.weight"); err == nil { + if label, err := provider.getLabel(application, "traefik.weight"); err == nil { return label } return "0" @@ -84,12 +84,12 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { return strings.Replace(s3, s1, s2, -1) }, "getProtocol": func(task marathon.Task, applications []marathon.Application) string { - application := getApplication(task, applications) - if application == nil { + application, errApp := getApplication(task, applications) + if errApp != nil { log.Errorf("Unable to get marathon application from task %s", task.AppID) return "http" } - if label, err := provider.getLabel(*application, "traefik.protocol"); err == nil { + if label, err := provider.getLabel(application, "traefik.protocol"); err == nil { return label } return "http" @@ -117,8 +117,8 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { log.Debug("Filtering marathon task without port", task.AppID) return false } - application := getApplication(task, applications.Apps) - if application == nil { + application, errApp := getApplication(task, applications.Apps) + if errApp != nil { log.Errorf("Unable to get marathon application from task %s", task.AppID) return false } @@ -193,13 +193,13 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { return configuration } -func getApplication(task marathon.Task, apps []marathon.Application) *marathon.Application { +func getApplication(task marathon.Task, apps []marathon.Application) (marathon.Application, error) { for _, application := range apps { if application.ID == task.AppID { - return &application + return application, nil } } - return nil + return marathon.Application{}, errors.New("Application not found: " + task.AppID) } func (provider *MarathonProvider) getLabel(application marathon.Application, label string) (string, error) { diff --git a/middlewares/websocketproxy.go b/middlewares/websocketproxy.go index b377b9940..7d497016d 100644 --- a/middlewares/websocketproxy.go +++ b/middlewares/websocketproxy.go @@ -133,7 +133,7 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { connBackend, resp, err := dialer.Dial(backendURL.String(), nil) if err != nil { log.Errorf("Websocketproxy: couldn't dial to remote backend url %s, %s, %+v", backendURL.String(), err, resp) - http.NotFound(rw, req) + http.Error(rw, "Remote backend unreachable", http.StatusBadGateway) return } defer connBackend.Close() From 33d912290bdc7a70a9f98a71acabdf3a5887a5ba Mon Sep 17 00:00:00 2001 From: emile Date: Wed, 28 Oct 2015 11:39:24 +0100 Subject: [PATCH 8/8] Update docs with Slack --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62a5f538a..84600bf31 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ ![Træfɪk](http://traefik.github.io/traefik.logo.svg "Træfɪk") ___ -[![Circle CI](https://img.shields.io/circleci/project/EmileVauge/traefik.svg)](https://circleci.com/gh/EmileVauge/traefik) +[![Circle CI](https://circleci.com/gh/emilevauge/traefik.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/emilevauge/traefik) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/EmileVauge/traefik/blob/master/LICENSE.md) -[![Join the chat at https://gitter.im/EmileVauge/traefik](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/EmileVauge/traefik?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![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) Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.