consul/command/agent/session_endpoint.go
Atin Malaviya 47241fc1c8 Ephemeral Nodes for via Session behavior settings.
Added a "delete" behavior for session invalidation, in addition to
the default "release" behavior. On session invalidation, the sessions
Behavior field is checked and if it is set to "delete", all nodes owned
by the session are deleted. If it is "release", then just the locks
are released as default.
2014-11-20 11:34:45 -05:00

193 lines
5.1 KiB
Go

package agent
import (
"fmt"
"github.com/hashicorp/consul/consul"
"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
type sessionCreateResponse struct {
ID string
}
// SessionCreate is used to create a new session
func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Mandate a PUT request
if req.Method != "PUT" {
resp.WriteHeader(405)
return nil, nil
}
// Default the session to our node + serf check + release session invalidate behavior
args := structs.SessionRequest{
Op: structs.SessionCreate,
Session: structs.Session{
Node: s.agent.config.NodeName,
Checks: []string{consul.SerfCheckID},
LockDelay: 15 * time.Second,
Behavior: structs.SessionKeysRelease,
},
}
s.parseDC(req, &args.Datacenter)
// Handle optional request body
if req.ContentLength > 0 {
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
}
}
// Create the session, get the ID
var out string
if err := s.agent.RPC("Session.Apply", &args, &out); err != nil {
return nil, err
}
// Format the response as a JSON object
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) {
// Mandate a PUT request
if req.Method != "PUT" {
resp.WriteHeader(405)
return nil, nil
}
args := structs.SessionRequest{
Op: structs.SessionDestroy,
}
s.parseDC(req, &args.Datacenter)
// Pull out the session id
args.Session.ID = strings.TrimPrefix(req.URL.Path, "/v1/session/destroy/")
if args.Session.ID == "" {
resp.WriteHeader(400)
resp.Write([]byte("Missing session"))
return nil, nil
}
var out string
if err := s.agent.RPC("Session.Apply", &args, &out); err != nil {
return nil, err
}
return true, nil
}
// SessionGet is used to get info for a particular session
func (s *HTTPServer) SessionGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
args := structs.SessionSpecificRequest{}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Pull out the session id
args.Session = strings.TrimPrefix(req.URL.Path, "/v1/session/info/")
if args.Session == "" {
resp.WriteHeader(400)
resp.Write([]byte("Missing session"))
return nil, nil
}
var out structs.IndexedSessions
defer setMeta(resp, &out.QueryMeta)
if err := s.agent.RPC("Session.Get", &args, &out); err != nil {
return nil, err
}
return out.Sessions, nil
}
// SessionList is used to list all the sessions
func (s *HTTPServer) SessionList(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
args := structs.DCSpecificRequest{}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
var out structs.IndexedSessions
defer setMeta(resp, &out.QueryMeta)
if err := s.agent.RPC("Session.List", &args, &out); err != nil {
return nil, err
}
return out.Sessions, nil
}
// SessionsForNode returns all the nodes belonging to a node
func (s *HTTPServer) SessionsForNode(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
args := structs.NodeSpecificRequest{}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Pull out the node name
args.Node = strings.TrimPrefix(req.URL.Path, "/v1/session/node/")
if args.Node == "" {
resp.WriteHeader(400)
resp.Write([]byte("Missing node name"))
return nil, nil
}
var out structs.IndexedSessions
defer setMeta(resp, &out.QueryMeta)
if err := s.agent.RPC("Session.NodeSessions", &args, &out); err != nil {
return nil, err
}
return out.Sessions, nil
}