Selaa lähdekoodia

fix: Propperly merge included configs (handling actions)

jamesread 8 kuukautta sitten
vanhempi
commit
1552c104e9
2 muutettua tiedostoa jossa 58 lisäystä ja 35 poistoa
  1. 45 25
      service/internal/config/config_reloader.go
  2. 13 10
      service/main.go

+ 45 - 25
service/internal/config/config_reloader.go

@@ -3,6 +3,8 @@ package config
 import (
 	"os"
 	"path/filepath"
+	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 
@@ -33,7 +35,11 @@ func AddListener(l func()) {
 }
 
 func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
-	log.Infof("Appending cfg source: %s", configPath)
+	log.WithFields(log.Fields{
+		"configPath": configPath,
+	}).Info("Appending cfg source")
+
+	loadIncludedConfigsFromDir(k, configPath)
 
 	if !unmarshalRoot(k, cfg) {
 		return
@@ -42,14 +48,6 @@ func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
 	afterLoadFinalize(cfg, configPath)
 }
 
-func AppendSourceWithIncludes(cfg *Config, k *koanf.Koanf, configPath string) {
-	AppendSource(cfg, k, configPath)
-
-	if cfg.Include != "" {
-		LoadIncludedConfigs(cfg, k, configPath)
-	}
-}
-
 func unmarshalRoot(k *koanf.Koanf, cfg *Config) bool {
 	err := k.UnmarshalWithConf("", cfg, koanf.UnmarshalConf{
 		Tag: "koanf",
@@ -74,14 +72,19 @@ func afterLoadFinalize(cfg *Config, configPath string) {
 	}
 }
 
-// LoadIncludedConfigs loads configuration files from an include directory and merges them
-func LoadIncludedConfigs(cfg *Config, k *koanf.Koanf, baseConfigPath string) {
-	if cfg.Include == "" {
+// loadIncludedConfigsFromDir loads configuration files from an include directory and merges them
+func loadIncludedConfigsFromDir(k *koanf.Koanf, baseConfigPath string) {
+	relativeIncludePath := k.String("include")
+
+	if relativeIncludePath == "" {
 		return
 	}
 
-	includePath := filepath.Join(filepath.Dir(baseConfigPath), cfg.Include)
-	log.Infof("Loading included configs from: %s", includePath)
+	includePath := filepath.Join(filepath.Dir(baseConfigPath), relativeIncludePath)
+
+	log.WithFields(log.Fields{
+		"includePath": includePath,
+	}).Infof("Loading included configs from dir")
 
 	yamlFiles, ok := listYamlFiles(includePath)
 	if !ok || len(yamlFiles) == 0 {
@@ -90,11 +93,10 @@ func LoadIncludedConfigs(cfg *Config, k *koanf.Koanf, baseConfigPath string) {
 
 	sort.Strings(yamlFiles)
 	for _, filename := range yamlFiles {
-		loadAndMergeIncludedFile(k, cfg, includePath, filename)
+		loadAndMergeIncludedFile(k, includePath, filename)
 	}
 
 	log.Infof("Finished loading %d included config file(s)", len(yamlFiles))
-	cfg.Sanitize()
 }
 
 func listYamlFiles(includePath string) ([]string, bool) {
@@ -128,24 +130,43 @@ func listYamlFiles(includePath string) ([]string, bool) {
 	return yamlFiles, true
 }
 
-func loadAndMergeIncludedFile(k *koanf.Koanf, cfg *Config, includePath, filename string) {
+func loadAndMergeIncludedFile(k *koanf.Koanf, includePath, filename string) {
 	filePath := filepath.Join(includePath, filename)
-	log.Infof("Loading included config file: %s", filePath)
 
-	if err := k.Load(file.Provider(filePath), yaml.Parser()); err != nil {
+	if err := k.Load(file.Provider(filePath), yaml.Parser(), koanf.WithMergeFunc(mergeFunc)); err != nil {
 		log.Errorf("Error loading included config file %s: %v", filePath, err)
 		return
 	}
 
-	if err := k.Unmarshal(".", cfg); err != nil {
-		log.Errorf("Error unmarshalling included config file %s: %v", filePath, err)
-		return
+	log.WithFields(log.Fields{
+		"filePath": filePath,
+	}).Info("Successfully loaded included config file")
+}
+
+func mergeFunc(src map[string]interface{}, dest map[string]interface{}) error {
+	// Handle actions merging - koanf provides []interface{} not []*Action
+	// Merge src (new) into dest (existing) by appending src's actions to dest's actions
+	if srcActions, ok := src["actions"]; ok {
+		if destActions, ok := dest["actions"]; ok {
+			// Both have actions - append src to dest
+			srcSlice, ok1 := srcActions.([]interface{})
+			destSlice, ok2 := destActions.([]interface{})
+			if ok1 && ok2 {
+				dest["actions"] = append(destSlice, srcSlice...)
+			} else {
+				// Fallback: if types don't match, just use src
+				dest["actions"] = srcActions
+			}
+		} else {
+			// dest doesn't have actions, so use src's actions
+			dest["actions"] = srcActions
+		}
 	}
+	// If src doesn't have actions, leave dest unchanged
 
-	log.Infof("Successfully loaded %s", filename)
+	return nil
 }
 
-/**
 var envRegex = regexp.MustCompile(`\${{ *?(\S+) *?}}`)
 
 func envDecodeHookFunc(from reflect.Type, to reflect.Type, data any) (any, error) {
@@ -168,4 +189,3 @@ func envDecodeHookFunc(from reflect.Type, to reflect.Type, data any) (any, error
 	log.Debugf("Environment variable interpolation result: %q -> %q", input, output)
 	return output, nil
 }
-*/

+ 13 - 10
service/main.go

@@ -146,23 +146,28 @@ func initConfig(configDir string) {
 		)
 	}
 
-	var firstConfigPath string
+	var baseConfigPath string
 
 	for _, directory := range directories {
 		configPath := getConfigPath(directory)
 
-		log.Debugf("Checking config path: %s", configPath)
+		log.WithFields(log.Fields{
+			"configPath": configPath,
+		}).Debug("Checking base config file path")
 
 		if _, err := os.Stat(configPath); err != nil {
 			log.Debugf("Config file not found at %s: %v", configPath, err)
 			continue
 		}
 
-		if firstConfigPath == "" {
-			firstConfigPath = configPath
+		if baseConfigPath == "" {
+			baseConfigPath = configPath
 		}
 
-		log.Infof("Loading config from %s", configPath)
+		log.WithFields(log.Fields{
+			"configPath": configPath,
+		}).Info("Loading config from path")
+
 		f := file.Provider(configPath)
 
 		if err := k.Load(f, yaml.Parser()); err != nil {
@@ -176,15 +181,13 @@ func initConfig(configDir string) {
 			k.Load(f, yaml.Parser())
 			config.AppendSource(cfg, k, configPath)
 		})
+
+		break
 	}
 
 	cfg = config.DefaultConfigWithBasePort(getBasePort())
 
-	if firstConfigPath != "" {
-		config.AppendSourceWithIncludes(cfg, k, firstConfigPath)
-	} else {
-		config.AppendSource(cfg, k, "base")
-	}
+	config.AppendSource(cfg, k, baseConfigPath)
 
 }