v0.2.5: Reloading a badly formated configuration wont crash, so the old configuration will keep running.

This commit is contained in:
yo 2025-01-13 20:15:32 +01:00
parent fe6ca819f4
commit 8958845f65

58
main.go
View File

@ -34,7 +34,7 @@ import (
) )
const ( const (
gVersion = "0.2.4" gVersion = "0.2.5"
// Default datasource timeout is 10 seconds // Default datasource timeout is 10 seconds
gDefaultDSTimeout = 10 gDefaultDSTimeout = 10
) )
@ -329,7 +329,7 @@ func getGraph(name string) (Graph, error) {
return Graph{}, fmt.Errorf("Graph not found: %s", name) 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) { r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "pong", "message": "pong",
@ -338,7 +338,10 @@ func initRoutes(r *gin.Engine) {
// An endpoint to force read of configuration file // An endpoint to force read of configuration file
r.POST("/reload", func(c *gin.Context) { 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{ c.JSON(http.StatusOK, gin.H{
"message": "configuration successfully reloaded", "message": "configuration successfully reloaded",
}) })
@ -469,16 +472,32 @@ func newEdgeClone(src *Edge) *Edge {
} }
} }
func reloadConfigFile() { // This function assume we already have a running configuration.
// First reread config file func reloadConfigFile(confFile string) error {
if err := viper.ReadInConfig(); err != nil { oldConfigRestored := false
if _, ok := err.(viper.ConfigFileNotFoundError); ok { // We need to keep this config, incase the new one is b0rken
log.Fatalf("config file not found") fname := fmt.Sprintf("/tmp/nodegopher.%d.yaml", os.Getpid())
os.Exit(1) 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 { } else {
log.Fatalf("unknown error looking for config file: %v", err) log.Errorf("unknown error looking for config file: %v", oldErr)
os.Exit(1)
} }
// 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) { switch viper.Get("language").(string) {
@ -503,6 +522,7 @@ func reloadConfigFile() {
gCfgMutex.Lock() gCfgMutex.Lock()
defer gCfgMutex.Unlock() defer gCfgMutex.Unlock()
// We need to keep this config, incase the new one is b0rken
for _, g := range gGraphs { for _, g := range gGraphs {
g.Nodes = nil g.Nodes = nil
g.Edges = nil g.Edges = nil
@ -538,8 +558,8 @@ func reloadConfigFile() {
} }
if viper.Get("datasources") == nil { if viper.Get("datasources") == nil {
log.Printf("no datasources found, data will be static") log.Warningf("no datasources found, data will be static")
return return nil
} }
dss := viper.Get("datasources").([]interface{}) dss := viper.Get("datasources").([]interface{})
for _, d := range dss { for _, d := range dss {
@ -552,6 +572,10 @@ func reloadConfigFile() {
} }
gDataSources = append(gDataSources, ds) gDataSources = append(gDataSources, ds)
} }
if oldConfigRestored {
return fmt.Errorf("Unable to load new configuration, keeping old one. See logs.")
}
return nil
} }
func main() { func main() {
@ -604,7 +628,7 @@ func main() {
// FIXME: Watch config changes. Does not work on FreeBSD. TODO: Test with linux // FIXME: Watch config changes. Does not work on FreeBSD. TODO: Test with linux
viper.OnConfigChange(func(e fsnotify.Event) { viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed, reloading data\n") log.Printf("Config file changed, reloading data\n")
reloadConfigFile() reloadConfigFile(confFile)
}) })
// Lets reload config on SIGHUP // Lets reload config on SIGHUP
@ -614,11 +638,11 @@ func main() {
for { for {
_ = <- sigs _ = <- sigs
log.Infof("SIGHUP received, reloading configuration\n") 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. // Capture variable name. There should be only one variable. Space is tolerated before and after name.
gDSVarCompRegex = regexp.MustCompile(`^\{\{(?:\ )?([a-zA-Z0-9\-_]+)(?:\ )?\}\}$`) gDSVarCompRegex = regexp.MustCompile(`^\{\{(?:\ )?([a-zA-Z0-9\-_]+)(?:\ )?\}\}$`)
@ -629,6 +653,6 @@ func main() {
log.Printf("Starting NodeGopher v.%s\n", gVersion) log.Printf("Starting NodeGopher v.%s\n", gVersion)
r := gin.Default() r := gin.Default()
initRoutes(r) initRoutes(r, confFile)
r.Run(listen) r.Run(listen)
} }