Skip to content

Commit dc43fa5

Browse files
committed
WIP
1 parent 9b894e8 commit dc43fa5

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

libkv/loader.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package libkv
2+
3+
import (
4+
"strings"
5+
6+
"github.com/docker/libkv/store"
7+
8+
"github.com/go-mixins/loader"
9+
)
10+
11+
// Loader implements loader.Loader
12+
type Loader struct {
13+
store kvStore
14+
prefix string
15+
changes chan struct{}
16+
}
17+
18+
var _ loader.Loader = (*Loader)(nil)
19+
20+
type kvStore interface {
21+
// Get a value given its key
22+
Get(key string) (*store.KVPair, error)
23+
// List the content of a given prefix
24+
List(directory string) ([]*store.KVPair, error)
25+
// Close the store connection
26+
Close()
27+
}
28+
29+
// Close closes underlying changes channel
30+
func (l *Loader) Close() error {
31+
l.store.Close()
32+
close(l.changes)
33+
return nil
34+
}
35+
36+
// Changes provides source of config change events. For environment variables
37+
// there will never be any.
38+
func (l *Loader) Changes() <-chan struct{} {
39+
return l.changes
40+
}
41+
42+
// New creates loader initialized with KV store prefix
43+
func New(prefix string, store kvStore) *Loader {
44+
return &Loader{
45+
store: store,
46+
prefix: strings.Trim(prefix, "/") + "/",
47+
changes: make(chan struct{}),
48+
}
49+
}

libkv/loader_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package libkv_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/docker/libkv/store"
7+
"github.com/go-mixins/loader/libkv"
8+
)
9+
10+
type kvMock struct{}
11+
12+
func (kvm *kvMock) Close() {}
13+
14+
func (kvm *kvMock) Get(key string) (res *store.KVPair, err error) {
15+
switch key {
16+
case "unknown":
17+
err = store.ErrKeyNotFound
18+
case "a/b/c/":
19+
res = &store.KVPair{
20+
Key: key,
21+
Value: []byte("1"),
22+
}
23+
}
24+
return
25+
}
26+
27+
func (kvm *kvMock) List(directory string) (res []*store.KVPair, err error) {
28+
switch directory {
29+
case "a/":
30+
res = []*store.KVPair{
31+
&store.KVPair{Key: "a/b/"},
32+
}
33+
case "a/b/":
34+
res = []*store.KVPair{
35+
&store.KVPair{Key: "a/b/c/"},
36+
}
37+
case "a/b/c/":
38+
res = []*store.KVPair{}
39+
default:
40+
err = store.ErrKeyNotFound
41+
}
42+
return
43+
}
44+
45+
func TestLoad(t *testing.T) {
46+
loader := libkv.New("a", &kvMock{})
47+
var dest struct {
48+
B struct {
49+
C int
50+
}
51+
}
52+
defer loader.Close()
53+
err := loader.Load(&dest)
54+
if err != nil {
55+
t.Fatalf("%+v", err)
56+
}
57+
t.Logf("%+v", dest)
58+
}

libkv/struct.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package libkv
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/docker/libkv/store"
8+
"github.com/go-mixins/loader"
9+
"github.com/mitchellh/mapstructure"
10+
)
11+
12+
// Load loads the target from environment
13+
func (l *Loader) Load(dest interface{}) error {
14+
cfg := &mapstructure.DecoderConfig{
15+
Result: dest,
16+
WeaklyTypedInput: true,
17+
}
18+
decoder, err := mapstructure.NewDecoder(cfg)
19+
if err != nil {
20+
return loader.Errors.Wrap(err, "creating map decoder")
21+
}
22+
data, err := l.getRecursive(l.prefix)
23+
if err != nil {
24+
return err
25+
}
26+
fmt.Printf("*** %+v\n", data)
27+
return loader.Errors.Wrap(decoder.Decode(data), "decoding values")
28+
}
29+
30+
func put(dest map[string]interface{}, prefix string, val interface{}) {
31+
path := strings.Split(strings.Trim(prefix, "/"), "/")
32+
fmt.Printf("&&& %s %#v\n", prefix, path)
33+
if len(path) == 0 {
34+
return
35+
}
36+
for i := len(path) - 1; i >= 1; i-- {
37+
val = map[string]interface{}{
38+
path[i]: val,
39+
}
40+
}
41+
dest[path[0]] = val
42+
}
43+
44+
func (l *Loader) getRecursive(prefix string) (interface{}, error) {
45+
fmt.Printf("### %s\n", prefix)
46+
pairs, err := l.store.List(prefix)
47+
if err == store.ErrKeyNotFound {
48+
return nil, nil
49+
}
50+
if err != nil {
51+
return nil, loader.Errors.Wrap(err, "getting KV list")
52+
}
53+
if len(pairs) == 0 {
54+
val, err := l.store.Get(prefix)
55+
if err != nil {
56+
return nil, loader.Errors.Wrap(err, "getting key value")
57+
}
58+
if val != nil {
59+
return string(val.Value), nil
60+
}
61+
return nil, nil
62+
}
63+
res := make(map[string]interface{})
64+
for _, p := range pairs {
65+
val, err := l.getRecursive(p.Key)
66+
if err != nil {
67+
return res, err
68+
}
69+
put(res, strings.TrimPrefix(p.Key, prefix), val)
70+
}
71+
return res, nil
72+
}

0 commit comments

Comments
 (0)