mirror of
https://github.com/prometheus/alertmanager
synced 2025-02-16 10:37:09 +00:00
Merge pull request #52 from quinox/pathprefix
Path prefix to support reverse proxies
This commit is contained in:
commit
82ced0dc2a
25
main.go
25
main.go
@ -16,6 +16,7 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -30,11 +31,19 @@ var (
|
||||
configFile = flag.String("config.file", "alertmanager.conf", "Alert Manager configuration file name.")
|
||||
silencesFile = flag.String("silences.file", "silences.json", "Silence storage file name.")
|
||||
minRefreshPeriod = flag.Duration("alerts.min-refresh-period", 5*time.Minute, "Minimum required alert refresh period before an alert is purged.")
|
||||
pathPrefix = flag.String("web.path-prefix", "/", "Prefix for all web paths.")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if !strings.HasPrefix(*pathPrefix, "/") {
|
||||
*pathPrefix = "/" + *pathPrefix
|
||||
}
|
||||
if !strings.HasSuffix(*pathPrefix, "/") {
|
||||
*pathPrefix = *pathPrefix + "/"
|
||||
}
|
||||
|
||||
versionInfoTmpl.Execute(os.Stdout, BuildInfo)
|
||||
|
||||
conf := config.MustLoadFromFile(*configFile)
|
||||
@ -79,17 +88,19 @@ func main() {
|
||||
})
|
||||
|
||||
statusHandler := &web.StatusHandler{
|
||||
Config: conf.String(),
|
||||
Flags: flags,
|
||||
BuildInfo: BuildInfo,
|
||||
Birth: time.Now(),
|
||||
Config: conf.String(),
|
||||
Flags: flags,
|
||||
BuildInfo: BuildInfo,
|
||||
Birth: time.Now(),
|
||||
PathPrefix: *pathPrefix,
|
||||
}
|
||||
|
||||
webService := &web.WebService{
|
||||
// REST API Service.
|
||||
AlertManagerService: &api.AlertManagerService{
|
||||
Manager: alertManager,
|
||||
Silencer: silencer,
|
||||
Manager: alertManager,
|
||||
Silencer: silencer,
|
||||
PathPrefix: *pathPrefix,
|
||||
},
|
||||
|
||||
// Template-based page handlers.
|
||||
@ -102,7 +113,7 @@ func main() {
|
||||
},
|
||||
StatusHandler: statusHandler,
|
||||
}
|
||||
go webService.ServeForever()
|
||||
go webService.ServeForever(*pathPrefix)
|
||||
|
||||
// React to configuration changes.
|
||||
watcher := config.NewFileWatcher(*configFile)
|
||||
|
@ -27,6 +27,7 @@ type AlertStatus struct {
|
||||
type AlertsHandler struct {
|
||||
Manager manager.AlertManager
|
||||
IsSilencedInterrogator manager.IsSilencedInterrogator
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
func (h *AlertsHandler) silenceForAlert(a *manager.Alert) *manager.Silence {
|
||||
@ -39,5 +40,5 @@ func (h *AlertsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
AlertAggregates: h.Manager.GetAll(nil),
|
||||
SilenceForAlert: h.silenceForAlert,
|
||||
}
|
||||
executeTemplate(w, "alerts", alertStatus)
|
||||
executeTemplate(w, "alerts", alertStatus, h.PathPrefix)
|
||||
}
|
||||
|
@ -27,17 +27,17 @@ import (
|
||||
type AlertManagerService struct {
|
||||
Manager manager.AlertManager
|
||||
Silencer *manager.Silencer
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
func (s AlertManagerService) Handler() http.Handler {
|
||||
r := httprouter.New()
|
||||
|
||||
r.POST("/api/alerts", s.addAlerts)
|
||||
r.GET("/api/silences", s.silenceSummary)
|
||||
r.POST("/api/silences", s.addSilence)
|
||||
r.GET("/api/silences/:id", s.getSilence)
|
||||
r.POST("/api/silences/:id", s.updateSilence)
|
||||
r.DELETE("/api/silences/:id", s.deleteSilence)
|
||||
r.POST(s.PathPrefix + "api/alerts", s.addAlerts)
|
||||
r.GET(s.PathPrefix + "api/silences", s.silenceSummary)
|
||||
r.POST(s.PathPrefix + "api/silences", s.addSilence)
|
||||
r.GET(s.PathPrefix + "api/silences/:id", s.getSilence)
|
||||
r.POST(s.PathPrefix + "api/silences/:id", s.updateSilence)
|
||||
r.DELETE(s.PathPrefix + "api/silences/:id", s.deleteSilence)
|
||||
|
||||
return r
|
||||
}
|
||||
|
@ -25,11 +25,12 @@ type SilenceStatus struct {
|
||||
|
||||
type SilencesHandler struct {
|
||||
Silencer *manager.Silencer
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
func (h *SilencesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
silenceStatus := &SilenceStatus{
|
||||
Silences: h.Silencer.SilenceSummary(),
|
||||
}
|
||||
executeTemplate(w, "silences", silenceStatus)
|
||||
executeTemplate(w, "silences", silenceStatus, h.PathPrefix)
|
||||
}
|
||||
|
@ -22,10 +22,11 @@ import (
|
||||
type StatusHandler struct {
|
||||
mu sync.Mutex
|
||||
|
||||
BuildInfo map[string]string
|
||||
Config string
|
||||
Flags map[string]string
|
||||
Birth time.Time
|
||||
BuildInfo map[string]string
|
||||
Config string
|
||||
Flags map[string]string
|
||||
Birth time.Time
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
func (h *StatusHandler) UpdateConfig(c string) {
|
||||
@ -39,5 +40,5 @@ func (h *StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
executeTemplate(w, "status", h)
|
||||
executeTemplate(w, "status", h, h.PathPrefix)
|
||||
}
|
||||
|
@ -7,13 +7,13 @@
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
||||
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<link href="{{ pathPrefix }}static/vendor/bootstrap/css/bootstrap.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<script src="{{ pathPrefix }}static/vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
|
||||
<link href="/static/vendor/tarruda_bootstrap_datetimepicker/css/bootstrap-datetimepicker.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<script src="/static/vendor/tarruda_bootstrap_datetimepicker/js/bootstrap-datetimepicker.min.js" type="text/javascript"></script>
|
||||
<link href="{{ pathPrefix }}static/vendor/tarruda_bootstrap_datetimepicker/css/bootstrap-datetimepicker.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<script src="{{ pathPrefix }}static/vendor/tarruda_bootstrap_datetimepicker/js/bootstrap-datetimepicker.min.js" type="text/javascript"></script>
|
||||
|
||||
<link href="/static/css/default.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<link href="{{ pathPrefix }}static/css/default.css" media="all" rel="stylesheet" type="text/css" />
|
||||
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
@ -26,9 +26,9 @@
|
||||
{{define "alertsTabClass"}}{{end}}
|
||||
{{define "silencesTabClass"}}{{end}}
|
||||
{{define "statusTabClass"}}{{end}}
|
||||
<li class="{{template "alertsTabClass"}}"><a href="/alerts">Alerts</a></li>
|
||||
<li class="{{template "silencesTabClass"}}"><a href="/silences">Silences</a></li>
|
||||
<li class="{{template "statusTabClass"}}"><a href="/status">Status</a></li>
|
||||
<li class="{{template "alertsTabClass"}}"><a href="{{ pathPrefix }}alerts">Alerts</a></li>
|
||||
<li class="{{template "silencesTabClass"}}"><a href="{{ pathPrefix }}silences">Silences</a></li>
|
||||
<li class="{{template "statusTabClass"}}"><a href="{{ pathPrefix }}status">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{define "alertsTabClass"}}active{{end}}
|
||||
|
||||
{{define "head"}}
|
||||
<script src="/static/js/alerts.js"></script>
|
||||
<script src="{{ pathPrefix }}static/js/alerts.js"></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{define "silencesTabClass"}}active{{end}}
|
||||
|
||||
{{define "head"}}
|
||||
<script src="/static/js/alerts.js"></script>
|
||||
<script src="{{ pathPrefix }}static/js/alerts.js"></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
|
45
web/web.go
45
web/web.go
@ -40,51 +40,58 @@ type WebService struct {
|
||||
StatusHandler *StatusHandler
|
||||
}
|
||||
|
||||
func (w WebService) ServeForever() error {
|
||||
func (w WebService) ServeForever(pathPrefix string) error {
|
||||
|
||||
http.Handle("/favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Handle(pathPrefix + "favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "", 404)
|
||||
}))
|
||||
|
||||
|
||||
http.HandleFunc("/", prometheus.InstrumentHandlerFunc("index", 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 != "/" {
|
||||
if req.URL.Path == pathPrefix {
|
||||
w.AlertsHandler.ServeHTTP(rw, req)
|
||||
} else if req.URL.Path == "/" {
|
||||
// We're running under a prefix but the user requested "/".
|
||||
http.Redirect(rw, req, pathPrefix, http.StatusFound)
|
||||
} else {
|
||||
http.NotFound(rw, req)
|
||||
return
|
||||
}
|
||||
w.AlertsHandler.ServeHTTP(rw, req)
|
||||
}))
|
||||
|
||||
http.Handle("/alerts", prometheus.InstrumentHandler("alerts", w.AlertsHandler))
|
||||
http.Handle("/silences", prometheus.InstrumentHandler("silences", w.SilencesHandler))
|
||||
http.Handle("/status", prometheus.InstrumentHandler("status", w.StatusHandler))
|
||||
http.Handle(pathPrefix + "alerts", prometheus.InstrumentHandler("alerts", w.AlertsHandler))
|
||||
http.Handle(pathPrefix + "silences", prometheus.InstrumentHandler("silences", w.SilencesHandler))
|
||||
http.Handle(pathPrefix + "status", prometheus.InstrumentHandler("status", w.StatusHandler))
|
||||
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
http.Handle(pathPrefix + "metrics", prometheus.Handler())
|
||||
if *useLocalAssets {
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||
http.Handle(pathPrefix + "static/", http.StripPrefix(pathPrefix + "static/", http.FileServer(http.Dir("web/static"))))
|
||||
} else {
|
||||
http.Handle("/static/", http.StripPrefix("/static/", new(blob.Handler)))
|
||||
http.Handle(pathPrefix + "static/", http.StripPrefix(pathPrefix + "static/", new(blob.Handler)))
|
||||
}
|
||||
http.Handle("/api/", w.AlertManagerService.Handler())
|
||||
http.Handle(pathPrefix + "api/", w.AlertManagerService.Handler())
|
||||
|
||||
glog.Info("listening on ", *listenAddress)
|
||||
|
||||
return http.ListenAndServe(*listenAddress, nil)
|
||||
}
|
||||
|
||||
func getLocalTemplate(name string) (*template.Template, error) {
|
||||
func getLocalTemplate(name string, pathPrefix string) (*template.Template, error) {
|
||||
t := template.New("_base.html")
|
||||
t.Funcs(webHelpers)
|
||||
t.Funcs(template.FuncMap{"pathPrefix": func() string { return pathPrefix }})
|
||||
|
||||
return t.ParseFiles(
|
||||
"web/templates/_base.html",
|
||||
fmt.Sprintf("web/templates/%s.html", name),
|
||||
)
|
||||
}
|
||||
|
||||
func getEmbeddedTemplate(name string) (*template.Template, error) {
|
||||
func getEmbeddedTemplate(name string, pathPrefix string) (*template.Template, error) {
|
||||
t := template.New("_base.html")
|
||||
t.Funcs(webHelpers)
|
||||
t.Funcs(template.FuncMap{"pathPrefix": func() string { return pathPrefix }})
|
||||
|
||||
file, err := blob.GetFile(blob.TemplateFiles, "_base.html")
|
||||
if err != nil {
|
||||
@ -103,11 +110,11 @@ func getEmbeddedTemplate(name string) (*template.Template, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func getTemplate(name string) (t *template.Template, err error) {
|
||||
func getTemplate(name string, pathPrefix string) (t *template.Template, err error) {
|
||||
if *useLocalAssets {
|
||||
t, err = getLocalTemplate(name)
|
||||
t, err = getLocalTemplate(name, pathPrefix)
|
||||
} else {
|
||||
t, err = getEmbeddedTemplate(name)
|
||||
t, err = getEmbeddedTemplate(name, pathPrefix)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -117,8 +124,8 @@ func getTemplate(name string) (t *template.Template, err error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func executeTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
tpl, err := getTemplate(name)
|
||||
func executeTemplate(w http.ResponseWriter, name string, data interface{}, pathPrefix string) {
|
||||
tpl, err := getTemplate(name, pathPrefix)
|
||||
if err != nil {
|
||||
glog.Error("Error preparing layout template: ", err)
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user