package config import ( "cmp" "encoding/json/v2" "errors" "log/slog" "net/url" "os" "sync" ) func die(err error, msg string) { if err == nil { return } log.Error(msg, "err", err) os.Exit(1) } func unwrap[T any](t T, err error) T { die(err, "bad value") return t } var log = slog.New(slog.NewJSONHandler(os.Stdout, nil)) type DomainGroup struct { Interval int `json:"interval"` Retries int `json:"retries"` Domains []string `json:"domains"` } type Cicada struct { DownString string `json:"down_string"` UpString string `json:"up_string"` SendAlertUrl string `json:"send_alert_url"` Key string `json:"key"` } type GetsSection struct { PassIfOne bool `json:"pass_if_one"` Interval int `json:"interval"` Severity string `json:"severity"` Urls []string `json:"urls"` } type Http struct { Timeout int `json:"timeout"` Ua string `json:"ua"` } type Config struct { Http Http `json:"http"` Gets map[string]GetsSection `json:"gets"` SeeOthers map[string]string `json:"see_others"` SeeOtherRetries int `json:"see_other_retries"` SeeOtherInterval int `json:"see_other_interval"` Commands []Command `json:"commands"` Cicada Cicada `json:"cicada"` } type Command struct { // arrange (name/describe) Name string `json:"name"` Interval int `json:"interval"` Service string `json:"service"` Component string `json:"component"` Retries int `json:"retries"` // act Command string `json:"command"` // Args []string `json:"args"` // assert ExactOutput string `json:"exact_output"` MatchAny []string `json:"match_any"` MatchAll []string `json:"match_all"` } var cfg Config var cfgOnce sync.Once func (c Config) MustValidate() { errs := []error{} if c.Cicada.Key == "" { errs = append(errs, errors.New("need to pass NYMPH_CICADA_KEY")) } if len(cfg.SeeOthers)+len(cfg.Gets)+len(cfg.Commands) == 0 { errs = append(errs, errors.New("no checks defined")) } _, urlParseErr := url.Parse(c.Cicada.SendAlertUrl) if urlParseErr != nil { errs = append(errs, urlParseErr) } die(errors.Join(errs...), "could not validate config") } func Must() Config { cfgOnce.Do(func() { path := cmp.Or(os.Getenv("NYMPH_CONFIG"), "/etc/nymph/nymph.json") die(json.Unmarshal(unwrap(os.ReadFile(path)), &cfg), "could not read config file") cicadaKey := os.Getenv("NYMPH_CICADA_KEY") cfg.Cicada.Key = cicadaKey if cfg.Http.Timeout == 0 { cfg.Http.Timeout = 10 } if cfg.Http.Ua == "" { cfg.Http.Ua = "nymph-uptime-checker" } cfg.MustValidate() }) return cfg }