Files
HarborForge.Monitor/internal/config/config.go

179 lines
4.8 KiB
Go

package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
type Config struct {
BackendURL string `json:"backendUrl"`
Identifier string `json:"identifier"`
APIKey string `json:"apiKey"`
ReportIntervalSec int `json:"reportIntervalSec"`
LogLevel string `json:"logLevel"`
RootFS string `json:"rootFs"`
MonitorPort int `json:"monitorPort"`
}
type Overrides struct {
BackendURL string
Identifier string
APIKey string
ReportIntervalSec int
LogLevel string
RootFS string
MonitorPort int
}
func Load(path string) (Config, error) {
return LoadWithOverrides(path, Overrides{})
}
func LoadWithOverrides(path string, overrides Overrides) (Config, error) {
cfg := Config{
BackendURL: getenvAny([]string{"HF_MONITER_BACKEND_URL", "HF_MONITOR_BACKEND_URL"}, "https://monitor.hangman-lab.top"),
Identifier: getenvAny([]string{"HF_MONITER_IDENTIFIER", "HF_MONITOR_IDENTIFIER"}, hostnameOr("unknown-host")),
APIKey: getenvAny([]string{"HF_MONITER_API_KEY", "HF_MONITOR_API_KEY"}, ""),
ReportIntervalSec: getenvIntAny([]string{"HF_MONITER_REPORT_INTERVAL", "HF_MONITOR_REPORT_INTERVAL"}, 30),
LogLevel: getenvAny([]string{"HF_MONITER_LOG_LEVEL", "HF_MONITOR_LOG_LEVEL"}, "info"),
RootFS: getenvAny([]string{"HF_MONITER_ROOTFS", "HF_MONITOR_ROOTFS"}, ""),
}
if path != "" {
if fileCfg, err := loadFile(path); err == nil {
merge(&cfg, fileCfg)
} else if !os.IsNotExist(err) {
return cfg, fmt.Errorf("load config file: %w", err)
}
}
// env always wins over file
cfg.BackendURL = getenvAny([]string{"HF_MONITER_BACKEND_URL", "HF_MONITOR_BACKEND_URL"}, cfg.BackendURL)
cfg.Identifier = getenvAny([]string{"HF_MONITER_IDENTIFIER", "HF_MONITOR_IDENTIFIER"}, cfg.Identifier)
if v := getenvAny([]string{"HF_MONITER_API_KEY", "HF_MONITOR_API_KEY"}, ""); v != "" {
cfg.APIKey = v
}
cfg.ReportIntervalSec = getenvIntAny([]string{"HF_MONITER_REPORT_INTERVAL", "HF_MONITOR_REPORT_INTERVAL"}, cfg.ReportIntervalSec)
cfg.LogLevel = getenvAny([]string{"HF_MONITER_LOG_LEVEL", "HF_MONITOR_LOG_LEVEL"}, cfg.LogLevel)
cfg.RootFS = getenvAny([]string{"HF_MONITER_ROOTFS", "HF_MONITOR_ROOTFS"}, cfg.RootFS)
cfg.MonitorPort = getenvIntAny([]string{"MONITOR_PORT", "HF_MONITOR_PORT"}, cfg.MonitorPort)
if overrides.BackendURL != "" {
cfg.BackendURL = overrides.BackendURL
}
if overrides.Identifier != "" {
cfg.Identifier = overrides.Identifier
}
if overrides.APIKey != "" {
cfg.APIKey = overrides.APIKey
}
if overrides.ReportIntervalSec > 0 {
cfg.ReportIntervalSec = overrides.ReportIntervalSec
}
if overrides.LogLevel != "" {
cfg.LogLevel = overrides.LogLevel
}
if overrides.RootFS != "" {
cfg.RootFS = overrides.RootFS
}
if overrides.MonitorPort > 0 {
cfg.MonitorPort = overrides.MonitorPort
}
if cfg.BackendURL == "" {
return cfg, fmt.Errorf("backendUrl is required")
}
if cfg.Identifier == "" {
return cfg, fmt.Errorf("identifier is required")
}
if cfg.ReportIntervalSec <= 0 {
cfg.ReportIntervalSec = 30
}
applyHostFSEnv(cfg.RootFS)
return cfg, nil
}
func loadFile(path string) (Config, error) {
var cfg Config
buf, err := os.ReadFile(path)
if err != nil {
return cfg, err
}
if err := json.Unmarshal(buf, &cfg); err != nil {
return cfg, err
}
return cfg, nil
}
func merge(dst *Config, src Config) {
if src.BackendURL != "" {
dst.BackendURL = src.BackendURL
}
if src.Identifier != "" {
dst.Identifier = src.Identifier
}
if src.APIKey != "" {
dst.APIKey = src.APIKey
}
if src.ReportIntervalSec > 0 {
dst.ReportIntervalSec = src.ReportIntervalSec
}
if src.LogLevel != "" {
dst.LogLevel = src.LogLevel
}
if src.RootFS != "" {
dst.RootFS = src.RootFS
}
if src.MonitorPort > 0 {
dst.MonitorPort = src.MonitorPort
}
}
func getenvAny(keys []string, fallback string) string {
for _, key := range keys {
if v := os.Getenv(key); v != "" {
return v
}
}
return fallback
}
func getenvIntAny(keys []string, fallback int) int {
for _, key := range keys {
if v := os.Getenv(key); v != "" {
var out int
if _, err := fmt.Sscanf(v, "%d", &out); err == nil && out > 0 {
return out
}
}
}
return fallback
}
func hostnameOr(fallback string) string {
name, err := os.Hostname()
if err != nil || name == "" {
return fallback
}
return name
}
func applyHostFSEnv(rootFS string) {
if rootFS == "" {
return
}
setIfEmpty("HOST_PROC", filepath.Join(rootFS, "proc"))
setIfEmpty("HOST_SYS", filepath.Join(rootFS, "sys"))
setIfEmpty("HOST_ETC", filepath.Join(rootFS, "etc"))
setIfEmpty("HOST_VAR", filepath.Join(rootFS, "var"))
setIfEmpty("HOST_RUN", filepath.Join(rootFS, "run"))
}
func setIfEmpty(key, value string) {
if os.Getenv(key) == "" {
_ = os.Setenv(key, value)
}
}