Browse Source

Merge pull request #1335 from unihorn/174

etcdserver/etcdhttp: store location adjustment
Yicheng Qin 11 years ago
parent
commit
f26bb6ad44

+ 1 - 1
etcdserver/cluster_store.go

@@ -77,7 +77,7 @@ func (s *clusterStore) Add(m Member) {
 func (s *clusterStore) Get() Cluster {
 func (s *clusterStore) Get() Cluster {
 	c := NewCluster()
 	c := NewCluster()
 	c.id = s.id
 	c.id = s.id
-	e, err := s.Store.Get(membersKVPrefix, true, true)
+	e, err := s.Store.Get(storeMembersPrefix, true, true)
 	if err != nil {
 	if err != nil {
 		if v, ok := err.(*etcdErr.Error); ok && v.ErrorCode == etcdErr.EcodeKeyNotFound {
 		if v, ok := err.(*etcdErr.Error); ok && v.ErrorCode == etcdErr.EcodeKeyNotFound {
 			return *c
 			return *c

+ 4 - 3
etcdserver/cluster_store_test.go

@@ -17,6 +17,7 @@
 package etcdserver
 package etcdserver
 
 
 import (
 import (
+	"path"
 	"reflect"
 	"reflect"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -34,7 +35,7 @@ func TestClusterStoreAdd(t *testing.T) {
 		{
 		{
 			name: "Create",
 			name: "Create",
 			params: []interface{}{
 			params: []interface{}{
-				membersKVPrefix + "1/raftAttributes",
+				path.Join(storeMembersPrefix, "1", "raftAttributes"),
 				false,
 				false,
 				`{"PeerURLs":null}`,
 				`{"PeerURLs":null}`,
 				false,
 				false,
@@ -44,7 +45,7 @@ func TestClusterStoreAdd(t *testing.T) {
 		{
 		{
 			name: "Create",
 			name: "Create",
 			params: []interface{}{
 			params: []interface{}{
-				membersKVPrefix + "1/attributes",
+				path.Join(storeMembersPrefix, "1", "attributes"),
 				false,
 				false,
 				`{"Name":"node1","ClientURLs":null}`,
 				`{"Name":"node1","ClientURLs":null}`,
 				false,
 				false,
@@ -113,7 +114,7 @@ func TestClusterStoreDelete(t *testing.T) {
 	cs.Add(newTestMember(1, nil, "node1", nil))
 	cs.Add(newTestMember(1, nil, "node1", nil))
 	cs.Remove(1)
 	cs.Remove(1)
 
 
-	wdeletes := []string{membersKVPrefix + "1"}
+	wdeletes := []string{path.Join(storeMembersPrefix, "1")}
 	if !reflect.DeepEqual(st.deletes, wdeletes) {
 	if !reflect.DeepEqual(st.deletes, wdeletes) {
 		t.Errorf("deletes = %v, want %v", st.deletes, wdeletes)
 		t.Errorf("deletes = %v, want %v", st.deletes, wdeletes)
 	}
 	}

+ 35 - 12
etcdserver/etcdhttp/http.go

@@ -24,6 +24,7 @@ import (
 	"log"
 	"log"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
+	"path"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
@@ -107,7 +108,7 @@ func (h serverHandler) serveKeys(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
 	ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
 	defer cancel()
 	defer cancel()
 
 
-	rr, err := parseRequest(r, etcdserver.GenID(), clockwork.NewRealClock())
+	rr, err := parseKeyRequest(r, etcdserver.GenID(), clockwork.NewRealClock())
 	if err != nil {
 	if err != nil {
 		writeError(w, err)
 		writeError(w, err)
 		return
 		return
@@ -121,14 +122,14 @@ func (h serverHandler) serveKeys(w http.ResponseWriter, r *http.Request) {
 
 
 	switch {
 	switch {
 	case resp.Event != nil:
 	case resp.Event != nil:
-		if err := writeEvent(w, resp.Event, h.timer); err != nil {
+		if err := writeKeyEvent(w, resp.Event, h.timer); err != nil {
 			// Should never be reached
 			// Should never be reached
 			log.Printf("error writing event: %v", err)
 			log.Printf("error writing event: %v", err)
 		}
 		}
 	case resp.Watcher != nil:
 	case resp.Watcher != nil:
 		ctx, cancel := context.WithTimeout(context.Background(), defaultWatchTimeout)
 		ctx, cancel := context.WithTimeout(context.Background(), defaultWatchTimeout)
 		defer cancel()
 		defer cancel()
-		handleWatch(ctx, w, resp.Watcher, rr.Stream, h.timer)
+		handleKeyWatch(ctx, w, resp.Watcher, rr.Stream, h.timer)
 	default:
 	default:
 		writeError(w, errors.New("received response with no Event/Watcher!"))
 		writeError(w, errors.New("received response with no Event/Watcher!"))
 	}
 	}
@@ -248,10 +249,10 @@ func (h serverHandler) serveRaft(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusNoContent)
 	w.WriteHeader(http.StatusNoContent)
 }
 }
 
 
-// parseRequest converts a received http.Request to a server Request,
-// performing validation of supplied fields as appropriate.
+// parseKeyRequest converts a received http.Request on keysPrefix to
+// a server Request, performing validation of supplied fields as appropriate.
 // If any validation fails, an empty Request and non-nil error is returned.
 // If any validation fails, an empty Request and non-nil error is returned.
-func parseRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdserverpb.Request, error) {
+func parseKeyRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdserverpb.Request, error) {
 	emptyReq := etcdserverpb.Request{}
 	emptyReq := etcdserverpb.Request{}
 
 
 	err := r.ParseForm()
 	err := r.ParseForm()
@@ -268,7 +269,7 @@ func parseRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdserver
 			"incorrect key prefix",
 			"incorrect key prefix",
 		)
 		)
 	}
 	}
-	p := r.URL.Path[len(keysPrefix):]
+	p := path.Join(etcdserver.StoreKeysPrefix, r.URL.Path[len(keysPrefix):])
 
 
 	var pIdx, wIdx uint64
 	var pIdx, wIdx uint64
 	if pIdx, err = getUint64(r.Form, "prevIndex"); err != nil {
 	if pIdx, err = getUint64(r.Form, "prevIndex"); err != nil {
@@ -425,10 +426,10 @@ func writeError(w http.ResponseWriter, err error) {
 	}
 	}
 }
 }
 
 
-// writeEvent serializes a single Event and writes the resulting
-// JSON to the given ResponseWriter, along with the appropriate
-// headers
-func writeEvent(w http.ResponseWriter, ev *store.Event, rt etcdserver.RaftTimer) error {
+// writeKeyEvent trims the prefix of key path in a single Event under
+// StoreKeysPrefix, serializes it and writes the resulting JSON to the given
+// ResponseWriter, along with the appropriate headers.
+func writeKeyEvent(w http.ResponseWriter, ev *store.Event, rt etcdserver.RaftTimer) error {
 	if ev == nil {
 	if ev == nil {
 		return errors.New("cannot write empty Event!")
 		return errors.New("cannot write empty Event!")
 	}
 	}
@@ -441,10 +442,11 @@ func writeEvent(w http.ResponseWriter, ev *store.Event, rt etcdserver.RaftTimer)
 		w.WriteHeader(http.StatusCreated)
 		w.WriteHeader(http.StatusCreated)
 	}
 	}
 
 
+	ev = trimEventPrefix(ev, etcdserver.StoreKeysPrefix)
 	return json.NewEncoder(w).Encode(ev)
 	return json.NewEncoder(w).Encode(ev)
 }
 }
 
 
-func handleWatch(ctx context.Context, w http.ResponseWriter, wa store.Watcher, stream bool, rt etcdserver.RaftTimer) {
+func handleKeyWatch(ctx context.Context, w http.ResponseWriter, wa store.Watcher, stream bool, rt etcdserver.RaftTimer) {
 	defer wa.Remove()
 	defer wa.Remove()
 	ech := wa.EventChan()
 	ech := wa.EventChan()
 	var nch <-chan bool
 	var nch <-chan bool
@@ -476,6 +478,7 @@ func handleWatch(ctx context.Context, w http.ResponseWriter, wa store.Watcher, s
 				// send to the client in time. Then we simply end streaming.
 				// send to the client in time. Then we simply end streaming.
 				return
 				return
 			}
 			}
+			ev = trimEventPrefix(ev, etcdserver.StoreKeysPrefix)
 			if err := json.NewEncoder(w).Encode(ev); err != nil {
 			if err := json.NewEncoder(w).Encode(ev); err != nil {
 				// Should never be reached
 				// Should never be reached
 				log.Printf("error writing event: %v\n", err)
 				log.Printf("error writing event: %v\n", err)
@@ -502,3 +505,23 @@ func allowMethod(w http.ResponseWriter, m string, ms ...string) bool {
 	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
 	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
 	return false
 	return false
 }
 }
+
+func trimEventPrefix(ev *store.Event, prefix string) *store.Event {
+	if ev == nil {
+		return nil
+	}
+	ev.Node = trimNodeExternPrefix(ev.Node, prefix)
+	ev.PrevNode = trimNodeExternPrefix(ev.PrevNode, prefix)
+	return ev
+}
+
+func trimNodeExternPrefix(n *store.NodeExtern, prefix string) *store.NodeExtern {
+	if n == nil {
+		return nil
+	}
+	n.Key = strings.TrimPrefix(n.Key, prefix)
+	for _, nn := range n.Nodes {
+		nn = trimNodeExternPrefix(nn, prefix)
+	}
+	return n
+}

+ 102 - 22
etcdserver/etcdhttp/http_test.go

@@ -211,7 +211,7 @@ func TestBadParseRequest(t *testing.T) {
 		},
 		},
 	}
 	}
 	for i, tt := range tests {
 	for i, tt := range tests {
-		got, err := parseRequest(tt.in, 1234, clockwork.NewFakeClock())
+		got, err := parseKeyRequest(tt.in, 1234, clockwork.NewFakeClock())
 		if err == nil {
 		if err == nil {
 			t.Errorf("#%d: unexpected nil error!", i)
 			t.Errorf("#%d: unexpected nil error!", i)
 			continue
 			continue
@@ -244,7 +244,7 @@ func TestGoodParseRequest(t *testing.T) {
 			etcdserverpb.Request{
 			etcdserverpb.Request{
 				ID:     1234,
 				ID:     1234,
 				Method: "GET",
 				Method: "GET",
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -258,7 +258,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:     1234,
 				ID:     1234,
 				Method: "PUT",
 				Method: "PUT",
 				Val:    "some_value",
 				Val:    "some_value",
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -272,7 +272,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				PrevIndex: 98765,
 				PrevIndex: 98765,
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -286,7 +286,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				Recursive: true,
 				Recursive: true,
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -300,7 +300,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:     1234,
 				ID:     1234,
 				Method: "PUT",
 				Method: "PUT",
 				Sorted: true,
 				Sorted: true,
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -310,7 +310,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:     1234,
 				ID:     1234,
 				Method: "GET",
 				Method: "GET",
 				Wait:   true,
 				Wait:   true,
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -319,7 +319,7 @@ func TestGoodParseRequest(t *testing.T) {
 			etcdserverpb.Request{
 			etcdserverpb.Request{
 				ID:         1234,
 				ID:         1234,
 				Method:     "GET",
 				Method:     "GET",
-				Path:       "/foo",
+				Path:       path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 				Expiration: 0,
 				Expiration: 0,
 			},
 			},
 		},
 		},
@@ -329,7 +329,7 @@ func TestGoodParseRequest(t *testing.T) {
 			etcdserverpb.Request{
 			etcdserverpb.Request{
 				ID:         1234,
 				ID:         1234,
 				Method:     "GET",
 				Method:     "GET",
-				Path:       "/foo",
+				Path:       path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 				Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
 				Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
 			},
 			},
 		},
 		},
@@ -339,7 +339,7 @@ func TestGoodParseRequest(t *testing.T) {
 			etcdserverpb.Request{
 			etcdserverpb.Request{
 				ID:         1234,
 				ID:         1234,
 				Method:     "GET",
 				Method:     "GET",
-				Path:       "/foo",
+				Path:       path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 				Expiration: fc.Now().UnixNano(),
 				Expiration: fc.Now().UnixNano(),
 			},
 			},
 		},
 		},
@@ -350,7 +350,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:     1234,
 				ID:     1234,
 				Method: "GET",
 				Method: "GET",
 				Dir:    true,
 				Dir:    true,
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -360,7 +360,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:     1234,
 				ID:     1234,
 				Method: "GET",
 				Method: "GET",
 				Dir:    false,
 				Dir:    false,
-				Path:   "/foo",
+				Path:   path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -374,7 +374,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				PrevExist: boolp(true),
 				PrevExist: boolp(true),
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		{
 		{
@@ -388,7 +388,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				PrevExist: boolp(false),
 				PrevExist: boolp(false),
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		// mix various fields
 		// mix various fields
@@ -408,7 +408,7 @@ func TestGoodParseRequest(t *testing.T) {
 				PrevExist: boolp(true),
 				PrevExist: boolp(true),
 				PrevValue: "previous value",
 				PrevValue: "previous value",
 				Val:       "some value",
 				Val:       "some value",
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		// query parameters should be used if given
 		// query parameters should be used if given
@@ -422,7 +422,7 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				PrevValue: "woof",
 				PrevValue: "woof",
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 		// but form values should take precedence over query parameters
 		// but form values should take precedence over query parameters
@@ -438,13 +438,13 @@ func TestGoodParseRequest(t *testing.T) {
 				ID:        1234,
 				ID:        1234,
 				Method:    "PUT",
 				Method:    "PUT",
 				PrevValue: "miaow",
 				PrevValue: "miaow",
-				Path:      "/foo",
+				Path:      path.Join(etcdserver.StoreKeysPrefix, "/foo"),
 			},
 			},
 		},
 		},
 	}
 	}
 
 
 	for i, tt := range tests {
 	for i, tt := range tests {
-		got, err := parseRequest(tt.in, 1234, fc)
+		got, err := parseKeyRequest(tt.in, 1234, fc)
 		if err != nil {
 		if err != nil {
 			t.Errorf("#%d: err = %v, want %v", i, err, nil)
 			t.Errorf("#%d: err = %v, want %v", i, err, nil)
 		}
 		}
@@ -526,7 +526,7 @@ func (drt dummyRaftTimer) Term() uint64  { return uint64(5) }
 func TestWriteEvent(t *testing.T) {
 func TestWriteEvent(t *testing.T) {
 	// nil event should not panic
 	// nil event should not panic
 	rw := httptest.NewRecorder()
 	rw := httptest.NewRecorder()
-	writeEvent(rw, nil, dummyRaftTimer{})
+	writeKeyEvent(rw, nil, dummyRaftTimer{})
 	h := rw.Header()
 	h := rw.Header()
 	if len(h) > 0 {
 	if len(h) > 0 {
 		t.Fatalf("unexpected non-empty headers: %#v", h)
 		t.Fatalf("unexpected non-empty headers: %#v", h)
@@ -569,7 +569,7 @@ func TestWriteEvent(t *testing.T) {
 
 
 	for i, tt := range tests {
 	for i, tt := range tests {
 		rw := httptest.NewRecorder()
 		rw := httptest.NewRecorder()
-		writeEvent(rw, tt.ev, dummyRaftTimer{})
+		writeKeyEvent(rw, tt.ev, dummyRaftTimer{})
 		if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
 		if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
 			t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
 			t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
 		}
 		}
@@ -1240,7 +1240,7 @@ func TestHandleWatch(t *testing.T) {
 		}
 		}
 		tt.doToChan(wa.echan)
 		tt.doToChan(wa.echan)
 
 
-		handleWatch(tt.getCtx(), rw, wa, false, dummyRaftTimer{})
+		handleKeyWatch(tt.getCtx(), rw, wa, false, dummyRaftTimer{})
 
 
 		wcode := http.StatusOK
 		wcode := http.StatusOK
 		wct := "application/json"
 		wct := "application/json"
@@ -1295,7 +1295,7 @@ func TestHandleWatchStreaming(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	ctx, cancel := context.WithCancel(context.Background())
 	done := make(chan struct{})
 	done := make(chan struct{})
 	go func() {
 	go func() {
-		handleWatch(ctx, rw, wa, true, dummyRaftTimer{})
+		handleKeyWatch(ctx, rw, wa, true, dummyRaftTimer{})
 		close(done)
 		close(done)
 	}()
 	}()
 
 
@@ -1561,6 +1561,86 @@ func TestServeAdminMembersDelete(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestTrimEventPrefix(t *testing.T) {
+	pre := "/abc"
+	tests := []struct {
+		ev  *store.Event
+		wev *store.Event
+	}{
+		{
+			nil,
+			nil,
+		},
+		{
+			&store.Event{},
+			&store.Event{},
+		},
+		{
+			&store.Event{Node: &store.NodeExtern{Key: "/abc/def"}},
+			&store.Event{Node: &store.NodeExtern{Key: "/def"}},
+		},
+		{
+			&store.Event{PrevNode: &store.NodeExtern{Key: "/abc/ghi"}},
+			&store.Event{PrevNode: &store.NodeExtern{Key: "/ghi"}},
+		},
+		{
+			&store.Event{
+				Node:     &store.NodeExtern{Key: "/abc/def"},
+				PrevNode: &store.NodeExtern{Key: "/abc/ghi"},
+			},
+			&store.Event{
+				Node:     &store.NodeExtern{Key: "/def"},
+				PrevNode: &store.NodeExtern{Key: "/ghi"},
+			},
+		},
+	}
+	for i, tt := range tests {
+		ev := trimEventPrefix(tt.ev, pre)
+		if !reflect.DeepEqual(ev, tt.wev) {
+			t.Errorf("#%d: event = %+v, want %+v", i, ev, tt.wev)
+		}
+	}
+}
+
+func TestTrimNodeExternPrefix(t *testing.T) {
+	pre := "/abc"
+	tests := []struct {
+		n  *store.NodeExtern
+		wn *store.NodeExtern
+	}{
+		{
+			nil,
+			nil,
+		},
+		{
+			&store.NodeExtern{Key: "/abc/def"},
+			&store.NodeExtern{Key: "/def"},
+		},
+		{
+			&store.NodeExtern{
+				Key: "/abc/def",
+				Nodes: []*store.NodeExtern{
+					{Key: "/abc/def/1"},
+					{Key: "/abc/def/2"},
+				},
+			},
+			&store.NodeExtern{
+				Key: "/def",
+				Nodes: []*store.NodeExtern{
+					{Key: "/def/1"},
+					{Key: "/def/2"},
+				},
+			},
+		},
+	}
+	for i, tt := range tests {
+		n := trimNodeExternPrefix(tt.n, pre)
+		if !reflect.DeepEqual(n, tt.wn) {
+			t.Errorf("#%d: node = %+v, want %+v", i, n, tt.wn)
+		}
+	}
+}
+
 type fakeCluster struct {
 type fakeCluster struct {
 	members []etcdserver.Member
 	members []etcdserver.Member
 }
 }

+ 1 - 3
etcdserver/member.go

@@ -28,8 +28,6 @@ import (
 	"github.com/coreos/etcd/pkg/types"
 	"github.com/coreos/etcd/pkg/types"
 )
 )
 
 
-const membersKVPrefix = "/_etcd/members/"
-
 // RaftAttributes represents the raft related attributes of an etcd member.
 // RaftAttributes represents the raft related attributes of an etcd member.
 type RaftAttributes struct {
 type RaftAttributes struct {
 	// TODO(philips): ensure these are URLs
 	// TODO(philips): ensure these are URLs
@@ -71,7 +69,7 @@ func newMember(name string, peerURLs types.URLs, now *time.Time) *Member {
 }
 }
 
 
 func (m Member) storeKey() string {
 func (m Member) storeKey() string {
-	return path.Join(membersKVPrefix, idAsHex(m.ID))
+	return path.Join(storeMembersPrefix, idAsHex(m.ID))
 }
 }
 
 
 func parseMemberID(key string) uint64 {
 func parseMemberID(key string) uint64 {

+ 6 - 0
etcdserver/server.go

@@ -22,6 +22,7 @@ import (
 	"log"
 	"log"
 	"math/rand"
 	"math/rand"
 	"os"
 	"os"
+	"path"
 	"strconv"
 	"strconv"
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
@@ -47,6 +48,9 @@ const (
 	DefaultSnapCount   = 10000
 	DefaultSnapCount   = 10000
 	// TODO: calculate based on heartbeat interval
 	// TODO: calculate based on heartbeat interval
 	defaultPublishRetryInterval = 5 * time.Second
 	defaultPublishRetryInterval = 5 * time.Second
+
+	StoreAdminPrefix = "/0"
+	StoreKeysPrefix  = "/1"
 )
 )
 
 
 var (
 var (
@@ -55,6 +59,8 @@ var (
 	ErrIDRemoved     = errors.New("etcdserver: ID removed")
 	ErrIDRemoved     = errors.New("etcdserver: ID removed")
 	ErrIDExists      = errors.New("etcdserver: ID exists")
 	ErrIDExists      = errors.New("etcdserver: ID exists")
 	ErrIDNotFound    = errors.New("etcdserver: ID not found")
 	ErrIDNotFound    = errors.New("etcdserver: ID not found")
+
+	storeMembersPrefix = path.Join(StoreAdminPrefix, "members")
 )
 )
 
 
 func init() {
 func init() {