2017-04-17 10:50:02 +00:00
package marathon
2015-09-12 13:10:03 +00:00
2015-09-09 20:39:08 +00:00
import (
2015-11-01 15:35:01 +00:00
"errors"
2017-04-20 20:05:21 +00:00
"fmt"
2016-12-30 08:21:13 +00:00
"math"
2016-10-05 15:42:58 +00:00
"net"
2016-12-30 08:21:13 +00:00
"net/http"
2015-11-13 10:50:32 +00:00
"net/url"
2015-09-24 15:16:13 +00:00
"strconv"
2016-02-01 15:08:58 +00:00
"strings"
2015-09-24 15:16:13 +00:00
"text/template"
2016-08-13 16:55:15 +00:00
"time"
2015-09-10 20:54:37 +00:00
"github.com/BurntSushi/ty/fun"
2016-09-19 17:08:39 +00:00
"github.com/cenk/backoff"
2017-03-27 09:51:53 +00:00
"github.com/containous/flaeg"
2016-09-19 17:08:39 +00:00
"github.com/containous/traefik/job"
2016-09-23 16:27:01 +00:00
"github.com/containous/traefik/log"
2017-04-17 10:50:02 +00:00
"github.com/containous/traefik/provider"
2016-03-31 16:57:08 +00:00
"github.com/containous/traefik/safe"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2015-09-12 13:10:03 +00:00
"github.com/gambol99/go-marathon"
2015-09-09 20:39:08 +00:00
)
2017-04-20 20:05:21 +00:00
const (
2017-03-15 18:16:06 +00:00
labelPort = "traefik.port"
labelPortIndex = "traefik.portIndex"
labelBackendHealthCheckPath = "traefik.backend.healthcheck.path"
labelBackendHealthCheckInterval = "traefik.backend.healthcheck.interval"
2017-04-20 20:05:21 +00:00
)
2017-04-17 10:50:02 +00:00
var _ provider . Provider = ( * Provider ) ( nil )
// Provider holds configuration of the provider.
type Provider struct {
provider . BaseProvider
Endpoint string ` description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon" `
Domain string ` description:"Default domain used" `
ExposedByDefault bool ` description:"Expose Marathon apps by default" `
GroupsAsSubDomains bool ` description:"Convert Marathon groups to subdomains" `
DCOSToken string ` description:"DCOSToken for DCOS environment, This will override the Authorization header" `
MarathonLBCompatibility bool ` description:"Add compatibility with marathon-lb labels" `
TLS * provider . ClientTLS ` description:"Enable Docker TLS support" `
DialerTimeout flaeg . Duration ` description:"Set a non-default connection timeout for Marathon" `
KeepAlive flaeg . Duration ` description:"Set a non-default TCP Keep Alive time in seconds" `
2017-04-21 14:06:14 +00:00
ForceTaskHostname bool ` description:"Force to use the task's hostname." `
2017-05-11 17:07:45 +00:00
Basic * Basic ` description:"Enable basic authentication" `
2016-10-06 15:42:19 +00:00
marathonClient marathon . Marathon
2015-11-13 10:50:32 +00:00
}
2017-04-17 10:50:02 +00:00
// Basic holds basic authentication specific configurations
type Basic struct {
2017-05-11 17:07:45 +00:00
HTTPBasicAuthUser string ` description:"Basic authentication User" `
HTTPBasicPassword string ` description:"Basic authentication Password" `
2016-01-18 10:52:18 +00:00
}
2015-11-13 10:50:32 +00:00
type lightMarathonClient interface {
2015-11-22 22:32:31 +00:00
AllTasks ( v url . Values ) ( * marathon . Tasks , error )
2016-05-03 14:52:14 +00:00
Applications ( url . Values ) ( * marathon . Applications , error )
2015-09-09 20:39:08 +00:00
}
2017-04-17 10:50:02 +00:00
// Provide allows the marathon provider to provide configurations to traefik
2015-11-01 18:29:47 +00:00
// using the given configuration channel.
2017-04-17 10:50:02 +00:00
func ( p * Provider ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , constraints types . Constraints ) error {
p . Constraints = append ( p . Constraints , constraints ... )
2016-04-15 16:59:51 +00:00
operation := func ( ) error {
config := marathon . NewDefaultConfig ( )
2017-04-17 10:50:02 +00:00
config . URL = p . Endpoint
2016-04-15 16:59:51 +00:00
config . EventsTransport = marathon . EventsTransportSSE
2017-04-17 10:50:02 +00:00
if p . Basic != nil {
config . HTTPBasicAuthUser = p . Basic . HTTPBasicAuthUser
config . HTTPBasicPassword = p . Basic . HTTPBasicPassword
2016-04-15 16:59:51 +00:00
}
2017-04-17 10:50:02 +00:00
if len ( p . DCOSToken ) > 0 {
config . DCOSToken = p . DCOSToken
2016-06-18 12:51:52 +00:00
}
2017-04-17 10:50:02 +00:00
TLSConfig , err := p . TLS . CreateTLSConfig ( )
2016-06-27 14:14:56 +00:00
if err != nil {
return err
}
2016-04-15 16:59:51 +00:00
config . HTTPClient = & http . Client {
Transport : & http . Transport {
2016-10-05 15:42:58 +00:00
DialContext : ( & net . Dialer {
2017-04-17 10:50:02 +00:00
KeepAlive : time . Duration ( p . KeepAlive ) ,
Timeout : time . Duration ( p . DialerTimeout ) ,
2016-10-05 15:42:58 +00:00
} ) . DialContext ,
2016-11-22 15:11:28 +00:00
TLSClientConfig : TLSConfig ,
2016-04-15 16:59:51 +00:00
} ,
}
client , err := marathon . NewClient ( config )
if err != nil {
log . Errorf ( "Failed to create a client for marathon, error: %s" , err )
return err
}
2017-04-17 10:50:02 +00:00
p . marathonClient = client
2017-01-06 15:26:50 +00:00
2017-04-17 10:50:02 +00:00
if p . Watch {
2017-01-06 15:26:50 +00:00
update , err := client . AddEventsListener ( marathon . EventIDApplications )
if err != nil {
2016-04-15 16:59:51 +00:00
log . Errorf ( "Failed to register for events, %s" , err )
return err
}
2016-04-13 18:36:23 +00:00
pool . Go ( func ( stop chan bool ) {
2016-04-19 10:00:22 +00:00
defer close ( update )
2015-09-24 15:16:13 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
case event := <- update :
2017-05-26 15:03:14 +00:00
log . Debug ( "Provider event received" , event )
2017-04-17 10:50:02 +00:00
configuration := p . loadMarathonConfig ( )
2016-04-13 18:36:23 +00:00
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2015-11-13 10:50:32 +00:00
}
2015-09-09 21:09:16 +00:00
}
2015-09-24 15:16:13 +00:00
}
2016-03-31 16:57:08 +00:00
} )
2015-09-09 21:09:16 +00:00
}
2017-04-17 10:50:02 +00:00
configuration := p . loadMarathonConfig ( )
2016-04-19 10:00:22 +00:00
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2016-04-15 16:59:51 +00:00
return nil
2015-09-09 20:39:08 +00:00
}
2015-09-24 15:16:13 +00:00
2016-04-15 16:59:51 +00:00
notify := func ( err error , time time . Duration ) {
2017-04-17 10:50:02 +00:00
log . Errorf ( "Provider connection error %+v, retrying in %s" , err , time )
2016-04-15 16:59:51 +00:00
}
2016-12-08 12:32:12 +00:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-04-15 16:59:51 +00:00
if err != nil {
2017-04-17 10:50:02 +00:00
log . Errorf ( "Cannot connect to Provider server %+v" , err )
2015-11-13 10:50:32 +00:00
}
2015-10-01 10:04:25 +00:00
return nil
2015-09-09 20:39:08 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) loadMarathonConfig ( ) * types . Configuration {
2015-10-08 19:21:51 +00:00
var MarathonFuncMap = template . FuncMap {
2017-04-17 10:50:02 +00:00
"getBackend" : p . getBackend ,
"getBackendServer" : p . getBackendServer ,
"getPort" : p . getPort ,
"getWeight" : p . getWeight ,
"getDomain" : p . getDomain ,
2017-05-31 21:46:20 +00:00
"getSubDomain" : p . getSubDomain ,
2017-04-17 10:50:02 +00:00
"getProtocol" : p . getProtocol ,
"getPassHostHeader" : p . getPassHostHeader ,
"getPriority" : p . getPriority ,
"getEntryPoints" : p . getEntryPoints ,
"getFrontendRule" : p . getFrontendRule ,
"getFrontendBackend" : p . getFrontendBackend ,
"hasCircuitBreakerLabels" : p . hasCircuitBreakerLabels ,
"hasLoadBalancerLabels" : p . hasLoadBalancerLabels ,
"hasMaxConnLabels" : p . hasMaxConnLabels ,
"getMaxConnExtractorFunc" : p . getMaxConnExtractorFunc ,
"getMaxConnAmount" : p . getMaxConnAmount ,
"getLoadBalancerMethod" : p . getLoadBalancerMethod ,
"getCircuitBreakerExpression" : p . getCircuitBreakerExpression ,
"getSticky" : p . getSticky ,
2017-03-15 18:16:06 +00:00
"hasHealthCheckLabels" : p . hasHealthCheckLabels ,
"getHealthCheckPath" : p . getHealthCheckPath ,
"getHealthCheckInterval" : p . getHealthCheckInterval ,
2017-06-28 00:22:17 +00:00
"getBasicAuth" : p . getBasicAuth ,
2017-04-17 10:50:02 +00:00
}
applications , err := p . marathonClient . Applications ( nil )
2015-09-12 13:10:03 +00:00
if err != nil {
2017-06-13 22:20:56 +00:00
log . Errorf ( "Failed to retrieve applications from Marathon, error: %s" , err )
2015-09-09 20:39:08 +00:00
return nil
}
2015-09-10 20:54:37 +00:00
2017-04-17 10:50:02 +00:00
tasks , err := p . marathonClient . AllTasks ( & marathon . AllTasksOpts { Status : "running" } )
2015-09-12 13:10:03 +00:00
if err != nil {
2017-06-13 22:20:56 +00:00
log . Errorf ( "Failed to retrieve task from Marathon, error: %s" , err )
2015-09-09 20:39:08 +00:00
return nil
}
2015-09-10 20:54:37 +00:00
//filter tasks
filteredTasks := fun . Filter ( func ( task marathon . Task ) bool {
2017-04-17 10:50:02 +00:00
return p . taskFilter ( task , applications , p . ExposedByDefault )
2015-09-10 20:54:37 +00:00
} , tasks . Tasks ) . ( [ ] marathon . Task )
//filter apps
filteredApps := fun . Filter ( func ( app marathon . Application ) bool {
2017-04-17 10:50:02 +00:00
return p . applicationFilter ( app , filteredTasks )
2015-09-10 20:54:37 +00:00
} , applications . Apps ) . ( [ ] marathon . Application )
2015-09-09 20:39:08 +00:00
templateObjects := struct {
Applications [ ] marathon . Application
Tasks [ ] marathon . Task
Domain string
} {
2015-09-10 20:54:37 +00:00
filteredApps ,
filteredTasks ,
2017-04-17 10:50:02 +00:00
p . Domain ,
2015-09-09 20:39:08 +00:00
}
2017-04-17 10:50:02 +00:00
configuration , err := p . GetConfiguration ( "templates/marathon.tmpl" , MarathonFuncMap , templateObjects )
2015-09-09 20:39:08 +00:00
if err != nil {
2015-11-13 10:50:32 +00:00
log . Error ( err )
2015-09-09 20:39:08 +00:00
}
2015-11-13 10:50:32 +00:00
return configuration
}
2015-09-09 20:39:08 +00:00
2017-04-17 10:50:02 +00:00
func ( p * Provider ) taskFilter ( task marathon . Task , applications * marathon . Applications , exposedByDefaultFlag bool ) bool {
2015-12-05 18:59:01 +00:00
application , err := getApplication ( task , applications . Apps )
if err != nil {
2017-04-20 20:05:21 +00:00
log . Errorf ( "Unable to get Marathon application %s for task %s" , task . AppID , task . ID )
2015-11-13 10:50:32 +00:00
return false
}
2017-04-20 20:05:21 +00:00
if _ , err = processPorts ( application , task ) ; err != nil {
log . Errorf ( "Filtering Marathon task %s from application %s without port: %s" , task . ID , application . ID , err )
2017-01-06 15:26:50 +00:00
return false
}
2017-04-20 20:05:21 +00:00
// Filter illegal port label specification.
_ , hasPortIndexLabel := p . getLabel ( application , labelPortIndex )
_ , hasPortLabel := p . getLabel ( application , labelPort )
if hasPortIndexLabel && hasPortLabel {
log . Debugf ( "Filtering Marathon task %s from application %s specifying both traefik.portIndex and traefik.port labels" , task . ID , application . ID )
return false
}
// Filter by constraints.
2017-04-17 10:50:02 +00:00
label , _ := p . getLabel ( application , "traefik.tags" )
2016-09-23 21:05:11 +00:00
constraintTags := strings . Split ( label , "," )
2017-04-17 10:50:02 +00:00
if p . MarathonLBCompatibility {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "HAPROXY_GROUP" ) ; ok {
2016-10-06 15:42:19 +00:00
constraintTags = append ( constraintTags , label )
}
}
2017-04-17 10:50:02 +00:00
if ok , failingConstraint := p . MatchConstraints ( constraintTags ) ; ! ok {
2016-09-23 21:05:11 +00:00
if failingConstraint != nil {
2017-04-20 20:05:21 +00:00
log . Debugf ( "Filtering Marathon task %s from application %s pruned by '%v' constraint" , task . ID , application . ID , failingConstraint . String ( ) )
2016-09-20 08:44:32 +00:00
}
2016-09-23 21:05:11 +00:00
return false
2016-09-20 08:44:32 +00:00
}
2016-03-21 09:37:02 +00:00
2017-04-20 20:05:21 +00:00
// Filter disabled application.
2016-03-21 09:37:02 +00:00
if ! isApplicationEnabled ( application , exposedByDefaultFlag ) {
2017-04-20 20:05:21 +00:00
log . Debugf ( "Filtering disabled Marathon task %s from application %s" , task . ID , application . ID )
2015-11-13 10:50:32 +00:00
return false
}
2015-12-05 18:59:01 +00:00
2017-04-20 20:05:21 +00:00
// Filter task with existing, bad health check results.
2015-11-13 10:50:32 +00:00
if application . HasHealthChecks ( ) {
if task . HasHealthCheckResults ( ) {
2016-02-09 22:10:24 +00:00
for _ , healthcheck := range task . HealthCheckResults {
2015-11-13 10:50:32 +00:00
if ! healthcheck . Alive {
2017-04-20 20:05:21 +00:00
log . Debugf ( "Filtering Marathon task %s from application %s with bad health check" , task . ID , application . ID )
2015-11-13 10:50:32 +00:00
return false
}
}
}
2015-09-09 20:39:08 +00:00
}
2017-04-20 20:05:21 +00:00
2015-11-13 10:50:32 +00:00
return true
}
2015-09-09 20:39:08 +00:00
2017-04-17 10:50:02 +00:00
func ( p * Provider ) applicationFilter ( app marathon . Application , filteredTasks [ ] marathon . Task ) bool {
label , _ := p . getLabel ( app , "traefik.tags" )
2016-09-23 21:05:11 +00:00
constraintTags := strings . Split ( label , "," )
2017-04-17 10:50:02 +00:00
if p . MarathonLBCompatibility {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( app , "HAPROXY_GROUP" ) ; ok {
2016-10-06 15:42:19 +00:00
constraintTags = append ( constraintTags , label )
}
}
2017-04-17 10:50:02 +00:00
if ok , failingConstraint := p . MatchConstraints ( constraintTags ) ; ! ok {
2016-09-20 08:44:32 +00:00
if failingConstraint != nil {
log . Debugf ( "Application %v pruned by '%v' constraint" , app . ID , failingConstraint . String ( ) )
}
return false
}
2015-11-13 10:50:32 +00:00
return fun . Exists ( func ( task marathon . Task ) bool {
return task . AppID == app . ID
} , filteredTasks )
2015-09-10 20:54:37 +00:00
}
2015-10-26 23:26:35 +00:00
func getApplication ( task marathon . Task , apps [ ] marathon . Application ) ( marathon . Application , error ) {
2015-09-10 20:54:37 +00:00
for _ , application := range apps {
2015-09-12 13:10:03 +00:00
if application . ID == task . AppID {
2015-10-26 23:26:35 +00:00
return application , nil
2015-09-10 20:54:37 +00:00
}
}
2015-10-26 23:26:35 +00:00
return marathon . Application { } , errors . New ( "Application not found: " + task . AppID )
2015-09-12 13:10:03 +00:00
}
2015-10-23 07:49:19 +00:00
2016-03-21 09:37:02 +00:00
func isApplicationEnabled ( application marathon . Application , exposedByDefault bool ) bool {
2016-06-18 12:51:52 +00:00
return exposedByDefault && ( * application . Labels ) [ "traefik.enable" ] != "false" || ( * application . Labels ) [ "traefik.enable" ] == "true"
2016-03-21 09:37:02 +00:00
}
2017-04-20 20:06:27 +00:00
func ( p * Provider ) getLabel ( application marathon . Application , label string ) ( string , bool ) {
2016-06-18 12:51:52 +00:00
for key , value := range * application . Labels {
2015-10-23 07:49:19 +00:00
if key == label {
2017-04-20 20:06:27 +00:00
return value , true
2015-10-23 07:49:19 +00:00
}
}
2017-04-20 20:06:27 +00:00
return "" , false
2015-10-23 07:49:19 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getPort ( task marathon . Task , applications [ ] marathon . Application ) string {
2015-12-05 18:59:01 +00:00
application , err := getApplication ( task , applications )
if err != nil {
2017-04-20 20:05:21 +00:00
log . Errorf ( "Unable to get Marathon application %s for task %s" , application . ID , task . ID )
2015-12-05 18:59:01 +00:00
return ""
}
2017-04-20 20:05:21 +00:00
port , err := processPorts ( application , task )
if err != nil {
log . Errorf ( "Unable to process ports for Marathon application %s and task %s: %s" , application . ID , task . ID , err )
return ""
2015-12-05 18:59:01 +00:00
}
2017-04-20 20:05:21 +00:00
return strconv . Itoa ( port )
2015-11-13 10:50:32 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getWeight ( task marathon . Task , applications [ ] marathon . Application ) string {
2015-11-13 10:50:32 +00:00
application , errApp := getApplication ( task , applications )
if errApp != nil {
log . Errorf ( "Unable to get marathon application from task %s" , task . AppID )
return "0"
}
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.weight" ) ; ok {
2015-11-13 10:50:32 +00:00
return label
}
return "0"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getDomain ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.domain" ) ; ok {
2015-11-13 10:50:32 +00:00
return label
}
2017-04-17 10:50:02 +00:00
return p . Domain
2015-11-13 10:50:32 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getProtocol ( task marathon . Task , applications [ ] marathon . Application ) string {
2015-11-13 10:50:32 +00:00
application , errApp := getApplication ( task , applications )
if errApp != nil {
log . Errorf ( "Unable to get marathon application from task %s" , task . AppID )
return "http"
}
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.protocol" ) ; ok {
2015-11-13 10:50:32 +00:00
return label
}
return "http"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getSticky ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if sticky , ok := p . getLabel ( application , "traefik.backend.loadbalancer.sticky" ) ; ok {
2016-05-13 14:22:11 +00:00
return sticky
}
return "false"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getPassHostHeader ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if passHostHeader , ok := p . getLabel ( application , "traefik.frontend.passHostHeader" ) ; ok {
2015-11-13 10:50:32 +00:00
return passHostHeader
}
2016-05-10 11:43:24 +00:00
return "true"
2015-10-23 07:49:19 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getPriority ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if priority , ok := p . getLabel ( application , "traefik.frontend.priority" ) ; ok {
2016-06-06 20:30:23 +00:00
return priority
}
return "0"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getEntryPoints ( application marathon . Application ) [ ] string {
2017-04-20 20:06:27 +00:00
if entryPoints , ok := p . getLabel ( application , "traefik.frontend.entryPoints" ) ; ok {
2016-02-01 15:08:58 +00:00
return strings . Split ( entryPoints , "," )
}
return [ ] string { }
}
2015-11-13 10:50:32 +00:00
// getFrontendRule returns the frontend rule for the specified application, using
2015-11-01 18:29:47 +00:00
// it's label. It returns a default one (Host) if the label is not present.
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getFrontendRule ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.frontend.rule" ) ; ok {
2015-10-23 07:49:19 +00:00
return label
}
2017-04-17 10:50:02 +00:00
if p . MarathonLBCompatibility {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "HAPROXY_0_VHOST" ) ; ok {
2016-10-06 15:42:19 +00:00
return "Host:" + label
}
}
2017-04-17 10:50:02 +00:00
return "Host:" + p . getSubDomain ( application . ID ) + "." + p . Domain
2015-10-23 07:49:19 +00:00
}
2016-01-20 18:55:10 +00:00
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getBackend ( task marathon . Task , applications [ ] marathon . Application ) string {
2016-02-12 13:45:36 +00:00
application , errApp := getApplication ( task , applications )
if errApp != nil {
log . Errorf ( "Unable to get marathon application from task %s" , task . AppID )
return ""
}
2017-04-17 10:50:02 +00:00
return p . getFrontendBackend ( application )
2016-02-12 13:45:36 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getFrontendBackend ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.backend" ) ; ok {
2016-01-20 18:55:10 +00:00
return label
}
2017-04-17 10:50:02 +00:00
return provider . Replace ( "/" , "-" , application . ID )
2016-01-20 18:55:10 +00:00
}
2016-05-31 21:23:23 +00:00
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getSubDomain ( name string ) string {
if p . GroupsAsSubDomains {
2016-06-01 14:47:39 +00:00
splitedName := strings . Split ( strings . TrimPrefix ( name , "/" ) , "/" )
2017-04-17 10:50:02 +00:00
provider . ReverseStringSlice ( & splitedName )
2016-06-01 14:47:39 +00:00
reverseName := strings . Join ( splitedName , "." )
return reverseName
}
return strings . Replace ( strings . TrimPrefix ( name , "/" ) , "/" , "-" , - 1 )
2016-05-31 21:23:23 +00:00
}
2016-08-13 16:55:15 +00:00
2017-04-17 10:50:02 +00:00
func ( p * Provider ) hasCircuitBreakerLabels ( application marathon . Application ) bool {
2017-04-20 20:06:27 +00:00
_ , ok := p . getLabel ( application , "traefik.backend.circuitbreaker.expression" )
return ok
2016-08-13 16:55:15 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) hasLoadBalancerLabels ( application marathon . Application ) bool {
_ , errMethod := p . getLabel ( application , "traefik.backend.loadbalancer.method" )
_ , errSticky := p . getLabel ( application , "traefik.backend.loadbalancer.sticky" )
2017-04-20 20:06:27 +00:00
return errMethod || errSticky
2016-08-13 16:55:15 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) hasMaxConnLabels ( application marathon . Application ) bool {
2017-04-20 20:06:27 +00:00
if _ , ok := p . getLabel ( application , "traefik.backend.maxconn.amount" ) ; ! ok {
2016-08-13 16:55:15 +00:00
return false
}
2017-04-20 20:06:27 +00:00
_ , ok := p . getLabel ( application , "traefik.backend.maxconn.extractorfunc" )
return ok
2016-08-13 16:55:15 +00:00
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getMaxConnAmount ( application marathon . Application ) int64 {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.backend.maxconn.amount" ) ; ok {
2016-08-13 16:55:15 +00:00
i , errConv := strconv . ParseInt ( label , 10 , 64 )
if errConv != nil {
log . Errorf ( "Unable to parse traefik.backend.maxconn.amount %s" , label )
return math . MaxInt64
}
return i
}
return math . MaxInt64
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getMaxConnExtractorFunc ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.backend.maxconn.extractorfunc" ) ; ok {
2016-08-13 16:55:15 +00:00
return label
}
return "request.host"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getLoadBalancerMethod ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.backend.loadbalancer.method" ) ; ok {
2016-08-13 16:55:15 +00:00
return label
}
return "wrr"
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getCircuitBreakerExpression ( application marathon . Application ) string {
2017-04-20 20:06:27 +00:00
if label , ok := p . getLabel ( application , "traefik.backend.circuitbreaker.expression" ) ; ok {
2016-08-13 16:55:15 +00:00
return label
}
return "NetworkErrorRatio() > 1"
}
2017-01-06 15:26:50 +00:00
2017-03-15 18:16:06 +00:00
func ( p * Provider ) hasHealthCheckLabels ( application marathon . Application ) bool {
return p . getHealthCheckPath ( application ) != ""
}
func ( p * Provider ) getHealthCheckPath ( application marathon . Application ) string {
if label , ok := p . getLabel ( application , labelBackendHealthCheckPath ) ; ok {
return label
}
return ""
}
func ( p * Provider ) getHealthCheckInterval ( application marathon . Application ) string {
if label , ok := p . getLabel ( application , labelBackendHealthCheckInterval ) ; ok {
return label
}
return ""
}
2017-06-28 00:22:17 +00:00
func ( p * Provider ) getBasicAuth ( application marathon . Application ) [ ] string {
if basicAuth , ok := p . getLabel ( application , "traefik.frontend.auth.basic" ) ; ok {
return strings . Split ( basicAuth , "," )
}
return [ ] string { }
}
2017-04-20 20:05:21 +00:00
func processPorts ( application marathon . Application , task marathon . Task ) ( int , error ) {
if portLabel , ok := ( * application . Labels ) [ labelPort ] ; ok {
port , err := strconv . Atoi ( portLabel )
switch {
case err != nil :
return 0 , fmt . Errorf ( "failed to parse port label: %s" , err )
case port <= 0 :
return 0 , fmt . Errorf ( "explicitly specified port %d must be larger than zero" , port )
}
return port , nil
}
ports := retrieveAvailablePorts ( application , task )
if len ( ports ) == 0 {
return 0 , errors . New ( "no port found" )
}
portIndex := 0
portIndexLabel , ok := ( * application . Labels ) [ labelPortIndex ]
if ok {
var err error
2017-04-22 20:07:27 +00:00
portIndex , err = parseIndex ( portIndexLabel , len ( ports ) )
if err != nil {
return 0 , fmt . Errorf ( "cannot use port index to select from %d ports: %s" , len ( ports ) , err )
2017-04-20 20:05:21 +00:00
}
}
return ports [ portIndex ] , nil
}
func retrieveAvailablePorts ( application marathon . Application , task marathon . Task ) [ ] int {
2017-01-06 15:26:50 +00:00
// Using default port configuration
if task . Ports != nil && len ( task . Ports ) > 0 {
return task . Ports
}
// Using port definition if available
if application . PortDefinitions != nil && len ( * application . PortDefinitions ) > 0 {
2017-02-02 20:07:44 +00:00
var ports [ ] int
2017-01-06 15:26:50 +00:00
for _ , def := range * application . PortDefinitions {
if def . Port != nil {
ports = append ( ports , * def . Port )
}
}
return ports
}
// If using IP-per-task using this port definition
if application . IPAddressPerTask != nil && len ( * ( ( * application . IPAddressPerTask ) . Discovery ) . Ports ) > 0 {
2017-02-02 20:07:44 +00:00
var ports [ ] int
2017-01-06 15:26:50 +00:00
for _ , def := range * ( ( * application . IPAddressPerTask ) . Discovery ) . Ports {
ports = append ( ports , def . Number )
}
return ports
}
return [ ] int { }
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) getBackendServer ( task marathon . Task , applications [ ] marathon . Application ) string {
2017-01-06 15:26:50 +00:00
application , err := getApplication ( task , applications )
if err != nil {
log . Errorf ( "Unable to get marathon application from task %s" , task . AppID )
return ""
}
2017-04-21 14:06:14 +00:00
numTaskIPAddresses := len ( task . IPAddresses )
2017-03-26 19:59:08 +00:00
switch {
case application . IPAddressPerTask == nil || p . ForceTaskHostname :
return task . Host
2017-04-21 14:06:14 +00:00
case numTaskIPAddresses == 0 :
log . Errorf ( "Missing IP address for Marathon application %s on task %s" , application . ID , task . ID )
2017-01-06 15:26:50 +00:00
return ""
2017-04-21 14:06:14 +00:00
case numTaskIPAddresses == 1 :
2017-01-06 15:26:50 +00:00
return task . IPAddresses [ 0 ] . IPAddress
2017-03-26 19:59:08 +00:00
default :
2017-04-20 20:06:27 +00:00
ipAddressIdxStr , ok := p . getLabel ( application , "traefik.ipAddressIdx" )
if ! ok {
2017-04-21 14:06:14 +00:00
log . Errorf ( "Found %d task IP addresses but missing IP address index for Marathon application %s on task %s" , numTaskIPAddresses , application . ID , task . ID )
2017-01-06 15:26:50 +00:00
return ""
}
2017-04-22 20:07:27 +00:00
ipAddressIdx , err := parseIndex ( ipAddressIdxStr , numTaskIPAddresses )
2017-01-06 15:26:50 +00:00
if err != nil {
2017-04-21 14:06:14 +00:00
log . Errorf ( "Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s: %s" , numTaskIPAddresses , application . ID , task . ID , err )
2017-01-06 15:26:50 +00:00
return ""
}
2017-04-21 14:06:14 +00:00
2017-01-06 15:26:50 +00:00
return task . IPAddresses [ ipAddressIdx ] . IPAddress
}
}
2017-04-22 20:07:27 +00:00
func parseIndex ( index string , length int ) ( int , error ) {
parsed , err := strconv . Atoi ( index )
switch {
case err != nil :
return 0 , fmt . Errorf ( "failed to parse index '%s': %s" , index , err )
case parsed < 0 , parsed > length - 1 :
return 0 , fmt . Errorf ( "index %d must be within range (0, %d)" , parsed , length - 1 )
}
return parsed , nil
}