agent: Adding support for specifying LockDelay, defaults to 15 seconds.

This commit is contained in:
Armon Dadgar 2014-05-19 13:12:15 -07:00
parent e0abf2e92c
commit 00a107dfd9
2 changed files with 90 additions and 6 deletions

View File

@ -6,6 +6,17 @@ import (
"github.com/hashicorp/consul/consul/structs"
"net/http"
"strings"
"time"
)
const (
// lockDelayMinThreshold is used to convert a numeric lock
// delay value from nanoseconds to seconds if it is below this
// threshold. Users often send a value like 5, which they assume
// is seconds, but because Go uses nanosecond granularity, ends
// up being very small. If we see a value below this threshold,
// we multply by time.Second
lockDelayMinThreshold = 1000
)
// sessionCreateResponse is used to wrap the session ID
@ -25,15 +36,16 @@ func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request)
args := structs.SessionRequest{
Op: structs.SessionCreate,
Session: structs.Session{
Node: s.agent.config.NodeName,
Checks: []string{consul.SerfCheckID},
Node: s.agent.config.NodeName,
Checks: []string{consul.SerfCheckID},
LockDelay: 15 * time.Second,
},
}
s.parseDC(req, &args.Datacenter)
// Handle optional request body
if req.ContentLength > 0 {
if err := decodeBody(req, &args.Session, nil); err != nil {
if err := decodeBody(req, &args.Session, FixupLockDelay); err != nil {
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err)))
return nil, nil
@ -50,6 +62,45 @@ func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request)
return sessionCreateResponse{out}, nil
}
// FixupLockDelay is used to handle parsing the JSON body to session/create
// and properly parsing out the lock delay duration value.
func FixupLockDelay(raw interface{}) error {
rawMap, ok := raw.(map[string]interface{})
if !ok {
return nil
}
var key string
for k, _ := range rawMap {
if strings.ToLower(k) == "lockdelay" {
key = k
break
}
}
if key != "" {
val := rawMap[key]
// Convert a string value into an integer
if vStr, ok := val.(string); ok {
dur, err := time.ParseDuration(vStr)
if err != nil {
return err
}
if dur < lockDelayMinThreshold {
dur = dur * time.Second
}
rawMap[key] = dur
}
// Convert low value integers into seconds
if vNum, ok := val.(float64); ok {
dur := time.Duration(vNum)
if dur < lockDelayMinThreshold {
dur = dur * time.Second
}
rawMap[key] = dur
}
}
return nil
}
// SessionDestroy is used to destroy an existing session
func (s *HTTPServer) SessionDestroy(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
args := structs.SessionRequest{

View File

@ -8,6 +8,7 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestSessionCreate(t *testing.T) {
@ -33,8 +34,9 @@ func TestSessionCreate(t *testing.T) {
body := bytes.NewBuffer(nil)
enc := json.NewEncoder(body)
raw := map[string]interface{}{
"Node": srv.agent.config.NodeName,
"Checks": []string{consul.SerfCheckID, "consul"},
"Node": srv.agent.config.NodeName,
"Checks": []string{consul.SerfCheckID, "consul"},
"LockDelay": "20s",
}
enc.Encode(raw)
@ -52,10 +54,41 @@ func TestSessionCreate(t *testing.T) {
if _, ok := obj.(sessionCreateResponse); !ok {
t.Fatalf("should work")
}
})
}
func TestFixupLockDelay(t *testing.T) {
inp := map[string]interface{}{
"lockdelay": float64(15),
}
if err := FixupLockDelay(inp); err != nil {
t.Fatalf("err: %v", err)
}
if inp["lockdelay"] != 15*time.Second {
t.Fatalf("bad: %v", inp)
}
inp = map[string]interface{}{
"lockDelay": float64(15 * time.Second),
}
if err := FixupLockDelay(inp); err != nil {
t.Fatalf("err: %v", err)
}
if inp["lockDelay"] != 15*time.Second {
t.Fatalf("bad: %v", inp)
}
inp = map[string]interface{}{
"LockDelay": "15s",
}
if err := FixupLockDelay(inp); err != nil {
t.Fatalf("err: %v", err)
}
if inp["LockDelay"] != 15*time.Second {
t.Fatalf("bad: %v", inp)
}
}
func makeTestSession(t *testing.T, srv *HTTPServer) string {
req, err := http.NewRequest("PUT", "/v1/session/create", nil)
if err != nil {