Merge branch '13365-default-secondaryFiles' refs #13365
[arvados.git] / services / keepstore / handlers.go
index 101f42c20c23b82d939137e8bd2e537ef35b6526..a84a84db3c6027147168cc78f0e4615bde54ad2b 100644 (file)
@@ -29,8 +29,8 @@ import (
 
        "github.com/gorilla/mux"
 
+       "git.curoverse.com/arvados.git/sdk/go/health"
        "git.curoverse.com/arvados.git/sdk/go/httpserver"
-       log "github.com/Sirupsen/logrus"
 )
 
 type router struct {
@@ -40,52 +40,63 @@ type router struct {
 
 // MakeRESTRouter returns a new router that forwards all Keep requests
 // to the appropriate handlers.
-func MakeRESTRouter() *router {
-       rest := mux.NewRouter()
-       rtr := &router{Router: rest}
+func MakeRESTRouter() http.Handler {
+       rtr := &router{Router: mux.NewRouter()}
 
-       rest.HandleFunc(
+       rtr.HandleFunc(
                `/{hash:[0-9a-f]{32}}`, GetBlockHandler).Methods("GET", "HEAD")
-       rest.HandleFunc(
+       rtr.HandleFunc(
                `/{hash:[0-9a-f]{32}}+{hints}`,
                GetBlockHandler).Methods("GET", "HEAD")
 
-       rest.HandleFunc(`/{hash:[0-9a-f]{32}}`, PutBlockHandler).Methods("PUT")
-       rest.HandleFunc(`/{hash:[0-9a-f]{32}}`, DeleteHandler).Methods("DELETE")
+       rtr.HandleFunc(`/{hash:[0-9a-f]{32}}`, PutBlockHandler).Methods("PUT")
+       rtr.HandleFunc(`/{hash:[0-9a-f]{32}}`, DeleteHandler).Methods("DELETE")
        // List all blocks stored here. Privileged client only.
-       rest.HandleFunc(`/index`, rtr.IndexHandler).Methods("GET", "HEAD")
+       rtr.HandleFunc(`/index`, rtr.IndexHandler).Methods("GET", "HEAD")
        // List blocks stored here whose hash has the given prefix.
        // Privileged client only.
-       rest.HandleFunc(`/index/{prefix:[0-9a-f]{0,32}}`, rtr.IndexHandler).Methods("GET", "HEAD")
+       rtr.HandleFunc(`/index/{prefix:[0-9a-f]{0,32}}`, rtr.IndexHandler).Methods("GET", "HEAD")
 
        // Internals/debugging info (runtime.MemStats)
-       rest.HandleFunc(`/debug.json`, rtr.DebugHandler).Methods("GET", "HEAD")
+       rtr.HandleFunc(`/debug.json`, rtr.DebugHandler).Methods("GET", "HEAD")
 
        // List volumes: path, device number, bytes used/avail.
-       rest.HandleFunc(`/status.json`, rtr.StatusHandler).Methods("GET", "HEAD")
+       rtr.HandleFunc(`/status.json`, rtr.StatusHandler).Methods("GET", "HEAD")
 
        // List mounts: UUID, readonly, tier, device ID, ...
-       rest.HandleFunc(`/mounts`, rtr.MountsHandler).Methods("GET")
-       rest.HandleFunc(`/mounts/{uuid}/blocks`, rtr.IndexHandler).Methods("GET")
-       rest.HandleFunc(`/mounts/{uuid}/blocks/`, rtr.IndexHandler).Methods("GET")
+       rtr.HandleFunc(`/mounts`, rtr.MountsHandler).Methods("GET")
+       rtr.HandleFunc(`/mounts/{uuid}/blocks`, rtr.IndexHandler).Methods("GET")
+       rtr.HandleFunc(`/mounts/{uuid}/blocks/`, rtr.IndexHandler).Methods("GET")
 
        // Replace the current pull queue.
-       rest.HandleFunc(`/pull`, PullHandler).Methods("PUT")
+       rtr.HandleFunc(`/pull`, PullHandler).Methods("PUT")
 
        // Replace the current trash queue.
-       rest.HandleFunc(`/trash`, TrashHandler).Methods("PUT")
+       rtr.HandleFunc(`/trash`, TrashHandler).Methods("PUT")
 
        // Untrash moves blocks from trash back into store
-       rest.HandleFunc(`/untrash/{hash:[0-9a-f]{32}}`, UntrashHandler).Methods("PUT")
+       rtr.HandleFunc(`/untrash/{hash:[0-9a-f]{32}}`, UntrashHandler).Methods("PUT")
 
-       // Health check ping
-       rest.HandleFunc(`/_health/ping`, HealthCheckPingHandler).Methods("GET")
+       rtr.Handle("/_health/{check}", &health.Handler{
+               Token:  theConfig.ManagementToken,
+               Prefix: "/_health/",
+       }).Methods("GET")
 
        // Any request which does not match any of these routes gets
        // 400 Bad Request.
-       rest.NotFoundHandler = http.HandlerFunc(BadRequestHandler)
+       rtr.NotFoundHandler = http.HandlerFunc(BadRequestHandler)
 
-       return rtr
+       theConfig.metrics.setup()
+
+       rtr.limiter = httpserver.NewRequestLimiter(theConfig.MaxRequests, rtr)
+
+       mux := http.NewServeMux()
+       mux.Handle("/", theConfig.metrics.Instrument(
+               httpserver.AddRequestIDs(httpserver.LogRequests(rtr.limiter))))
+       mux.HandleFunc("/metrics.json", theConfig.metrics.exportJSON)
+       mux.Handle("/metrics", theConfig.metrics.exportProm)
+
+       return mux
 }
 
 // BadRequestHandler is a HandleFunc to address bad requests.
@@ -288,7 +299,7 @@ func (rtr *router) MountsHandler(resp http.ResponseWriter, req *http.Request) {
 
 // PoolStatus struct
 type PoolStatus struct {
-       Alloc uint64 `json:"BytesAllocated"`
+       Alloc uint64 `json:"BytesAllocatedCumulative"`
        Cap   int    `json:"BuffersMax"`
        Len   int    `json:"BuffersInUse"`
 }
@@ -308,6 +319,7 @@ type NodeStatus struct {
        TrashQueue      WorkQueueStatus
        RequestsCurrent int
        RequestsMax     int
+       Version         string
 }
 
 var st NodeStatus
@@ -343,6 +355,7 @@ func (rtr *router) StatusHandler(resp http.ResponseWriter, req *http.Request) {
 
 // populate the given NodeStatus struct with current values.
 func (rtr *router) readNodeStatus(st *NodeStatus) {
+       st.Version = version
        vols := KeepVM.AllReadable()
        if cap(st.Volumes) < len(vols) {
                st.Volumes = make([]*volumeStatusEnt, len(vols))
@@ -501,7 +514,7 @@ type PullRequest struct {
        Servers []string `json:"servers"`
 
        // Destination mount, or "" for "anywhere"
-       MountUUID string
+       MountUUID string `json:"mount_uuid"`
 }
 
 // PullHandler processes "PUT /pull" requests for the data manager.
@@ -534,13 +547,13 @@ func PullHandler(resp http.ResponseWriter, req *http.Request) {
        pullq.ReplaceQueue(plist)
 }
 
-// TrashRequest consists of a block locator and it's Mtime
+// TrashRequest consists of a block locator and its Mtime
 type TrashRequest struct {
        Locator    string `json:"locator"`
        BlockMtime int64  `json:"block_mtime"`
 
        // Target mount, or "" for "everywhere"
-       MountUUID string
+       MountUUID string `json:"mount_uuid"`
 }
 
 // TrashHandler processes /trash requests.
@@ -620,45 +633,6 @@ func UntrashHandler(resp http.ResponseWriter, req *http.Request) {
        }
 }
 
-// HealthCheckPingHandler processes "GET /_health/ping" requests
-func HealthCheckPingHandler(resp http.ResponseWriter, req *http.Request) {
-       fn := func() interface{} {
-               return map[string]string{"health": "OK"}
-       }
-
-       healthCheckDo(resp, req, fn)
-}
-
-// Any health check handlers can pass this "func" which returns json to healthCheckDo
-type healthCheckFunc func() interface{}
-
-func healthCheckDo(resp http.ResponseWriter, req *http.Request, fn healthCheckFunc) {
-       msg, code := healthCheckAuth(resp, req)
-       if msg != "" {
-               http.Error(resp, msg, code)
-               return
-       }
-
-       ok, err := json.Marshal(fn())
-       if err != nil {
-               http.Error(resp, err.Error(), 500)
-               return
-       }
-
-       resp.Write(ok)
-}
-
-func healthCheckAuth(resp http.ResponseWriter, req *http.Request) (string, int) {
-       if theConfig.ManagementToken == "" {
-               return "disabled", http.StatusNotFound
-       } else if h := req.Header.Get("Authorization"); h == "" {
-               return "authorization required", http.StatusUnauthorized
-       } else if h != "Bearer "+theConfig.ManagementToken {
-               return "authorization error", http.StatusForbidden
-       }
-       return "", 0
-}
-
 // GetBlock and PutBlock implement lower-level code for handling
 // blocks by rooting through volumes connected to the local machine.
 // Once the handler has determined that system policy permits the