Merge pull request #52 from quinox/pathprefix

Path prefix to support reverse proxies
This commit is contained in:
Julius Volz 2015-05-12 13:39:49 +02:00
commit 82ced0dc2a
9 changed files with 71 additions and 50 deletions

25
main.go
View File

@ -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)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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>

View File

@ -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"}}

View File

@ -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"}}

View File

@ -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