Browse Source

add list feature

Xiang Li 12 years ago
parent
commit
93c96b3813
6 changed files with 127 additions and 9 deletions
  1. 15 0
      command.go
  2. 1 0
      etcd.go
  3. 26 0
      handlers.go
  4. 23 1
      store/store.go
  5. 46 8
      store/tree.go
  6. 16 0
      store/tree_store_test.go

+ 15 - 0
command.go

@@ -61,6 +61,21 @@ func (c *GetCommand) GeneratePath() string {
 	return "get/" + c.Key
 }
 
+// List command
+type ListCommand struct {
+	Prefix string `json:"prefix"`
+}
+
+// The name of the command in the log
+func (c *ListCommand) CommandName() string {
+	return "list"
+}
+
+// Set the value of key to value
+func (c *ListCommand) Apply(server *raft.Server) (interface{}, error) {
+	return store.List(c.Prefix)
+}
+
 // Delete command
 type DeleteCommand struct {
 	Key string `json:"key"`

+ 1 - 0
etcd.go

@@ -318,6 +318,7 @@ func startClientTransport(port int, st int) {
 	// external commands
 	http.HandleFunc("/v1/keys/", Multiplexer)
 	http.HandleFunc("/v1/watch/", WatchHttpHandler)
+	http.HandleFunc("/v1/list/", ListHttpHandler)
 	http.HandleFunc("/master", MasterHttpHandler)
 
 	switch st {

+ 26 - 0
handlers.go

@@ -231,6 +231,32 @@ func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 
 }
 
+func ListHttpHandler(w http.ResponseWriter, req *http.Request) {
+	prefix := req.URL.Path[len("/v1/list/"):]
+
+	debug("[recv] GET http://%v/v1/list/%s", server.Name(), prefix)
+
+	command := &ListCommand{}
+	command.Prefix = prefix
+
+	if body, err := command.Apply(server); err != nil {
+		warn("raftd: Unable to write file: %v", err)
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	} else {
+		w.WriteHeader(http.StatusOK)
+
+		body, ok := body.([]byte)
+		if !ok {
+			panic("wrong type")
+		}
+
+		w.Write(body)
+		return
+	}
+
+}
+
 func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/watch/"):]
 

+ 23 - 1
store/store.go

@@ -70,11 +70,17 @@ type Response struct {
 	Expiration time.Time `json:"expiration"`
 
 	// countdown until expiration in seconds
-	TTL int64 `json:"TTL"`
+	TTL int64 `json:"ttl"`
 
 	Index uint64 `json:"index"`
 }
 
+type ListNode struct {
+	Key 	string
+	Value   string
+	Type    string
+}
+
 // make a new stroe
 func CreateStore(max int) *Store {
 	s = new(Store)
@@ -303,6 +309,22 @@ func Get(key string) Response {
 	}
 }
 
+// // List all the item in the prefix
+func List(prefix string) ([]byte, error) {
+	nodes, keys, dirs, ok := s.Tree.list(prefix)
+
+	var ln []ListNode
+
+	if ok {
+		ln = make([]ListNode, len(nodes))
+		for i := 0; i < len(nodes); i++ {
+			ln[i] = ListNode{keys[i], nodes[i].Value, dirs[i]}
+		}
+	}
+
+	return json.Marshal(ln)
+}
+
 // delete the key
 func Delete(key string, index uint64) ([]byte, error) {
 	//update index

+ 46 - 8
store/tree.go

@@ -36,7 +36,7 @@ func (s tnWithKeySlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
 var emptyNode = Node{".", PERMANENT, nil}
 
 // set the key to value, return the old value if the key exists 
-func (s *tree) set(key string, value Node) bool {
+func (t *tree) set(key string, value Node) bool {
 	key = "/" + key
 	key = path.Clean(key)
 
@@ -45,7 +45,7 @@ func (s *tree) set(key string, value Node) bool {
 
 	//fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes))
 
-	nodeMap := s.Root.NodeMap
+	nodeMap := t.Root.NodeMap
 
 	i := 0
 	newDir := false
@@ -94,8 +94,8 @@ func (s *tree) set(key string, value Node) bool {
 
 }
 
-// get the node of the key
-func (s *tree) get(key string) (Node, bool) {
+// use internally to get the internal tree node 
+func (t *tree)internalGet(key string) (*treeNode, bool) {
 	key = "/" + key
 	key = path.Clean(key)
 
@@ -104,29 +104,67 @@ func (s *tree) get(key string) (Node, bool) {
 
 	//fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes))
 
-	nodeMap := s.Root.NodeMap
+	nodeMap := t.Root.NodeMap
 		
 	var i int
 
 	for i = 0; i < len(nodes) - 1; i++ {
 		node, ok := nodeMap[nodes[i]]
 		if !ok || !node.Dir {
-			return emptyNode, false
+			return nil, false
 		}
 		nodeMap = node.NodeMap
 	}
 
 	treeNode, ok := nodeMap[nodes[i]]
+	if ok {
+		return treeNode, ok
+	} else {
+		return nil, ok
+	}
+} 
+
+// get the node of the key
+func (t *tree) get(key string) (Node, bool) {
+	treeNode, ok := t.internalGet(key)
+
 	if ok {
 		return treeNode.Value, ok
 	} else {
 		return emptyNode, ok
 	}
+}
+
+// return the nodes under the directory
+func (t *tree) list(prefix string) ([]Node, []string, []string, bool) {
+	treeNode, ok := t.internalGet(prefix)
 
+	if !ok {
+		return nil, nil, nil, ok
+	} else {
+		length := len(treeNode.NodeMap)
+		nodes := make([]Node, length)
+		keys := make([]string, length)
+		dirs := make([]string, length)
+		i := 0
+
+		for key, node := range treeNode.NodeMap {
+			nodes[i] = node.Value
+			keys[i] = key
+			if node.Dir {
+				dirs[i] = "d"
+			} else {
+				dirs[i] = "f"
+			}
+			i++
+		}
+
+		return nodes, keys, dirs, ok
+	}
 }
 
 // delete the key, return the old value if the key exists
-func (s *tree) delete(key string) bool {
+func (t *tree) delete(key string) bool {
 	key = "/" + key
 	key = path.Clean(key)
 
@@ -135,7 +173,7 @@ func (s *tree) delete(key string) bool {
 
 	//fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes))
 
-	nodeMap := s.Root.NodeMap
+	nodeMap := t.Root.NodeMap
 		
 	var i int
 

+ 16 - 0
store/tree_store_test.go

@@ -61,6 +61,22 @@ func TestStoreGet(t *testing.T) {
 	}
 
 
+	// test list
+	ts.set("/hello/fooo", CreateTestNode("barbarbar"))
+	ts.set("/hello/foooo/foo", CreateTestNode("barbarbar"))
+
+	nodes, keys, dirs, ok := ts.list("/hello")
+
+	if !ok {
+		t.Fatalf("cannot list!")
+	} else {
+		length := len(nodes)
+
+		for i := 0; i < length; i++ {
+			fmt.Println(keys[i] , "=", nodes[i].Value, "[", dirs[i], "]")
+		} 
+	}
+
 	// speed test
 	for i:=0; i < 100; i++ {
 		key := "/"