mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-11 13:57:36 -08:00
web: simplify prefix handling using util/route package.
This commit is contained in:
parent
6c24114a7b
commit
e88e5f680b
|
@ -60,8 +60,7 @@ func New() *Router {
|
||||||
return &Router{rtr: httprouter.New()}
|
return &Router{rtr: httprouter.New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPrefix returns a router that prefixes all registered routes
|
// WithPrefix returns a router that prefixes all registered routes with prefix.
|
||||||
// with preifx.
|
|
||||||
func (r *Router) WithPrefix(prefix string) *Router {
|
func (r *Router) WithPrefix(prefix string) *Router {
|
||||||
return &Router{rtr: r.rtr, prefix: r.prefix + prefix}
|
return &Router{rtr: r.rtr, prefix: r.prefix + prefix}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/prometheus/prometheus/promql"
|
"github.com/prometheus/prometheus/promql"
|
||||||
"github.com/prometheus/prometheus/storage/local"
|
"github.com/prometheus/prometheus/storage/local"
|
||||||
"github.com/prometheus/prometheus/util/httputil"
|
"github.com/prometheus/prometheus/util/httputil"
|
||||||
|
"github.com/prometheus/prometheus/util/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetricsService manages the /api HTTP endpoint.
|
// MetricsService manages the /api HTTP endpoint.
|
||||||
|
@ -33,19 +34,15 @@ type MetricsService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHandler registers the handler for the various endpoints below /api.
|
// RegisterHandler registers the handler for the various endpoints below /api.
|
||||||
func (msrv *MetricsService) RegisterHandler(mux *http.ServeMux, pathPrefix string) {
|
func (msrv *MetricsService) RegisterHandler(router *route.Router) {
|
||||||
handler := func(h func(http.ResponseWriter, *http.Request)) http.Handler {
|
router.Get("/query", handle("query", msrv.Query))
|
||||||
return httputil.CompressionHandler{
|
router.Get("/query_range", handle("query_range", msrv.QueryRange))
|
||||||
Handler: http.HandlerFunc(h),
|
router.Get("/metrics", handle("metrics", msrv.Metrics))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
mux.Handle(pathPrefix+"/api/query", prometheus.InstrumentHandler(
|
func handle(name string, f http.HandlerFunc) http.HandlerFunc {
|
||||||
pathPrefix+"/api/query", handler(msrv.Query),
|
h := httputil.CompressionHandler{
|
||||||
))
|
Handler: f,
|
||||||
mux.Handle(pathPrefix+"/api/query_range", prometheus.InstrumentHandler(
|
}
|
||||||
pathPrefix+"/api/query_range", handler(msrv.QueryRange),
|
return prometheus.InstrumentHandler(name, h)
|
||||||
))
|
|
||||||
mux.Handle(pathPrefix+"/api/metrics", prometheus.InstrumentHandler(
|
|
||||||
pathPrefix+"/api/metrics", handler(msrv.Metrics),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/promql"
|
"github.com/prometheus/prometheus/promql"
|
||||||
"github.com/prometheus/prometheus/storage/local"
|
"github.com/prometheus/prometheus/storage/local"
|
||||||
|
"github.com/prometheus/prometheus/util/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a bit annoying. On one hand, we have to choose a current timestamp
|
// This is a bit annoying. On one hand, we have to choose a current timestamp
|
||||||
|
@ -97,9 +98,10 @@ func TestQuery(t *testing.T) {
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
QueryEngine: promql.NewEngine(storage),
|
QueryEngine: promql.NewEngine(storage),
|
||||||
}
|
}
|
||||||
api.RegisterHandler(http.DefaultServeMux, "")
|
rtr := route.New()
|
||||||
|
api.RegisterHandler(rtr.WithPrefix("/api"))
|
||||||
|
|
||||||
server := httptest.NewServer(http.DefaultServeMux)
|
server := httptest.NewServer(rtr)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
for i, s := range scenarios {
|
for i, s := range scenarios {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/prometheus/log"
|
"github.com/prometheus/log"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/util/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sub-directories for templates and static content.
|
// Sub-directories for templates and static content.
|
||||||
|
@ -46,7 +48,9 @@ func GetFile(bucket string, name string) ([]byte, error) {
|
||||||
type Handler struct{}
|
type Handler struct{}
|
||||||
|
|
||||||
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.URL.Path
|
ctx := route.Context(r)
|
||||||
|
|
||||||
|
name := strings.Trim(route.Param(ctx, "filepath"), "/")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "index.html"
|
name = "index.html"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,10 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/promql"
|
"github.com/prometheus/prometheus/promql"
|
||||||
"github.com/prometheus/prometheus/template"
|
"github.com/prometheus/prometheus/template"
|
||||||
|
"github.com/prometheus/prometheus/util/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -38,7 +40,10 @@ type ConsolesHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
file, err := http.Dir(*consoleTemplatesPath).Open(r.URL.Path)
|
ctx := route.Context(r)
|
||||||
|
name := route.Param(ctx, "filepath")
|
||||||
|
|
||||||
|
file, err := http.Dir(*consoleTemplatesPath).Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
@ -67,10 +72,10 @@ func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}{
|
}{
|
||||||
RawParams: rawParams,
|
RawParams: rawParams,
|
||||||
Params: params,
|
Params: params,
|
||||||
Path: r.URL.Path,
|
Path: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := template.NewTemplateExpander(string(text), "__console_"+r.URL.Path, data, clientmodel.Now(), h.QueryEngine, h.PathPrefix)
|
tmpl := template.NewTemplateExpander(string(text), "__console_"+name, data, clientmodel.Now(), h.QueryEngine, h.PathPrefix)
|
||||||
filenames, err := filepath.Glob(*consoleLibrariesPath + "/*.lib")
|
filenames, err := filepath.Glob(*consoleLibrariesPath + "/*.lib")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
|
78
web/web.go
78
web/web.go
|
@ -28,6 +28,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/log"
|
"github.com/prometheus/log"
|
||||||
|
"github.com/prometheus/prometheus/util/route"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ var (
|
||||||
// WebService handles the HTTP endpoints with the exception of /api.
|
// WebService handles the HTTP endpoints with the exception of /api.
|
||||||
type WebService struct {
|
type WebService struct {
|
||||||
QuitChan chan struct{}
|
QuitChan chan struct{}
|
||||||
mux *http.ServeMux
|
router *route.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebServiceOptions struct {
|
type WebServiceOptions struct {
|
||||||
|
@ -64,61 +65,46 @@ type WebServiceOptions struct {
|
||||||
|
|
||||||
// NewWebService returns a new WebService.
|
// NewWebService returns a new WebService.
|
||||||
func NewWebService(o *WebServiceOptions) *WebService {
|
func NewWebService(o *WebServiceOptions) *WebService {
|
||||||
mux := http.NewServeMux()
|
router := route.New()
|
||||||
|
|
||||||
ws := &WebService{
|
ws := &WebService{
|
||||||
mux: mux,
|
router: router,
|
||||||
QuitChan: make(chan struct{}),
|
QuitChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
mux.HandleFunc("/", prometheus.InstrumentHandlerFunc(o.PathPrefix, func(rw http.ResponseWriter, req *http.Request) {
|
if o.PathPrefix != "" {
|
||||||
// The "/" pattern matches everything, so we need to check
|
// If the prefix is missing for the root path, append it.
|
||||||
// that we're at the root here.
|
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if req.URL.Path == o.PathPrefix+"/" {
|
http.Redirect(w, r, o.PathPrefix, 301)
|
||||||
o.StatusHandler.ServeHTTP(rw, req)
|
})
|
||||||
} else if req.URL.Path == o.PathPrefix {
|
router = router.WithPrefix(o.PathPrefix)
|
||||||
http.Redirect(rw, req, o.PathPrefix+"/", http.StatusFound)
|
}
|
||||||
} else if !strings.HasPrefix(req.URL.Path, o.PathPrefix+"/") {
|
|
||||||
// We're running under a prefix but the user requested something
|
instr := prometheus.InstrumentHandler
|
||||||
// outside of it. Let's see if this page exists under the prefix.
|
|
||||||
http.Redirect(rw, req, o.PathPrefix+req.URL.Path, http.StatusFound)
|
router.Get("/", instr("status", o.StatusHandler))
|
||||||
} else {
|
router.Get("/alerts", instr("alerts", o.AlertsHandler))
|
||||||
http.NotFound(rw, req)
|
router.Get("/graph", instr("graph", o.GraphsHandler))
|
||||||
}
|
router.Get("/heap", instr("heap", http.HandlerFunc(dumpHeap)))
|
||||||
}))
|
|
||||||
mux.Handle(o.PathPrefix+"/alerts", prometheus.InstrumentHandler(
|
router.Get(*metricsPath, prometheus.Handler().ServeHTTP)
|
||||||
o.PathPrefix+"/alerts", o.AlertsHandler,
|
|
||||||
))
|
o.MetricsHandler.RegisterHandler(router.WithPrefix("/api"))
|
||||||
mux.Handle(o.PathPrefix+"/consoles/", prometheus.InstrumentHandler(
|
|
||||||
o.PathPrefix+"/consoles/", http.StripPrefix(o.PathPrefix+"/consoles/", o.ConsolesHandler),
|
router.Get("/consoles/*filepath", instr("consoles", o.ConsolesHandler))
|
||||||
))
|
|
||||||
mux.Handle(o.PathPrefix+"/graph", prometheus.InstrumentHandler(
|
|
||||||
o.PathPrefix+"/graph", o.GraphsHandler,
|
|
||||||
))
|
|
||||||
mux.Handle(o.PathPrefix+"/heap", prometheus.InstrumentHandler(
|
|
||||||
o.PathPrefix+"/heap", http.HandlerFunc(dumpHeap),
|
|
||||||
))
|
|
||||||
|
|
||||||
o.MetricsHandler.RegisterHandler(mux, o.PathPrefix)
|
|
||||||
mux.Handle(o.PathPrefix+*metricsPath, prometheus.Handler())
|
|
||||||
if *useLocalAssets {
|
if *useLocalAssets {
|
||||||
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler(
|
router.Get("/static/*filepath", instr("static", route.FileServe("web/static")))
|
||||||
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", http.FileServer(http.Dir("web/static"))),
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler(
|
router.Get("/static/*filepath", instr("static", blob.Handler{}))
|
||||||
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", new(blob.Handler)),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *userAssetsPath != "" {
|
if *userAssetsPath != "" {
|
||||||
mux.Handle(o.PathPrefix+"/user/", prometheus.InstrumentHandler(
|
router.Get("/user/*filepath", instr("user", route.FileServe(*userAssetsPath)))
|
||||||
o.PathPrefix+"/user/", http.StripPrefix(o.PathPrefix+"/user/", http.FileServer(http.Dir(*userAssetsPath))),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *enableQuit {
|
if *enableQuit {
|
||||||
mux.Handle(o.PathPrefix+"/-/quit", http.HandlerFunc(ws.quitHandler))
|
router.Post("/-/quit", ws.quitHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ws
|
return ws
|
||||||
|
@ -130,7 +116,7 @@ func (ws *WebService) Run() {
|
||||||
|
|
||||||
// If we cannot bind to a port, retry after 30 seconds.
|
// If we cannot bind to a port, retry after 30 seconds.
|
||||||
for {
|
for {
|
||||||
err := http.ListenAndServe(*listenAddress, ws.mux)
|
err := http.ListenAndServe(*listenAddress, ws.router)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not listen on %s: %s", *listenAddress, err)
|
log.Errorf("Could not listen on %s: %s", *listenAddress, err)
|
||||||
}
|
}
|
||||||
|
@ -139,12 +125,6 @@ func (ws *WebService) Run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
|
func (ws *WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != "POST" {
|
|
||||||
w.Header().Add("Allow", "POST")
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "Requesting termination... Goodbye!")
|
fmt.Fprintf(w, "Requesting termination... Goodbye!")
|
||||||
|
|
||||||
close(ws.QuitChan)
|
close(ws.QuitChan)
|
||||||
|
|
Loading…
Reference in a new issue