git » gofer » commit a0b4fd4

Reduce log.Fatalf calls to make code easier to test

author Alberto Bertogli
2022-10-14 10:10:15 UTC
committer Alberto Bertogli
2022-10-14 10:10:15 UTC
parent 759773f36c718248812b59dd0bacbf361d780743

Reduce log.Fatalf calls to make code easier to test

log.Fatalf calls cause an abrupt exit of the process, and can't be
properly tested. Some of these are reasonable and useful, but most are
not needed, we can just log and return an error instead.

This patch changes several `log.Fatalf` calls to `return log.Errorf`, to
make code easier to test.

debug/debug.go +2 -2
gofer.go +51 -9
reqlog/reqlog.go +1 -2
server/http.go +19 -14
server/raw.go +4 -4

diff --git a/debug/debug.go b/debug/debug.go
index fb69d16..26cddab 100644
--- a/debug/debug.go
+++ b/debug/debug.go
@@ -37,7 +37,7 @@ func init() {
 	SourceDateStr = SourceDate.Format("2006-01-02 15:04:05 -0700")
 }
 
-func ServeDebugging(addr string, conf *config.Config) {
+func ServeDebugging(addr string, conf *config.Config) error {
 	hostname, _ := os.Hostname()
 
 	indexData := struct {
@@ -70,7 +70,7 @@ func ServeDebugging(addr string, conf *config.Config) {
 
 	log.Infof("debugging HTTP server listening on %q", addr)
 	err := http.ListenAndServe(addr, nil)
-	log.Errorf("debugging HTTP server died: %v", err)
+	return log.Errorf("debugging HTTP server died: %v", err)
 }
 
 func DumpConfigFunc(conf *config.Config) http.HandlerFunc {
diff --git a/gofer.go b/gofer.go
index 68ff250..e065ad6 100644
--- a/gofer.go
+++ b/gofer.go
@@ -5,8 +5,8 @@ import (
 	"fmt"
 	"os"
 	"os/signal"
+	"sync"
 	"syscall"
-	"time"
 
 	"blitiri.com.ar/go/gofer/config"
 	"blitiri.com.ar/go/gofer/debug"
@@ -56,28 +56,46 @@ func main() {
 	go signalHandler()
 
 	for name, rlog := range conf.ReqLog {
-		reqlog.FromConfig(name, rlog)
+		err := reqlog.FromConfig(name, rlog)
+		if err != nil {
+			log.Fatalf(err.Error())
+		}
 	}
 
+	servers := []runnerFunc{}
+
 	for addr, https := range conf.HTTPS {
-		go server.HTTPS(addr, https)
+		addr := addr
+		https := https
+		servers = append(servers, func() error {
+			return server.HTTPS(addr, https)
+		})
 	}
 
 	for addr, http := range conf.HTTP {
-		go server.HTTP(addr, http)
+		addr := addr
+		http := http
+		servers = append(servers, func() error {
+			return server.HTTP(addr, http)
+		})
 	}
 
 	for addr, raw := range conf.Raw {
-		go server.Raw(addr, raw)
+		addr := addr
+		raw := raw
+		servers = append(servers, func() error {
+			return server.Raw(addr, raw)
+		})
 	}
 
 	if conf.ControlAddr != "" {
-		go debug.ServeDebugging(conf.ControlAddr, conf)
+		servers = append(servers, func() error {
+			return debug.ServeDebugging(conf.ControlAddr, conf)
+		})
 	}
 
-	for {
-		time.Sleep(1 * time.Hour)
-	}
+	err = runMany(servers...)
+	log.Fatalf(err.Error())
 }
 
 func signalHandler() {
@@ -102,3 +120,27 @@ func signalHandler() {
 		}
 	}
 }
+
+type runnerFunc func() error
+
+func runMany(fs ...runnerFunc) error {
+	var err error
+	mu := &sync.Mutex{}
+	cond := sync.NewCond(mu)
+
+	mu.Lock()
+
+	for _, f := range fs {
+		go func(f runnerFunc) {
+			e := f()
+			mu.Lock()
+			err = e
+			cond.Broadcast()
+			mu.Unlock()
+		}(f)
+	}
+
+	cond.Wait()
+
+	return err
+}
diff --git a/reqlog/reqlog.go b/reqlog/reqlog.go
index 942e84e..8e41ce6 100644
--- a/reqlog/reqlog.go
+++ b/reqlog/reqlog.go
@@ -150,8 +150,7 @@ var registry = map[string]*Log{}
 func FromConfig(name string, conf config.ReqLog) error {
 	h, err := New(conf.File, conf.BufSize, conf.Format)
 	if err != nil {
-		log.Fatalf("reqlog %q failed to initialize: %v", name, err)
-		return err
+		return fmt.Errorf("reqlog %q failed to initialize: %v", name, err)
 	}
 	registry[name] = h
 	log.Infof("reqlog %q writing to %q", name, conf.File)
diff --git a/server/http.go b/server/http.go
index 87aaf1b..384e9fd 100644
--- a/server/http.go
+++ b/server/http.go
@@ -22,7 +22,7 @@ import (
 	"blitiri.com.ar/go/systemd"
 )
 
-func httpServer(addr string, conf config.HTTP) *http.Server {
+func httpServer(addr string, conf config.HTTP) (*http.Server, error) {
 	tr := trace.New("httpserver", addr)
 	tr.SetMaxEvents(1000)
 
@@ -67,7 +67,7 @@ func httpServer(addr string, conf config.HTTP) *http.Server {
 		for path, dbPath := range conf.Auth {
 			users, err := LoadAuthFile(dbPath)
 			if err != nil {
-				log.Fatalf(
+				return nil, log.Errorf(
 					"failed to load auth file %q: %v", dbPath, err)
 			}
 			authMux.Handle(path,
@@ -109,7 +109,7 @@ func httpServer(addr string, conf config.HTTP) *http.Server {
 		for path, logName := range conf.ReqLog {
 			l := reqlog.FromName(logName)
 			if l == nil {
-				log.Fatalf("unknown reqlog name %q", logName)
+				return nil, log.Errorf("unknown reqlog name %q", logName)
 			}
 			logMux.Handle(path, WithReqLog(srv.Handler, l))
 			log.Infof("%s reqlog %q to %q", srv.Addr, path, logName)
@@ -124,39 +124,44 @@ func httpServer(addr string, conf config.HTTP) *http.Server {
 	// Tracing for all entries.
 	srv.Handler = WithTrace("http@"+srv.Addr, srv.Handler)
 
-	return srv
+	return srv, nil
 }
 
-func HTTP(addr string, conf config.HTTP) {
-	srv := httpServer(addr, conf)
+func HTTP(addr string, conf config.HTTP) error {
+	srv, err := httpServer(addr, conf)
+	if err != nil {
+		return err
+	}
 	lis, err := systemd.Listen("tcp", addr)
 	if err != nil {
-		log.Fatalf("%s error listening: %v", addr, err)
+		return log.Errorf("%s error listening: %v", addr, err)
 	}
 	log.Infof("%s http starting on %q", addr, lis.Addr())
 	err = srv.Serve(lis)
-	log.Fatalf("%s http exited: %v", addr, err)
+	return log.Errorf("%s http exited: %v", addr, err)
 }
 
-func HTTPS(addr string, conf config.HTTPS) {
-	var err error
-	srv := httpServer(addr, conf.HTTP)
+func HTTPS(addr string, conf config.HTTPS) error {
+	srv, err := httpServer(addr, conf.HTTP)
+	if err != nil {
+		return err
+	}
 
 	srv.TLSConfig, err = util.LoadCertsForHTTPS(conf)
 	if err != nil {
-		log.Fatalf("%s error loading certs: %v", addr, err)
+		return log.Errorf("%s error loading certs: %v", addr, err)
 	}
 
 	rawLis, err := systemd.Listen("tcp", addr)
 	if err != nil {
-		log.Fatalf("%s error listening: %v", addr, err)
+		return log.Errorf("%s error listening: %v", addr, err)
 	}
 
 	lis := tls.NewListener(rawLis, srv.TLSConfig)
 
 	log.Infof("%s https starting on %q", addr, lis.Addr())
 	err = srv.Serve(lis)
-	log.Fatalf("%s https exited: %v", addr, err)
+	return log.Errorf("%s https exited: %v", addr, err)
 }
 
 // joinPath joins to HTTP paths. We can't use path.Join because it strips the
diff --git a/server/raw.go b/server/raw.go
index 37196ba..b5bcd15 100644
--- a/server/raw.go
+++ b/server/raw.go
@@ -14,14 +14,14 @@ import (
 	"blitiri.com.ar/go/systemd"
 )
 
-func Raw(addr string, conf config.Raw) {
+func Raw(addr string, conf config.Raw) error {
 	var err error
 
 	var tlsConfig *tls.Config
 	if conf.Certs != "" {
 		tlsConfig, err = util.LoadCertsFromDir(conf.Certs)
 		if err != nil {
-			log.Fatalf("error loading certs: %v", err)
+			return log.Errorf("error loading certs: %v", err)
 		}
 	}
 
@@ -33,7 +33,7 @@ func Raw(addr string, conf config.Raw) {
 		lis, err = systemd.Listen("tcp", addr)
 	}
 	if err != nil {
-		log.Fatalf("Raw proxy error listening on %q: %v", addr, err)
+		return log.Errorf("Raw proxy error listening on %q: %v", addr, err)
 	}
 
 	rlog := reqlog.FromName(conf.ReqLog)
@@ -42,7 +42,7 @@ func Raw(addr string, conf config.Raw) {
 	for {
 		conn, err := lis.Accept()
 		if err != nil {
-			log.Fatalf("%s error accepting: %v", addr, err)
+			return log.Errorf("%s error accepting: %v", addr, err)
 		}
 
 		go forward(conn, conf.To, conf.ToTLS, rlog)