2023-07-12 14:56:48 +02:00
|
|
|
// Package config defines the configuration file.
|
|
|
|
package config
|
2022-10-10 01:20:18 +02:00
|
|
|
|
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-07-12 14:56:48 +02:00
|
|
|
// AlertMode determines if alerts grouped by Alertmanager are being kept together.
|
|
|
|
type AlertMode int
|
2023-01-20 15:03:46 +01:00
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
// The different modes for AlertMode.
|
2023-01-20 15:03:46 +01:00
|
|
|
const (
|
2023-07-12 14:56:48 +02:00
|
|
|
Single AlertMode = iota
|
|
|
|
Multi
|
2023-01-20 15:03:46 +01:00
|
|
|
)
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
// Config is the configuration of the bridge.
|
|
|
|
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-08-13 16:02:02 +02:00
|
|
|
LogFormat string
|
2023-07-12 14:56:48 +02:00
|
|
|
AlertMode AlertMode
|
2022-10-10 14:04:51 +02:00
|
|
|
User string
|
|
|
|
Password string
|
2023-07-12 14:56:48 +02:00
|
|
|
Ntfy ntfyConfig
|
|
|
|
Labels labels
|
2023-07-12 19:00:25 +02:00
|
|
|
Cache CacheConfig
|
2023-07-12 14:56:48 +02:00
|
|
|
Am alertmanagerConfig
|
|
|
|
Resolved resolvedConfig
|
2022-10-10 02:42:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type ntfyConfig struct {
|
2023-08-25 14:57:54 +02:00
|
|
|
Topic string
|
|
|
|
User string
|
|
|
|
Password string
|
|
|
|
AccessToken string
|
|
|
|
CertFingerprint string
|
|
|
|
EmailAddress string
|
|
|
|
Call 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 {
|
2023-03-09 17:02:05 +01:00
|
|
|
Priority string
|
|
|
|
Tags []string
|
|
|
|
Icon string
|
2023-07-12 14:56:48 +02:00
|
|
|
EmailAddress string
|
|
|
|
Call string
|
2022-10-12 16:35:04 +02:00
|
|
|
}
|
|
|
|
|
2023-07-12 19:00:25 +02:00
|
|
|
// CacheConfig is the configuration of the cache.
|
|
|
|
type CacheConfig struct {
|
2023-03-08 22:17:32 +01:00
|
|
|
// shared settings
|
2023-07-12 19:00:25 +02:00
|
|
|
Type string
|
2023-03-08 22:17:32 +01:00
|
|
|
Duration time.Duration
|
|
|
|
// memory settings
|
2023-02-08 14:46:14 +01:00
|
|
|
CleanupInterval time.Duration
|
2023-03-08 22:17:32 +01:00
|
|
|
// redis settings
|
|
|
|
RedisURL string
|
2023-02-08 14:46:14 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
// ReadConfig reads an scfg formatted file and returns the configuration struct.
|
|
|
|
func ReadConfig(path string) (*Config, error) {
|
2022-10-10 01:20:18 +02:00
|
|
|
cfg, err := scfg.Load(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config := new(Config)
|
2022-10-10 01:20:18 +02:00
|
|
|
// Set default values
|
|
|
|
config.HTTPAddress = "127.0.0.1:8080"
|
|
|
|
config.LogLevel = "info"
|
2023-08-13 16:02:02 +02:00
|
|
|
config.LogFormat = "text"
|
2023-07-12 19:17:47 +02:00
|
|
|
config.AlertMode = Multi
|
2022-10-10 01:20:18 +02:00
|
|
|
|
2023-07-12 19:17:47 +02:00
|
|
|
config.Cache.Type = "disabled"
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Cache.Duration = time.Hour * 24
|
2023-03-08 22:17:32 +01:00
|
|
|
// memory
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Cache.CleanupInterval = time.Hour
|
2023-03-08 22:17:32 +01:00
|
|
|
// redis
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Cache.RedisURL = "redis://localhost:6379"
|
2023-02-08 14:46:14 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-13 16:02:02 +02:00
|
|
|
d = cfg.Get("log-format")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.LogFormat); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
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":
|
2023-07-12 14:56:48 +02:00
|
|
|
config.AlertMode = Single
|
2023-01-20 15:03:46 +01:00
|
|
|
|
|
|
|
case "multi":
|
2023-07-12 14:56:48 +02:00
|
|
|
config.AlertMode = Multi
|
2023-01-20 15:03:46 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Labels.Order = strings.Split(order, ",")
|
2022-10-12 16:35:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
labels := make(map[string]labelConfig)
|
2023-07-12 14:56:48 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 17:02:05 +01:00
|
|
|
d = labelDir.Children.Get("email-address")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&labelConfig.EmailAddress); err != nil {
|
2023-03-09 17:02:05 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-07 21:37:11 +02:00
|
|
|
d = labelDir.Children.Get("call")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&labelConfig.Call); err != nil {
|
2023-07-07 21:37:11 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-12 16:35:04 +02:00
|
|
|
labels[fmt.Sprintf("%s:%s", labelName, name)] = *labelConfig
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Labels.Label = labels
|
2022-10-12 16:35:04 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2023-07-12 14:56:48 +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 {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Ntfy.User); err != nil {
|
2022-10-10 02:42:13 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = ntfyDir.Children.Get("password")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Ntfy.Password); err != nil {
|
2022-10-10 02:42:13 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
if (config.Ntfy.Password != "" && config.Ntfy.User == "") ||
|
|
|
|
(config.Ntfy.Password == "" && config.Ntfy.User != "") {
|
2023-02-21 12:10:13 +01:00
|
|
|
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 {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Ntfy.AccessToken); err != nil {
|
2023-02-21 11:50:50 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
if config.Ntfy.User != "" && config.Ntfy.AccessToken != "" {
|
2023-02-21 11:50:50 +01:00
|
|
|
return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time")
|
|
|
|
}
|
|
|
|
|
2023-08-25 14:57:54 +02:00
|
|
|
d = ntfyDir.Children.Get("certificate-fingerprint")
|
|
|
|
if d != nil {
|
|
|
|
if err := d.ParseParams(&config.Ntfy.CertFingerprint); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-08-28 00:36:41 +02:00
|
|
|
|
|
|
|
// hex.EncodeToString outputs a lower case string
|
|
|
|
config.Ntfy.CertFingerprint = strings.ToLower(strings.ReplaceAll(config.Ntfy.CertFingerprint, ":", ""))
|
2023-08-25 14:57:54 +02:00
|
|
|
}
|
|
|
|
|
2023-03-09 17:02:05 +01:00
|
|
|
d = ntfyDir.Children.Get("email-address")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Ntfy.EmailAddress); err != nil {
|
2023-03-09 17:02:05 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-07 21:37:11 +02:00
|
|
|
d = ntfyDir.Children.Get("call")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Ntfy.Call); err != nil {
|
2023-07-07 21:37:11 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2023-07-12 19:00:25 +02:00
|
|
|
if err := d.ParseParams(&config.Cache.Type); err != nil {
|
2023-03-08 16:30:58 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Cache.Duration = duration
|
2023-02-08 14:46:14 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 22:17:32 +01:00
|
|
|
// memory
|
2023-02-08 14:46:14 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Cache.CleanupInterval = interval
|
2023-02-08 14:46:14 +01:00
|
|
|
}
|
2023-03-08 22:17:32 +01:00
|
|
|
|
|
|
|
// redis
|
|
|
|
d = cacheDir.Children.Get("redis-url")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Cache.RedisURL); err != nil {
|
2023-03-08 22:17:32 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-08 14:46:14 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Am.SilenceDuration = duration
|
2023-02-10 17:16:13 +01:00
|
|
|
}
|
2023-02-12 14:15:02 +01:00
|
|
|
|
|
|
|
d = amDir.Children.Get("user")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Am.User); err != nil {
|
2023-02-12 14:15:02 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d = amDir.Children.Get("password")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Am.Password); err != nil {
|
2023-02-12 14:15:02 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-12 14:58:37 +01:00
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
if (config.Am.Password != "" && config.Am.User == "") ||
|
|
|
|
(config.Am.Password == "" && config.Am.User != "") {
|
2023-02-21 12:10:13 +01:00
|
|
|
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 {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Am.URL); err != nil {
|
2023-02-12 14:58:37 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-07-12 14:56:48 +02:00
|
|
|
config.Resolved.Tags = strings.Split(tags, ",")
|
2023-02-12 17:08:58 +01:00
|
|
|
}
|
2023-02-20 13:08:59 +01:00
|
|
|
|
|
|
|
d = resolvedDir.Children.Get("icon")
|
|
|
|
if d != nil {
|
2023-07-12 14:56:48 +02:00
|
|
|
if err := d.ParseParams(&config.Resolved.Icon); err != nil {
|
2023-02-20 13:08:59 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-12 17:08:58 +01:00
|
|
|
}
|
|
|
|
|
2022-10-10 01:20:18 +02:00
|
|
|
return config, nil
|
|
|
|
}
|