2026-07-07 18:29:51 -03:00

69 lines
1.8 KiB
Go

package config
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
// Load runs one orchestration or CLI step.
// Signature: Load(path string) (Config, error).
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
func Load(path string) (Config, error) {
cfg := defaults()
b, err := os.ReadFile(path)
if err != nil {
return Config{}, fmt.Errorf("read config %s: %w", path, err)
}
startupProbeRepairConfigured, err := yamlPathExists(b, "startup", "critical_service_startup_probe_repair")
if err != nil {
return Config{}, fmt.Errorf("decode config %s: %w", path, err)
}
if err := yaml.Unmarshal(b, &cfg); err != nil {
return Config{}, fmt.Errorf("decode config %s: %w", path, err)
}
cfg.applyDefaults()
if !startupProbeRepairConfigured {
cfg.Startup.CriticalServiceStartupProbeRepair = defaults().Startup.CriticalServiceStartupProbeRepair
}
if err := cfg.Validate(); err != nil {
return Config{}, err
}
return cfg, nil
}
// yamlPathExists runs one orchestration or CLI step.
// Signature: yamlPathExists(raw []byte, path ...string) (bool, error).
// Why: selected boolean defaults need to distinguish an omitted YAML key from an
// explicit false value after nested startup mappings are decoded.
func yamlPathExists(raw []byte, path ...string) (bool, error) {
var root yaml.Node
if err := yaml.Unmarshal(raw, &root); err != nil {
return false, err
}
node := &root
if node.Kind == yaml.DocumentNode && len(node.Content) > 0 {
node = node.Content[0]
}
for _, key := range path {
if node.Kind != yaml.MappingNode {
return false, nil
}
found := false
for i := 0; i+1 < len(node.Content); i += 2 {
if node.Content[i].Value == key {
node = node.Content[i+1]
found = true
break
}
}
if !found {
return false, nil
}
}
return true, nil
}