2017-04-17 13:50:02 +03:00
package marathon
2015-09-12 16:10:03 +03:00
2015-09-09 23:39:08 +03:00
import (
2016-10-05 18:42:58 +03:00
"net"
2016-12-30 11:21:13 +03:00
"net/http"
2016-08-13 19:55:15 +03:00
"time"
2017-07-08 00:48:28 +03:00
"github.com/Sirupsen/logrus"
2016-09-19 20:08:39 +03:00
"github.com/cenk/backoff"
2017-03-27 12:51:53 +03:00
"github.com/containous/flaeg"
2016-09-19 20:08:39 +03:00
"github.com/containous/traefik/job"
2016-09-23 19:27:01 +03:00
"github.com/containous/traefik/log"
2017-04-17 13:50:02 +03:00
"github.com/containous/traefik/provider"
2016-03-31 19:57:08 +03:00
"github.com/containous/traefik/safe"
2016-02-24 18:43:39 +03:00
"github.com/containous/traefik/types"
2015-09-12 16:10:03 +03:00
"github.com/gambol99/go-marathon"
2015-09-09 23:39:08 +03:00
)
2017-04-20 23:05:21 +03:00
const (
2017-07-10 17:58:12 +03:00
traceMaxScanTokenSize = 1024 * 1024
2017-08-18 04:08:03 +03:00
marathonEventIDs = marathon . EventIDApplications |
marathon . EventIDAddHealthCheck |
marathon . EventIDDeploymentSuccess |
marathon . EventIDDeploymentFailed |
marathon . EventIDDeploymentInfo |
marathon . EventIDDeploymentStepSuccess |
marathon . EventIDDeploymentStepFailed
2017-07-17 14:42:48 +03:00
)
// TaskState denotes the Mesos state a task can have.
type TaskState string
const (
taskStateRunning TaskState = "TASK_RUNNING"
taskStateStaging TaskState = "TASK_STAGING"
2017-04-20 23:05:21 +03:00
)
2017-12-02 21:27:47 +03:00
const (
labelIPAddressIdx = "traefik.ipAddressIdx"
labelLbCompatibilityGroup = "HAPROXY_GROUP"
labelLbCompatibility = "HAPROXY_0_VHOST"
)
2017-04-17 13:50:02 +03:00
2017-12-02 21:27:47 +03:00
var _ provider . Provider = ( * Provider ) ( nil )
2017-08-21 11:46:03 +03:00
2017-04-17 13:50:02 +03:00
// Provider holds configuration of the provider.
type Provider struct {
provider . BaseProvider
2017-11-21 12:48:04 +03:00
Endpoint string ` description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon" export:"true" `
Domain string ` description:"Default domain used" export:"true" `
ExposedByDefault bool ` description:"Expose Marathon apps by default" export:"true" `
GroupsAsSubDomains bool ` description:"Convert Marathon groups to subdomains" export:"true" `
DCOSToken string ` description:"DCOSToken for DCOS environment, This will override the Authorization header" export:"true" `
MarathonLBCompatibility bool ` description:"Add compatibility with marathon-lb labels" export:"true" `
FilterMarathonConstraints bool ` description:"Enable use of Marathon constraints in constraint filtering" export:"true" `
2017-11-24 21:18:03 +03:00
TLS * types . ClientTLS ` description:"Enable TLS support" export:"true" `
2017-11-21 12:48:04 +03:00
DialerTimeout flaeg . Duration ` description:"Set a non-default connection timeout for Marathon" export:"true" `
KeepAlive flaeg . Duration ` description:"Set a non-default TCP Keep Alive time in seconds" export:"true" `
ForceTaskHostname bool ` description:"Force to use the task's hostname." export:"true" `
Basic * Basic ` description:"Enable basic authentication" export:"true" `
RespectReadinessChecks bool ` description:"Filter out tasks with non-successful readiness checks during deployments" export:"true" `
readyChecker * readinessChecker
marathonClient marathon . Marathon
2015-11-13 13:50:32 +03:00
}
2017-04-17 13:50:02 +03:00
// Basic holds basic authentication specific configurations
type Basic struct {
2017-05-11 20:07:45 +03:00
HTTPBasicAuthUser string ` description:"Basic authentication User" `
HTTPBasicPassword string ` description:"Basic authentication Password" `
2016-01-18 13:52:18 +03:00
}
2017-04-17 13:50:02 +03:00
// Provide allows the marathon provider to provide configurations to traefik
2015-11-01 21:29:47 +03:00
// using the given configuration channel.
2017-04-17 13:50:02 +03: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 19:59:51 +03:00
operation := func ( ) error {
config := marathon . NewDefaultConfig ( )
2017-04-17 13:50:02 +03:00
config . URL = p . Endpoint
2016-04-15 19:59:51 +03:00
config . EventsTransport = marathon . EventsTransportSSE
2017-07-08 00:48:28 +03:00
if p . Trace {
config . LogOutput = log . CustomWriterLevel ( logrus . DebugLevel , traceMaxScanTokenSize )
}
2017-04-17 13:50:02 +03:00
if p . Basic != nil {
config . HTTPBasicAuthUser = p . Basic . HTTPBasicAuthUser
config . HTTPBasicPassword = p . Basic . HTTPBasicPassword
2016-04-15 19:59:51 +03:00
}
2017-08-18 04:08:03 +03:00
var rc * readinessChecker
if p . RespectReadinessChecks {
log . Debug ( "Enabling Marathon readiness checker" )
rc = defaultReadinessChecker ( p . Trace )
}
p . readyChecker = rc
2017-04-17 13:50:02 +03:00
if len ( p . DCOSToken ) > 0 {
config . DCOSToken = p . DCOSToken
2016-06-18 15:51:52 +03:00
}
2017-04-17 13:50:02 +03:00
TLSConfig , err := p . TLS . CreateTLSConfig ( )
2016-06-27 17:14:56 +03:00
if err != nil {
return err
}
2016-04-15 19:59:51 +03:00
config . HTTPClient = & http . Client {
Transport : & http . Transport {
2016-10-05 18:42:58 +03:00
DialContext : ( & net . Dialer {
2017-04-17 13:50:02 +03:00
KeepAlive : time . Duration ( p . KeepAlive ) ,
Timeout : time . Duration ( p . DialerTimeout ) ,
2016-10-05 18:42:58 +03:00
} ) . DialContext ,
2016-11-22 18:11:28 +03:00
TLSClientConfig : TLSConfig ,
2016-04-15 19:59:51 +03: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 13:50:02 +03:00
p . marathonClient = client
2017-01-06 18:26:50 +03:00
2017-04-17 13:50:02 +03:00
if p . Watch {
2017-08-18 04:08:03 +03:00
update , err := client . AddEventsListener ( marathonEventIDs )
2017-01-06 18:26:50 +03:00
if err != nil {
2016-04-15 19:59:51 +03:00
log . Errorf ( "Failed to register for events, %s" , err )
return err
}
2016-04-13 21:36:23 +03:00
pool . Go ( func ( stop chan bool ) {
2016-04-19 13:00:22 +03:00
defer close ( update )
2015-09-24 18:16:13 +03:00
for {
2016-04-13 21:36:23 +03:00
select {
case <- stop :
return
case event := <- update :
2017-08-18 04:08:03 +03:00
log . Debugf ( "Received provider event %s" , event )
2017-12-02 21:27:47 +03:00
configuration := p . buildConfiguration ( )
2016-04-13 21:36:23 +03:00
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2015-11-13 13:50:32 +03:00
}
2015-09-10 00:09:16 +03:00
}
2015-09-24 18:16:13 +03:00
}
2016-03-31 19:57:08 +03:00
} )
2015-09-10 00:09:16 +03:00
}
2017-12-02 21:27:47 +03:00
configuration := p . buildConfiguration ( )
2016-04-19 13:00:22 +03:00
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2016-04-15 19:59:51 +03:00
return nil
2015-09-09 23:39:08 +03:00
}
2015-09-24 18:16:13 +03:00
2016-04-15 19:59:51 +03:00
notify := func ( err error , time time . Duration ) {
2017-04-17 13:50:02 +03:00
log . Errorf ( "Provider connection error %+v, retrying in %s" , err , time )
2016-04-15 19:59:51 +03:00
}
2016-12-08 15:32:12 +03:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-04-15 19:59:51 +03:00
if err != nil {
2017-04-17 13:50:02 +03:00
log . Errorf ( "Cannot connect to Provider server %+v" , err )
2015-11-13 13:50:32 +03:00
}
2015-10-01 13:04:25 +03:00
return nil
2015-09-09 23:39:08 +03:00
}