traefik/provider/mesos/config.go
2018-04-18 10:48:03 +02:00

237 lines
7.4 KiB
Go

package mesos
import (
"fmt"
"math"
"strconv"
"strings"
"text/template"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/label"
"github.com/containous/traefik/types"
"github.com/mesosphere/mesos-dns/records/state"
)
type taskData struct {
state.Task
TraefikLabels map[string]string
}
func (p *Provider) buildConfigurationV2(tasks []state.Task) *types.Configuration {
var mesosFuncMap = template.FuncMap{
"getDomain": label.GetFuncString(label.TraefikDomain, p.Domain),
"getID": getID,
// Backend functions
"getBackendName": getBackendName,
"getCircuitBreaker": label.GetCircuitBreaker,
"getLoadBalancer": label.GetLoadBalancer,
"getMaxConn": label.GetMaxConn,
"getHealthCheck": label.GetHealthCheck,
"getBuffering": label.GetBuffering,
"getServers": p.getServers,
"getHost": p.getHost,
"getServerPort": p.getServerPort,
// Frontend functions
"getFrontEndName": getFrontendName,
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic),
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
"getFrontendRule": p.getFrontendRule,
"getRedirect": label.GetRedirect,
"getErrorPages": label.GetErrorPages,
"getRateLimit": label.GetRateLimit,
"getHeaders": label.GetHeaders,
"getWhiteList": label.GetWhiteList,
}
// filter tasks
appsTasks := make(map[string][]taskData)
for _, task := range tasks {
data := taskData{
Task: task,
TraefikLabels: extractLabels(task),
}
if taskFilter(data, p.ExposedByDefault) {
if _, ok := appsTasks[task.DiscoveryInfo.Name]; !ok {
appsTasks[task.DiscoveryInfo.Name] = []taskData{data}
} else {
appsTasks[task.DiscoveryInfo.Name] = append(appsTasks[task.DiscoveryInfo.Name], data)
}
}
}
templateObjects := struct {
ApplicationsTasks map[string][]taskData
Domain string
}{
ApplicationsTasks: appsTasks,
Domain: p.Domain,
}
configuration, err := p.GetConfiguration("templates/mesos.tmpl", mesosFuncMap, templateObjects)
if err != nil {
log.Error(err)
}
return configuration
}
func taskFilter(task taskData, exposedByDefaultFlag bool) bool {
if len(task.DiscoveryInfo.Ports.DiscoveryPorts) == 0 {
log.Debugf("Filtering Mesos task without port %s", task.Name)
return false
}
if !isEnabled(task, exposedByDefaultFlag) {
log.Debugf("Filtering disabled Mesos task %s", task.DiscoveryInfo.Name)
return false
}
// filter indeterminable task port
portIndexLabel := label.GetStringValue(task.TraefikLabels, label.TraefikPortIndex, "")
portValueLabel := label.GetStringValue(task.TraefikLabels, label.TraefikPort, "")
if portIndexLabel != "" && portValueLabel != "" {
log.Debugf("Filtering Mesos task %s specifying both %q' and %q labels", task.Name, label.TraefikPortIndex, label.TraefikPort)
return false
}
if portIndexLabel != "" {
index, err := strconv.Atoi(portIndexLabel)
if err != nil || index < 0 || index > len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1 {
log.Debugf("Filtering Mesos task %s with unexpected value for %q label", task.Name, label.TraefikPortIndex)
return false
}
}
if portValueLabel != "" {
port, err := strconv.Atoi(portValueLabel)
if err != nil {
log.Debugf("Filtering Mesos task %s with unexpected value for %q label", task.Name, label.TraefikPort)
return false
}
var foundPort bool
for _, exposedPort := range task.DiscoveryInfo.Ports.DiscoveryPorts {
if port == exposedPort.Number {
foundPort = true
break
}
}
if !foundPort {
log.Debugf("Filtering Mesos task %s without a matching port for %q label", task.Name, label.TraefikPort)
return false
}
}
// filter healthChecks
if task.Statuses != nil && len(task.Statuses) > 0 && task.Statuses[0].Healthy != nil && !*task.Statuses[0].Healthy {
log.Debugf("Filtering Mesos task %s with bad healthCheck", task.DiscoveryInfo.Name)
return false
}
return true
}
func getID(task taskData) string {
return provider.Normalize(task.ID)
}
func getBackendName(task taskData) string {
return label.GetStringValue(task.TraefikLabels, label.TraefikBackend, provider.Normalize(task.DiscoveryInfo.Name))
}
func getFrontendName(task taskData) string {
// TODO task.ID -> task.Name + task.ID
return provider.Normalize(task.ID)
}
func (p *Provider) getSubDomain(name string) string {
if p.GroupsAsSubDomains {
splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/")
provider.ReverseStringSlice(&splitedName)
reverseName := strings.Join(splitedName, ".")
return reverseName
}
return strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1)
}
// getFrontendRule returns the frontend rule for the specified application, using it's label.
// It returns a default one (Host) if the label is not present.
func (p *Provider) getFrontendRule(task taskData) string {
if v := label.GetStringValue(task.TraefikLabels, label.TraefikFrontendRule, ""); len(v) > 0 {
return v
}
domain := label.GetStringValue(task.TraefikLabels, label.TraefikDomain, p.Domain)
return "Host:" + strings.ToLower(strings.Replace(p.getSubDomain(task.DiscoveryInfo.Name), "_", "-", -1)) + "." + domain
}
func (p *Provider) getServers(tasks []taskData) map[string]types.Server {
var servers map[string]types.Server
for _, task := range tasks {
if servers == nil {
servers = make(map[string]types.Server)
}
protocol := label.GetStringValue(task.TraefikLabels, label.TraefikProtocol, label.DefaultProtocol)
host := p.getHost(task)
port := p.getServerPort(task)
serverName := "server-" + getID(task)
servers[serverName] = types.Server{
URL: fmt.Sprintf("%s://%s:%s", protocol, host, port),
Weight: getIntValue(task.TraefikLabels, label.TraefikWeight, label.DefaultWeight, math.MaxInt32),
}
}
return servers
}
func (p *Provider) getHost(task taskData) string {
return task.IP(strings.Split(p.IPSources, ",")...)
}
func (p *Provider) getServerPort(task taskData) string {
plv := getIntValue(task.TraefikLabels, label.TraefikPortIndex, math.MinInt32, len(task.DiscoveryInfo.Ports.DiscoveryPorts)-1)
if plv >= 0 {
return strconv.Itoa(task.DiscoveryInfo.Ports.DiscoveryPorts[plv].Number)
}
if pv := label.GetStringValue(task.TraefikLabels, label.TraefikPort, ""); len(pv) > 0 {
return pv
}
for _, port := range task.DiscoveryInfo.Ports.DiscoveryPorts {
return strconv.Itoa(port.Number)
}
return ""
}
func isEnabled(task taskData, exposedByDefault bool) bool {
return label.GetBoolValue(task.TraefikLabels, label.TraefikEnable, exposedByDefault)
}
// Label functions
func getIntValue(labels map[string]string, labelName string, defaultValue int, maxValue int) int {
value := label.GetIntValue(labels, labelName, defaultValue)
if value <= maxValue {
return value
}
log.Warnf("The value %q for %q exceed the max authorized value %q, falling back to %v.", value, labelName, maxValue, defaultValue)
return defaultValue
}
func extractLabels(task state.Task) map[string]string {
labels := make(map[string]string)
for _, lbl := range task.Labels {
labels[lbl.Key] = lbl.Value
}
return labels
}