2017-04-17 10:50:02 +00:00
|
|
|
package kv
|
2015-10-01 10:04:25 +00:00
|
|
|
|
|
|
|
import (
|
2016-12-30 08:21:13 +00:00
|
|
|
"errors"
|
2016-02-19 16:10:48 +00:00
|
|
|
"fmt"
|
2015-10-01 10:04:25 +00:00
|
|
|
"strings"
|
2015-11-01 15:35:01 +00:00
|
|
|
"time"
|
2015-10-01 10:04:25 +00:00
|
|
|
|
2018-01-24 16:52:03 +00:00
|
|
|
"github.com/abronan/valkeyrie"
|
|
|
|
"github.com/abronan/valkeyrie/store"
|
2016-09-19 17:08:39 +00:00
|
|
|
"github.com/cenk/backoff"
|
|
|
|
"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-10-01 10:04:25 +00:00
|
|
|
)
|
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
// Provider holds common configurations of key-value providers.
|
|
|
|
type Provider struct {
|
2017-10-02 08:32:02 +00:00
|
|
|
provider.BaseProvider `mapstructure:",squash" export:"true"`
|
2017-08-25 16:22:03 +00:00
|
|
|
Endpoint string `description:"Comma separated server endpoints"`
|
2017-10-02 08:32:02 +00:00
|
|
|
Prefix string `description:"Prefix used for KV store" export:"true"`
|
|
|
|
TLS *types.ClientTLS `description:"Enable TLS support" export:"true"`
|
2017-08-25 16:22:03 +00:00
|
|
|
Username string `description:"KV Username"`
|
|
|
|
Password string `description:"KV Password"`
|
2017-05-11 17:09:06 +00:00
|
|
|
storeType store.Backend
|
2017-12-23 16:45:25 +00:00
|
|
|
kvClient store.Store
|
2015-10-01 10:04:25 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
// CreateStore create the K/V store
|
|
|
|
func (p *Provider) CreateStore() (store.Store, error) {
|
2016-07-13 15:18:55 +00:00
|
|
|
storeConfig := &store.Config{
|
|
|
|
ConnectionTimeout: 30 * time.Second,
|
|
|
|
Bucket: "traefik",
|
2017-03-28 15:54:48 +00:00
|
|
|
Username: p.Username,
|
|
|
|
Password: p.Password,
|
2016-07-13 15:18:55 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
if p.TLS != nil {
|
2016-07-13 15:18:55 +00:00
|
|
|
var err error
|
2017-04-17 10:50:02 +00:00
|
|
|
storeConfig.TLS, err = p.TLS.CreateTLSConfig()
|
2016-07-13 15:18:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2018-01-24 16:52:03 +00:00
|
|
|
return valkeyrie.NewStore(
|
2017-05-11 17:09:06 +00:00
|
|
|
p.storeType,
|
2017-04-17 10:50:02 +00:00
|
|
|
strings.Split(p.Endpoint, ","),
|
2016-07-13 15:18:55 +00:00
|
|
|
storeConfig,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2017-05-11 17:09:06 +00:00
|
|
|
// SetStoreType storeType setter
|
|
|
|
func (p *Provider) SetStoreType(storeType store.Backend) {
|
|
|
|
p.storeType = storeType
|
|
|
|
}
|
|
|
|
|
2017-12-23 16:45:25 +00:00
|
|
|
// SetKVClient kvClient setter
|
2017-05-11 17:09:06 +00:00
|
|
|
func (p *Provider) SetKVClient(kvClient store.Store) {
|
2017-12-23 16:45:25 +00:00
|
|
|
p.kvClient = kvClient
|
2017-05-11 17:09:06 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
func (p *Provider) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error {
|
2016-04-19 20:06:33 +00:00
|
|
|
operation := func() error {
|
2017-12-23 16:45:25 +00:00
|
|
|
events, err := p.kvClient.WatchTree(p.Prefix, make(chan struct{}), nil)
|
2016-03-04 22:52:08 +00:00
|
|
|
if err != nil {
|
2017-12-23 16:45:25 +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")
|
|
|
|
}
|
2017-12-23 16:45:25 +00:00
|
|
|
configuration := p.buildConfiguration()
|
2016-04-19 20:06:33 +00:00
|
|
|
if configuration != nil {
|
|
|
|
configurationChan <- types.ConfigMessage{
|
2017-05-11 17:09:06 +00:00
|
|
|
ProviderName: string(p.storeType),
|
2016-04-19 20:06:33 +00:00
|
|
|
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
|
|
|
}
|
2016-12-08 12:32:12 +00:00
|
|
|
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
2016-04-19 20:06:33 +00:00
|
|
|
if err != nil {
|
2017-12-23 16:45:25 +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
|
|
|
}
|
|
|
|
|
2017-04-17 10:50:02 +00:00
|
|
|
// Provide provides the configuration to traefik via the configuration channel
|
|
|
|
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
|
|
|
p.Constraints = append(p.Constraints, constraints...)
|
2016-04-19 20:06:33 +00:00
|
|
|
operation := func() error {
|
2017-12-23 16:45:25 +00:00
|
|
|
if _, err := p.kvClient.Exists(p.Prefix+"/qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj", nil); err != nil {
|
|
|
|
return fmt.Errorf("failed to test KV store connection: %v", err)
|
2016-04-19 20:06:33 +00:00
|
|
|
}
|
2017-04-17 10:50:02 +00:00
|
|
|
if p.Watch {
|
2016-04-19 20:06:33 +00:00
|
|
|
pool.Go(func(stop chan bool) {
|
2017-04-17 10:50:02 +00:00
|
|
|
err := p.watchKv(configurationChan, p.Prefix, stop)
|
2016-04-27 23:43:43 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Cannot watch KV store: %v", err)
|
|
|
|
}
|
2016-04-19 20:06:33 +00:00
|
|
|
})
|
|
|
|
}
|
2017-12-23 16:45:25 +00:00
|
|
|
configuration := p.buildConfiguration()
|
2016-04-19 20:06:33 +00:00
|
|
|
configurationChan <- types.ConfigMessage{
|
2017-05-11 17:09:06 +00:00
|
|
|
ProviderName: string(p.storeType),
|
2016-04-19 20:06:33 +00:00
|
|
|
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-12-08 12:32:12 +00:00
|
|
|
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
2016-04-19 20:06:33 +00:00
|
|
|
if err != nil {
|
2017-12-23 16:45:25 +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
|
|
|
|
}
|