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 option to send "incident resolved" notifications
|
||||||
- [ ] Add Matrix notificator
|
- [ ] Add Matrix notificator
|
||||||
- [ ] Add Telegram notificator
|
- [ ] Add Telegram notificator
|
||||||
|
|
42
config.go
42
config.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
@ -54,15 +55,48 @@ func ReadConfig(file string) (*Config, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("config: %w", err)
|
return nil, fmt.Errorf("config: %w", err)
|
||||||
}
|
}
|
||||||
|
return parseConfig(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseConfig(data []byte) (*Config, error) {
|
||||||
c := new(Config)
|
c := new(Config)
|
||||||
err = yaml.Unmarshal(data, c)
|
if err := yaml.Unmarshal(data, c); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("config: %w", err)
|
return nil, fmt.Errorf("config: %w", err)
|
||||||
}
|
}
|
||||||
return c, validateConfig(c)
|
return c, validateConfig(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateConfig(_ *Config) error {
|
func validateConfig(cfg *Config) error {
|
||||||
// todo(alexvanin): set defaults and validate values such as bad email addresses
|
// 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
|
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 (
|
require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/robfig/cron/v3 v3.0.0
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/zap v1.21.0
|
go.uber.org/zap v1.21.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99
|
gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
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/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
||||||
|
|
Loading…
Reference in a new issue