2017-04-17 12:50:02 +02:00
package marathon
2015-09-12 15:10:03 +02:00
2015-09-09 22:39:08 +02:00
import (
2016-10-05 11:42:58 -04:00
"net"
2016-12-30 09:21:13 +01:00
"net/http"
2018-03-26 15:32:04 +02:00
"net/url"
2016-08-13 12:55:15 -04:00
"time"
2016-09-19 19:08:39 +02:00
"github.com/cenk/backoff"
2017-03-27 11:51:53 +02:00
"github.com/containous/flaeg"
2016-09-19 19:08:39 +02:00
"github.com/containous/traefik/job"
2016-09-23 18:27:01 +02:00
"github.com/containous/traefik/log"
2017-04-17 12:50:02 +02:00
"github.com/containous/traefik/provider"
2016-03-31 18:57:08 +02:00
"github.com/containous/traefik/safe"
2016-02-24 16:43:39 +01:00
"github.com/containous/traefik/types"
2015-09-12 15:10:03 +02:00
"github.com/gambol99/go-marathon"
2018-01-22 12:16:03 +01:00
"github.com/sirupsen/logrus"
2015-09-09 22:39:08 +02:00
)
2017-04-20 22:05:21 +02:00
const (
2017-07-10 16:58:12 +02:00
traceMaxScanTokenSize = 1024 * 1024
2017-08-18 03:08:03 +02:00
marathonEventIDs = marathon . EventIDApplications |
marathon . EventIDAddHealthCheck |
marathon . EventIDDeploymentSuccess |
marathon . EventIDDeploymentFailed |
marathon . EventIDDeploymentInfo |
marathon . EventIDDeploymentStepSuccess |
marathon . EventIDDeploymentStepFailed
2017-07-17 13:42:48 +02: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 22:05:21 +02:00
)
2017-12-02 19:27:47 +01:00
const (
labelIPAddressIdx = "traefik.ipAddressIdx"
labelLbCompatibilityGroup = "HAPROXY_GROUP"
labelLbCompatibility = "HAPROXY_0_VHOST"
)
2017-04-17 12:50:02 +02:00
2017-12-02 19:27:47 +01:00
var _ provider . Provider = ( * Provider ) ( nil )
2017-08-21 10:46:03 +02:00
2017-04-17 12:50:02 +02:00
// Provider holds configuration of the provider.
type Provider struct {
provider . BaseProvider
2017-11-21 03:48:04 -06: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 19:18:03 +01:00
TLS * types . ClientTLS ` description:"Enable TLS support" export:"true" `
2018-05-22 22:38:03 +02:00
DialerTimeout flaeg . Duration ` description:"Set a dialer timeout for Marathon" export:"true" `
ResponseHeaderTimeout flaeg . Duration ` description:"Set a response header timeout for Marathon" export:"true" `
TLSHandshakeTimeout flaeg . Duration ` description:"Set a TLS handhsake timeout for Marathon" export:"true" `
KeepAlive flaeg . Duration ` description:"Set a TCP Keep Alive time in seconds" export:"true" `
2017-11-21 03:48:04 -06:00
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 11:50:32 +01:00
}
2017-04-17 12:50:02 +02:00
// Basic holds basic authentication specific configurations
type Basic struct {
2017-05-11 19:07:45 +02:00
HTTPBasicAuthUser string ` description:"Basic authentication User" `
HTTPBasicPassword string ` description:"Basic authentication Password" `
2016-01-18 11:52:18 +01:00
}
2018-07-11 09:08:03 +02:00
// Init the provider
func ( p * Provider ) Init ( constraints types . Constraints ) error {
return p . BaseProvider . Init ( constraints )
}
2017-04-17 12:50:02 +02:00
// Provide allows the marathon provider to provide configurations to traefik
2015-11-01 19:29:47 +01:00
// using the given configuration channel.
2018-07-11 09:08:03 +02:00
func ( p * Provider ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool ) error {
2016-04-15 18:59:51 +02:00
operation := func ( ) error {
config := marathon . NewDefaultConfig ( )
2017-04-17 12:50:02 +02:00
config . URL = p . Endpoint
2016-04-15 18:59:51 +02:00
config . EventsTransport = marathon . EventsTransportSSE
2017-07-07 23:48:28 +02:00
if p . Trace {
config . LogOutput = log . CustomWriterLevel ( logrus . DebugLevel , traceMaxScanTokenSize )
}
2017-04-17 12:50:02 +02:00
if p . Basic != nil {
config . HTTPBasicAuthUser = p . Basic . HTTPBasicAuthUser
config . HTTPBasicPassword = p . Basic . HTTPBasicPassword
2016-04-15 18:59:51 +02:00
}
2017-08-18 03:08:03 +02:00
var rc * readinessChecker
if p . RespectReadinessChecks {
log . Debug ( "Enabling Marathon readiness checker" )
rc = defaultReadinessChecker ( p . Trace )
}
p . readyChecker = rc
2017-04-17 12:50:02 +02:00
if len ( p . DCOSToken ) > 0 {
config . DCOSToken = p . DCOSToken
2016-06-18 14:51:52 +02:00
}
2017-04-17 12:50:02 +02:00
TLSConfig , err := p . TLS . CreateTLSConfig ( )
2016-06-27 16:14:56 +02:00
if err != nil {
return err
}
2016-04-15 18:59:51 +02:00
config . HTTPClient = & http . Client {
Transport : & http . Transport {
2016-10-05 11:42:58 -04:00
DialContext : ( & net . Dialer {
2017-04-17 12:50:02 +02:00
KeepAlive : time . Duration ( p . KeepAlive ) ,
Timeout : time . Duration ( p . DialerTimeout ) ,
2016-10-05 11:42:58 -04:00
} ) . DialContext ,
2018-05-22 22:38:03 +02:00
ResponseHeaderTimeout : time . Duration ( p . ResponseHeaderTimeout ) ,
TLSHandshakeTimeout : time . Duration ( p . TLSHandshakeTimeout ) ,
TLSClientConfig : TLSConfig ,
2016-04-15 18:59:51 +02: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 12:50:02 +02:00
p . marathonClient = client
2017-01-06 13:26:50 -02:00
2017-04-17 12:50:02 +02:00
if p . Watch {
2017-08-18 03:08:03 +02:00
update , err := client . AddEventsListener ( marathonEventIDs )
2017-01-06 13:26:50 -02:00
if err != nil {
2016-04-15 18:59:51 +02:00
log . Errorf ( "Failed to register for events, %s" , err )
return err
}
2016-04-13 20:36:23 +02:00
pool . Go ( func ( stop chan bool ) {
2016-04-19 12:00:22 +02:00
defer close ( update )
2015-09-24 17:16:13 +02:00
for {
2016-04-13 20:36:23 +02:00
select {
case <- stop :
return
case event := <- update :
2017-08-18 03:08:03 +02:00
log . Debugf ( "Received provider event %s" , event )
2018-03-26 15:32:04 +02:00
configuration := p . getConfiguration ( )
2016-04-13 20:36:23 +02:00
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2015-11-13 11:50:32 +01:00
}
2015-09-09 23:09:16 +02:00
}
2015-09-24 17:16:13 +02:00
}
2016-03-31 18:57:08 +02:00
} )
2015-09-09 23:09:16 +02:00
}
2018-03-26 15:32:04 +02:00
configuration := p . getConfiguration ( )
2016-04-19 12:00:22 +02:00
configurationChan <- types . ConfigMessage {
ProviderName : "marathon" ,
Configuration : configuration ,
}
2016-04-15 18:59:51 +02:00
return nil
2015-09-09 22:39:08 +02:00
}
2015-09-24 17:16:13 +02:00
2016-04-15 18:59:51 +02:00
notify := func ( err error , time time . Duration ) {
2017-04-17 12:50:02 +02:00
log . Errorf ( "Provider connection error %+v, retrying in %s" , err , time )
2016-04-15 18:59:51 +02:00
}
2016-12-08 13:32:12 +01:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-04-15 18:59:51 +02:00
if err != nil {
2017-04-17 12:50:02 +02:00
log . Errorf ( "Cannot connect to Provider server %+v" , err )
2015-11-13 11:50:32 +01:00
}
2015-10-01 12:04:25 +02:00
return nil
2015-09-09 22:39:08 +02:00
}
2018-03-26 15:32:04 +02:00
func ( p * Provider ) getConfiguration ( ) * types . Configuration {
applications , err := p . getApplications ( )
if err != nil {
log . Errorf ( "Failed to retrieve Marathon applications: %v" , err )
return nil
}
return p . buildConfiguration ( applications )
}
func ( p * Provider ) getApplications ( ) ( * marathon . Applications , error ) {
v := url . Values { }
v . Add ( "embed" , "apps.tasks" )
v . Add ( "embed" , "apps.deployments" )
v . Add ( "embed" , "apps.readiness" )
return p . marathonClient . Applications ( v )
}