2015-11-01 18:29:47 +00:00
|
|
|
// Package provider holds the different provider implementation.
|
2015-11-01 15:35:01 +00:00
|
|
|
package provider
|
2015-10-01 10:04:25 +00:00
|
|
|
|
|
|
|
import (
|
2016-02-19 16:10:48 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2015-10-01 10:04:25 +00:00
|
|
|
"strings"
|
|
|
|
"text/template"
|
2015-11-01 15:35:01 +00:00
|
|
|
"time"
|
2015-10-01 10:04:25 +00:00
|
|
|
|
2016-04-19 20:06:33 +00:00
|
|
|
"errors"
|
2015-10-01 10:04:25 +00:00
|
|
|
"github.com/BurntSushi/ty/fun"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
2016-04-19 20:06:33 +00:00
|
|
|
"github.com/cenkalti/backoff"
|
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-11-01 15:35:01 +00:00
|
|
|
"github.com/docker/libkv"
|
2015-10-01 10:04:25 +00:00
|
|
|
"github.com/docker/libkv/store"
|
|
|
|
)
|
|
|
|
|
2015-11-01 18:29:47 +00:00
|
|
|
// Kv holds common configurations of key-value providers.
|
2015-11-02 18:48:34 +00:00
|
|
|
type Kv struct {
|
2016-01-13 21:46:44 +00:00
|
|
|
BaseProvider `mapstructure:",squash"`
|
|
|
|
Endpoint string
|
|
|
|
Prefix string
|
2016-02-19 16:10:48 +00:00
|
|
|
TLS *KvTLS
|
2016-01-13 21:46:44 +00:00
|
|
|
storeType store.Backend
|
|
|
|
kvclient store.Store
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-19 16:10:48 +00:00
|
|
|
// KvTLS holds TLS specific configurations
|
|
|
|
type KvTLS struct {
|
|
|
|
CA string
|
|
|
|
Cert string
|
|
|
|
Key string
|
|
|
|
InsecureSkipVerify bool
|
|
|
|
}
|
|
|
|
|
2016-04-27 23:43:43 +00:00
|
|
|
func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error {
|
2016-04-19 20:06:33 +00:00
|
|
|
operation := func() error {
|
2016-04-27 23:43:43 +00:00
|
|
|
events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{}))
|
2016-03-04 22:52:08 +00:00
|
|
|
if err != nil {
|
2016-04-27 23:43:43 +00:00
|
|
|
return fmt.Errorf("Failed to KV WatchTree: %v", err)
|
2016-03-04 22:52:08 +00:00
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return nil
|
|
|
|
case _, ok := <-events:
|
|
|
|
if !ok {
|
|
|
|
return errors.New("watchtree channel closed")
|
|
|
|
}
|
|
|
|
configuration := provider.loadConfig()
|
|
|
|
if configuration != nil {
|
|
|
|
configurationChan <- types.ConfigMessage{
|
|
|
|
ProviderName: string(provider.storeType),
|
|
|
|
Configuration: configuration,
|
|
|
|
}
|
2016-03-04 22:52:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
|
|
|
|
notify := func(err error, time time.Duration) {
|
2016-04-27 23:43:43 +00:00
|
|
|
log.Errorf("KV connection error: %+v, retrying in %s", err, time)
|
2016-04-19 20:06:33 +00:00
|
|
|
}
|
|
|
|
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
|
|
|
|
if err != nil {
|
2016-04-27 23:43:43 +00:00
|
|
|
return fmt.Errorf("Cannot connect to KV server: %v", err)
|
2016-04-19 20:06:33 +00:00
|
|
|
}
|
2016-04-27 23:43:43 +00:00
|
|
|
return nil
|
2016-03-04 22:52:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 18:36:23 +00:00
|
|
|
func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
|
2016-02-19 16:10:48 +00:00
|
|
|
storeConfig := &store.Config{
|
|
|
|
ConnectionTimeout: 30 * time.Second,
|
|
|
|
Bucket: "traefik",
|
|
|
|
}
|
|
|
|
|
|
|
|
if provider.TLS != nil {
|
|
|
|
caPool := x509.NewCertPool()
|
|
|
|
|
|
|
|
if provider.TLS.CA != "" {
|
|
|
|
ca, err := ioutil.ReadFile(provider.TLS.CA)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to read CA. %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
caPool.AppendCertsFromPEM(ca)
|
|
|
|
}
|
|
|
|
|
|
|
|
cert, err := tls.LoadX509KeyPair(provider.TLS.Cert, provider.TLS.Key)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to load keypair. %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
storeConfig.TLS = &tls.Config{
|
|
|
|
Certificates: []tls.Certificate{cert},
|
|
|
|
RootCAs: caPool,
|
|
|
|
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 20:06:33 +00:00
|
|
|
operation := func() error {
|
|
|
|
kv, err := libkv.NewStore(
|
|
|
|
provider.storeType,
|
|
|
|
strings.Split(provider.Endpoint, ","),
|
|
|
|
storeConfig,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2016-04-27 23:43:43 +00:00
|
|
|
return fmt.Errorf("Failed to Connect to KV store: %v", err)
|
2016-04-19 20:06:33 +00:00
|
|
|
}
|
2016-04-27 23:43:43 +00:00
|
|
|
if _, err := kv.Exists("qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"); err != nil {
|
|
|
|
return fmt.Errorf("Failed to test KV store connection: %v", err)
|
2016-04-19 20:06:33 +00:00
|
|
|
}
|
|
|
|
provider.kvclient = kv
|
|
|
|
if provider.Watch {
|
|
|
|
pool.Go(func(stop chan bool) {
|
2016-04-27 23:43:43 +00:00
|
|
|
err := provider.watchKv(configurationChan, provider.Prefix, stop)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Cannot watch KV store: %v", err)
|
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
configuration := provider.loadConfig()
|
|
|
|
configurationChan <- types.ConfigMessage{
|
|
|
|
ProviderName: string(provider.storeType),
|
|
|
|
Configuration: configuration,
|
|
|
|
}
|
|
|
|
return nil
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
notify := func(err error, time time.Duration) {
|
2016-04-27 23:43:43 +00:00
|
|
|
log.Errorf("KV connection error: %+v, retrying in %s", err, time)
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
|
|
|
|
if err != nil {
|
2016-04-27 23:43:43 +00:00
|
|
|
return fmt.Errorf("Cannot connect to KV server: %v", err)
|
2015-11-13 10:50:32 +00:00
|
|
|
}
|
2015-10-01 10:04:25 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-02 18:48:34 +00:00
|
|
|
func (provider *Kv) loadConfig() *types.Configuration {
|
2015-10-01 10:04:25 +00:00
|
|
|
templateObjects := struct {
|
|
|
|
Prefix string
|
|
|
|
}{
|
2016-02-15 23:14:21 +00:00
|
|
|
// Allow `/traefik/alias` to superesede `provider.Prefix`
|
|
|
|
strings.TrimSuffix(provider.get(provider.Prefix, provider.Prefix+"/alias"), "/"),
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
|
|
|
var KvFuncMap = template.FuncMap{
|
2016-02-01 15:08:58 +00:00
|
|
|
"List": provider.list,
|
|
|
|
"Get": provider.get,
|
|
|
|
"SplitGet": provider.splitGet,
|
|
|
|
"Last": provider.last,
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
|
|
|
|
2015-11-13 10:50:32 +00:00
|
|
|
configuration, err := provider.getConfiguration("templates/kv.tmpl", KvFuncMap, templateObjects)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
2015-11-13 10:50:32 +00:00
|
|
|
return configuration
|
|
|
|
}
|
2015-10-01 10:04:25 +00:00
|
|
|
|
2015-11-13 10:50:32 +00:00
|
|
|
func (provider *Kv) list(keys ...string) []string {
|
|
|
|
joinedKeys := strings.Join(keys, "")
|
|
|
|
keysPairs, err := provider.kvclient.List(joinedKeys)
|
2015-10-01 10:04:25 +00:00
|
|
|
if err != nil {
|
2016-02-01 15:08:58 +00:00
|
|
|
log.Errorf("Error getting keys %s %s ", joinedKeys, err)
|
2015-10-01 10:04:25 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-11-13 10:50:32 +00:00
|
|
|
directoryKeys := make(map[string]string)
|
|
|
|
for _, key := range keysPairs {
|
2016-04-27 20:28:02 +00:00
|
|
|
directory := strings.Split(strings.TrimPrefix(key.Key, joinedKeys), "/")[0]
|
2015-11-13 10:50:32 +00:00
|
|
|
directoryKeys[directory] = joinedKeys + directory
|
|
|
|
}
|
|
|
|
return fun.Values(directoryKeys).([]string)
|
|
|
|
}
|
2015-10-01 10:04:25 +00:00
|
|
|
|
2016-02-01 15:08:58 +00:00
|
|
|
func (provider *Kv) get(defaultValue string, keys ...string) string {
|
2015-11-13 10:50:32 +00:00
|
|
|
joinedKeys := strings.Join(keys, "")
|
2016-04-27 20:28:02 +00:00
|
|
|
keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/"))
|
2015-11-13 10:50:32 +00:00
|
|
|
if err != nil {
|
2016-02-01 15:08:58 +00:00
|
|
|
log.Warnf("Error getting key %s %s, setting default %s", joinedKeys, err, defaultValue)
|
|
|
|
return defaultValue
|
2015-11-13 10:50:32 +00:00
|
|
|
} else if keyPair == nil {
|
2016-02-01 15:08:58 +00:00
|
|
|
log.Warnf("Error getting key %s, setting default %s", joinedKeys, defaultValue)
|
|
|
|
return defaultValue
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
2015-11-13 10:50:32 +00:00
|
|
|
return string(keyPair.Value)
|
|
|
|
}
|
2015-10-01 10:04:25 +00:00
|
|
|
|
2016-02-01 15:08:58 +00:00
|
|
|
func (provider *Kv) splitGet(keys ...string) []string {
|
|
|
|
joinedKeys := strings.Join(keys, "")
|
|
|
|
keyPair, err := provider.kvclient.Get(joinedKeys)
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("Error getting key %s %s, setting default empty", joinedKeys, err)
|
|
|
|
return []string{}
|
|
|
|
} else if keyPair == nil {
|
|
|
|
log.Warnf("Error getting key %s, setting default %empty", joinedKeys)
|
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
return strings.Split(string(keyPair.Value), ",")
|
|
|
|
}
|
|
|
|
|
2015-11-13 10:50:32 +00:00
|
|
|
func (provider *Kv) last(key string) string {
|
|
|
|
splittedKey := strings.Split(key, "/")
|
|
|
|
return splittedKey[len(splittedKey)-1]
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|