Archived
1
0
Fork 0

Initial release

This commit is contained in:
Julien Riou 2019-03-08 18:14:22 +01:00
commit 9818566369
No known key found for this signature in database
GPG key ID: BA3E15176E45E85D
10 changed files with 634 additions and 0 deletions

142
src/frontend.go Normal file
View file

@ -0,0 +1,142 @@
package main
import (
"fmt"
"io"
"net/http"
"strings"
"github.com/gorilla/mux"
)
// Frontend exposes statuses over HTTP(S)
type Frontend struct {
backend Backend
host string
port int
certfile string
keyfile string
}
var backend Backend
var logFormat string
// Start creates an HTTP server and listen
func (f *Frontend) Start() error {
Debug("creating router")
r := mux.NewRouter()
r.Use(loggingMiddleware)
r.Use(headersMiddleware)
Debug("registering routes")
r.HandleFunc("/health", HealthHandler).Methods("GET")
r.HandleFunc("/master", PrimaryHandler).Methods("GET", "OPTIONS")
r.HandleFunc("/replica", ReplicaHandler).Methods("GET", "OPTIONS")
Info("listening on %s", f)
var err error
if f.certfile != "" && f.keyfile != "" {
err = http.ListenAndServeTLS(f.String(), f.certfile, f.keyfile, r)
} else {
err = http.ListenAndServe(f.String(), r)
}
if err != nil {
return err
}
return nil
}
func (f *Frontend) String() string {
return fmt.Sprintf("%s:%d", f.host, f.port)
}
// NewFrontend creates a Frontend
func NewFrontend(config FrontendConfig, b Backend) (*Frontend, error) {
backend = b
logFormat = config.LogFormat
return &Frontend{
host: config.Host,
port: config.Port,
certfile: config.Certfile,
keyfile: config.Keyfile,
}, nil
}
// Log requests
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Info(formatRequest(r, logFormat))
next.ServeHTTP(w, r)
})
}
// formatRequest replaces request placeholders for logging purpose
func formatRequest(r *http.Request, format string) string {
if format == "" {
format = "%a - %m %U"
}
definitions := map[string]string{
"%a": r.RemoteAddr,
"%m": r.Method,
"%U": r.RequestURI,
}
output := format
for placeholder, value := range definitions {
output = strings.Replace(output, placeholder, value, -1)
}
return output
}
// Add headers
func headersMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}
// HealthHandler returns frontend health status
func HealthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, `{"healthy": true}`)
}
// PrimaryHandler exposes primary status
func PrimaryHandler(w http.ResponseWriter, r *http.Request) {
var message string
var status int
primary, err := backend.IsPrimary()
if err != nil {
message = fmt.Sprintf("{\"error\":\"%v\"}", err)
status = http.StatusServiceUnavailable
}
message = fmt.Sprintf("{\"primary\":%t}", primary)
status = http.StatusServiceUnavailable
if primary {
status = http.StatusOK
}
w.WriteHeader(status)
io.WriteString(w, message)
}
// ReplicaHandler exposes replica status
func ReplicaHandler(w http.ResponseWriter, r *http.Request) {
var message string
var status int
replica, err := backend.IsReplica()
if err != nil {
message = fmt.Sprintf("{\"error\":\"%v\"}", err)
status = http.StatusServiceUnavailable
}
message = fmt.Sprintf("{\"replica\":%t}", replica)
status = http.StatusServiceUnavailable
if replica {
status = http.StatusOK
}
w.WriteHeader(status)
io.WriteString(w, message)
}