Run config validator before start
This commit is contained in:
parent
7692bfe779
commit
08a94ac0b2
4 changed files with 143 additions and 5 deletions
1
TODO.md
1
TODO.md
|
@ -1,4 +1,3 @@
|
|||
- [ ] Check config relations between runner and notification groups on start
|
||||
- [ ] Add option to send "incident resolved" notifications
|
||||
- [ ] Add Matrix notificator
|
||||
- [ ] Add Telegram notificator
|
||||
|
|
42
config.go
42
config.go
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -54,15 +55,48 @@ func ReadConfig(file string) (*Config, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("config: %w", err)
|
||||
}
|
||||
return parseConfig(data)
|
||||
}
|
||||
|
||||
func parseConfig(data []byte) (*Config, error) {
|
||||
c := new(Config)
|
||||
err = yaml.Unmarshal(data, c)
|
||||
if err != nil {
|
||||
if err := yaml.Unmarshal(data, c); err != nil {
|
||||
return nil, fmt.Errorf("config: %w", err)
|
||||
}
|
||||
return c, validateConfig(c)
|
||||
}
|
||||
|
||||
func validateConfig(_ *Config) error {
|
||||
// todo(alexvanin): set defaults and validate values such as bad email addresses
|
||||
func validateConfig(cfg *Config) error {
|
||||
// Step 1: sanity check of notification groups
|
||||
configuredNotifications := make(map[string]map[string]struct{})
|
||||
if cfg.Notifications.Email != nil {
|
||||
emailMap := make(map[string]struct{}, len(cfg.Notifications.Email.Groups))
|
||||
for _, group := range cfg.Notifications.Email.Groups {
|
||||
if len(group.Addresses) == 0 {
|
||||
return fmt.Errorf("email group %s contains no addresses", group.Name)
|
||||
}
|
||||
if _, ok := emailMap[group.Name]; ok {
|
||||
return fmt.Errorf("non unique email group name %s", group.Name)
|
||||
}
|
||||
emailMap[group.Name] = struct{}{}
|
||||
}
|
||||
configuredNotifications["email"] = emailMap
|
||||
}
|
||||
// With new notification channels, add more sanity checks
|
||||
|
||||
// Step 2: sanity check of command notifications
|
||||
for _, cmd := range cfg.Commands {
|
||||
for _, n := range cmd.Notifications {
|
||||
elements := strings.Split(n, ":")
|
||||
if len(elements) != 2 {
|
||||
return fmt.Errorf("invalid notification tuple %s in command %s", n, cmd.Name)
|
||||
} else if groups, ok := configuredNotifications[elements[0]]; !ok {
|
||||
return fmt.Errorf("invalid notification type %s in command %s", elements[0], cmd.Name)
|
||||
} else if _, ok = groups[elements[1]]; !ok {
|
||||
return fmt.Errorf("invalid notification group %s in command %s", elements[1], cmd.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
102
config_test.go
Normal file
102
config_test.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testCases = [...]struct {
|
||||
config string
|
||||
isValid bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
config: `
|
||||
state:
|
||||
bolt: ./nz.state
|
||||
notifications:
|
||||
email:
|
||||
smtp: smtp.gmail.com:587
|
||||
login: nzbx@corp.com
|
||||
password: secret
|
||||
groups:
|
||||
- name: developers
|
||||
addresses:
|
||||
- alex@corp.com
|
||||
commands:
|
||||
- name: Test command
|
||||
exec: ./script.sh arg1
|
||||
cron: "*/5 * * * *"
|
||||
interval: 10s
|
||||
timeout: 2s
|
||||
threshold: 3
|
||||
threshold_sleep: 5s
|
||||
notifications:
|
||||
- email:developers`,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
config: `
|
||||
notifications:
|
||||
email:
|
||||
groups:
|
||||
- name: developers`,
|
||||
errMsg: "contains no addresses",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
notifications:
|
||||
email:
|
||||
groups:
|
||||
- name: developers
|
||||
addresses:
|
||||
- foo@corp.com
|
||||
- name: developers
|
||||
addresses:
|
||||
- bar@corp.com
|
||||
`,
|
||||
errMsg: "non unique email group name",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
commands:
|
||||
- name: Test command
|
||||
notifications:
|
||||
- hello wolrd`,
|
||||
errMsg: "invalid notification tuple",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
commands:
|
||||
- name: Test command
|
||||
notifications:
|
||||
- foo:bar`,
|
||||
errMsg: "invalid notification type",
|
||||
},
|
||||
{
|
||||
config: `
|
||||
notifications:
|
||||
email:
|
||||
groups:
|
||||
- name: developers
|
||||
addresses:
|
||||
- foo@corp.com
|
||||
commands:
|
||||
- name: Test command
|
||||
notifications:
|
||||
- email:ops`,
|
||||
errMsg: "invalid notification group",
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseConfig(t *testing.T) {
|
||||
for i, testCase := range testCases {
|
||||
_, err := parseConfig([]byte(testCase.config))
|
||||
require.Equal(t, testCase.isValid, err == nil, fmt.Sprintf("test:%d, err:%s", i, err))
|
||||
if !testCase.isValid {
|
||||
require.Contains(t, err.Error(), testCase.errMsg)
|
||||
}
|
||||
}
|
||||
}
|
3
go.mod
3
go.mod
|
@ -5,12 +5,15 @@ go 1.18
|
|||
require (
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/robfig/cron/v3 v3.0.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/zap v1.21.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
||||
|
|
Loading…
Reference in a new issue