From 4ba89adb7d56c3ccf2c27469e642f468210ef6ad Mon Sep 17 00:00:00 2001 From: Ryan Uber Date: Tue, 1 Sep 2015 18:19:18 -0700 Subject: [PATCH] consul/state: adding KVSList for listing a given prefix --- consul/state/state_store.go | 26 +++++++++++++++ consul/state/state_store_test.go | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/consul/state/state_store.go b/consul/state/state_store.go index 880477432c..4a352a8112 100644 --- a/consul/state/state_store.go +++ b/consul/state/state_store.go @@ -755,6 +755,32 @@ func (s *StateStore) KVSGet(key string) (*structs.DirEntry, error) { return entry.(*structs.DirEntry), nil } +// KVSList is used to list out all keys under a given prefix. If the +// prefix is left empty, all keys in the KVS will be returned. The +// returned index is the max index of the returned kvs entries. +func (s *StateStore) KVSList(prefix string) (uint64, []string, error) { + tx := s.db.Txn(false) + defer tx.Abort() + + // Query the prefix and list the available keys + entries, err := tx.Get("kvs", "id_prefix", prefix) + if err != nil { + return 0, nil, fmt.Errorf("failed kvs lookup: %s", err) + } + + // Gather all of the keys found in the store + var keys []string + var lindex uint64 + for entry := entries.Next(); entry != nil; entry = entries.Next() { + e := entry.(*structs.DirEntry) + keys = append(keys, e.Key) + if e.ModifyIndex > lindex { + lindex = e.ModifyIndex + } + } + return lindex, keys, nil +} + // KVSDelete is used to perform a shallow delete on a single key in the // the state store. func (s *StateStore) KVSDelete(idx uint64, key string) error { diff --git a/consul/state/state_store_test.go b/consul/state/state_store_test.go index 7008f4ccd9..e2e08d3a79 100644 --- a/consul/state/state_store_test.go +++ b/consul/state/state_store_test.go @@ -883,3 +883,59 @@ func TestStateStore_KVSDelete(t *testing.T) { t.Fatalf("bad index: %d", idx) } } + +func TestStateStore_KVSList(t *testing.T) { + s := testStateStore(t) + + // Listing an empty KVS returns nothing + idx, keys, err := s.KVSList("") + if err != nil { + t.Fatalf("err: %s", err) + } + if idx != 0 { + t.Fatalf("bad index: %d", idx) + } + if keys != nil { + t.Fatalf("expected nil, got: %#v", keys) + } + + // Create some KVS entries + testSetKey(t, s, 1, "foo", "foo") + testSetKey(t, s, 2, "foo/bar", "bar") + testSetKey(t, s, 3, "foo/bar/zip", "zip") + testSetKey(t, s, 4, "foo/bar/zip/zorp", "zorp") + testSetKey(t, s, 5, "foo/bar/baz", "baz") + + // List out all of the keys + idx, keys, err = s.KVSList("") + if err != nil { + t.Fatalf("err: %s", err) + } + + // Check the index + if idx != 5 { + t.Fatalf("bad index: %d", idx) + } + + // Check that all of the keys were returned + if n := len(keys); n != 5 { + t.Fatalf("expected 5 kvs entries, got: %d", n) + } + + // Try listing with a provided prefix + idx, keys, err = s.KVSList("foo/bar/zip") + if err != nil { + t.Fatalf("err: %s", err) + } + if idx != 4 { + t.Fatalf("bad index: %d", idx) + } + + // Check that only the keys in the prefix were returned + if n := len(keys); n != 2 { + t.Fatalf("expected 2 kvs entries, got: %d", n) + } + if keys[0] != "foo/bar/zip" || keys[1] != "foo/bar/zip/zorp" { + t.Fatalf("bad: %#v", keys) + } +}