2022-10-10 01:20:18 +02:00
|
|
|
package main
|
|
|
|
|
2022-10-10 01:30:39 +02:00
|
|
|
import (
|
2023-02-21 11:50:50 +01:00
|
|
|
"errors"
|
2022-10-10 01:30:39 +02:00
|
|
|
"fmt"
|
2022-10-12 16:35:04 +02:00
|
|
|
"strings"
|
2023-02-08 14:46:14 +01:00
|
|
|
"time"
|
2022-10-10 01:30:39 +02:00
|
|
|
|
|
|
|
"git.sr.ht/~emersion/go-scfg"
|
|
|
|
)
|
2022-10-10 01:20:18 +02:00
|
|
|
|
2023-01-20 15:03:46 +01:00
|
|
|
type alertMode int
|
|
|
|
|
|
|
|
const (
|
|
|
|
single alertMode = iota
|
|
|
|
multi
|
|
|
|
)
|
|
|
|
|
2023-03-08 16:30:58 +01:00
|
|
|
type cacheType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
memory cacheType = iota
|
2023-03-08 22:05:15 +01:00
|
|
|
redis
|
2023-03-08 16:30:58 +01:00
|
|
|
)
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
type config struct {
|
2023-02-10 17:16:13 +01:00
|
|
|
BaseURL string
|
2022-10-10 01:20:18 +02:00
|
|
|
HTTPAddress string
|
|
|
|
LogLevel string
|
2023-01-20 15:03:46 +01:00
|
|
|
alertMode alertMode
|
2022-10-10 14:04:51 +02:00
|
|
|
User string
|
|
|
|
Password string
|
2022-10-10 02:42:13 +02:00
|
|
|
ntfy ntfyConfig
|
2022-10-12 16:35:04 +02:00
|
|
|
labels labels
|
2023-02-08 14:46:14 +01:00
|
|
|
cache cacheConfig
|
2023-02-10 17:16:13 +01:00
|
|
|
am alertmanagerConfig
|
2023-02-12 17:08:58 +01:00
|
|
|
resolved resolvedConfig
|
2022-10-10 02:42:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type ntfyConfig struct {
|
2023-02-21 11:50:50 +01:00
|
|
|
Topic string
|
|
|
|
User string
|
|
|
|
Password string
|
|
|
|
AccessToken string
|
2022-10-10 01:20:18 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 16:35:04 +02:00
|
|
|
type labels struct {
|
|
|
|
Order []string
|
|
|
|
Label map[string]labelConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
type labelConfig struct {
|
|
|
|
Priority string
|
2022-10-12 17:04:44 +02:00
|
|
|
Tags []string
|
2023-02-20 13:08:59 +01:00
|
|
|
Icon string
|
2022-10-12 16:35:04 +02:00
|
|
|
}
|
|
|
|
|
2023-02-08 14:46:14 +01:00
|
|
|
type cacheConfig struct {
|
2023-03-08 16:30:58 +01:00
|
|
|
Type cacheType
|
2023-02-08 14:46:14 +01:00
|
|
|
CleanupInterval time.Duration
|
|
|
|
Duration time.Duration
|
|
|
|
}
|
|
|
|
|
2023-02-10 17:16:13 +01:00
|
|
|
type alertmanagerConfig struct {
|
2023-02-12 14:15:02 +01:00
|
|
|
User string
|
|
|
|
Password string
|
2023-02-10 17:16:13 +01:00
|
|
|
SilenceDuration time.Duration
|
2023-02-12 14:58:37 +01:00
|
|
|
URL string
|
2023-02-10 17:16:13 +01:00
|
|
|
}
|
|
|
|
|
2023-02-12 17:08:58 +01:00
|
|
|
type resolvedConfig struct {
|
|
|
|
Tags []string
|
2023-02-20 13:08:59 +01:00
|
|
|
Icon string
|
2023-02-12 17:08:58 +01:00
|
|
|
}
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
func readConfig(path string) (*config, error) {
|
|
|
|
cfg, err := scfg.Load(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config := new(config)
|
|
|
|
// Set default values
|
|
|
|
config.HTTPAddress = "127.0.0.1:8080"
|
|
|
|
config.LogLevel = "info"
|
2023-01-20 15:03:46 +01:00
|
|
|
config.alertMode = single
|
2022-10-10 01:20:18 +02:00
|
|
|
|
2023-03-08 16:30:58 +01:00
|
|
|
config.cache.Type = memory
|
2023-02-08 14:46:14 +01:00
|
|
|
config.cache.CleanupInterval = time.Hour
|
|
|
|
config.cache.Duration = time.Hour * 24
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
d := cfg.Get("log-level")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.LogLevel); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = cfg.Get("http-address")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.HTTPAddress); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-10 17:16:13 +01:00
|
|
|
d = cfg.Get("base-url")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.BaseURL); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-20 15:03:46 +01:00
|
|
|
d = cfg.Get("alert-mode")
|
|
|
|
if d != nil {
|
|
|
|
var mode string
|
|
|
|
if err := d.ParseParams(&mode); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch strings.ToLower(mode) {
|
|
|
|
case "single":
|
|
|
|
config.alertMode = single
|
|
|
|
|
|
|
|
case "multi":
|
|
|
|
config.alertMode = multi
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("%q directive: illegal mode %q", d.Name, mode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 14:04:51 +02:00
|
|
|
d = cfg.Get("user")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.User); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = cfg.Get("password")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.Password); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-21 12:10:13 +01:00
|
|
|
if (config.Password != "" && config.User == "") ||
|
|
|
|
(config.Password == "" && config.User != "") {
|
|
|
|
return nil, errors.New("user and password have to be set together")
|
|
|
|
}
|
|
|
|
|
2022-10-12 16:35:04 +02:00
|
|
|
labelsDir := cfg.Get("labels")
|
|
|
|
if labelsDir != nil {
|
|
|
|
d = labelsDir.Children.Get("order")
|
|
|
|
if d != nil {
|
|
|
|
var order string
|
|
|
|
if err := d.ParseParams(&order); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config.labels.Order = strings.Split(order, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
labels := make(map[string]labelConfig)
|
|
|
|
for _, labelName := range config.labels.Order {
|
2023-01-16 00:48:11 +01:00
|
|
|
for _, labelDir := range labelsDir.Children.GetAll(labelName) {
|
2022-10-12 16:35:04 +02:00
|
|
|
labelConfig := new(labelConfig)
|
|
|
|
var name string
|
|
|
|
|
|
|
|
if err := labelDir.ParseParams(&name); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
d = labelDir.Children.Get("priority")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&labelConfig.Priority); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-12 17:04:44 +02:00
|
|
|
d = labelDir.Children.Get("tags")
|
|
|
|
if d != nil {
|
|
|
|
var tags string
|
|
|
|
if err := d.ParseParams(&tags); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
labelConfig.Tags = strings.Split(tags, ",")
|
|
|
|
}
|
|
|
|
|
2023-02-20 13:08:59 +01:00
|
|
|
d = labelDir.Children.Get("icon")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&labelConfig.Icon); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-12 16:35:04 +02:00
|
|
|
labels[fmt.Sprintf("%s:%s", labelName, name)] = *labelConfig
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
config.labels.Label = labels
|
|
|
|
}
|
|
|
|
|
2022-10-10 02:42:13 +02:00
|
|
|
ntfyDir := cfg.Get("ntfy")
|
|
|
|
if ntfyDir == nil {
|
|
|
|
return nil, fmt.Errorf("%q directive missing", "ntfy")
|
|
|
|
}
|
|
|
|
|
|
|
|
d = ntfyDir.Children.Get("topic")
|
2022-10-10 01:30:39 +02:00
|
|
|
if d == nil {
|
2022-10-10 02:42:13 +02:00
|
|
|
return nil, fmt.Errorf("%q missing from %q directive", "topic", "ntfy")
|
2022-10-10 01:30:39 +02:00
|
|
|
}
|
2022-10-10 02:42:13 +02:00
|
|
|
if err := d.ParseParams(&config.ntfy.Topic); err != nil {
|
2022-10-10 01:30:39 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-10-10 02:42:13 +02:00
|
|
|
d = ntfyDir.Children.Get("user")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.ntfy.User); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = ntfyDir.Children.Get("password")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.ntfy.Password); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-21 12:10:13 +01:00
|
|
|
if (config.ntfy.Password != "" && config.ntfy.User == "") ||
|
|
|
|
(config.ntfy.Password == "" && config.ntfy.User != "") {
|
|
|
|
return nil, errors.New("ntfy: user and password have to be set together")
|
|
|
|
}
|
|
|
|
|
2023-02-21 11:50:50 +01:00
|
|
|
d = ntfyDir.Children.Get("access-token")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.ntfy.AccessToken); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.ntfy.User != "" && config.ntfy.AccessToken != "" {
|
|
|
|
return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time")
|
|
|
|
}
|
|
|
|
|
2023-02-08 14:46:14 +01:00
|
|
|
cacheDir := cfg.Get("cache")
|
|
|
|
|
|
|
|
if cacheDir != nil {
|
2023-03-08 16:30:58 +01:00
|
|
|
d = cacheDir.Children.Get("type")
|
|
|
|
if d != nil {
|
|
|
|
var cacheType string
|
|
|
|
if err := d.ParseParams(&cacheType); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch strings.ToLower(cacheType) {
|
|
|
|
case "memory":
|
|
|
|
config.cache.Type = memory
|
2023-03-08 22:05:15 +01:00
|
|
|
case "redis":
|
|
|
|
config.cache.Type = redis
|
2023-03-08 16:30:58 +01:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("cache: illegal type %q", cacheType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-08 14:46:14 +01:00
|
|
|
var durationString string
|
|
|
|
d = cacheDir.Children.Get("duration")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&durationString); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
duration, err := time.ParseDuration(durationString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config.cache.Duration = duration
|
|
|
|
}
|
|
|
|
|
|
|
|
var cleanupIntervalString string
|
|
|
|
d = cacheDir.Children.Get("cleanup-interval")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&cleanupIntervalString); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
interval, err := time.ParseDuration(cleanupIntervalString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config.cache.CleanupInterval = interval
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-10 17:16:13 +01:00
|
|
|
amDir := cfg.Get("alertmanager")
|
|
|
|
|
|
|
|
if amDir != nil {
|
|
|
|
var durationString string
|
|
|
|
d = amDir.Children.Get("silence-duration")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&durationString); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
duration, err := time.ParseDuration(durationString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config.am.SilenceDuration = duration
|
|
|
|
}
|
2023-02-12 14:15:02 +01:00
|
|
|
|
|
|
|
d = amDir.Children.Get("user")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.am.User); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = amDir.Children.Get("password")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.am.Password); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-12 14:58:37 +01:00
|
|
|
|
2023-02-21 12:10:13 +01:00
|
|
|
if (config.am.Password != "" && config.am.User == "") ||
|
|
|
|
(config.am.Password == "" && config.am.User != "") {
|
|
|
|
return nil, errors.New("alertmanager: user and password have to be set together")
|
|
|
|
}
|
|
|
|
|
2023-02-12 14:58:37 +01:00
|
|
|
d = amDir.Children.Get("url")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.am.URL); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-10 17:16:13 +01:00
|
|
|
}
|
|
|
|
|
2023-02-12 17:08:58 +01:00
|
|
|
resolvedDir := cfg.Get("resolved")
|
|
|
|
if resolvedDir != nil {
|
|
|
|
d = resolvedDir.Children.Get("tags")
|
|
|
|
if d != nil {
|
|
|
|
var tags string
|
|
|
|
if err := d.ParseParams(&tags); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
config.resolved.Tags = strings.Split(tags, ",")
|
|
|
|
}
|
2023-02-20 13:08:59 +01:00
|
|
|
|
|
|
|
d = resolvedDir.Children.Get("icon")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.resolved.Icon); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-12 17:08:58 +01:00
|
|
|
}
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
return config, nil
|
|
|
|
}
|