This commit is contained in:
parent
5dea2e7902
commit
d671cc3821
6 changed files with 157 additions and 95 deletions
84
docker.go
84
docker.go
|
@ -73,18 +73,14 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage)
|
||||||
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
|
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
|
||||||
var DockerFuncMap = template.FuncMap{
|
var DockerFuncMap = template.FuncMap{
|
||||||
"getBackend": func(container docker.Container) string {
|
"getBackend": func(container docker.Container) string {
|
||||||
for key, value := range container.Config.Labels {
|
if label, err := provider.getLabel(container, "traefik.backend"); err == nil {
|
||||||
if key == "traefik.backend" {
|
return label
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return getHost(container)
|
return provider.getEscapedName(container.Name)
|
||||||
},
|
},
|
||||||
"getPort": func(container docker.Container) string {
|
"getPort": func(container docker.Container) string {
|
||||||
for key, value := range container.Config.Labels {
|
if label, err := provider.getLabel(container, "traefik.port"); err == nil {
|
||||||
if key == "traefik.port" {
|
return label
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for key := range container.NetworkSettings.Ports {
|
for key := range container.NetworkSettings.Ports {
|
||||||
return key.Port()
|
return key.Port()
|
||||||
|
@ -92,30 +88,33 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
"getWeight": func(container docker.Container) string {
|
"getWeight": func(container docker.Container) string {
|
||||||
for key, value := range container.Config.Labels {
|
if label, err := provider.getLabel(container, "traefik.weight"); err == nil {
|
||||||
if key == "traefik.weight" {
|
return label
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
},
|
},
|
||||||
"getDomain": func(container docker.Container) string {
|
"getDomain": func(container docker.Container) string {
|
||||||
for key, value := range container.Config.Labels {
|
if label, err := provider.getLabel(container, "traefik.domain"); err == nil {
|
||||||
if key == "traefik.domain" {
|
return label
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return provider.Domain
|
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 {
|
"replace": func(s1 string, s2 string, s3 string) string {
|
||||||
return strings.Replace(s3, s1, s2, -1)
|
return strings.Replace(s3, s1, s2, -1)
|
||||||
},
|
},
|
||||||
"getHost": getHost,
|
|
||||||
}
|
}
|
||||||
configuration := new(Configuration)
|
configuration := new(Configuration)
|
||||||
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
|
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
|
||||||
containersInspected := []docker.Container{}
|
containersInspected := []docker.Container{}
|
||||||
hosts := map[string][]docker.Container{}
|
frontends := map[string][]docker.Container{}
|
||||||
|
|
||||||
// get inspect containers
|
// get inspect containers
|
||||||
for _, container := range containerList {
|
for _, container := range containerList {
|
||||||
|
@ -138,20 +137,28 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C
|
||||||
log.Debugf("Filtering disabled container %s", container.Name)
|
log.Debugf("Filtering disabled container %s", container.Name)
|
||||||
return false
|
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
|
return true
|
||||||
}, containersInspected).([]docker.Container)
|
}, containersInspected).([]docker.Container)
|
||||||
|
|
||||||
for _, container := range filteredContainers {
|
for _, container := range filteredContainers {
|
||||||
hosts[getHost(container)] = append(hosts[getHost(container)], container)
|
frontends[provider.getFrontendName(container)] = append(frontends[provider.getFrontendName(container)], container)
|
||||||
}
|
}
|
||||||
|
|
||||||
templateObjects := struct {
|
templateObjects := struct {
|
||||||
Containers []docker.Container
|
Containers []docker.Container
|
||||||
Hosts map[string][]docker.Container
|
Frontends map[string][]docker.Container
|
||||||
Domain string
|
Domain string
|
||||||
}{
|
}{
|
||||||
filteredContainers,
|
filteredContainers,
|
||||||
hosts,
|
frontends,
|
||||||
provider.Domain,
|
provider.Domain,
|
||||||
}
|
}
|
||||||
tmpl := template.New(provider.Filename).Funcs(DockerFuncMap)
|
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 {
|
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 nil
|
||||||
}
|
}
|
||||||
return configuration
|
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 {
|
for key, value := range container.Config.Labels {
|
||||||
if key == "traefik.host" {
|
if key == label {
|
||||||
return value
|
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"
|
||||||
}
|
}
|
||||||
|
|
74
marathon.go
74
marathon.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"errors"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/BurntSushi/ty/fun"
|
"github.com/BurntSushi/ty/fun"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
@ -62,41 +63,39 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
"getHost": func(application marathon.Application) string {
|
"getWeight": func(task marathon.Task, applications []marathon.Application) string {
|
||||||
for key, value := range application.Labels {
|
application := getApplication(task, applications)
|
||||||
if key == "traefik.host" {
|
if application == nil {
|
||||||
return value
|
log.Errorf("Unable to get marathon application from task %s", task.AppID)
|
||||||
}
|
return "0"
|
||||||
}
|
}
|
||||||
return strings.Replace(application.ID, "/", "", 1)
|
if label, err := provider.getLabel(*application, "traefik.weight"); err == nil {
|
||||||
},
|
return label
|
||||||
"getWeight": func(application marathon.Application) string {
|
|
||||||
for key, value := range application.Labels {
|
|
||||||
if key == "traefik.weight" {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
},
|
},
|
||||||
"getDomain": func(application marathon.Application) string {
|
"getDomain": func(application marathon.Application) string {
|
||||||
for key, value := range application.Labels {
|
if label, err := provider.getLabel(application, "traefik.domain"); err == nil {
|
||||||
if key == "traefik.domain" {
|
return label
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return provider.Domain
|
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 {
|
"replace": func(s1 string, s2 string, s3 string) string {
|
||||||
return strings.Replace(s3, s1, s2, -1)
|
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)
|
configuration := new(Configuration)
|
||||||
|
|
||||||
|
@ -202,3 +201,30 @@ func getApplication(task marathon.Task, apps []marathon.Application) *marathon.A
|
||||||
}
|
}
|
||||||
return nil
|
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"
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
[backends]{{range .Containers}}
|
[backends]{{range .Containers}}
|
||||||
[backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/" "" | replace "." "-"}}]
|
[backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/" "" | replace "." "-"}}]
|
||||||
url = "http://{{.NetworkSettings.IPAddress}}:{{getPort .}}"
|
url = "{{getProtocole .}}://{{.NetworkSettings.IPAddress}}:{{getPort .}}"
|
||||||
weight = {{getWeight .}}
|
weight = {{getWeight .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
[frontends]{{range $host, $containers := .Hosts}}
|
[frontends]{{range $frontend, $containers := .Frontends}}
|
||||||
[frontends.frontend-{{$host}}]
|
[frontends."frontend-{{$frontend}}"]{{$container := index $containers 0}}
|
||||||
{{$container := index $containers 0}}
|
|
||||||
backend = "backend-{{getBackend $container}}"
|
backend = "backend-{{getBackend $container}}"
|
||||||
[frontends.frontend-{{$host}}.routes.route-host-{{$host}}]
|
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
|
||||||
rule = "Host"
|
rule = "{{getFrontendRule $container}}"
|
||||||
value = "{{$host}}.{{getDomain $container}}"
|
value = "{{getFrontendValue $container}}"
|
||||||
{{end}}
|
{{end}}
|
|
@ -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}}
|
|
|
@ -1,19 +1,14 @@
|
||||||
{{$apps := .Applications}}
|
{{$apps := .Applications}}
|
||||||
[backends]{{range .Tasks}}
|
[backends]{{range .Tasks}}
|
||||||
[backends.backend{{.AppID | replace "/" "-"}}.servers.server-{{.ID | replace "." "-"}}]
|
[backends.backend{{.AppID | replace "/" "-"}}.servers.server-{{.ID | replace "." "-"}}]
|
||||||
url = "http://{{.Host}}:{{getPort .}}"
|
url = "{{getProtocole . $apps}}://{{.Host}}:{{getPort .}}"
|
||||||
{{$appID := .AppID}}
|
weight = {{getWeight . $apps}}
|
||||||
{{range $apps}}
|
|
||||||
{{if eq $appID .ID}}
|
|
||||||
weight = {{getWeight .}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
[frontends]{{range .Applications}}
|
[frontends]{{range .Applications}}
|
||||||
[frontends.frontend{{.ID | replace "/" "-"}}]
|
[frontends.frontend{{.ID | replace "/" "-"}}]
|
||||||
backend = "backend{{.ID | replace "/" "-"}}"
|
backend = "backend{{.ID | replace "/" "-"}}"
|
||||||
[frontends.frontend-{{getHost . | replace "/" "-"}}.routes.route-host-{{getHost . | replace "/" "-"}}]
|
[frontends.frontend-{{.ID | replace "/" ""}}.routes.route-host-{{.ID | replace "/" ""}}]
|
||||||
rule = "Host"
|
rule = "{{getFrontendRule .}}"
|
||||||
value = "{{getHost . | replace "/" "-"}}.{{getDomain .}}"
|
value = "{{getFrontendValue .}}"
|
||||||
{{end}}
|
{{end}}
|
39
tests/whoami.json
Normal file
39
tests/whoami.json
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue