|
|
|
@ -28,6 +28,7 @@ import (
|
|
|
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
"github.com/prometheus/log"
|
|
|
|
|
"github.com/prometheus/prometheus/util/route"
|
|
|
|
|
|
|
|
|
|
clientmodel "github.com/prometheus/client_golang/model"
|
|
|
|
|
|
|
|
|
@ -50,7 +51,7 @@ var (
|
|
|
|
|
// WebService handles the HTTP endpoints with the exception of /api.
|
|
|
|
|
type WebService struct {
|
|
|
|
|
QuitChan chan struct{}
|
|
|
|
|
mux *http.ServeMux
|
|
|
|
|
router *route.Router
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type WebServiceOptions struct {
|
|
|
|
@ -64,61 +65,46 @@ type WebServiceOptions struct {
|
|
|
|
|
|
|
|
|
|
// NewWebService returns a new WebService.
|
|
|
|
|
func NewWebService(o *WebServiceOptions) *WebService {
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
router := route.New()
|
|
|
|
|
|
|
|
|
|
ws := &WebService{
|
|
|
|
|
mux: mux,
|
|
|
|
|
router: router,
|
|
|
|
|
QuitChan: make(chan struct{}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("/", prometheus.InstrumentHandlerFunc(o.PathPrefix, func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
|
// The "/" pattern matches everything, so we need to check
|
|
|
|
|
// that we're at the root here.
|
|
|
|
|
if req.URL.Path == o.PathPrefix+"/" {
|
|
|
|
|
o.StatusHandler.ServeHTTP(rw, req)
|
|
|
|
|
} else if req.URL.Path == 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
|
|
|
|
|
// outside of it. Let's see if this page exists under the prefix.
|
|
|
|
|
http.Redirect(rw, req, o.PathPrefix+req.URL.Path, http.StatusFound)
|
|
|
|
|
} else {
|
|
|
|
|
http.NotFound(rw, req)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
mux.Handle(o.PathPrefix+"/alerts", prometheus.InstrumentHandler(
|
|
|
|
|
o.PathPrefix+"/alerts", o.AlertsHandler,
|
|
|
|
|
))
|
|
|
|
|
mux.Handle(o.PathPrefix+"/consoles/", prometheus.InstrumentHandler(
|
|
|
|
|
o.PathPrefix+"/consoles/", http.StripPrefix(o.PathPrefix+"/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 o.PathPrefix != "" {
|
|
|
|
|
// If the prefix is missing for the root path, append it.
|
|
|
|
|
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
http.Redirect(w, r, o.PathPrefix, 301)
|
|
|
|
|
})
|
|
|
|
|
router = router.WithPrefix(o.PathPrefix)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instr := prometheus.InstrumentHandler
|
|
|
|
|
|
|
|
|
|
router.Get("/", instr("status", o.StatusHandler))
|
|
|
|
|
router.Get("/alerts", instr("alerts", o.AlertsHandler))
|
|
|
|
|
router.Get("/graph", instr("graph", o.GraphsHandler))
|
|
|
|
|
router.Get("/heap", instr("heap", http.HandlerFunc(dumpHeap)))
|
|
|
|
|
|
|
|
|
|
router.Get(*metricsPath, prometheus.Handler().ServeHTTP)
|
|
|
|
|
|
|
|
|
|
o.MetricsHandler.RegisterHandler(router.WithPrefix("/api"))
|
|
|
|
|
|
|
|
|
|
router.Get("/consoles/*filepath", instr("consoles", o.ConsolesHandler))
|
|
|
|
|
|
|
|
|
|
if *useLocalAssets {
|
|
|
|
|
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler(
|
|
|
|
|
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", http.FileServer(http.Dir("web/static"))),
|
|
|
|
|
))
|
|
|
|
|
router.Get("/static/*filepath", instr("static", route.FileServe("web/static")))
|
|
|
|
|
} else {
|
|
|
|
|
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler(
|
|
|
|
|
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", new(blob.Handler)),
|
|
|
|
|
))
|
|
|
|
|
router.Get("/static/*filepath", instr("static", blob.Handler{}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if *userAssetsPath != "" {
|
|
|
|
|
mux.Handle(o.PathPrefix+"/user/", prometheus.InstrumentHandler(
|
|
|
|
|
o.PathPrefix+"/user/", http.StripPrefix(o.PathPrefix+"/user/", http.FileServer(http.Dir(*userAssetsPath))),
|
|
|
|
|
))
|
|
|
|
|
router.Get("/user/*filepath", instr("user", route.FileServe(*userAssetsPath)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if *enableQuit {
|
|
|
|
|
mux.Handle(o.PathPrefix+"/-/quit", http.HandlerFunc(ws.quitHandler))
|
|
|
|
|
router.Post("/-/quit", ws.quitHandler)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ws
|
|
|
|
@ -130,7 +116,7 @@ func (ws *WebService) Run() {
|
|
|
|
|
|
|
|
|
|
// If we cannot bind to a port, retry after 30 seconds.
|
|
|
|
|
for {
|
|
|
|
|
err := http.ListenAndServe(*listenAddress, ws.mux)
|
|
|
|
|
err := http.ListenAndServe(*listenAddress, ws.router)
|
|
|
|
|
if err != nil {
|
|
|
|
|
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) {
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
w.Header().Add("Allow", "POST")
|
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "Requesting termination... Goodbye!")
|
|
|
|
|
|
|
|
|
|
close(ws.QuitChan)
|
|
|
|
|