2017-12-02 21:29:34 +03:00
package mesos
import (
"fmt"
"math"
2018-06-13 11:08:03 +03:00
"net"
2017-12-02 21:29:34 +03:00
"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"
)
2018-03-29 01:01:24 +03:00
type taskData struct {
state . Task
TraefikLabels map [ string ] string
2018-06-06 01:26:03 +03:00
SegmentName string
2018-03-29 01:01:24 +03:00
}
func ( p * Provider ) buildConfigurationV2 ( tasks [ ] state . Task ) * types . Configuration {
2017-12-02 21:29:34 +03:00
var mesosFuncMap = template . FuncMap {
2018-06-06 01:26:03 +03:00
"getDomain" : label . GetFuncString ( label . TraefikDomain , p . Domain ) ,
"getSubDomain" : p . getSubDomain ,
"getSegmentSubDomain" : p . getSegmentSubDomain ,
"getID" : getID ,
2018-01-10 06:05:33 +03:00
// Backend functions
2018-01-10 23:54:46 +03:00
"getBackendName" : getBackendName ,
2018-03-29 01:01:24 +03:00
"getCircuitBreaker" : label . GetCircuitBreaker ,
"getLoadBalancer" : label . GetLoadBalancer ,
"getMaxConn" : label . GetMaxConn ,
"getHealthCheck" : label . GetHealthCheck ,
"getBuffering" : label . GetBuffering ,
2018-01-10 23:54:46 +03:00
"getServers" : p . getServers ,
"getHost" : p . getHost ,
"getServerPort" : p . getServerPort ,
2018-01-10 06:05:33 +03:00
// Frontend functions
2018-06-06 01:26:03 +03:00
"getSegmentNameSuffix" : getSegmentNameSuffix ,
"getFrontEndName" : getFrontendName ,
"getEntryPoints" : label . GetFuncSliceString ( label . TraefikFrontendEntryPoints ) ,
2018-07-06 17:52:04 +03:00
"getBasicAuth" : label . GetFuncSliceString ( label . TraefikFrontendAuthBasic ) , // Deprecated
"getAuth" : label . GetAuth ,
2018-06-06 01:26:03 +03:00
"getPriority" : label . GetFuncInt ( label . TraefikFrontendPriority , label . DefaultFrontendPriority ) ,
"getPassHostHeader" : label . GetFuncBool ( label . TraefikFrontendPassHostHeader , label . DefaultPassHostHeader ) ,
"getPassTLSCert" : label . GetFuncBool ( label . TraefikFrontendPassTLSCert , label . DefaultPassTLSCert ) ,
2018-08-29 12:36:03 +03:00
"getPassTLSClientCert" : label . GetTLSClientCert ,
2018-06-06 01:26:03 +03:00
"getFrontendRule" : p . getFrontendRule ,
"getRedirect" : label . GetRedirect ,
"getErrorPages" : label . GetErrorPages ,
"getRateLimit" : label . GetRateLimit ,
"getHeaders" : label . GetHeaders ,
"getWhiteList" : label . GetWhiteList ,
2018-01-10 23:54:46 +03:00
}
2018-06-06 01:26:03 +03:00
appsTasks := p . filterTasks ( tasks )
2017-12-02 21:29:34 +03:00
templateObjects := struct {
2018-03-29 01:01:24 +03:00
ApplicationsTasks map [ string ] [ ] taskData
2018-01-10 23:54:46 +03:00
Domain string
2017-12-02 21:29:34 +03:00
} {
2018-01-10 23:54:46 +03:00
ApplicationsTasks : appsTasks ,
Domain : p . Domain ,
2017-12-02 21:29:34 +03:00
}
configuration , err := p . GetConfiguration ( "templates/mesos.tmpl" , mesosFuncMap , templateObjects )
if err != nil {
log . Error ( err )
}
2018-03-29 01:01:24 +03:00
2017-12-02 21:29:34 +03:00
return configuration
}
2018-06-06 01:26:03 +03:00
func ( p * Provider ) filterTasks ( tasks [ ] state . Task ) map [ string ] [ ] taskData {
appsTasks := make ( map [ string ] [ ] taskData )
for _ , task := range tasks {
taskLabels := label . ExtractTraefikLabels ( extractLabels ( task ) )
for segmentName , traefikLabels := range taskLabels {
data := taskData {
Task : task ,
TraefikLabels : traefikLabels ,
SegmentName : segmentName ,
}
if taskFilter ( data , p . ExposedByDefault ) {
name := getName ( data )
if _ , ok := appsTasks [ name ] ; ! ok {
appsTasks [ name ] = [ ] taskData { data }
} else {
appsTasks [ name ] = append ( appsTasks [ name ] , data )
}
}
}
}
return appsTasks
}
2018-03-29 01:01:24 +03:00
func taskFilter ( task taskData , exposedByDefaultFlag bool ) bool {
2018-06-06 01:26:03 +03:00
name := getName ( task )
2017-12-02 21:29:34 +03:00
if len ( task . DiscoveryInfo . Ports . DiscoveryPorts ) == 0 {
2018-06-06 01:26:03 +03:00
log . Debugf ( "Filtering Mesos task without port %s" , name )
2017-12-02 21:29:34 +03:00
return false
}
if ! isEnabled ( task , exposedByDefaultFlag ) {
2018-06-06 01:26:03 +03:00
log . Debugf ( "Filtering disabled Mesos task %s" , name )
2017-12-02 21:29:34 +03:00
return false
}
// filter indeterminable task port
2018-03-29 01:01:24 +03:00
portIndexLabel := label . GetStringValue ( task . TraefikLabels , label . TraefikPortIndex , "" )
2018-06-06 01:26:03 +03:00
portNameLabel := label . GetStringValue ( task . TraefikLabels , label . TraefikPortName , "" )
2018-03-29 01:01:24 +03:00
portValueLabel := label . GetStringValue ( task . TraefikLabels , label . TraefikPort , "" )
2017-12-02 21:29:34 +03:00
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
}
}
2018-06-06 01:26:03 +03:00
if portNameLabel != "" {
var foundPort bool
for _ , exposedPort := range task . DiscoveryInfo . Ports . DiscoveryPorts {
if portNameLabel == exposedPort . Name {
foundPort = true
break
}
}
if ! foundPort {
log . Debugf ( "Filtering Mesos task %s without a matching port for %q label" , task . Name , label . TraefikPortName )
return false
}
}
2017-12-02 21:29:34 +03:00
2018-01-10 06:05:33 +03:00
// filter healthChecks
2017-12-02 21:29:34 +03:00
if task . Statuses != nil && len ( task . Statuses ) > 0 && task . Statuses [ 0 ] . Healthy != nil && ! * task . Statuses [ 0 ] . Healthy {
2018-06-06 01:26:03 +03:00
log . Debugf ( "Filtering Mesos task %s with bad healthCheck" , name )
2017-12-02 21:29:34 +03:00
return false
}
return true
}
2018-03-29 01:01:24 +03:00
func getID ( task taskData ) string {
2018-06-06 01:26:03 +03:00
return provider . Normalize ( task . ID + getSegmentNameSuffix ( task . SegmentName ) )
}
func getName ( task taskData ) string {
return provider . Normalize ( task . DiscoveryInfo . Name + getSegmentNameSuffix ( task . SegmentName ) )
2017-12-02 21:29:34 +03:00
}
2018-03-29 01:01:24 +03:00
func getBackendName ( task taskData ) string {
2018-06-06 01:26:03 +03:00
return label . GetStringValue ( task . TraefikLabels , label . TraefikBackend , getName ( task ) )
2017-12-02 21:29:34 +03:00
}
2018-03-29 01:01:24 +03:00
func getFrontendName ( task taskData ) string {
2018-01-10 23:54:46 +03:00
// TODO task.ID -> task.Name + task.ID
2018-06-06 01:26:03 +03:00
return provider . Normalize ( task . ID + getSegmentNameSuffix ( task . SegmentName ) )
}
func getSegmentNameSuffix ( serviceName string ) string {
if len ( serviceName ) > 0 {
return "-service-" + provider . Normalize ( serviceName )
}
return ""
2017-12-02 21:29:34 +03:00
}
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
}
2018-06-06 01:26:03 +03:00
return strings . Replace ( strings . Replace ( strings . TrimPrefix ( name , "/" ) , "/" , "-" , - 1 ) , "_" , "-" , - 1 )
}
func ( p * Provider ) getSegmentSubDomain ( task taskData ) string {
subDomain := strings . ToLower ( p . getSubDomain ( task . DiscoveryInfo . Name ) )
if len ( task . SegmentName ) > 0 {
subDomain = strings . ToLower ( provider . Normalize ( task . SegmentName ) ) + "." + subDomain
}
return subDomain
2017-12-02 21:29:34 +03:00
}
2018-03-29 01:01:24 +03:00
// 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
2018-01-31 17:32:04 +03:00
}
2018-04-17 21:58:24 +03:00
domain := label . GetStringValue ( task . TraefikLabels , label . TraefikDomain , p . Domain )
2018-06-06 01:26:03 +03:00
return "Host:" + p . getSegmentSubDomain ( task ) + "." + domain
2018-01-31 17:32:04 +03:00
}
2018-03-29 01:01:24 +03:00
func ( p * Provider ) getServers ( tasks [ ] taskData ) map [ string ] types . Server {
2018-01-10 23:54:46 +03:00
var servers map [ string ] types . Server
for _ , task := range tasks {
if servers == nil {
servers = make ( map [ string ] types . Server )
}
2018-03-29 01:01:24 +03:00
protocol := label . GetStringValue ( task . TraefikLabels , label . TraefikProtocol , label . DefaultProtocol )
2018-01-10 23:54:46 +03:00
host := p . getHost ( task )
port := p . getServerPort ( task )
serverName := "server-" + getID ( task )
servers [ serverName ] = types . Server {
2018-06-13 11:08:03 +03:00
URL : fmt . Sprintf ( "%s://%s" , protocol , net . JoinHostPort ( host , port ) ) ,
2018-04-11 17:30:04 +03:00
Weight : getIntValue ( task . TraefikLabels , label . TraefikWeight , label . DefaultWeight , math . MaxInt32 ) ,
2018-01-10 23:54:46 +03:00
}
}
return servers
}
2018-03-29 01:01:24 +03:00
func ( p * Provider ) getHost ( task taskData ) string {
return task . IP ( strings . Split ( p . IPSources , "," ) ... )
2018-01-10 23:54:46 +03:00
}
2018-03-29 01:01:24 +03:00
func ( p * Provider ) getServerPort ( task taskData ) string {
2018-06-06 01:26:03 +03:00
if label . Has ( task . TraefikLabels , label . TraefikPort ) {
pv := label . GetIntValue ( task . TraefikLabels , label . TraefikPort , 0 )
if pv <= 0 {
log . Errorf ( "explicitly specified port %d must be larger than zero" , pv )
return ""
}
return strconv . Itoa ( pv )
}
2018-03-29 01:01:24 +03:00
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 )
2018-01-10 23:54:46 +03:00
}
2018-06-06 01:26:03 +03:00
// Find named port using traefik.portName or the segment name
if pn := label . GetStringValue ( task . TraefikLabels , label . TraefikPortName , task . SegmentName ) ; len ( pn ) > 0 {
for _ , port := range task . DiscoveryInfo . Ports . DiscoveryPorts {
if pn == port . Name {
return strconv . Itoa ( port . Number )
}
}
2018-01-10 23:54:46 +03:00
}
2018-03-29 01:01:24 +03:00
for _ , port := range task . DiscoveryInfo . Ports . DiscoveryPorts {
return strconv . Itoa ( port . Number )
2018-01-10 23:54:46 +03:00
}
2018-03-29 01:01:24 +03:00
return ""
2018-01-10 23:54:46 +03:00
}
2018-03-29 01:01:24 +03:00
func isEnabled ( task taskData , exposedByDefault bool ) bool {
return label . GetBoolValue ( task . TraefikLabels , label . TraefikEnable , exposedByDefault )
2018-01-10 23:54:46 +03:00
}
2017-12-02 21:29:34 +03:00
// Label functions
2018-03-29 01:01:24 +03:00
func getIntValue ( labels map [ string ] string , labelName string , defaultValue int , maxValue int ) int {
value := label . GetIntValue ( labels , labelName , defaultValue )
if value <= maxValue {
return value
2018-01-10 23:54:46 +03:00
}
2018-06-06 01:26:03 +03:00
log . Warnf ( "The value %d for %s exceed the max authorized value %d, falling back to %d." , value , labelName , maxValue , defaultValue )
2018-01-10 23:54:46 +03:00
return defaultValue
}
2018-03-29 01:01:24 +03:00
func extractLabels ( task state . Task ) map [ string ] string {
2018-01-10 23:54:46 +03:00
labels := make ( map [ string ] string )
for _ , lbl := range task . Labels {
labels [ lbl . Key ] = lbl . Value
}
return labels
2017-12-02 21:29:34 +03:00
}