From dc13c6537ba037134b3084fe4960b5f37d5b491b Mon Sep 17 00:00:00 2001 From: Chris Banks Date: Sat, 5 Aug 2023 23:42:41 +0100 Subject: [PATCH] Pass NewRouter a struct instead of too many params. NewRouter()'s parameter list was already getting out of hand. Copying it straight into struct Router for now just for the sake of convenience. Probably change that soon. --- lib/router.go | 72 +++++++++++++++++++++++++++++---------------------- main.go | 72 +++++++++++++++++++++++++++------------------------ 2 files changed, 79 insertions(+), 65 deletions(-) diff --git a/lib/router.go b/lib/router.go index 651c0b4a..65b01f55 100644 --- a/lib/router.go +++ b/lib/router.go @@ -29,17 +29,30 @@ const ( // Router is a wrapper around an HTTP multiplexer (trie.Mux) which retrieves its // routes from a passed mongo database. +// +// TODO: decouple Router from its database backend. Router should not know +// anything about the database backend. Its representation of the route table +// should be independent of the underlying DBMS. Route should define an +// abstract interface for some other module to be able to bulk-load and +// incrementally update routes. Since Router should not care where its routes +// come from, Route and Backend should not contain bson fields. +// MongoReplicaSet, MongoReplicaSetMember etc. should move out of this module. type Router struct { - mux *triemux.Mux - lock sync.RWMutex - mongoURL string - mongoDbName string - mongoPollInterval time.Duration - backendConnectTimeout time.Duration - backendHeaderTimeout time.Duration - mongoReadToOptime bson.MongoTimestamp - logger logger.Logger - ReloadChan chan bool + mux *triemux.Mux + lock sync.RWMutex + mongoReadToOptime bson.MongoTimestamp + logger logger.Logger + opts Options + ReloadChan chan bool +} + +type Options struct { + MongoURL string + MongoDbName string + MongoPollInterval time.Duration + BackendConnTimeout time.Duration + BackendHeaderTimeout time.Duration + LogFileName string } type Backend struct { @@ -71,16 +84,16 @@ type Route struct { // NewRouter returns a new empty router instance. You will need to call // SelfUpdateRoutes() to initialise the self-update process for routes. -func NewRouter(mongoURL, mongoDbName string, mongoPollInterval, beConnTimeout, beHeaderTimeout time.Duration, logFileName string) (rt *Router, err error) { - logInfo("router: using mongo poll interval:", mongoPollInterval) - logInfo("router: using backend connect timeout:", beConnTimeout) - logInfo("router: using backend header timeout:", beHeaderTimeout) +func NewRouter(o Options) (rt *Router, err error) { + logInfo("router: using mongo poll interval:", o.MongoPollInterval) + logInfo("router: using backend connect timeout:", o.BackendConnTimeout) + logInfo("router: using backend header timeout:", o.BackendHeaderTimeout) - l, err := logger.New(logFileName) + l, err := logger.New(o.LogFileName) if err != nil { return nil, err } - logInfo("router: logging errors as JSON to", logFileName) + logInfo("router: logging errors as JSON to", o.LogFileName) // TODO: avoid using the global registry. registerMetrics(prometheus.DefaultRegisterer) @@ -92,15 +105,11 @@ func NewRouter(mongoURL, mongoDbName string, mongoPollInterval, beConnTimeout, b reloadChan := make(chan bool, 1) rt = &Router{ - mux: triemux.NewMux(), - mongoURL: mongoURL, - mongoPollInterval: mongoPollInterval, - mongoDbName: mongoDbName, - backendConnectTimeout: beConnTimeout, - backendHeaderTimeout: beHeaderTimeout, - mongoReadToOptime: mongoReadToOptime, - logger: l, - ReloadChan: reloadChan, + mux: triemux.NewMux(), + mongoReadToOptime: mongoReadToOptime, + logger: l, + opts: o, + ReloadChan: reloadChan, } go rt.pollAndReload() @@ -137,9 +146,9 @@ func (rt *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { } func (rt *Router) SelfUpdateRoutes() { - logInfo(fmt.Sprintf("router: starting self-update process, polling for route changes every %v", rt.mongoPollInterval)) + logInfo(fmt.Sprintf("router: starting self-update process, polling for route changes every %v", rt.opts.MongoPollInterval)) - tick := time.Tick(rt.mongoPollInterval) + tick := time.Tick(rt.opts.MongoPollInterval) for range tick { logDebug("router: polling MongoDB for changes") @@ -159,9 +168,9 @@ func (rt *Router) pollAndReload() { } }() - logDebug("mgo: connecting to", rt.mongoURL) + logDebug("mgo: connecting to", rt.opts.MongoURL) - sess, err := mgo.Dial(rt.mongoURL) + sess, err := mgo.Dial(rt.opts.MongoURL) if err != nil { logWarn(fmt.Sprintf("mgo: error connecting to MongoDB, skipping update (error: %v)", err)) return @@ -184,7 +193,7 @@ func (rt *Router) pollAndReload() { if rt.shouldReload(currentMongoInstance) { logDebug("router: updates found") - rt.reloadRoutes(sess.DB(rt.mongoDbName), currentMongoInstance.Optime) + rt.reloadRoutes(sess.DB(rt.opts.MongoDbName), currentMongoInstance.Optime) } else { logDebug("router: no updates found") } @@ -290,7 +299,8 @@ func (rt *Router) loadBackends(c *mgo.Collection) (backends map[string]http.Hand backends[backend.BackendID] = handlers.NewBackendHandler( backend.BackendID, backendURL, - rt.backendConnectTimeout, rt.backendHeaderTimeout, + rt.opts.BackendConnTimeout, + rt.opts.BackendHeaderTimeout, rt.logger, ) } diff --git a/main.go b/main.go index 739f0b55..8e636dd4 100644 --- a/main.go +++ b/main.go @@ -40,13 +40,24 @@ ROUTER_FRONTEND_WRITE_TIMEOUT=60s See https://cs.opensource.google/go/go/+/mast os.Exit(ErrUsage) } -func getenvDefault(key string, defaultVal string) string { - val := os.Getenv(key) - if val == "" { - val = defaultVal +func getenv(key string, defaultVal string) string { + if s := os.Getenv(key); s != "" { + return s } + return defaultVal +} - return val +func getenvDuration(key string, defaultVal string) time.Duration { + s := getenv(key, defaultVal) + return mustParseDuration(s) +} + +func mustParseDuration(s string) (d time.Duration) { + d, err := time.ParseDuration(s) + if err != nil { + log.Fatal(err) + } + return } func listenAndServeOrFatal(addr string, handler http.Handler, rTimeout time.Duration, wTimeout time.Duration) { @@ -61,30 +72,7 @@ func listenAndServeOrFatal(addr string, handler http.Handler, rTimeout time.Dura } } -func parseDurationOrFatal(s string) (d time.Duration) { - d, err := time.ParseDuration(s) - if err != nil { - log.Fatal(err) - } - return -} - func main() { - router.EnableDebugOutput = os.Getenv("ROUTER_DEBUG") != "" - var ( - pubAddr = getenvDefault("ROUTER_PUBADDR", ":8080") - apiAddr = getenvDefault("ROUTER_APIADDR", ":8081") - mongoURL = getenvDefault("ROUTER_MONGO_URL", "127.0.0.1") - mongoDbName = getenvDefault("ROUTER_MONGO_DB", "router") - mongoPollInterval = getenvDefault("ROUTER_MONGO_POLL_INTERVAL", "2s") - errorLogFile = getenvDefault("ROUTER_ERROR_LOG", "STDERR") - tlsSkipVerify = os.Getenv("ROUTER_TLS_SKIP_VERIFY") != "" - backendConnectTimeout = getenvDefault("ROUTER_BACKEND_CONNECT_TIMEOUT", "1s") - backendHeaderTimeout = getenvDefault("ROUTER_BACKEND_HEADER_TIMEOUT", "20s") - frontendReadTimeout = getenvDefault("ROUTER_FRONTEND_READ_TIMEOUT", "60s") - frontendWriteTimeout = getenvDefault("ROUTER_FRONTEND_WRITE_TIMEOUT", "60s") - ) - returnVersion := flag.Bool("version", false, "") flag.Usage = usage flag.Parse() @@ -94,11 +82,20 @@ func main() { os.Exit(0) } - feReadTimeout := parseDurationOrFatal(frontendReadTimeout) - feWriteTimeout := parseDurationOrFatal(frontendWriteTimeout) - beConnectTimeout := parseDurationOrFatal(backendConnectTimeout) - beHeaderTimeout := parseDurationOrFatal(backendHeaderTimeout) - mgoPollInterval := parseDurationOrFatal(mongoPollInterval) + router.EnableDebugOutput = os.Getenv("ROUTER_DEBUG") != "" + var ( + pubAddr = getenv("ROUTER_PUBADDR", ":8080") + apiAddr = getenv("ROUTER_APIADDR", ":8081") + mongoURL = getenv("ROUTER_MONGO_URL", "127.0.0.1") + mongoDbName = getenv("ROUTER_MONGO_DB", "router") + mongoPollInterval = getenvDuration("ROUTER_MONGO_POLL_INTERVAL", "2s") + errorLogFile = getenv("ROUTER_ERROR_LOG", "STDERR") + tlsSkipVerify = os.Getenv("ROUTER_TLS_SKIP_VERIFY") != "" + beConnTimeout = getenvDuration("ROUTER_BACKEND_CONNECT_TIMEOUT", "1s") + beHeaderTimeout = getenvDuration("ROUTER_BACKEND_HEADER_TIMEOUT", "20s") + feReadTimeout = getenvDuration("ROUTER_FRONTEND_READ_TIMEOUT", "60s") + feWriteTimeout = getenvDuration("ROUTER_FRONTEND_WRITE_TIMEOUT", "60s") + ) log.Printf("using frontend read timeout: %v", feReadTimeout) log.Printf("using frontend write timeout: %v", feWriteTimeout) @@ -110,7 +107,14 @@ func main() { "Do not use this option in a production environment.") } - rout, err := router.NewRouter(mongoURL, mongoDbName, mgoPollInterval, beConnectTimeout, beHeaderTimeout, errorLogFile) + rout, err := router.NewRouter(router.Options{ + MongoURL: mongoURL, + MongoDbName: mongoDbName, + MongoPollInterval: mongoPollInterval, + BackendConnTimeout: beConnTimeout, + BackendHeaderTimeout: beHeaderTimeout, + LogFileName: errorLogFile, + }) if err != nil { log.Fatal(err) }