2017-05-08 01:20:38 +00:00
|
|
|
package rancher
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
"github.com/cenk/backoff"
|
|
|
|
"github.com/containous/traefik/job"
|
|
|
|
"github.com/containous/traefik/log"
|
|
|
|
"github.com/containous/traefik/safe"
|
|
|
|
"github.com/containous/traefik/types"
|
|
|
|
|
|
|
|
rancher "github.com/rancher/go-rancher-metadata/metadata"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MetadataConfiguration contains configuration properties specific to
|
|
|
|
// the Rancher metadata service provider.
|
|
|
|
type MetadataConfiguration struct {
|
|
|
|
IntervalPoll bool `description:"Poll the Rancher metadata service every 'rancher.refreshseconds' (less accurate)"`
|
|
|
|
Prefix string `description:"Prefix used for accessing the Rancher metadata service"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
|
|
|
p.Constraints = append(p.Constraints, constraints...)
|
|
|
|
|
|
|
|
metadataServiceURL := fmt.Sprintf("http://rancher-metadata.rancher.internal/%s", p.Metadata.Prefix)
|
|
|
|
|
|
|
|
safe.Go(func() {
|
|
|
|
operation := func() error {
|
|
|
|
client, err := rancher.NewClientAndWait(metadataServiceURL)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorln("Failed to create Rancher metadata service client: %s", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
updateConfiguration := func(version string) {
|
|
|
|
log.WithField("metadata_version", version).Debugln("Refreshing configuration from Rancher metadata service")
|
|
|
|
|
2017-09-12 13:06:04 +00:00
|
|
|
stacks, err := client.GetStacks()
|
2017-05-08 01:20:38 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Failed to query Rancher metadata service: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-12 13:06:04 +00:00
|
|
|
rancherData := parseMetadataSourcedRancherData(stacks)
|
2017-05-08 01:20:38 +00:00
|
|
|
configuration := p.loadRancherConfig(rancherData)
|
|
|
|
configurationChan <- types.ConfigMessage{
|
|
|
|
ProviderName: "rancher",
|
|
|
|
Configuration: configuration,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updateConfiguration("init")
|
|
|
|
|
|
|
|
if p.Watch {
|
|
|
|
pool.Go(func(stop chan bool) {
|
|
|
|
switch {
|
|
|
|
case p.Metadata.IntervalPoll:
|
|
|
|
p.intervalPoll(client, updateConfiguration, stop)
|
|
|
|
default:
|
|
|
|
p.longPoll(client, updateConfiguration, stop)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
notify := func(err error, time time.Duration) {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"error": err,
|
|
|
|
"retry_in": time,
|
|
|
|
}).Errorln("Rancher metadata service connection error")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := backoff.RetryNotify(operation, job.NewBackOff(backoff.NewExponentialBackOff()), notify); err != nil {
|
|
|
|
log.WithField("endpoint", metadataServiceURL).Errorln("Cannot connect to Rancher metadata service")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Provider) intervalPoll(client rancher.Client, updateConfiguration func(string), stop chan bool) {
|
|
|
|
_, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
|
2017-10-12 14:26:03 +00:00
|
|
|
ticker := time.NewTicker(time.Second * time.Duration(p.RefreshSeconds))
|
2017-05-08 01:20:38 +00:00
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
var version string
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
newVersion, err := client.GetVersion()
|
|
|
|
if err != nil {
|
|
|
|
log.WithField("error", err).Errorln("Failed to read Rancher metadata service version")
|
|
|
|
} else if version != newVersion {
|
|
|
|
version = newVersion
|
|
|
|
updateConfiguration(version)
|
|
|
|
}
|
|
|
|
case <-stop:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Provider) longPoll(client rancher.Client, updateConfiguration func(string), stop chan bool) {
|
|
|
|
_, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
// Holds the connection until there is either a change in the metadata
|
|
|
|
// repository or `p.RefreshSeconds` has elapsed. Long polling should be
|
|
|
|
// favoured for the most accurate configuration updates.
|
2017-07-19 12:11:45 +00:00
|
|
|
safe.Go(func() {
|
|
|
|
client.OnChange(p.RefreshSeconds, updateConfiguration)
|
|
|
|
})
|
2017-05-08 01:20:38 +00:00
|
|
|
<-stop
|
|
|
|
}
|
|
|
|
|
2017-09-12 13:06:04 +00:00
|
|
|
func parseMetadataSourcedRancherData(stacks []rancher.Stack) (rancherDataList []rancherData) {
|
|
|
|
for _, stack := range stacks {
|
|
|
|
for _, service := range stack.Services {
|
|
|
|
var containerIPAddresses []string
|
|
|
|
for _, container := range service.Containers {
|
|
|
|
if containerFilter(container.Name, container.HealthState, container.State) {
|
|
|
|
containerIPAddresses = append(containerIPAddresses, container.PrimaryIp)
|
|
|
|
}
|
2017-05-08 01:20:38 +00:00
|
|
|
}
|
|
|
|
|
2017-09-12 13:06:04 +00:00
|
|
|
rancherDataList = append(rancherDataList, rancherData{
|
|
|
|
Name: stack.Name + "/" + service.Name,
|
|
|
|
State: service.State,
|
|
|
|
Labels: service.Labels,
|
|
|
|
Containers: containerIPAddresses,
|
|
|
|
})
|
|
|
|
}
|
2017-05-08 01:20:38 +00:00
|
|
|
}
|
|
|
|
return rancherDataList
|
|
|
|
}
|