Browse Source

Merge pull request #17 from xiangli-cmu/master

merge get and list operation
Xiang Li 12 years ago
parent
commit
82d0ad007f
11 changed files with 225 additions and 114 deletions
  1. 37 4
      README.md
  2. 45 38
      client_handlers.go
  3. 9 20
      command.go
  4. 2 0
      error.go
  5. 29 24
      etcd.go
  6. 30 0
      machines.go
  7. 1 1
      raft_handlers.go
  8. 6 0
      store/error.go
  9. 7 0
      store/keywords.go
  10. 43 22
      store/store.go
  11. 16 5
      store/tree.go

+ 37 - 4
README.md

@@ -205,19 +205,19 @@ We already have `/foo/foo=barbar`
 We create another one `/foo/foo_dir/foo=barbarbar`
 We create another one `/foo/foo_dir/foo=barbarbar`
 
 
 ```sh
 ```sh
-http://127.0.0.1:4001/v1/keys/foo/foo_dir/bar -d value=barbarbar
+curl http://127.0.0.1:4001/v1/keys/foo/foo_dir/bar -d value=barbarbar
 ```
 ```
 
 
 Let us list them next.
 Let us list them next.
 
 
 ```sh
 ```sh
-curl http://127.0.0.1:4001/v1/list/foo/
+curl http://127.0.0.1:4001/v1/get/foo/
 ```
 ```
 
 
-We should see the response as
+We should see the response as an array of items
 
 
 ```json
 ```json
-{"Key":"foo","Value":"barbar","Type":"f"} {"Key":"foo_dir","Value":".","Type":"d"}
+[{"action":"GET","key":"/foo/foo","value":"barbar","index":10},{"action":"GET","key":"/foo/foo_dir","dir":true,"index":10}]
 ```
 ```
 
 
 which meas `foo=barbar` is a key-value pair under `/foo` and `foo_dir` is a directory.
 which meas `foo=barbar` is a key-value pair under `/foo` and `foo_dir` is a directory.
@@ -241,6 +241,29 @@ Let the join two more nodes to this cluster using the -C argument:
 ./etcd -c 4003 -s 7003 -C 127.0.0.1:7001 -d nod/node3
 ./etcd -c 4003 -s 7003 -C 127.0.0.1:7001 -d nod/node3
 ```
 ```
 
 
+Get the machines in the cluster
+
+```sh
+curl http://127.0.0.1:4001/machines
+```
+
+We should see there are three nodes in the cluster
+
+```
+0.0.0.0:7001,0.0.0.0:7002,0.0.0.0:7003
+```
+
+Also try to get the current leader in the cluster
+
+```
+curl http://127.0.0.1:4001/leader
+```
+The first server we set up should be the leader, if it has not dead during these commands.
+
+```
+0.0.0.0:7001
+```
+
 Now we can do normal SET and GET operations on keys as we explored earlier.
 Now we can do normal SET and GET operations on keys as we explored earlier.
 
 
 ```sh
 ```sh
@@ -259,6 +282,16 @@ Let's kill the leader of the cluster and get the value from the other machine:
 curl http://127.0.0.1:4002/v1/keys/foo
 curl http://127.0.0.1:4002/v1/keys/foo
 ```
 ```
 
 
+A new leader should have been elected.
+
+```
+curl http://127.0.0.1:4001/leader
+```
+
+```
+0.0.0.0:7002 or 0.0.0.0:7003
+```
+
 You should be able to see this:
 You should be able to see this:
 
 
 ```json
 ```json

+ 45 - 38
client_handlers.go

@@ -135,19 +135,24 @@ func dispatch(c Command, w *http.ResponseWriter, req *http.Request, client bool)
 				(*w).Write(newJsonError(101, err.Error()))
 				(*w).Write(newJsonError(101, err.Error()))
 				return
 				return
 			}
 			}
+
+			if _, ok := err.(store.NotFile); ok {
+				(*w).WriteHeader(http.StatusBadRequest)
+				(*w).Write(newJsonError(102, err.Error()))
+				return
+			}
 			(*w).WriteHeader(http.StatusInternalServerError)
 			(*w).WriteHeader(http.StatusInternalServerError)
 			(*w).Write(newJsonError(300, "No Leader"))
 			(*w).Write(newJsonError(300, "No Leader"))
 			return
 			return
 		} else {
 		} else {
 
 
-			body, ok := body.([]byte)
-			if !ok {
-				panic("wrong type")
-			}
-
 			if body == nil {
 			if body == nil {
 				http.NotFound((*w), req)
 				http.NotFound((*w), req)
 			} else {
 			} else {
+				body, ok := body.([]byte)
+				if !ok {
+					panic("wrong type")
+				}
 				(*w).WriteHeader(http.StatusOK)
 				(*w).WriteHeader(http.StatusOK)
 				(*w).Write(body)
 				(*w).Write(body)
 			}
 			}
@@ -174,7 +179,8 @@ func dispatch(c Command, w *http.ResponseWriter, req *http.Request, client bool)
 		var url string
 		var url string
 
 
 		if client {
 		if client {
-			url = scheme + raftTransporter.GetLeaderClientAddress() + path
+			clientAddr, _ := getClientAddr(raftServer.Leader())
+			url = scheme + clientAddr + path
 		} else {
 		} else {
 			url = scheme + raftServer.Leader() + path
 			url = scheme + raftServer.Leader() + path
 		}
 		}
@@ -198,8 +204,40 @@ func dispatch(c Command, w *http.ResponseWriter, req *http.Request, client bool)
 
 
 // Handler to return the current leader name
 // Handler to return the current leader name
 func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) {
 func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) {
+	leader := raftServer.Leader()
+
+	if leader != "" {
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(raftServer.Leader()))
+	} else {
+
+		// not likely, but it may happen
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write(newJsonError(301, ""))
+	}
+}
+
+// Handler to return all the known machines in the current cluster
+func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) {
+	peers := raftServer.Peers()
+
+	// Add itself to the machine list first
+	// Since peer map does not contain the server itself
+	machines, _ := getClientAddr(raftServer.Name())
+
+	// Add all peers to the list and sepearte by comma
+	// We do not use json here since we accept machines list
+	// in the command line seperate by comma.
+
+	for peerName, _ := range peers {
+		if addr, ok := getClientAddr(peerName); ok {
+			machines = machines + "," + addr
+		}
+	}
+
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	w.Write([]byte(raftServer.Leader()))
+	w.Write([]byte(machines))
+
 }
 }
 
 
 // Get Handler
 // Get Handler
@@ -235,37 +273,6 @@ func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 
 
 }
 }
 
 
-// List Handler
-func ListHttpHandler(w http.ResponseWriter, req *http.Request) {
-	prefix := req.URL.Path[len("/v1/list/"):]
-
-	debug("[recv] GET http://%v/v1/list/%s", raftServer.Name(), prefix)
-
-	command := &ListCommand{}
-	command.Prefix = prefix
-
-	if body, err := command.Apply(raftServer); err != nil {
-		if _, ok := err.(store.NotFoundError); ok {
-			http.NotFound(w, req)
-			return
-		}
-		w.WriteHeader(http.StatusInternalServerError)
-		w.Write(newJsonError(300, ""))
-		return
-	} else {
-		w.WriteHeader(http.StatusOK)
-
-		body, ok := body.([]byte)
-		if !ok {
-			panic("wrong type")
-		}
-
-		w.Write(body)
-		return
-	}
-
-}
-
 // Watch handler
 // Watch handler
 func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/watch/"):]
 	key := req.URL.Path[len("/v1/watch/"):]

+ 9 - 20
command.go

@@ -64,21 +64,6 @@ func (c *GetCommand) Apply(server *raft.Server) (interface{}, error) {
 	return etcdStore.Get(c.Key)
 	return etcdStore.Get(c.Key)
 }
 }
 
 
-// List command
-type ListCommand struct {
-	Prefix string `json:"prefix"`
-}
-
-// The name of the list command in the log
-func (c *ListCommand) CommandName() string {
-	return "list"
-}
-
-// List all the keys have the given prefix path
-func (c *ListCommand) Apply(server *raft.Server) (interface{}, error) {
-	return etcdStore.List(c.Prefix)
-}
-
 // Delete command
 // Delete command
 type DeleteCommand struct {
 type DeleteCommand struct {
 	Key string `json:"key"`
 	Key string `json:"key"`
@@ -120,7 +105,10 @@ func (c *WatchCommand) Apply(server *raft.Server) (interface{}, error) {
 
 
 // JoinCommand
 // JoinCommand
 type JoinCommand struct {
 type JoinCommand struct {
-	Name string `json:"name"`
+	Name       string `json:"name"`
+	Hostname   string `json:"hostName"`
+	RaftPort   int    `json:"raftPort"`
+	ClientPort int    `json:"clientPort"`
 }
 }
 
 
 // The name of the join command in the log
 // The name of the join command in the log
@@ -129,8 +117,9 @@ func (c *JoinCommand) CommandName() string {
 }
 }
 
 
 // Join a server to the cluster
 // Join a server to the cluster
-func (c *JoinCommand) Apply(server *raft.Server) (interface{}, error) {
-	err := server.AddPeer(c.Name)
-	// no result will be returned
-	return nil, err
+func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
+	err := raftServer.AddPeer(c.Name)
+	addMachine(c.Name, c.Hostname, c.RaftPort, c.ClientPort)
+
+	return []byte("join success"), err
 }
 }

+ 2 - 0
error.go

@@ -12,6 +12,7 @@ func init() {
 	// command related errors
 	// command related errors
 	errors[100] = "Key Not Found"
 	errors[100] = "Key Not Found"
 	errors[101] = "The given PrevValue is not equal to the value of the key"
 	errors[101] = "The given PrevValue is not equal to the value of the key"
+	errors[102] = "Not A File"
 	// Post form related errors
 	// Post form related errors
 	errors[200] = "Value is Required in POST form"
 	errors[200] = "Value is Required in POST form"
 	errors[201] = "PrevValue is Required in POST form"
 	errors[201] = "PrevValue is Required in POST form"
@@ -19,6 +20,7 @@ func init() {
 	errors[203] = "The given index in POST form is not a number"
 	errors[203] = "The given index in POST form is not a number"
 	// raft related errors
 	// raft related errors
 	errors[300] = "Raft Internal Error"
 	errors[300] = "Raft Internal Error"
+	errors[301] = "During Leader Election"
 }
 }
 
 
 type jsonError struct {
 type jsonError struct {

+ 29 - 24
etcd.go

@@ -33,9 +33,9 @@ var machinesFile string
 
 
 var cluster []string
 var cluster []string
 
 
-var address string
+var hostname string
 var clientPort int
 var clientPort int
-var serverPort int
+var raftPort int
 var webPort int
 var webPort int
 
 
 var serverCertFile string
 var serverCertFile string
@@ -58,9 +58,9 @@ func init() {
 	flag.StringVar(&machines, "C", "", "the ip address and port of a existing machines in the cluster, sepearate by comma")
 	flag.StringVar(&machines, "C", "", "the ip address and port of a existing machines in the cluster, sepearate by comma")
 	flag.StringVar(&machinesFile, "CF", "", "the file contains a list of existing machines in the cluster, seperate by comma")
 	flag.StringVar(&machinesFile, "CF", "", "the file contains a list of existing machines in the cluster, seperate by comma")
 
 
-	flag.StringVar(&address, "a", "0.0.0.0", "the ip address of the local machine")
+	flag.StringVar(&hostname, "h", "0.0.0.0", "the hostname of the local machine")
 	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
 	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
-	flag.IntVar(&serverPort, "s", 7001, "the port to communicate with servers")
+	flag.IntVar(&raftPort, "s", 7001, "the port to communicate with servers")
 	flag.IntVar(&webPort, "w", -1, "the port of web interface")
 	flag.IntVar(&webPort, "w", -1, "the port of web interface")
 
 
 	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
 	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
@@ -97,7 +97,7 @@ const (
 	// Timeout for internal raft http connection
 	// Timeout for internal raft http connection
 	// The original timeout for http is 45 seconds
 	// The original timeout for http is 45 seconds
 	// which is too long for our usage.
 	// which is too long for our usage.
-	HTTPTIMEOUT = time.Second
+	HTTPTIMEOUT = 10 * time.Second
 )
 )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -107,8 +107,8 @@ const (
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
 type Info struct {
 type Info struct {
-	Address    string `json:"address"`
-	ServerPort int    `json:"serverPort"`
+	Hostname   string `json:"hostname"`
+	RaftPort   int    `json:"raftPort"`
 	ClientPort int    `json:"clientPort"`
 	ClientPort int    `json:"clientPort"`
 	WebPort    int    `json:"webPort"`
 	WebPort    int    `json:"webPort"`
 
 
@@ -194,7 +194,7 @@ func main() {
 func startRaft(securityType int) {
 func startRaft(securityType int) {
 	var err error
 	var err error
 
 
-	raftName := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)
+	raftName := fmt.Sprintf("%s:%d", info.Hostname, info.RaftPort)
 
 
 	// Create transporter for raft
 	// Create transporter for raft
 	raftTransporter = createTransporter(securityType)
 	raftTransporter = createTransporter(securityType)
@@ -223,7 +223,7 @@ func startRaft(securityType int) {
 	if raftServer.IsLogEmpty() {
 	if raftServer.IsLogEmpty() {
 
 
 		// start as a leader in a new cluster
 		// start as a leader in a new cluster
-		if len(cluster) == 1 && cluster[0] == "" {
+		if len(cluster) == 0 {
 			raftServer.StartLeader()
 			raftServer.StartLeader()
 
 
 			time.Sleep(time.Millisecond * 20)
 			time.Sleep(time.Millisecond * 20)
@@ -232,6 +232,9 @@ func startRaft(securityType int) {
 			for {
 			for {
 				command := &JoinCommand{}
 				command := &JoinCommand{}
 				command.Name = raftServer.Name()
 				command.Name = raftServer.Name()
+				command.Hostname = hostname
+				command.RaftPort = raftPort
+				command.ClientPort = clientPort
 				_, err := raftServer.Do(command)
 				_, err := raftServer.Do(command)
 				if err == nil {
 				if err == nil {
 					break
 					break
@@ -268,7 +271,7 @@ func startRaft(securityType int) {
 	// go server.Snapshot()
 	// go server.Snapshot()
 
 
 	// start to response to raft requests
 	// start to response to raft requests
-	go startRaftTransport(info.ServerPort, securityType)
+	go startRaftTransport(info.RaftPort, securityType)
 
 
 }
 }
 
 
@@ -338,11 +341,11 @@ func startRaftTransport(port int, st int) {
 	switch st {
 	switch st {
 
 
 	case HTTP:
 	case HTTP:
-		fmt.Printf("raft server [%s] listen on http port %v\n", address, port)
+		fmt.Printf("raft server [%s] listen on http port %v\n", hostname, port)
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 
 
 	case HTTPS:
 	case HTTPS:
-		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
+		fmt.Printf("raft server [%s] listen on https port %v\n", hostname, port)
 		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil))
 		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil))
 
 
 	case HTTPSANDVERIFY:
 	case HTTPSANDVERIFY:
@@ -354,7 +357,7 @@ func startRaftTransport(port int, st int) {
 			},
 			},
 			Addr: fmt.Sprintf(":%d", port),
 			Addr: fmt.Sprintf(":%d", port),
 		}
 		}
-		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
+		fmt.Printf("raft server [%s] listen on https port %v\n", hostname, port)
 		err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)
 		err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)
 
 
 		if err != nil {
 		if err != nil {
@@ -369,18 +372,18 @@ func startClientTransport(port int, st int) {
 	// external commands
 	// external commands
 	http.HandleFunc("/"+version+"/keys/", Multiplexer)
 	http.HandleFunc("/"+version+"/keys/", Multiplexer)
 	http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
 	http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
-	http.HandleFunc("/"+version+"/list/", ListHttpHandler)
 	http.HandleFunc("/"+version+"/testAndSet/", TestAndSetHttpHandler)
 	http.HandleFunc("/"+version+"/testAndSet/", TestAndSetHttpHandler)
 	http.HandleFunc("/leader", LeaderHttpHandler)
 	http.HandleFunc("/leader", LeaderHttpHandler)
+	http.HandleFunc("/machines", MachinesHttpHandler)
 
 
 	switch st {
 	switch st {
 
 
 	case HTTP:
 	case HTTP:
-		fmt.Printf("etcd [%s] listen on http port %v\n", address, clientPort)
+		fmt.Printf("etcd [%s] listen on http port %v\n", hostname, clientPort)
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 
 
 	case HTTPS:
 	case HTTPS:
-		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
+		fmt.Printf("etcd [%s] listen on https port %v\n", hostname, clientPort)
 		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)
 		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)
 
 
 	case HTTPSANDVERIFY:
 	case HTTPSANDVERIFY:
@@ -392,7 +395,7 @@ func startClientTransport(port int, st int) {
 			},
 			},
 			Addr: fmt.Sprintf(":%d", port),
 			Addr: fmt.Sprintf(":%d", port),
 		}
 		}
-		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
+		fmt.Printf("etcd [%s] listen on https port %v\n", hostname, clientPort)
 		err := server.ListenAndServeTLS(clientCertFile, clientKeyFile)
 		err := server.ListenAndServeTLS(clientCertFile, clientKeyFile)
 
 
 		if err != nil {
 		if err != nil {
@@ -480,15 +483,15 @@ func getInfo(path string) *Info {
 	} else {
 	} else {
 		// Otherwise ask user for info and write it to file.
 		// Otherwise ask user for info and write it to file.
 
 
-		if address == "" {
+		if hostname == "" {
 			fatal("Please give the address of the local machine")
 			fatal("Please give the address of the local machine")
 		}
 		}
 
 
-		info.Address = address
-		info.Address = strings.TrimSpace(info.Address)
-		fmt.Println("address ", info.Address)
+		info.Hostname = hostname
+		info.Hostname = strings.TrimSpace(info.Hostname)
+		fmt.Println("address ", info.Hostname)
 
 
-		info.ServerPort = serverPort
+		info.RaftPort = raftPort
 		info.ClientPort = clientPort
 		info.ClientPort = clientPort
 		info.WebPort = webPort
 		info.WebPort = webPort
 
 
@@ -537,6 +540,9 @@ func joinCluster(s *raft.Server, serverName string) error {
 
 
 	command := &JoinCommand{}
 	command := &JoinCommand{}
 	command.Name = s.Name()
 	command.Name = s.Name()
+	command.Hostname = info.Hostname
+	command.RaftPort = info.RaftPort
+	command.ClientPort = info.ClientPort
 
 
 	json.NewEncoder(&b).Encode(command)
 	json.NewEncoder(&b).Encode(command)
 
 
@@ -561,7 +567,7 @@ func joinCluster(s *raft.Server, serverName string) error {
 				return nil
 				return nil
 			}
 			}
 			if resp.StatusCode == http.StatusTemporaryRedirect {
 			if resp.StatusCode == http.StatusTemporaryRedirect {
-				address = resp.Header.Get("Location")
+				address := resp.Header.Get("Location")
 				debug("Leader is %s", address)
 				debug("Leader is %s", address)
 				debug("Send Join Request to %s", address)
 				debug("Send Join Request to %s", address)
 				json.NewEncoder(&b).Encode(command)
 				json.NewEncoder(&b).Encode(command)
@@ -582,6 +588,5 @@ func registerCommands() {
 	raft.RegisterCommand(&GetCommand{})
 	raft.RegisterCommand(&GetCommand{})
 	raft.RegisterCommand(&DeleteCommand{})
 	raft.RegisterCommand(&DeleteCommand{})
 	raft.RegisterCommand(&WatchCommand{})
 	raft.RegisterCommand(&WatchCommand{})
-	raft.RegisterCommand(&ListCommand{})
 	raft.RegisterCommand(&TestAndSetCommand{})
 	raft.RegisterCommand(&TestAndSetCommand{})
 }
 }

+ 30 - 0
machines.go

@@ -0,0 +1,30 @@
+package main
+
+import (
+	"fmt"
+)
+
+type machine struct {
+	hostname   string
+	raftPort   int
+	clientPort int
+}
+
+var machinesMap = map[string]machine{}
+
+func addMachine(name string, hostname string, raftPort int, clientPort int) {
+
+	machinesMap[name] = machine{hostname, raftPort, clientPort}
+
+}
+
+func getClientAddr(name string) (string, bool) {
+	machine, ok := machinesMap[name]
+	if !ok {
+		return "", false
+	}
+
+	addr := fmt.Sprintf("%s:%v", machine.hostname, machine.clientPort)
+
+	return addr, true
+}

+ 1 - 1
raft_handlers.go

@@ -75,7 +75,7 @@ func SnapshotHttpHandler(w http.ResponseWriter, req *http.Request) {
 func ClientHttpHandler(w http.ResponseWriter, req *http.Request) {
 func ClientHttpHandler(w http.ResponseWriter, req *http.Request) {
 	debug("[recv] Get http://%v/client/ ", raftServer.Name())
 	debug("[recv] Get http://%v/client/ ", raftServer.Name())
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	client := address + ":" + strconv.Itoa(clientPort)
+	client := hostname + ":" + strconv.Itoa(clientPort)
 	w.Write([]byte(client))
 	w.Write([]byte(client))
 }
 }
 
 

+ 6 - 0
store/error.go

@@ -16,4 +16,10 @@ type TestFail string
 
 
 func (e TestFail) Error() string {
 func (e TestFail) Error() string {
 	return string(e)
 	return string(e)
+}
+
+type Keyword string
+
+func (e Keyword) Error() string {
+	return string(e)
 }
 }

+ 7 - 0
store/keywords.go

@@ -0,0 +1,7 @@
+package store
+
+// keywords for internal useage
+var keywords = map[string]bool{
+    "/acoounts": true,
+    "/ephemeralNodes": true,
+}

+ 43 - 22
store/store.go

@@ -64,11 +64,12 @@ type Node struct {
 type Response struct {
 type Response struct {
 	Action    string `json:"action"`
 	Action    string `json:"action"`
 	Key       string `json:"key"`
 	Key       string `json:"key"`
+	Dir       bool   `json:"dir,omitempty"`
 	PrevValue string `json:"prevValue,omitempty"`
 	PrevValue string `json:"prevValue,omitempty"`
 	Value     string `json:"value,omitempty"`
 	Value     string `json:"value,omitempty"`
 
 
-	// If the key existed before the action, this field should be true
-	// If the key did not exist before the action, this field should be false
+	// If the key did not exist before the action, 
+	// this field should be set to true
 	NewKey    bool `json:"newKey,omitempty"`
 	NewKey    bool `json:"newKey,omitempty"`
 
 
 	Expiration *time.Time `json:"expiration,omitempty"`
 	Expiration *time.Time `json:"expiration,omitempty"`
@@ -241,19 +242,8 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
 		s.addToResponseMap(index, &resp)
 		s.addToResponseMap(index, &resp)
 		return msg, err
 		return msg, err
 	}
 	}
-}
-
-// Get the value of the key
-func (s *Store) Get(key string) ([]byte, error) {
-	resp := s.internalGet(key)
 
 
-	if resp != nil {
-		return json.Marshal(resp)
-	} else {
-		err := NotFoundError(key)
-		return nil, err
-	}
-}	
+}
 
 
 // Get the value of the key and return the raw response
 // Get the value of the key and return the raw response
 func (s *Store) internalGet(key string) *Response {
 func (s *Store) internalGet(key string) *Response {
@@ -291,21 +281,51 @@ func (s *Store) internalGet(key string) *Response {
 }
 }
 
 
 
 
-// List all the item in the prefix
-func (s *Store) List(prefix string) ([]byte, error) {
+// Get all the items under key
+// If key is a file return the file
+// If key is a directory reuturn an array of files
+func (s *Store) Get(key string) ([]byte, error) {
 
 
-	nodes, keys, dirs, ok := s.Tree.list(prefix)
+	key = path.Clean("/" + key)
 
 
-	var ln []ListNode
+	nodes, keys, dirs, ok := s.Tree.list(key)
 
 
 	if ok {
 	if ok {
-		ln = make([]ListNode, len(nodes))
+		resps := make([]Response, len(nodes))
 		for i := 0; i < len(nodes); i++ {
 		for i := 0; i < len(nodes); i++ {
-			ln[i] = ListNode{keys[i], nodes[i].Value, dirs[i]}
+
+			var TTL int64
+			var isExpire bool = false
+
+			isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
+
+			resps[i] = Response{
+				Action: "GET", 
+				Index: s.Index,
+				Key: path.Join(key, keys[i]),
+			}
+
+			if !dirs[i] {
+				resps[i].Value = nodes[i].Value
+			} else {
+				resps[i].Dir = true
+			}
+
+			// Update ttl
+			if isExpire {
+				TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second)
+				resps[i].Expiration = &nodes[i].ExpireTime 
+				resps[i].TTL = TTL 
+			} 
+
 		}
 		}
+		if len(resps) == 1 {
+			return json.Marshal(resps[0])
+		}
+		return json.Marshal(resps)
 	}
 	}
 
 
-	err := NotFoundError(prefix)
+	err := NotFoundError(key)
 	return nil, err
 	return nil, err
 }
 }
 
 
@@ -332,12 +352,13 @@ func (s *Store) Delete(key string, index uint64) ([]byte, error) {
 
 
 			s.Tree.delete(key)
 			s.Tree.delete(key)
 
 
+
 		} else {
 		} else {
 			resp.Expiration = &node.ExpireTime
 			resp.Expiration = &node.ExpireTime
 			// Kill the expire go routine
 			// Kill the expire go routine
 			node.update <- PERMANENT
 			node.update <- PERMANENT
 			s.Tree.delete(key)
 			s.Tree.delete(key)
-
+			
 		}
 		}
 
 
 		msg, err := json.Marshal(resp)
 		msg, err := json.Marshal(resp)

+ 16 - 5
store/tree.go

@@ -104,6 +104,9 @@ func (t *tree) set(key string, value Node) bool {
 		nodeMap[nodesName[i]] = tn
 		nodeMap[nodesName[i]] = tn
 	
 	
 	} else {
 	} else {
+		if tn.Dir {
+			return false
+		}
 		// we change the value of a old Treenode
 		// we change the value of a old Treenode
 		tn.InternalNode = value
 		tn.InternalNode = value
 	}
 	}
@@ -140,32 +143,40 @@ func (t *tree) get(key string) (Node, bool) {
 	tn, ok := t.internalGet(key)
 	tn, ok := t.internalGet(key)
 
 
 	if ok {
 	if ok {
+		if tn.Dir {
+			return emptyNode, false
+		}
 		return tn.InternalNode, ok
 		return tn.InternalNode, ok
 	} else {
 	} else {
 		return emptyNode, ok
 		return emptyNode, ok
 	}
 	}
 }
 }
 
 
-// return the nodes information under the directory
-func (t *tree) list(directory string) ([]Node, []string, []string, bool) {
+// get the internalNode of the key
+func (t *tree) list(directory string) ([]Node, []string, []bool, bool) {
 	treeNode, ok := t.internalGet(directory)
 	treeNode, ok := t.internalGet(directory)
 
 
 	if !ok {
 	if !ok {
 		return nil, nil, nil, ok
 		return nil, nil, nil, ok
 	} else {
 	} else {
+		if !treeNode.Dir {
+			nodes := make([]Node, 1)
+			nodes[0] = treeNode.InternalNode
+			return nodes, make([]string, 1), make([]bool, 1), true
+		} 
 		length := len(treeNode.NodeMap)
 		length := len(treeNode.NodeMap)
 		nodes := make([]Node, length)
 		nodes := make([]Node, length)
 		keys := make([]string, length)
 		keys := make([]string, length)
-		dirs := make([]string, length)
+		dirs := make([]bool, length)
 		i := 0
 		i := 0
 
 
 		for key, node := range treeNode.NodeMap {
 		for key, node := range treeNode.NodeMap {
 			nodes[i] = node.InternalNode
 			nodes[i] = node.InternalNode
 			keys[i] = key
 			keys[i] = key
 			if node.Dir {
 			if node.Dir {
-				dirs[i] = "d"
+				dirs[i] = true
 			} else {
 			} else {
-				dirs[i] = "f"
+				dirs[i] = false
 			}
 			}
 			i++
 			i++
 		}
 		}