author | ThinkChaos
<ThinkChaos@users.noreply.github.com> 2020-10-31 20:46:47 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-11-12 23:24:21 UTC |
parent | e9c6775418f9bb2c13097a5e05fa6aed4ecba0b3 |
chasquid.go | +1 | -1 |
monitoring.go | +29 | -1 |
test/t-13-reload/run.sh | +11 | -0 |
test/util/lib.sh | +9 | -0 |
diff --git a/chasquid.go b/chasquid.go index 32491ba..00bb2a7 100644 --- a/chasquid.go +++ b/chasquid.go @@ -93,7 +93,7 @@ func main() { go signalHandler() if conf.MonitoringAddress != "" { - launchMonitoringServer(conf) + go launchMonitoringServer(conf) } s := smtpsrv.NewServer() diff --git a/monitoring.go b/monitoring.go index 1009bce..6f6aab3 100644 --- a/monitoring.go +++ b/monitoring.go @@ -1,6 +1,7 @@ package main import ( + "context" "flag" "fmt" "html/template" @@ -49,11 +50,16 @@ func launchMonitoringServer(conf *config.Config) { } }) + srv := &http.Server{Addr: conf.MonitoringAddress} + + http.HandleFunc("/exit", exitHandler(srv)) http.HandleFunc("/metrics", expvarom.MetricsHandler) http.HandleFunc("/debug/flags", debugFlagsHandler) http.HandleFunc("/debug/config", debugConfigHandler(conf)) - go http.ListenAndServe(conf.MonitoringAddress, nil) + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("Monitoring server failed: %v", err) + } } // Functions available inside the templates. @@ -123,6 +129,28 @@ os hostname <i>{{.Hostname}}</i><br> </html> `)) +func exitHandler(srv *http.Server) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "Use POST method for exiting", http.StatusMethodNotAllowed) + return + } + + log.Infof("Received /exit") + http.Error(w, "OK exiting", http.StatusOK) + + // Launch srv.Shutdown asynchronously, and then exit. + // The http documentation says to wait for Shutdown to return before + // exiting, to gracefully close all ongoing requests. + go func() { + if err := srv.Shutdown(context.Background()); err != nil { + log.Fatalf("Monitoring server shutdown failed: %v", err) + } + os.Exit(0) + }() + } +} + func debugFlagsHandler(w http.ResponseWriter, r *http.Request) { visited := make(map[string]bool) diff --git a/test/t-13-reload/run.sh b/test/t-13-reload/run.sh index a3a0486..b952497 100755 --- a/test/t-13-reload/run.sh +++ b/test/t-13-reload/run.sh @@ -62,4 +62,15 @@ then fail "new chasquid log did not have the expected entry" fi + +# Test that we can make the server exit using the /exit endpoint. +# First, a GET should fail with status 405. +fexp http://localhost:1099/exit -status 405 + +# A POST should succeed, return an OK body, and the daemon should +# eventually exit. +CHASQUID_PID=$(pgrep -s 0 chasquid) +fexp http://localhost:1099/exit -method POST -bodyre "OK" +wait_until ! kill -s 0 $CHASQUID_PID 2> /dev/null + success diff --git a/test/util/lib.sh b/test/util/lib.sh index 10870b6..f637655 100644 --- a/test/util/lib.sh +++ b/test/util/lib.sh @@ -153,6 +153,15 @@ function wait_for_file() { done } +function wait_until() { + while true; do + if eval "$@"; then + return 0 + fi + sleep 0.05 + done +} + # Generate certs for the given hostname. function generate_certs_for() { CONFDIR="${CONFDIR:-config}"