package config import ( "bytes" "io" "io/ioutil" "github.com/bketelsen/crypt/backend" "github.com/bketelsen/crypt/backend/consul" "github.com/bketelsen/crypt/backend/etcd" "github.com/bketelsen/crypt/backend/firestore" "github.com/bketelsen/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 } // NewStandardFirestoreConfigManager returns a new ConfigManager backed by Firestore. func NewStandardFirestoreConfigManager(machines []string) (ConfigManager, error) { store, err := firestore.New(machines) if err != nil { return nil, err } return NewStandardConfigManager(store) } // 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) } // NewFirestoreConfigManager returns a new ConfigManager backed by Firestore. // Data will be encrypted. func NewFirestoreConfigManager(machines []string, keystore io.Reader) (ConfigManager, error) { store, err := firestore.New(machines) if err != nil { return nil, err } return NewConfigManager(store, keystore) } // 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] = &KVPair{} 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] = &KVPair{*kv} } 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 }