consul: Adding a key listing mechanism

This commit is contained in:
Armon Dadgar 2014-04-28 16:26:31 -07:00 committed by Jack Pearkes
parent 416ff8f7d6
commit dcbb55b874
2 changed files with 147 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import (
"log"
"os"
"runtime"
"strings"
)
const (
@ -245,6 +246,7 @@ func (s *StateStore) initialize() error {
"NodeDump": MDBTables{s.nodeTable, s.serviceTable, s.checkTable},
"KVSGet": MDBTables{s.kvsTable},
"KVSList": MDBTables{s.kvsTable},
"KVSListKeys": MDBTables{s.kvsTable},
}
return nil
}
@ -902,6 +904,51 @@ func (s *StateStore) KVSList(prefix string) (uint64, structs.DirEntries, error)
return idx, ents, err
}
// KVSListKeys is used to list keys with a prefix, and up to a given seperator
func (s *StateStore) KVSListKeys(prefix, seperator string) (uint64, []string, error) {
tx, err := s.kvsTable.StartTxn(true, nil)
if err != nil {
return 0, nil, err
}
defer tx.Abort()
idx, err := s.kvsTable.LastIndexTxn(tx)
if err != nil {
return 0, nil, err
}
// Aggregate the stream
stream := make(chan interface{}, 128)
done := make(chan struct{})
var keys []string
go func() {
prefixLen := len(prefix)
last := ""
for raw := range stream {
ent := raw.(*structs.DirEntry)
after := ent.Key[prefixLen:]
// Check for the seperator
if idx := strings.Index(after, seperator); idx >= 0 {
toSep := ent.Key[:prefixLen+idx+1]
if last != toSep {
keys = append(keys, toSep)
last = toSep
}
} else {
keys = append(keys, ent.Key)
}
}
close(done)
}()
// Start the stream, and wait for completion
err = s.kvsTable.StreamTxn(stream, tx, "id_prefix", prefix)
<-done
return idx, keys, err
}
// KVSDelete is used to delete a KVS entry
func (s *StateStore) KVSDelete(index uint64, key string) error {
return s.kvsDeleteWithIndex(index, "id", key)

View File

@ -1401,6 +1401,106 @@ func TestKVS_List(t *testing.T) {
}
}
func TestKVS_ListKeys(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Should not exist
idx, keys, err := store.KVSListKeys("", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 0 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 0 {
t.Fatalf("bad: %v", keys)
}
// Create the entries
d := &structs.DirEntry{Key: "/web/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1001, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/sub/c", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1002, d); err != nil {
t.Fatalf("err: %v", err)
}
// Should list
idx, keys, err = store.KVSListKeys("", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/" {
t.Fatalf("bad: %v", keys)
}
// Should list just web
idx, keys, err = store.KVSListKeys("/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/" {
t.Fatalf("bad: %v", keys)
}
// Should list a, b, sub/
idx, keys, err = store.KVSListKeys("/web/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 3 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/a" {
t.Fatalf("bad: %v", keys)
}
if keys[1] != "/web/b" {
t.Fatalf("bad: %v", keys)
}
if keys[2] != "/web/sub/" {
t.Fatalf("bad: %v", keys)
}
// Should list c
idx, keys, err = store.KVSListKeys("/web/sub/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/sub/c" {
t.Fatalf("bad: %v", keys)
}
}
func TestKVSDeleteTree(t *testing.T) {
store, err := testStateStore()
if err != nil {