2015-10-01 13:04:25 +03:00
/ *
Copyright
* /
2015-11-01 18:35:01 +03:00
package provider
2015-10-01 13:04:25 +03:00
import (
"bytes"
2015-11-01 18:35:01 +03:00
"errors"
2015-10-01 13:04:25 +03:00
"strings"
"text/template"
2015-11-01 18:35:01 +03:00
"time"
2015-10-01 13:04:25 +03:00
"github.com/BurntSushi/toml"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
2015-11-01 18:35:01 +03:00
"github.com/docker/libkv"
2015-10-01 13:04:25 +03:00
"github.com/docker/libkv/store"
2015-11-01 18:35:01 +03:00
"github.com/docker/libkv/store/boltdb"
"github.com/docker/libkv/store/consul"
"github.com/docker/libkv/store/etcd"
"github.com/docker/libkv/store/zookeeper"
"github.com/emilevauge/traefik/autogen"
"github.com/emilevauge/traefik/types"
2015-10-01 13:04:25 +03:00
)
type KvProvider struct {
Watch bool
Endpoint string
Prefix string
Filename string
StoreType store . Backend
kvclient store . Store
}
func NewConsulProvider ( provider * ConsulProvider ) * KvProvider {
kvProvider := new ( KvProvider )
kvProvider . Watch = provider . Watch
kvProvider . Endpoint = provider . Endpoint
kvProvider . Prefix = provider . Prefix
kvProvider . Filename = provider . Filename
kvProvider . StoreType = store . CONSUL
return kvProvider
}
func NewEtcdProvider ( provider * EtcdProvider ) * KvProvider {
kvProvider := new ( KvProvider )
kvProvider . Watch = provider . Watch
kvProvider . Endpoint = provider . Endpoint
kvProvider . Prefix = provider . Prefix
kvProvider . Filename = provider . Filename
kvProvider . StoreType = store . ETCD
return kvProvider
}
func NewZkProvider ( provider * ZookepperProvider ) * KvProvider {
kvProvider := new ( KvProvider )
kvProvider . Watch = provider . Watch
kvProvider . Endpoint = provider . Endpoint
kvProvider . Prefix = provider . Prefix
kvProvider . Filename = provider . Filename
kvProvider . StoreType = store . ZK
return kvProvider
}
func NewBoltDbProvider ( provider * BoltDbProvider ) * KvProvider {
kvProvider := new ( KvProvider )
kvProvider . Watch = provider . Watch
kvProvider . Endpoint = provider . Endpoint
kvProvider . Prefix = provider . Prefix
kvProvider . Filename = provider . Filename
kvProvider . StoreType = store . BOLTDB
return kvProvider
}
2015-11-01 18:35:01 +03:00
func ( provider * KvProvider ) provide ( configurationChan chan <- types . ConfigMessage ) error {
2015-10-01 13:04:25 +03:00
switch provider . StoreType {
case store . CONSUL :
consul . Register ( )
case store . ETCD :
etcd . Register ( )
case store . ZK :
zookeeper . Register ( )
case store . BOLTDB :
boltdb . Register ( )
default :
return errors . New ( "Invalid kv store: " + string ( provider . StoreType ) )
}
kv , err := libkv . NewStore (
provider . StoreType ,
[ ] string { provider . Endpoint } ,
& store . Config {
ConnectionTimeout : 30 * time . Second ,
2015-10-23 19:59:08 +03:00
Bucket : "traefik" ,
2015-10-01 13:04:25 +03:00
} ,
)
if err != nil {
return err
}
if _ , err := kv . List ( "" ) ; err != nil {
return err
}
provider . kvclient = kv
if provider . Watch {
stopCh := make ( chan struct { } )
chanKeys , err := kv . WatchTree ( provider . Prefix , stopCh )
if err != nil {
return err
}
go func ( ) {
for {
<- chanKeys
configuration := provider . loadConfig ( )
if configuration != nil {
2015-11-01 18:35:01 +03:00
configurationChan <- types . ConfigMessage { string ( provider . StoreType ) , configuration }
2015-10-01 13:04:25 +03:00
}
defer close ( stopCh )
}
} ( )
}
configuration := provider . loadConfig ( )
2015-11-01 18:35:01 +03:00
configurationChan <- types . ConfigMessage { string ( provider . StoreType ) , configuration }
2015-10-01 13:04:25 +03:00
return nil
}
2015-11-01 18:35:01 +03:00
func ( provider * KvProvider ) loadConfig ( ) * types . Configuration {
configuration := new ( types . Configuration )
2015-10-01 13:04:25 +03:00
templateObjects := struct {
Prefix string
} {
provider . Prefix ,
}
var KvFuncMap = template . FuncMap {
"List" : func ( keys ... string ) [ ] string {
joinedKeys := strings . Join ( keys , "" )
keysPairs , err := provider . kvclient . List ( joinedKeys )
if err != nil {
log . Error ( "Error getting keys: " , joinedKeys , err )
return nil
}
directoryKeys := make ( map [ string ] string )
for _ , key := range keysPairs {
directory := strings . Split ( strings . TrimPrefix ( key . Key , strings . TrimPrefix ( joinedKeys , "/" ) ) , "/" ) [ 0 ]
directoryKeys [ directory ] = joinedKeys + directory
}
return fun . Values ( directoryKeys ) . ( [ ] string )
} ,
"Get" : func ( keys ... string ) string {
joinedKeys := strings . Join ( keys , "" )
keyPair , err := provider . kvclient . Get ( joinedKeys )
if err != nil {
log . Debug ( "Error getting key: " , joinedKeys , err )
return ""
} else if keyPair == nil {
return ""
}
return string ( keyPair . Value )
} ,
"Last" : func ( key string ) string {
splittedKey := strings . Split ( key , "/" )
return splittedKey [ len ( splittedKey ) - 1 ]
} ,
}
tmpl := template . New ( provider . Filename ) . Funcs ( KvFuncMap )
if len ( provider . Filename ) > 0 {
_ , err := tmpl . ParseFiles ( provider . Filename )
if err != nil {
log . Error ( "Error reading file" , err )
return nil
}
} else {
2015-11-01 18:35:01 +03:00
buf , err := autogen . Asset ( "templates/kv.tmpl" )
2015-10-01 13:04:25 +03:00
if err != nil {
log . Error ( "Error reading file" , err )
}
_ , err = tmpl . Parse ( string ( buf ) )
if err != nil {
log . Error ( "Error reading file" , err )
return nil
}
}
var buffer bytes . Buffer
err := tmpl . Execute ( & buffer , templateObjects )
if err != nil {
log . Error ( "Error with kv template:" , err )
return nil
}
if _ , err := toml . Decode ( buffer . String ( ) , configuration ) ; err != nil {
log . Error ( "Error creating kv configuration:" , err )
log . Error ( buffer . String ( ) )
return nil
}
return configuration
}