Split operations code and add comments

pull/6/head
Clayton Coleman 2014-07-29 17:36:41 -04:00
parent 9069696514
commit fe7f611d88
2 changed files with 59 additions and 42 deletions

View File

@ -79,35 +79,32 @@ func New(storage map[string]RESTStorage, prefix string) *APIServer {
mux: http.NewServeMux(),
}
s.mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))))
// Primary API methods
s.mux.HandleFunc(s.prefix+"/", s.handleREST)
healthz.InstallHandler(s.mux)
s.mux.HandleFunc(s.watchPrefix()+"/", s.handleWatch)
s.mux.HandleFunc("/version", s.handleVersionReq)
// Support services for the apiserver
s.mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))))
healthz.InstallHandler(s.mux)
s.mux.HandleFunc("/version", handleVersion)
s.mux.HandleFunc("/", handleIndex)
// Handle both operations and operations/* with the same handler
s.mux.HandleFunc(s.operationPrefix(), s.handleOperationRequest)
s.mux.HandleFunc(s.operationPrefix()+"/", s.handleOperationRequest)
s.mux.HandleFunc(s.watchPrefix()+"/", s.handleWatch)
// Proxy minion requests
s.mux.HandleFunc("/proxy/minion/", s.handleProxyMinion)
return s
}
// handleVersionReq writes the server's version information.
func (s *APIServer) handleVersionReq(w http.ResponseWriter, req *http.Request) {
writeRawJSON(http.StatusOK, version.Get(), w)
}
// HTTP Handler interface
// ServeHTTP implements the standard net/http interface.
func (s *APIServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer func() {
if x := recover(); x != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "apiserver panic. Look in log for details.")
fmt.Fprint(w, "apis panic. Look in log for details.")
glog.Infof("APIServer panic'd on %v %v: %#v\n%s\n", req.Method, req.RequestURI, x, debug.Stack())
}
}()
@ -145,33 +142,6 @@ func (s *APIServer) handleREST(w http.ResponseWriter, req *http.Request) {
s.handleRESTStorage(requestParts, req, w, storage)
}
// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an
// Operation to receive the result and returning its ID down the writer.
func (s *APIServer) finishReq(out <-chan interface{}, sync bool, timeout time.Duration, w http.ResponseWriter) {
op := s.ops.NewOperation(out)
if sync {
op.WaitFor(timeout)
}
obj, complete := op.StatusOrResult()
if complete {
status := http.StatusOK
switch stat := obj.(type) {
case api.Status:
httplog.LogOf(w).Addf("programmer error: use *api.Status as a result, not api.Status.")
if stat.Code != 0 {
status = stat.Code
}
case *api.Status:
if stat.Code != 0 {
status = stat.Code
}
}
writeJSON(status, obj, w)
} else {
writeJSON(http.StatusAccepted, obj, w)
}
}
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then
// on path length, according to the following table:
// Method Path Action
@ -217,6 +187,7 @@ func (s *APIServer) handleRESTStorage(parts []string, req *http.Request, w http.
default:
notFound(w, req)
}
case "POST":
if len(parts) != 1 {
notFound(w, req)
@ -245,7 +216,9 @@ func (s *APIServer) handleRESTStorage(parts []string, req *http.Request, w http.
internalError(err, w)
return
}
s.finishReq(out, sync, timeout, w)
op := s.createOperation(out, sync, timeout)
s.finishReq(op, w)
case "DELETE":
if len(parts) != 2 {
notFound(w, req)
@ -260,7 +233,9 @@ func (s *APIServer) handleRESTStorage(parts []string, req *http.Request, w http.
internalError(err, w)
return
}
s.finishReq(out, sync, timeout, w)
op := s.createOperation(out, sync, timeout)
s.finishReq(op, w)
case "PUT":
if len(parts) != 2 {
notFound(w, req)
@ -289,12 +264,51 @@ func (s *APIServer) handleRESTStorage(parts []string, req *http.Request, w http.
internalError(err, w)
return
}
s.finishReq(out, sync, timeout, w)
op := s.createOperation(out, sync, timeout)
s.finishReq(op, w)
default:
notFound(w, req)
}
}
// handleVersionReq writes the server's version information.
func handleVersion(w http.ResponseWriter, req *http.Request) {
writeRawJSON(http.StatusOK, version.Get(), w)
}
// createOperation creates an operation to process a channel response
func (s *APIServer) createOperation(out <-chan interface{}, sync bool, timeout time.Duration) *Operation {
op := s.ops.NewOperation(out)
if sync {
op.WaitFor(timeout)
}
return op
}
// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an
// Operation to receive the result and returning its ID down the writer.
func (s *APIServer) finishReq(op *Operation, w http.ResponseWriter) {
obj, complete := op.StatusOrResult()
if complete {
status := http.StatusOK
switch stat := obj.(type) {
case api.Status:
httplog.LogOf(w).Addf("programmer error: use *api.Status as a result, not api.Status.")
if stat.Code != 0 {
status = stat.Code
}
case *api.Status:
if stat.Code != 0 {
status = stat.Code
}
}
writeJSON(status, obj, w)
} else {
writeJSON(http.StatusAccepted, obj, w)
}
}
func (s *APIServer) operationPrefix() string {
return path.Join(s.prefix, "operations")
}
@ -340,6 +354,7 @@ func (s *APIServer) watchPrefix() string {
return path.Join(s.prefix, "watch")
}
// handleWatch processes a watch request
func (s *APIServer) handleWatch(w http.ResponseWriter, req *http.Request) {
prefix := s.watchPrefix()
if !strings.HasPrefix(req.URL.Path, prefix) {
@ -381,6 +396,7 @@ func (s *APIServer) handleWatch(w http.ResponseWriter, req *http.Request) {
notFound(w, req)
}
// writeJSON renders an object as JSON to the response
func writeJSON(statusCode int, object interface{}, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)

View File

@ -21,6 +21,7 @@ import (
"net/http"
)
// internalError renders a generic error to the response
func internalError(err error, w http.ResponseWriter) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Internal Error: %#v", err)