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 (
|
2016-10-05 15:42:58 +00:00
|
|
|
"net"
|
2016-12-30 08:21:13 +00:00
|
|
|
"net/http"
|
2018-03-26 13:32:04 +00:00
|
|
|
"net/url"
|
2016-08-13 16:55:15 +00:00
|
|
|
"time"
|
|
|
|
|
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"
|
2018-01-22 11:16:03 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2015-09-09 20:39:08 +00:00
|
|
|
)
|
|
|
|
|
2017-04-20 20:05:21 +00:00
|
|
|
const (
|
2017-07-10 14:58:12 +00:00
|
|
|
traceMaxScanTokenSize = 1024 * 1024
|
2017-08-18 01:08:03 +00:00
|
|
|
marathonEventIDs = marathon.EventIDApplications |
|
|
|
|
marathon.EventIDAddHealthCheck |
|
|
|
|
marathon.EventIDDeploymentSuccess |
|
|
|
|
marathon.EventIDDeploymentFailed |
|
|
|
|
marathon.EventIDDeploymentInfo |
|
|
|
|
marathon.EventIDDeploymentStepSuccess |
|
|
|
|
marathon.EventIDDeploymentStepFailed
|
2017-07-17 11:42:48 +00: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 20:05:21 +00:00
|
|
|
)
|
|
|
|
|
2017-12-02 18:27:47 +00:00
|
|
|
const (
|
|
|
|
labelIPAddressIdx = "traefik.ipAddressIdx"
|
|
|
|
labelLbCompatibilityGroup = "HAPROXY_GROUP"
|
|
|
|
labelLbCompatibility = "HAPROXY_0_VHOST"
|
|
|
|
)
|
2017-04-17 10:50:02 +00:00
|
|
|
|
2017-12-02 18:27:47 +00:00
|
|
|
var _ provider.Provider = (*Provider)(nil)
|
2017-08-21 08:46:03 +00:00
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
// Provider holds configuration of the provider.
|
|
|
|
type Provider struct {
|
|
|
|
provider.BaseProvider
|
2017-11-21 09:48:04 +00: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 18:18:03 +00:00
|
|
|
TLS *types.ClientTLS `description:"Enable TLS support" export:"true"`
|
2018-05-22 20:38:03 +00: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 09:48:04 +00: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 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
|
|
|
}
|
|
|
|
|
2018-07-11 07:08:03 +00:00
|
|
|
// Init the provider
|
|
|
|
func (p *Provider) Init(constraints types.Constraints) error {
|
|
|
|
return p.BaseProvider.Init(constraints)
|
|
|
|
}
|
|
|
|
|
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.
|
2018-07-11 07:08:03 +00:00
|
|
|
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
|
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-07-07 21:48:28 +00:00
|
|
|
if p.Trace {
|
|
|
|
config.LogOutput = log.CustomWriterLevel(logrus.DebugLevel, traceMaxScanTokenSize)
|
|
|
|
}
|
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-08-18 01:08:03 +00:00
|
|
|
var rc *readinessChecker
|
|
|
|
if p.RespectReadinessChecks {
|
|
|
|
log.Debug("Enabling Marathon readiness checker")
|
|
|
|
rc = defaultReadinessChecker(p.Trace)
|
|
|
|
}
|
|
|
|
p.readyChecker = rc
|
|
|
|
|
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,
|
2018-05-22 20:38:03 +00:00
|
|
|
ResponseHeaderTimeout: time.Duration(p.ResponseHeaderTimeout),
|
|
|
|
TLSHandshakeTimeout: time.Duration(p.TLSHandshakeTimeout),
|
|
|
|
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-08-18 01:08:03 +00:00
|
|
|
update, err := client.AddEventsListener(marathonEventIDs)
|
2017-01-06 15:26:50 +00:00
|
|
|
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-08-18 01:08:03 +00:00
|
|
|
log.Debugf("Received provider event %s", event)
|
2018-03-26 13:32:04 +00:00
|
|
|
|
|
|
|
configuration := p.getConfiguration()
|
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
|
|
|
}
|
2018-03-26 13:32:04 +00:00
|
|
|
|
|
|
|
configuration := p.getConfiguration()
|
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
|
|
|
}
|
2018-03-26 13:32:04 +00: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)
|
|
|
|
}
|