mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-15 16:30:55 +01:00
80a9dc79fe
* VMAlert start with empty rules dir There are some applications (operator for instance), that generates alerts configuration at runtime and vmalert must start correctly without rules to support this behaviour. Later application will add rules files and send SIGHUP to vmalert, which will trigger reading rules files and start rules exectuion. Removing rules files with SIGHUP signal must stop rules execution and vmalert will wait for new rules. * imports sorted * added test cases for empty rules, removed blank line * fixed imports conflict * updated tests
323 lines
6.7 KiB
Go
323 lines
6.7 KiB
Go
package config
|
|
|
|
import (
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/notifier"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
u, _ := url.Parse("https://victoriametrics.com/path")
|
|
notifier.InitTemplateFunc(u)
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestParseGood(t *testing.T) {
|
|
if _, err := Parse([]string{"testdata/*good.rules", "testdata/dir/*good.*"}, true, true); err != nil {
|
|
t.Errorf("error parsing files %s", err)
|
|
}
|
|
}
|
|
|
|
func TestParseBad(t *testing.T) {
|
|
testCases := []struct {
|
|
path []string
|
|
expErr string
|
|
}{
|
|
{
|
|
[]string{"testdata/rules0-bad.rules"},
|
|
"unexpected token",
|
|
},
|
|
{
|
|
[]string{"testdata/dir/rules0-bad.rules"},
|
|
"error parsing annotation",
|
|
},
|
|
{
|
|
[]string{"testdata/dir/rules1-bad.rules"},
|
|
"duplicate in file",
|
|
},
|
|
{
|
|
[]string{"testdata/dir/rules2-bad.rules"},
|
|
"function \"value\" not defined",
|
|
},
|
|
{
|
|
[]string{"testdata/dir/rules3-bad.rules"},
|
|
"either `record` or `alert` must be set",
|
|
},
|
|
{
|
|
[]string{"testdata/dir/rules4-bad.rules"},
|
|
"either `record` or `alert` must be set",
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
_, err := Parse(tc.path, true, true)
|
|
if err == nil {
|
|
t.Errorf("expected to get error")
|
|
return
|
|
}
|
|
if !strings.Contains(err.Error(), tc.expErr) {
|
|
t.Errorf("expected err to contain %q; got %q instead", tc.expErr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRule_Validate(t *testing.T) {
|
|
if err := (&Rule{}).Validate(); err == nil {
|
|
t.Errorf("expected empty name error")
|
|
}
|
|
if err := (&Rule{Alert: "alert"}).Validate(); err == nil {
|
|
t.Errorf("expected empty expr error")
|
|
}
|
|
if err := (&Rule{Alert: "alert", Expr: "test>0"}).Validate(); err != nil {
|
|
t.Errorf("expected valid rule; got %s", err)
|
|
}
|
|
}
|
|
|
|
func TestGroup_Validate(t *testing.T) {
|
|
testCases := []struct {
|
|
group *Group
|
|
rules []Rule
|
|
validateAnnotations bool
|
|
validateExpressions bool
|
|
expErr string
|
|
}{
|
|
{
|
|
group: &Group{},
|
|
expErr: "group name must be set",
|
|
},
|
|
{
|
|
group: &Group{Name: "test"},
|
|
expErr: "contain no rules",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{
|
|
Record: "record",
|
|
Expr: "up | 0",
|
|
},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{
|
|
Record: "record",
|
|
Expr: "up | 0",
|
|
},
|
|
},
|
|
},
|
|
expErr: "invalid expression",
|
|
validateExpressions: true,
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{
|
|
Alert: "alert",
|
|
Expr: "up == 1",
|
|
Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{
|
|
Alert: "alert",
|
|
Expr: "up == 1",
|
|
Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "error parsing annotation",
|
|
validateAnnotations: true,
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{
|
|
Alert: "alert",
|
|
Expr: "up == 1",
|
|
},
|
|
{
|
|
Alert: "alert",
|
|
Expr: "up == 1",
|
|
},
|
|
},
|
|
},
|
|
expErr: "duplicate",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
},
|
|
},
|
|
expErr: "duplicate",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{Record: "record", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
{Record: "record", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
},
|
|
},
|
|
expErr: "duplicate",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"description": "{{ value|query }}",
|
|
}},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
{
|
|
group: &Group{Name: "test",
|
|
Rules: []Rule{
|
|
{Record: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"summary": "{{ value|query }}",
|
|
}},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
err := tc.group.Validate(tc.validateAnnotations, tc.validateExpressions)
|
|
if err == nil {
|
|
if tc.expErr != "" {
|
|
t.Errorf("expected to get err %q; got nil insted", tc.expErr)
|
|
}
|
|
continue
|
|
}
|
|
if !strings.Contains(err.Error(), tc.expErr) {
|
|
t.Errorf("expected err to contain %q; got %q instead", tc.expErr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHashRule(t *testing.T) {
|
|
testCases := []struct {
|
|
a, b Rule
|
|
equal bool
|
|
}{
|
|
{
|
|
Rule{Record: "record", Expr: "up == 1"},
|
|
Rule{Record: "record", Expr: "up == 1"},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1"},
|
|
Rule{Alert: "alert", Expr: "up == 1"},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"baz": "foo",
|
|
"foo": "bar",
|
|
}},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "record", Expr: "up == 1"},
|
|
Rule{Alert: "record", Expr: "up == 1"},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", For: time.Minute},
|
|
Rule{Alert: "alert", Expr: "up == 1"},
|
|
true,
|
|
},
|
|
{
|
|
Rule{Alert: "record", Expr: "up == 1"},
|
|
Rule{Record: "record", Expr: "up == 1"},
|
|
false,
|
|
},
|
|
{
|
|
Rule{Record: "record", Expr: "up == 1"},
|
|
Rule{Record: "record", Expr: "up == 2"},
|
|
false,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"baz": "foo",
|
|
"foo": "baz",
|
|
}},
|
|
false,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"baz": "foo",
|
|
}},
|
|
false,
|
|
},
|
|
{
|
|
Rule{Alert: "alert", Expr: "up == 1", Labels: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "foo",
|
|
}},
|
|
Rule{Alert: "alert", Expr: "up == 1"},
|
|
false,
|
|
},
|
|
}
|
|
for i, tc := range testCases {
|
|
aID, bID := HashRule(tc.a), HashRule(tc.b)
|
|
if tc.equal != (aID == bID) {
|
|
t.Fatalf("missmatch for rule %d", i)
|
|
}
|
|
}
|
|
}
|