+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
package main
// REST handlers for Keep are implemented here.
"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 {
// 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`, 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}}`, 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.Mounts).Methods("GET")
- rest.HandleFunc(`/mounts/{uuid}/blocks`, rtr.MountBlocks).Methods("GET")
- rest.HandleFunc(`/mounts/{uuid}/blocks/{prefix:[0-9a-f]{0,32}}`, rtr.MountBlocks).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")
+
+ 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)
+
+ theConfig.metrics.setup()
- return rtr
+ 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.
resp.Write([]byte(returnHash + "\n"))
}
-// IndexHandler is a HandleFunc to address /index and /index/{prefix} requests.
-func IndexHandler(resp http.ResponseWriter, req *http.Request) {
- // Reject unauthorized requests.
+// IndexHandler responds to "/index", "/index/{prefix}", and
+// "/mounts/{uuid}/blocks" requests.
+func (rtr *router) IndexHandler(resp http.ResponseWriter, req *http.Request) {
if !IsSystemAuth(GetAPIToken(req)) {
http.Error(resp, UnauthorizedError.Error(), UnauthorizedError.HTTPCode)
return
}
prefix := mux.Vars(req)["prefix"]
+ if prefix == "" {
+ req.ParseForm()
+ prefix = req.Form.Get("prefix")
+ }
- for _, vol := range KeepVM.AllReadable() {
- if err := vol.IndexTo(prefix, resp); err != nil {
+ uuid := mux.Vars(req)["uuid"]
+
+ var vols []Volume
+ if uuid == "" {
+ vols = KeepVM.AllReadable()
+ } else if v := KeepVM.Lookup(uuid, false); v == nil {
+ http.Error(resp, "mount not found", http.StatusNotFound)
+ return
+ } else {
+ vols = []Volume{v}
+ }
+
+ for _, v := range vols {
+ if err := v.IndexTo(prefix, resp); err != nil {
// The only errors returned by IndexTo are
// write errors returned by resp.Write(),
// which probably means the client has
resp.Write([]byte{'\n'})
}
-// Mounts responds to "GET /mounts" requests.
-func (rtr *router) Mounts(resp http.ResponseWriter, req *http.Request) {
+// MountsHandler responds to "GET /mounts" requests.
+func (rtr *router) MountsHandler(resp http.ResponseWriter, req *http.Request) {
err := json.NewEncoder(resp).Encode(KeepVM.Mounts())
if err != nil {
http.Error(resp, err.Error(), http.StatusInternalServerError)
}
}
-// MountBlocks responds to "GET /mounts/{uuid}/blocks" requests.
-func (rtr *router) MountBlocks(resp http.ResponseWriter, req *http.Request) {
- if !IsSystemAuth(GetAPIToken(req)) {
- http.Error(resp, UnauthorizedError.Error(), UnauthorizedError.HTTPCode)
- return
- }
-
- uuid := mux.Vars(req)["uuid"]
- prefix := mux.Vars(req)["prefix"]
- if v := KeepVM.Lookup(uuid, false); v == nil {
- http.Error(resp, "mount not found", http.StatusNotFound)
- } else if err := v.IndexTo(prefix, resp); err != nil {
- http.Error(resp, err.Error(), http.StatusInternalServerError)
- } else {
- resp.Write([]byte{'\n'})
- }
-}
-
// PoolStatus struct
type PoolStatus struct {
- Alloc uint64 `json:"BytesAllocated"`
+ Alloc uint64 `json:"BytesAllocatedCumulative"`
Cap int `json:"BuffersMax"`
Len int `json:"BuffersInUse"`
}
TrashQueue WorkQueueStatus
RequestsCurrent int
RequestsMax int
+ Version string
}
var st NodeStatus
// 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))