traefik/consul.go
Thorhallur Sverrisson f534d8817f Adding caching and merging of configurations
Configurations are now cached from each provider separately so that
we can merge them together when one provider has changed config.

The Web module also returns full config info through the HTML call,
but REST API is working on a separate web configuration that is
sent in just like any other.
2015-09-23 11:25:11 +00:00

165 lines
3.9 KiB
Go

package main
import (
"bytes"
"net/http"
"strings"
"text/template"
"github.com/BurntSushi/toml"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/hashicorp/consul/api"
)
type Key struct {
Value string
}
type ConsulProvider struct {
Watch bool
Endpoint string
Prefix string
Filename string
consulClient *api.Client
}
var kvClient *api.KV
var ConsulFuncMap = template.FuncMap{
"List": func(keys ...string) []string {
joinedKeys := strings.Join(keys, "")
keysPairs, _, err := kvClient.Keys(joinedKeys, "/", nil)
if err != nil {
log.Error("Error getting keys ", joinedKeys, err)
return nil
}
keysPairs = fun.Filter(func(key string) bool {
if key == joinedKeys {
return false
}
return true
}, keysPairs).([]string)
return keysPairs
},
"Get": func(keys ...string) string {
joinedKeys := strings.Join(keys, "")
keyPair, _, err := kvClient.Get(joinedKeys, nil)
if err != nil {
log.Error("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)-2]
},
}
func NewConsulProvider() *ConsulProvider {
consulProvider := new(ConsulProvider)
// default values
consulProvider.Watch = true
consulProvider.Prefix = "traefik"
return consulProvider
}
func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) {
config := &api.Config{
Address: provider.Endpoint,
Scheme: "http",
HttpClient: http.DefaultClient,
}
consulClient, _ := api.NewClient(config)
provider.consulClient = consulClient
if provider.Watch {
keypairs, meta, err := consulClient.KV().Keys("", "", nil)
if keypairs == nil {
log.Error("Key was not found")
} else if err != nil {
log.Error("Error connecting to consul %s", err)
} else {
var waitIndex uint64
waitIndex = meta.LastIndex
go func() {
for {
opts := api.QueryOptions{
WaitIndex: waitIndex,
}
keypairs, meta, err := consulClient.KV().Keys("", "", &opts)
if keypairs == nil {
log.Error("Key was not found")
} else if err != nil {
log.Error("Error connecting to consul %s", err)
} else {
waitIndex = meta.LastIndex
configuration := provider.loadConsulConfig()
if configuration != nil {
configurationChan <- configMessage{"consul", configuration}
}
}
}
}()
}
}
configuration := provider.loadConsulConfig()
configurationChan <- configMessage{"consul", configuration}
}
func (provider *ConsulProvider) loadConsulConfig() *Configuration {
configuration := new(Configuration)
services := []*api.CatalogService{}
kvClient = provider.consulClient.KV()
servicesName, _, _ := provider.consulClient.Catalog().Services(nil)
for serviceName := range servicesName {
catalogServices, _, _ := provider.consulClient.Catalog().Service(serviceName, "", nil)
for _, catalogService := range catalogServices {
services = append(services, catalogService)
}
}
templateObjects := struct {
Services []*api.CatalogService
}{
services,
}
tmpl := template.New(provider.Filename).Funcs(ConsulFuncMap)
if len(provider.Filename) > 0 {
_, err := tmpl.ParseFiles(provider.Filename)
if err != nil {
log.Error("Error reading file", err)
return nil
}
} else {
buf, err := Asset("providerTemplates/consul.tmpl")
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 consul template:", err)
return nil
}
if _, err := toml.Decode(buffer.String(), configuration); err != nil {
log.Error("Error creating consul configuration:", err)
return nil
}
return configuration
}