Skip to content
This repository was archived by the owner on Apr 27, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ We developed [Flæg](https://github.com/containous/flaeg) and Stært in order to
- Three native sources :
- Command line arguments using [Flæg](https://github.com/containous/flaeg) package
- TOML config file using [TOML](http://github.com/BurntSushi/toml) package
- [Key-Value Store](#kvstore) using [libkv](https://github.com/docker/libkv) and [mapstructure](https://github.com/mitchellh/mapstructure) packages
- [Key-Value Store](#kvstore) using [valkeyrie](https://github.com/abronan/valkeyrie) and [mapstructure](https://github.com/mitchellh/mapstructure) packages
- An interface to add your own sources
- Handle pointers field :
- You can give a structure of default values for pointers
Expand Down Expand Up @@ -211,7 +211,7 @@ Thank you [@debovema](https://github.com/debovema) for this work :)
## KvStore

As with Flæg and TOML sources, the configuration structure can be loaded from a Key-Value Store.
The package [libkv](https://github.com/docker/libkv) provides connection to many KV Store like `Consul`, `Etcd` or `Zookeeper`.
The package [valkeyrie](https://github.com/abronan/valkeyrie) provides connection to many KV Store like `Consul`, `Etcd` or `Zookeeper`.

The whole configuration structure is stored, using architecture like this pattern:

Expand Down
28 changes: 28 additions & 0 deletions kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,34 @@ func (kv *KvSource) ListValuedPairWithPrefix(key string) (map[string][]byte, err
return pairs, nil
}

// ReplaceConfig will replace the existing config in the KV store with
// the new config. Unused (stale) keys will be removed from the kv
// store, while existing keys remain and get updated.
func (kv *KvSource) ReplaceConfig(config interface{}) error {
existingConfigPairs, err := kv.ListValuedPairWithPrefix(kv.Prefix)

if err != nil {
return err
}

newConfigPairs := map[string]string{}
if err = collateKvRecursive(reflect.ValueOf(config), newConfigPairs, kv.Prefix); err != nil {
return err
}

// Loop over the keys in the store and remove the keys that are not present in the new config
for existingKey := range existingConfigPairs {
if _, ok := newConfigPairs[existingKey]; !ok {
err = kv.Delete(existingKey)
if err != nil {
return err
}
}
}

return kv.StoreConfig(config)
}

func convertPairs(pairs map[string][]byte) []*store.KVPair {
slicePairs := make([]*store.KVPair, len(pairs))
i := 0
Expand Down
11 changes: 10 additions & 1 deletion kv_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ func (s *Mock) Get(key string, options *store.ReadOptions) (*store.KVPair, error
}

func (s *Mock) Delete(key string) error {
return errors.New("delete not supported")
pairs := make([]*store.KVPair, 0)
for _, pair := range s.KVPairs {
if pair.Key != key {
pairs = append(pairs, pair)
}
}

s.KVPairs = pairs

return nil
}

// Exists mock
Expand Down
71 changes: 71 additions & 0 deletions kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1238,3 +1238,74 @@ func TestDecodeHookCustomMarshaller(t *testing.T) {

assert.Exactly(t, data, output)
}

func TestReplaceConfig(t *testing.T) {
kv := &KvSource{
&Mock{
KVPairs: []*store.KVPair{
{Key: "prefix/foo", Value: []byte("foo")},
{Key: "prefix/bar/baz", Value: []byte("foo")},
{Key: "prefix/bar/boo", Value: []byte("foo")},
},
WatchTreeMethod: nil,
},
"prefix",
}

config := &struct {
Foo string
}{
Foo: "bar",
}

err := kv.ReplaceConfig(config)
require.NoError(t, err)

results, err := kv.ListValuedPairWithPrefix("prefix")
require.NoError(t, err)

require.EqualValues(t, map[string][]byte{
"prefix/foo": []byte("bar"),
}, results)
}

func TestReplaceNestedConfig(t *testing.T) {
kv := &KvSource{
&Mock{
KVPairs: []*store.KVPair{
{Key: "prefix/foo", Value: []byte("foo")},
{Key: "prefix/bar/baz", Value: []byte("foo")},
{Key: "prefix/bar/boo", Value: []byte("foo")},
{Key: "prefix/bar/far/baz", Value: []byte("foo")},
},
WatchTreeMethod: nil,
},
"prefix",
}

config := &struct {
Foo string
Bar map[string]map[string]string
New string
}{
Foo: "bar",
Bar: map[string]map[string]string{
"far": {
"baz": "faz",
},
},
New: "foo",
}

err := kv.ReplaceConfig(config)
require.NoError(t, err)

results, err := kv.ListValuedPairWithPrefix("prefix")
require.NoError(t, err)

require.EqualValues(t, map[string][]byte{
"prefix/foo": []byte("bar"),
"prefix/bar/far/baz": []byte("faz"),
"prefix/new": []byte("foo"),
}, results)
}