From 8958845f65785f1ff50ff95dd6517e5d98495f2d Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 13 Jan 2025 20:15:32 +0100 Subject: [PATCH] v0.2.5: Reloading a badly formated configuration wont crash, so the old configuration will keep running. --- main.go | 58 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/main.go b/main.go index 0dc033d..db55286 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ import ( ) const ( - gVersion = "0.2.4" + gVersion = "0.2.5" // Default datasource timeout is 10 seconds gDefaultDSTimeout = 10 ) @@ -329,7 +329,7 @@ func getGraph(name string) (Graph, error) { return Graph{}, fmt.Errorf("Graph not found: %s", name) } -func initRoutes(r *gin.Engine) { +func initRoutes(r *gin.Engine, confFile string) { r.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", @@ -338,7 +338,10 @@ func initRoutes(r *gin.Engine) { // An endpoint to force read of configuration file r.POST("/reload", func(c *gin.Context) { - reloadConfigFile() + if err := reloadConfigFile(confFile); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } c.JSON(http.StatusOK, gin.H{ "message": "configuration successfully reloaded", }) @@ -469,16 +472,32 @@ func newEdgeClone(src *Edge) *Edge { } } -func reloadConfigFile() { - // First reread config file - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - log.Fatalf("config file not found") - os.Exit(1) +// This function assume we already have a running configuration. +func reloadConfigFile(confFile string) error { + oldConfigRestored := false + // We need to keep this config, incase the new one is b0rken + fname := fmt.Sprintf("/tmp/nodegopher.%d.yaml", os.Getpid()) + if err := viper.WriteConfigAs(fname); err != nil { + log.Errorf("Unable to save current running config to %s, wont reload configuration.\n", fname) + return fmt.Errorf("Unable to save current configuration, configuration not reloaded. See logs.") + } + defer os.Remove(fname) + + // Reread config file + if oldErr := viper.ReadInConfig(); oldErr != nil { + if _, ok := oldErr.(viper.ConfigFileNotFoundError); ok { + log.Errorf("config file not found") } else { - log.Fatalf("unknown error looking for config file: %v", err) - os.Exit(1) + log.Errorf("unknown error looking for config file: %v", oldErr) } + // Restore old configuration and notify. + log.Debugf("Fallback on previous configuration.\n") + viper.SetConfigFile(fname) + if err := viper.ReadInConfig(); err != nil { + log.Fatalf("Unable to restore configuration, and new is invalid. fix it now.\n") + } + viper.SetConfigFile(confFile) + oldConfigRestored = true } switch viper.Get("language").(string) { @@ -503,6 +522,7 @@ func reloadConfigFile() { gCfgMutex.Lock() defer gCfgMutex.Unlock() + // We need to keep this config, incase the new one is b0rken for _, g := range gGraphs { g.Nodes = nil g.Edges = nil @@ -538,8 +558,8 @@ func reloadConfigFile() { } if viper.Get("datasources") == nil { - log.Printf("no datasources found, data will be static") - return + log.Warningf("no datasources found, data will be static") + return nil } dss := viper.Get("datasources").([]interface{}) for _, d := range dss { @@ -552,6 +572,10 @@ func reloadConfigFile() { } gDataSources = append(gDataSources, ds) } + if oldConfigRestored { + return fmt.Errorf("Unable to load new configuration, keeping old one. See logs.") + } + return nil } func main() { @@ -604,7 +628,7 @@ func main() { // FIXME: Watch config changes. Does not work on FreeBSD. TODO: Test with linux viper.OnConfigChange(func(e fsnotify.Event) { log.Printf("Config file changed, reloading data\n") - reloadConfigFile() + reloadConfigFile(confFile) }) // Lets reload config on SIGHUP @@ -614,11 +638,11 @@ func main() { for { _ = <- sigs log.Infof("SIGHUP received, reloading configuration\n") - reloadConfigFile() + reloadConfigFile(confFile) } }() - reloadConfigFile() + reloadConfigFile(confFile) // Capture variable name. There should be only one variable. Space is tolerated before and after name. gDSVarCompRegex = regexp.MustCompile(`^\{\{(?:\ )?([a-zA-Z0-9\-_]+)(?:\ )?\}\}$`) @@ -629,6 +653,6 @@ func main() { log.Printf("Starting NodeGopher v.%s\n", gVersion) r := gin.Default() - initRoutes(r) + initRoutes(r, confFile) r.Run(listen) }