webhookd/main.go

147 lines
3.5 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"
"github.com/ncarlier/webhookd/pkg/auth"
2018-01-09 09:16:33 +00:00
"github.com/ncarlier/webhookd/pkg/logger"
"github.com/ncarlier/webhookd/pkg/worker"
)
2018-01-09 09:16:33 +00:00
type key int
const (
requestIDKey key = 0
)
var (
healthy int32
)
func main() {
flag.Parse()
if *version {
printVersion()
2018-07-24 16:02:57 +00:00
return
}
var authmethod auth.Method
name := *config.Authentication
if _, ok := auth.AvailableMethods[name]; ok {
authmethod = auth.AvailableMethods[name]
if err := authmethod.ParseParam(*config.AuthenticationParam); err != nil {
fmt.Println("Authentication parameter is not valid:", err.Error())
fmt.Println(authmethod.Usage())
os.Exit(2)
}
} else {
fmt.Println("Authentication name is not valid:", name)
os.Exit(2)
}
2018-01-09 09:16:33 +00:00
level := "info"
if *config.Debug {
2018-01-09 09:16:33 +00:00
level = "debug"
}
logger.Init(level)
logger.Debug.Println("Starting webhookd server...")
logger.Debug.Println("Using Authentication:", name)
authmethod.Init(*config.Debug)
2018-01-09 09:16:33 +00:00
router := http.NewServeMux()
router.Handle("/", api.Index(*config.Timeout, *config.ScriptDir))
2018-01-09 09:16:33 +00:00
router.Handle("/healthz", healthz())
nextRequestID := func() string {
return fmt.Sprintf("%d", time.Now().UnixNano())
}
server := &http.Server{
Addr: *config.ListenAddr,
Handler: authmethod.Middleware()(tracing(nextRequestID)(logging(logger.Debug)(router))),
ErrorLog: logger.Error,
2018-01-09 09:16:33 +00:00
}
// Start the dispatcher.
logger.Debug.Printf("Starting the dispatcher (%d workers)...\n", *config.NbWorkers)
worker.StartDispatcher(*config.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", *config.ListenAddr)
2018-01-09 09:16:33 +00:00
atomic.StoreInt32(&healthy, 1)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error.Fatalf("Could not listen on %s: %v\n", *config.ListenAddr, err)
2018-01-09 09:16:33 +00:00
}
<-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))
})
}
}