package config
import (
"bytes"
"io"
"io/ioutil"
"github.com/xordataexchange/crypt/backend"
"github.com/xordataexchange/crypt/backend/consul"
"github.com/xordataexchange/crypt/backend/etcd"
"github.com/xordataexchange/crypt/encoding/secconf"
)
type KVPair struct {
backend.KVPair
}
type KVPairs []*KVPair
type configManager struct {
keystore []byte
store backend.Store
}
// A ConfigManager retrieves and decrypts configuration from a key/value store.
type ConfigManager interface {
Get(key string) ([]byte, error)
List(key string) (KVPairs, error)
Set(key string, value []byte) error
Watch(key string, stop chan bool) <-chan *Response
}
type standardConfigManager struct {
store backend.Store
}
func NewStandardConfigManager(client backend.Store) (ConfigManager, error) {
return standardConfigManager{client}, nil
}
func NewConfigManager(client backend.Store, keystore io.Reader) (ConfigManager, error) {
bytes, err := ioutil.ReadAll(keystore)
if err != nil {
return nil, err
}
return configManager{bytes, client}, nil
}
// NewStandardEtcdConfigManager returns a new ConfigManager backed by etcd.
func NewStandardEtcdConfigManager(machines []string) (ConfigManager, error) {
store, err := etcd.New(machines)
if err != nil {
return nil, err
}
return NewStandardConfigManager(store)
}
// NewStandardConsulConfigManager returns a new ConfigManager backed by consul.
func NewStandardConsulConfigManager(machines []string) (ConfigManager, error) {
store, err := consul.New(machines)
if err != nil {
return nil, err
}
return NewStandardConfigManager(store)
}
// NewEtcdConfigManager returns a new ConfigManager backed by etcd.
// Data will be encrypted.
func NewEtcdConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) {
store, err := etcd.New(machines)
if err != nil {
return nil, err
}
return NewConfigManager(store, keystore)
}
// NewConsulConfigManager returns a new ConfigManager backed by consul.
// Data will be encrypted.
func NewConsulConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) {
store, err := consul.New(machines)
if err != nil {
return nil, err
}
return NewConfigManager(store, keystore)
}
// Get retrieves and decodes a secconf value stored at key.
func (c configManager) Get(key string) ([]byte, error) {
value, err := c.store.Get(key)
if err != nil {
return nil, err
}
return secconf.Decode(value, bytes.NewBuffer(c.keystore))
}
// Get retrieves a value stored at key.
// convenience function, no additional value provided over
// `etcdctl`
func (c standardConfigManager) Get(key string) ([]byte, error) {
value, err := c.store.Get(key)
if err != nil {
return nil, err
}
return value, err
}
// List retrieves and decodes all secconf value stored under key.
func (c configManager) List(key string) (KVPairs, error) {
list, err := c.store.List(key)
retList := make(KVPairs, len(list))
if err != nil {
return nil, err
}
for i, kv := range list {
retList[i].Key = kv.Key
retList[i].Value, err = secconf.Decode(kv.Value, bytes.NewBuffer(c.keystore))
if err != nil {
return nil, err
}
}
return retList, nil
}
// List retrieves all values under key.
// convenience function, no additional value provided over
// `etcdctl`
func (c standardConfigManager) List(key string) (KVPairs, error) {
list, err := c.store.List(key)
retList := make(KVPairs, len(list))
if err != nil {
return nil, err
}
for i, kv := range list {
retList[i].Key = kv.Key
retList[i].Value = kv.Value
}
return retList, err
}
// Set will put a key/value into the data store
// and encode it with secconf
func (c configManager) Set(key string, value []byte) error {
encodedValue, err := secconf.Encode(value, bytes.NewBuffer(c.keystore))
if err == nil {
err = c.store.Set(key, encodedValue)
}
return err
}
// Set will put a key/value into the data store
func (c standardConfigManager) Set(key string, value []byte) error {
err := c.store.Set(key, value)
return err
}
type Response struct {
Value []byte
Error error
}
func (c configManager) Watch(key string, stop chan bool) <-chan *Response {
resp := make(chan *Response, 0)
backendResp := c.store.Watch(key, stop)
go func() {
for {
select {
case <-stop:
return
case r := <-backendResp:
if r.Error != nil {
resp <- &Response{nil, r.Error}
continue
}
value, err := secconf.Decode(r.Value, bytes.NewBuffer(c.keystore))
resp <- &Response{value, err}
}
}
}()
return resp
}
func (c standardConfigManager) Watch(key string, stop chan bool) <-chan *Response {
resp := make(chan *Response, 0)
backendResp := c.store.Watch(key, stop)
go func() {
for {
select {
case <-stop:
return
case r := <-backendResp:
if r.Error != nil {
resp <- &Response{nil, r.Error}
continue
}
resp <- &Response{r.Value, nil}
}
}
}()
return resp
}