|
@@ -2,12 +2,12 @@ package main
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
- etcdErr "github.com/coreos/etcd/error"
|
|
|
|
|
- "github.com/coreos/etcd/store"
|
|
|
|
|
- "github.com/coreos/go-raft"
|
|
|
|
|
"net/http"
|
|
"net/http"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+
|
|
|
|
|
+ etcdErr "github.com/coreos/etcd/error"
|
|
|
|
|
+ "github.com/coreos/go-raft"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
//-------------------------------------------------------------------
|
|
@@ -18,7 +18,6 @@ func NewEtcdMuxer() *http.ServeMux {
|
|
|
// external commands
|
|
// external commands
|
|
|
etcdMux := http.NewServeMux()
|
|
etcdMux := http.NewServeMux()
|
|
|
etcdMux.Handle("/"+version+"/keys/", errorHandler(Multiplexer))
|
|
etcdMux.Handle("/"+version+"/keys/", errorHandler(Multiplexer))
|
|
|
- etcdMux.Handle("/"+version+"/watch/", errorHandler(WatchHttpHandler))
|
|
|
|
|
etcdMux.Handle("/"+version+"/leader", errorHandler(LeaderHttpHandler))
|
|
etcdMux.Handle("/"+version+"/leader", errorHandler(LeaderHttpHandler))
|
|
|
etcdMux.Handle("/"+version+"/machines", errorHandler(MachinesHttpHandler))
|
|
etcdMux.Handle("/"+version+"/machines", errorHandler(MachinesHttpHandler))
|
|
|
etcdMux.Handle("/"+version+"/stats", errorHandler(StatsHttpHandler))
|
|
etcdMux.Handle("/"+version+"/stats", errorHandler(StatsHttpHandler))
|
|
@@ -47,15 +46,16 @@ func Multiplexer(w http.ResponseWriter, req *http.Request) error {
|
|
|
case "GET":
|
|
case "GET":
|
|
|
return GetHttpHandler(w, req)
|
|
return GetHttpHandler(w, req)
|
|
|
case "POST":
|
|
case "POST":
|
|
|
- return SetHttpHandler(w, req)
|
|
|
|
|
|
|
+ return CreateHttpHandler(w, req)
|
|
|
case "PUT":
|
|
case "PUT":
|
|
|
- return SetHttpHandler(w, req)
|
|
|
|
|
|
|
+ return UpdateHttpHandler(w, req)
|
|
|
case "DELETE":
|
|
case "DELETE":
|
|
|
return DeleteHttpHandler(w, req)
|
|
return DeleteHttpHandler(w, req)
|
|
|
default:
|
|
default:
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------
|
|
//--------------------------------------
|
|
@@ -63,63 +63,102 @@ func Multiplexer(w http.ResponseWriter, req *http.Request) error {
|
|
|
// Set/Delete will dispatch to leader
|
|
// Set/Delete will dispatch to leader
|
|
|
//--------------------------------------
|
|
//--------------------------------------
|
|
|
|
|
|
|
|
-// Set Command Handler
|
|
|
|
|
-func SetHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
|
|
- key := req.URL.Path[len("/v1/keys/"):]
|
|
|
|
|
-
|
|
|
|
|
- if store.CheckKeyword(key) {
|
|
|
|
|
- return etcdErr.NewError(etcdErr.EcodeKeyIsPreserved, "Set")
|
|
|
|
|
- }
|
|
|
|
|
|
|
+func CreateHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
|
|
+ key := req.URL.Path[len("/v2/keys"):]
|
|
|
|
|
|
|
|
- debugf("[recv] POST %v/v1/keys/%s [%s]", e.url, key, req.RemoteAddr)
|
|
|
|
|
|
|
+ debugf("recv.post[%v] [%v%v]\n", req.RemoteAddr, req.Host, req.URL)
|
|
|
|
|
|
|
|
value := req.FormValue("value")
|
|
value := req.FormValue("value")
|
|
|
|
|
|
|
|
- if len(value) == 0 {
|
|
|
|
|
- return etcdErr.NewError(etcdErr.EcodeValueRequired, "Set")
|
|
|
|
|
|
|
+ ttl := req.FormValue("ttl")
|
|
|
|
|
+
|
|
|
|
|
+ expireTime, err := durationToExpireTime(ttl)
|
|
|
|
|
+
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Create")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- prevValue := req.FormValue("prevValue")
|
|
|
|
|
|
|
+ command := &CreateCommand{
|
|
|
|
|
+ Key: key,
|
|
|
|
|
+ Value: value,
|
|
|
|
|
+ ExpireTime: expireTime,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return dispatch(command, w, req, true)
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- strDuration := req.FormValue("ttl")
|
|
|
|
|
|
|
+func UpdateHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
|
|
+ key := req.URL.Path[len("/v2/keys"):]
|
|
|
|
|
|
|
|
- expireTime, err := durationToExpireTime(strDuration)
|
|
|
|
|
|
|
+ debugf("recv.put[%v] [%v%v]\n", req.RemoteAddr, req.Host, req.URL)
|
|
|
|
|
+
|
|
|
|
|
+ value := req.FormValue("value")
|
|
|
|
|
+
|
|
|
|
|
+ ttl := req.FormValue("ttl")
|
|
|
|
|
+
|
|
|
|
|
+ expireTime, err := durationToExpireTime(ttl)
|
|
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Set")
|
|
|
|
|
|
|
+ return etcdErr.NewError(etcdErr.EcodeTTLNaN, "Update")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if len(prevValue) != 0 {
|
|
|
|
|
- command := &TestAndSetCommand{
|
|
|
|
|
|
|
+ // TODO: update should give at least one option
|
|
|
|
|
+ if value == "" && ttl == "" {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ prevValue := req.FormValue("prevValue")
|
|
|
|
|
+
|
|
|
|
|
+ prevIndexStr := req.FormValue("prevIndex")
|
|
|
|
|
+
|
|
|
|
|
+ if prevValue == "" && prevIndexStr == "" { // update without test
|
|
|
|
|
+ command := &UpdateCommand{
|
|
|
Key: key,
|
|
Key: key,
|
|
|
Value: value,
|
|
Value: value,
|
|
|
- PrevValue: prevValue,
|
|
|
|
|
ExpireTime: expireTime,
|
|
ExpireTime: expireTime,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return dispatch(command, w, req, true)
|
|
return dispatch(command, w, req, true)
|
|
|
|
|
|
|
|
- } else {
|
|
|
|
|
- command := &SetCommand{
|
|
|
|
|
- Key: key,
|
|
|
|
|
- Value: value,
|
|
|
|
|
- ExpireTime: expireTime,
|
|
|
|
|
|
|
+ } else { // update with test
|
|
|
|
|
+ var prevIndex uint64
|
|
|
|
|
+
|
|
|
|
|
+ if prevIndexStr != "" {
|
|
|
|
|
+ prevIndex, err = strconv.ParseUint(prevIndexStr, 10, 64)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: add error type
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ command := &TestAndSetCommand{
|
|
|
|
|
+ Key: key,
|
|
|
|
|
+ Value: value,
|
|
|
|
|
+ PrevValue: prevValue,
|
|
|
|
|
+ PrevIndex: prevIndex,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return dispatch(command, w, req, true)
|
|
return dispatch(command, w, req, true)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Delete Handler
|
|
// Delete Handler
|
|
|
func DeleteHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
func DeleteHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
- key := req.URL.Path[len("/v1/keys/"):]
|
|
|
|
|
|
|
+ key := req.URL.Path[len("/v2/keys"):]
|
|
|
|
|
|
|
|
- debugf("[recv] DELETE %v/v1/keys/%s [%s]", e.url, key, req.RemoteAddr)
|
|
|
|
|
|
|
+ debugf("recv.delete[%v] [%v%v]\n", req.RemoteAddr, req.Host, req.URL)
|
|
|
|
|
|
|
|
command := &DeleteCommand{
|
|
command := &DeleteCommand{
|
|
|
Key: key,
|
|
Key: key,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if req.FormValue("recursive") == "true" {
|
|
|
|
|
+ command.Recursive = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return dispatch(command, w, req, true)
|
|
return dispatch(command, w, req, true)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -212,64 +251,63 @@ func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Get Handler
|
|
|
|
|
func GetHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
func GetHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
- key := req.URL.Path[len("/v1/keys/"):]
|
|
|
|
|
|
|
+ var err error
|
|
|
|
|
+ var event interface{}
|
|
|
|
|
+ key := req.URL.Path[len("/v1/keys"):]
|
|
|
|
|
|
|
|
- debugf("[recv] GET %s/v1/keys/%s [%s]", e.url, key, req.RemoteAddr)
|
|
|
|
|
|
|
+ debugf("recv.get[%v] [%v%v]\n", req.RemoteAddr, req.Host, req.URL)
|
|
|
|
|
|
|
|
- command := &GetCommand{
|
|
|
|
|
- Key: key,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ recursive := req.FormValue("recursive")
|
|
|
|
|
|
|
|
- if body, err := command.Apply(r.Server); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- } else {
|
|
|
|
|
- body, _ := body.([]byte)
|
|
|
|
|
- w.WriteHeader(http.StatusOK)
|
|
|
|
|
- w.Write(body)
|
|
|
|
|
|
|
+ if req.FormValue("wait") == "true" {
|
|
|
|
|
+ command := &WatchCommand{
|
|
|
|
|
+ Key: key,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if recursive == "true" {
|
|
|
|
|
+ command.Recursive = true
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ indexStr := req.FormValue("wait_index")
|
|
|
|
|
|
|
|
-// Watch handler
|
|
|
|
|
-func WatchHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
|
|
|
|
- key := req.URL.Path[len("/v1/watch/"):]
|
|
|
|
|
|
|
+ if indexStr != "" {
|
|
|
|
|
+ sinceIndex, err := strconv.ParseUint(indexStr, 10, 64)
|
|
|
|
|
|
|
|
- command := &WatchCommand{
|
|
|
|
|
- Key: key,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return etcdErr.NewError(etcdErr.EcodeIndexNaN, "Watch From Index")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if req.Method == "GET" {
|
|
|
|
|
- debugf("[recv] GET %s/watch/%s [%s]", e.url, key, req.RemoteAddr)
|
|
|
|
|
- command.SinceIndex = 0
|
|
|
|
|
|
|
+ command.SinceIndex = sinceIndex
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } else if req.Method == "POST" {
|
|
|
|
|
- // watch from a specific index
|
|
|
|
|
|
|
+ event, err = command.Apply(r.Server)
|
|
|
|
|
|
|
|
- debugf("[recv] POST %s/watch/%s [%s]", e.url, key, req.RemoteAddr)
|
|
|
|
|
- content := req.FormValue("index")
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ command := &GetCommand{
|
|
|
|
|
+ Key: key,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- sinceIndex, err := strconv.ParseUint(string(content), 10, 64)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return etcdErr.NewError(etcdErr.EcodeIndexNaN, "Watch From Index")
|
|
|
|
|
|
|
+ sorted := req.FormValue("sorted")
|
|
|
|
|
+
|
|
|
|
|
+ if sorted == "true" {
|
|
|
|
|
+ command.Sorted = true
|
|
|
}
|
|
}
|
|
|
- command.SinceIndex = sinceIndex
|
|
|
|
|
|
|
|
|
|
- } else {
|
|
|
|
|
- w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
|
- return nil
|
|
|
|
|
|
|
+ if recursive == "true" {
|
|
|
|
|
+ command.Recursive = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ event, err = command.Apply(r.Server)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if body, err := command.Apply(r.Server); err != nil {
|
|
|
|
|
- return etcdErr.NewError(etcdErr.EcodeWatcherCleared, key)
|
|
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ event, _ := event.([]byte)
|
|
|
w.WriteHeader(http.StatusOK)
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
+ w.Write(event)
|
|
|
|
|
|
|
|
- body, _ := body.([]byte)
|
|
|
|
|
- w.Write(body)
|
|
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|