diff --git a/command/agent/http.go b/command/agent/http.go index 515514ad2..c44c04980 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -93,6 +93,12 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) { s.mux.HandleFunc("/v1/kv/", s.wrap(s.KVSEndpoint)) + s.mux.HandleFunc("/v1/session/create", s.wrap(s.SessionCreate)) + s.mux.HandleFunc("/v1/session/destroy/", s.wrap(s.SessionDestroy)) + s.mux.HandleFunc("/v1/session/info/", s.wrap(s.SessionGet)) + s.mux.HandleFunc("/v1/session/node/", s.wrap(s.SessionsForNode)) + s.mux.HandleFunc("/v1/session/list", s.wrap(s.SessionList)) + if enableDebug { s.mux.HandleFunc("/debug/pprof/", pprof.Index) s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) diff --git a/command/agent/session_endpoint.go b/command/agent/session_endpoint.go new file mode 100644 index 000000000..8408baf94 --- /dev/null +++ b/command/agent/session_endpoint.go @@ -0,0 +1,126 @@ +package agent + +import ( + "fmt" + "github.com/hashicorp/consul/consul" + "github.com/hashicorp/consul/consul/structs" + "net/http" + "strings" +) + +// SessionCreate is used to create a new session +func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + // Default the session to our node + serf check + args := structs.SessionRequest{ + Op: structs.SessionCreate, + Session: structs.Session{ + Node: s.agent.config.NodeName, + Checks: []string{consul.SerfCheckID}, + }, + } + s.parseDC(req, &args.Datacenter) + + // Handle optional request body + if req.ContentLength > 0 { + if err := decodeBody(req, &args.Session, nil); 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 + type response struct { + ID string + } + return response{out}, nil +} + +// SessionDestroy is used to destroy an existing session +func (s *HTTPServer) SessionDestroy(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + 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 +} diff --git a/consul/session_endpoint.go b/consul/session_endpoint.go index 79938fc18..069f374d1 100644 --- a/consul/session_endpoint.go +++ b/consul/session_endpoint.go @@ -46,7 +46,7 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error { } // Get is used to retrieve a single session -func (s *Session) Get(args *structs.SessionGetRequest, +func (s *Session) Get(args *structs.SessionSpecificRequest, reply *structs.IndexedSessions) error { if done, err := s.srv.forward("Session.Get", args, args, reply); done { return err diff --git a/consul/session_endpoint_test.go b/consul/session_endpoint_test.go index 20366feda..8b59d7a28 100644 --- a/consul/session_endpoint_test.go +++ b/consul/session_endpoint_test.go @@ -81,7 +81,7 @@ func TestSessionEndpoint_Get(t *testing.T) { t.Fatalf("err: %v", err) } - getR := structs.SessionGetRequest{ + getR := structs.SessionSpecificRequest{ Datacenter: "dc1", Session: out, } diff --git a/consul/structs/structs.go b/consul/structs/structs.go index d2e5aba24..0673ba39f 100644 --- a/consul/structs/structs.go +++ b/consul/structs/structs.go @@ -369,14 +369,14 @@ func (r *SessionRequest) RequestDatacenter() string { return r.Datacenter } -// SessionGetRequest is used to request a session by ID -type SessionGetRequest struct { +// SessionSpecificRequest is used to request a session by ID +type SessionSpecificRequest struct { Datacenter string Session string QueryOptions } -func (r *SessionGetRequest) RequestDatacenter() string { +func (r *SessionSpecificRequest) RequestDatacenter() string { return r.Datacenter }