238 lines
6.7 KiB
Go
238 lines
6.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Kubeconfig string `yaml:"kubeconfig"`
|
|
SSHUser string `yaml:"ssh_user"`
|
|
IACRepoPath string `yaml:"iac_repo_path"`
|
|
ExpectedFluxBranch string `yaml:"expected_flux_branch"`
|
|
ControlPlanes []string `yaml:"control_planes"`
|
|
Workers []string `yaml:"workers"`
|
|
LocalBootstrapPaths []string `yaml:"local_bootstrap_paths"`
|
|
ExcludedNamespaces []string `yaml:"excluded_namespaces"`
|
|
Shutdown Shutdown `yaml:"shutdown"`
|
|
UPS UPS `yaml:"ups"`
|
|
Coordination Coordination `yaml:"coordination"`
|
|
Metrics Metrics `yaml:"metrics"`
|
|
State State `yaml:"state"`
|
|
}
|
|
|
|
type Shutdown struct {
|
|
DefaultBudgetSeconds int `yaml:"default_budget_seconds"`
|
|
SkipEtcdSnapshot bool `yaml:"skip_etcd_snapshot"`
|
|
SkipDrain bool `yaml:"skip_drain"`
|
|
PoweroffEnabled bool `yaml:"poweroff_enabled"`
|
|
PoweroffDelaySeconds int `yaml:"poweroff_delay_seconds"`
|
|
PoweroffLocalHost bool `yaml:"poweroff_local_host"`
|
|
ExtraPoweroffHosts []string `yaml:"extra_poweroff_hosts"`
|
|
}
|
|
|
|
type UPS struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
Provider string `yaml:"provider"`
|
|
Target string `yaml:"target"`
|
|
Targets []UPSTarget `yaml:"targets"`
|
|
PollSeconds int `yaml:"poll_seconds"`
|
|
RuntimeSafetyFactor float64 `yaml:"runtime_safety_factor"`
|
|
DebounceCount int `yaml:"debounce_count"`
|
|
TelemetryTimeoutSeconds int `yaml:"telemetry_timeout_seconds"`
|
|
}
|
|
|
|
type UPSTarget struct {
|
|
Name string `yaml:"name"`
|
|
Target string `yaml:"target"`
|
|
}
|
|
|
|
type Coordination struct {
|
|
ForwardShutdownHost string `yaml:"forward_shutdown_host"`
|
|
ForwardShutdownUser string `yaml:"forward_shutdown_user"`
|
|
ForwardShutdownConfig string `yaml:"forward_shutdown_config"`
|
|
FallbackLocalShutdown bool `yaml:"fallback_local_shutdown"`
|
|
CommandTimeoutSeconds int `yaml:"command_timeout_seconds"`
|
|
}
|
|
|
|
type Metrics struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
BindAddr string `yaml:"bind_addr"`
|
|
Path string `yaml:"path"`
|
|
}
|
|
|
|
type State struct {
|
|
Dir string `yaml:"dir"`
|
|
RunHistoryPath string `yaml:"run_history_path"`
|
|
LockPath string `yaml:"lock_path"`
|
|
}
|
|
|
|
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)
|
|
}
|
|
if err := yaml.Unmarshal(b, &cfg); err != nil {
|
|
return Config{}, fmt.Errorf("decode config %s: %w", path, err)
|
|
}
|
|
|
|
cfg.applyDefaults()
|
|
if err := cfg.Validate(); err != nil {
|
|
return Config{}, err
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func (c Config) Validate() error {
|
|
if len(c.ControlPlanes) == 0 {
|
|
return fmt.Errorf("config.control_planes must not be empty")
|
|
}
|
|
if c.ExpectedFluxBranch == "" {
|
|
return fmt.Errorf("config.expected_flux_branch must not be empty")
|
|
}
|
|
if c.IACRepoPath == "" {
|
|
return fmt.Errorf("config.iac_repo_path must not be empty")
|
|
}
|
|
if c.Shutdown.DefaultBudgetSeconds <= 0 {
|
|
return fmt.Errorf("config.shutdown.default_budget_seconds must be > 0")
|
|
}
|
|
if c.UPS.Enabled {
|
|
if c.UPS.Provider == "" {
|
|
return fmt.Errorf("config.ups.provider must not be empty when ups is enabled")
|
|
}
|
|
if c.UPS.Target == "" && len(c.UPS.Targets) == 0 {
|
|
return fmt.Errorf("config.ups.target or config.ups.targets must be set when ups is enabled")
|
|
}
|
|
for _, t := range c.UPS.Targets {
|
|
if t.Target == "" {
|
|
return fmt.Errorf("config.ups.targets[].target must not be empty")
|
|
}
|
|
}
|
|
}
|
|
if c.Coordination.ForwardShutdownHost != "" {
|
|
if c.Coordination.ForwardShutdownConfig == "" {
|
|
return fmt.Errorf("config.coordination.forward_shutdown_config must not be empty when forward_shutdown_host is set")
|
|
}
|
|
}
|
|
if c.State.RunHistoryPath == "" || c.State.LockPath == "" {
|
|
return fmt.Errorf("config.state.run_history_path and config.state.lock_path must not be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func defaults() Config {
|
|
c := Config{
|
|
IACRepoPath: "/opt/titan-iac",
|
|
ExpectedFluxBranch: "main",
|
|
ControlPlanes: []string{"titan-0a", "titan-0b", "titan-0c"},
|
|
LocalBootstrapPaths: []string{
|
|
"infrastructure/core",
|
|
"infrastructure/flux-system",
|
|
"infrastructure/sources/helm",
|
|
"infrastructure/metallb",
|
|
"infrastructure/traefik",
|
|
"infrastructure/vault-csi",
|
|
"infrastructure/vault-injector",
|
|
"services/vault",
|
|
"infrastructure/postgres",
|
|
"services/gitea",
|
|
},
|
|
ExcludedNamespaces: []string{
|
|
"kube-system",
|
|
"kube-public",
|
|
"kube-node-lease",
|
|
"flux-system",
|
|
"traefik",
|
|
"metallb-system",
|
|
"cert-manager",
|
|
"longhorn-system",
|
|
"vault",
|
|
"postgres",
|
|
"maintenance",
|
|
},
|
|
Shutdown: Shutdown{
|
|
DefaultBudgetSeconds: 300,
|
|
PoweroffEnabled: true,
|
|
PoweroffDelaySeconds: 25,
|
|
PoweroffLocalHost: true,
|
|
},
|
|
UPS: UPS{
|
|
Enabled: true,
|
|
Provider: "nut",
|
|
PollSeconds: 5,
|
|
RuntimeSafetyFactor: 1.10,
|
|
DebounceCount: 3,
|
|
TelemetryTimeoutSeconds: 90,
|
|
},
|
|
Coordination: Coordination{
|
|
ForwardShutdownConfig: "/etc/hecate/hecate.yaml",
|
|
FallbackLocalShutdown: true,
|
|
CommandTimeoutSeconds: 25,
|
|
},
|
|
Metrics: Metrics{
|
|
Enabled: true,
|
|
BindAddr: "0.0.0.0:9560",
|
|
Path: "/metrics",
|
|
},
|
|
State: State{
|
|
Dir: "/var/lib/hecate",
|
|
RunHistoryPath: "/var/lib/hecate/runs.json",
|
|
LockPath: "/var/lib/hecate/hecate.lock",
|
|
},
|
|
}
|
|
c.applyDefaults()
|
|
return c
|
|
}
|
|
|
|
func (c *Config) applyDefaults() {
|
|
if c.ExpectedFluxBranch == "" {
|
|
c.ExpectedFluxBranch = "main"
|
|
}
|
|
if c.IACRepoPath == "" {
|
|
c.IACRepoPath = "/opt/titan-iac"
|
|
}
|
|
if c.Shutdown.DefaultBudgetSeconds <= 0 {
|
|
c.Shutdown.DefaultBudgetSeconds = 300
|
|
}
|
|
if c.Shutdown.PoweroffDelaySeconds <= 0 {
|
|
c.Shutdown.PoweroffDelaySeconds = 25
|
|
}
|
|
if c.UPS.PollSeconds <= 0 {
|
|
c.UPS.PollSeconds = 5
|
|
}
|
|
if c.UPS.RuntimeSafetyFactor <= 0 {
|
|
c.UPS.RuntimeSafetyFactor = 1.10
|
|
}
|
|
if c.UPS.DebounceCount <= 0 {
|
|
c.UPS.DebounceCount = 3
|
|
}
|
|
if c.UPS.TelemetryTimeoutSeconds <= 0 {
|
|
c.UPS.TelemetryTimeoutSeconds = 90
|
|
}
|
|
if c.Coordination.ForwardShutdownConfig == "" {
|
|
c.Coordination.ForwardShutdownConfig = "/etc/hecate/hecate.yaml"
|
|
}
|
|
if c.Coordination.CommandTimeoutSeconds <= 0 {
|
|
c.Coordination.CommandTimeoutSeconds = 25
|
|
}
|
|
if c.Metrics.BindAddr == "" {
|
|
c.Metrics.BindAddr = "0.0.0.0:9560"
|
|
}
|
|
if c.Metrics.Path == "" {
|
|
c.Metrics.Path = "/metrics"
|
|
}
|
|
if c.State.Dir == "" {
|
|
c.State.Dir = "/var/lib/hecate"
|
|
}
|
|
if c.State.RunHistoryPath == "" {
|
|
c.State.RunHistoryPath = "/var/lib/hecate/runs.json"
|
|
}
|
|
if c.State.LockPath == "" {
|
|
c.State.LockPath = "/var/lib/hecate/hecate.lock"
|
|
}
|
|
}
|