webhookd/main.go

134 lines
3.1 KiB
Go
Raw Normal View History

package main
import (
2018-01-09 09:16:33 +00:00
"context"
"flag"
2018-01-09 09:16:33 +00:00
"fmt"
"log"
"net/http"
2018-01-09 09:16:33 +00:00
"os"
"os/signal"
"sync/atomic"
"time"
"github.com/ncarlier/webhookd/pkg/api"
2018-01-09 09:16:33 +00:00
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/worker"
)
2018-02-12 20:18:01 +00:00
// Version of the app
var Version = "snapshot"
2018-01-09 09:16:33 +00:00
type key int
const (
requestIDKey key = 0
)
var (
healthy int32
)
var (
2018-01-09 09:16:33 +00:00
listenAddr = flag.String("l", ":8080", "HTTP service address (e.g.address, ':8080')")
nbWorkers = flag.Int("n", 2, "The number of workers to start")
debug = flag.Bool("d", false, "Output debug logs")
)
func main() {
flag.Parse()
2018-01-09 09:16:33 +00:00
level := "info"
if *debug {
level = "debug"
}
logger.Init(level)
logger.Debug.Println("Starting webhookd server...")
router := http.NewServeMux()
router.Handle("/", api.Index())
router.Handle("/healthz", healthz())
nextRequestID := func() string {
return fmt.Sprintf("%d", time.Now().UnixNano())
}
server := &http.Server{
Addr: *listenAddr,
Handler: tracing(nextRequestID)(logging(logger.Debug)(router)),
ErrorLog: logger.Error,
2018-01-09 09:16:33 +00:00
}
// Start the dispatcher.
2018-01-09 09:16:33 +00:00
logger.Debug.Printf("Starting the dispatcher (%d workers)...\n", *nbWorkers)
worker.StartDispatcher(*nbWorkers)
2018-01-09 09:16:33 +00:00
done := make(chan bool)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
go func() {
<-quit
logger.Debug.Println("Server is shutting down...")
atomic.StoreInt32(&healthy, 0)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
server.SetKeepAlivesEnabled(false)
if err := server.Shutdown(ctx); err != nil {
logger.Error.Fatalf("Could not gracefully shutdown the server: %v\n", err)
}
close(done)
}()
logger.Info.Println("Server is ready to handle requests at", *listenAddr)
atomic.StoreInt32(&healthy, 1)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error.Fatalf("Could not listen on %s: %v\n", *listenAddr, err)
}
<-done
logger.Debug.Println("Server stopped")
}
func healthz() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&healthy) == 1 {
w.WriteHeader(http.StatusNoContent)
return
}
w.WriteHeader(http.StatusServiceUnavailable)
})
}
func logging(logger *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
requestID, ok := r.Context().Value(requestIDKey).(string)
if !ok {
requestID = "unknown"
}
logger.Println(requestID, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
}()
next.ServeHTTP(w, r)
})
}
}
func tracing(nextRequestID func() string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestID := r.Header.Get("X-Request-Id")
if requestID == "" {
requestID = nextRequestID()
}
ctx := context.WithValue(r.Context(), requestIDKey, requestID)
w.Header().Set("X-Request-Id", requestID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}