mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
8c8ff5d0cb
The change introduces new entity `manager` which replaces `watchdog`, decouples requestHandler and groups. Manager supposed to control life cycle of groups, rules and config reloads. Groups export an ID method which returns a hash from filename and group name. ID supposed to be unique identifier across all loaded groups. Some tests were added to improve coverage. Bug with wrong annotation value if $value is used in templates after metrics being restored fixed. Notifier interface was extended to accept context. New set of metrics was introduced for config reload.
152 lines
3.8 KiB
Go
152 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestManagerUpdateError(t *testing.T) {
|
|
m := &manager{groups: make(map[uint64]*Group)}
|
|
path := []string{"foo/bar"}
|
|
err := m.update(context.Background(), path, true, false)
|
|
if err == nil {
|
|
t.Fatalf("expected to have err; got nil instead")
|
|
}
|
|
expErr := "no groups found"
|
|
if !strings.Contains(err.Error(), expErr) {
|
|
t.Fatalf("expected to got err %s; got %s", expErr, err)
|
|
}
|
|
}
|
|
|
|
// TestManagerUpdateConcurrent supposed to test concurrent
|
|
// execution of configuration update.
|
|
// Should be executed with -race flag
|
|
func TestManagerUpdateConcurrent(t *testing.T) {
|
|
m := &manager{groups: make(map[uint64]*Group)}
|
|
paths := []string{
|
|
"testdata/dir/rules0-good.rules",
|
|
"testdata/dir/rules1-good.rules",
|
|
"testdata/rules0-good.rules",
|
|
}
|
|
|
|
const n = 500
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(n)
|
|
for i := 0; i < n; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
rnd := rand.Intn(len(paths))
|
|
path := []string{paths[rnd]}
|
|
err := m.update(context.Background(), path, true, false)
|
|
if err != nil {
|
|
t.Errorf("update error: %s", err)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestManagerUpdate tests sequential configuration
|
|
// updates.
|
|
func TestManagerUpdate(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
initPath string
|
|
updatePath string
|
|
want []*Group
|
|
}{
|
|
{
|
|
name: "update good rules",
|
|
initPath: "testdata/rules0-good.rules",
|
|
updatePath: "testdata/dir/rules1-good.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/dir/rules1-good.rules",
|
|
Name: "duplicatedGroupDiffFiles",
|
|
Rules: []*Rule{newTestRule("VMRows", time.Second*10)},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update good rules from 1 to 2 groups",
|
|
initPath: "testdata/dir/rules1-good.rules",
|
|
updatePath: "testdata/rules0-good.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "groupGorSingleAlert", Rules: []*Rule{
|
|
newTestRule("VMRows", time.Second*10),
|
|
}},
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "TestGroup", Rules: []*Rule{
|
|
newTestRule("Conns", time.Duration(0)),
|
|
newTestRule("ExampleAlertAlwaysFiring", time.Duration(0)),
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "update with one bad rule file",
|
|
initPath: "testdata/rules0-good.rules",
|
|
updatePath: "testdata/dir/rules2-bad.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "groupGorSingleAlert", Rules: []*Rule{
|
|
newTestRule("VMRows", time.Second*10),
|
|
}},
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "TestGroup", Rules: []*Rule{
|
|
newTestRule("Conns", time.Duration(0)),
|
|
newTestRule("ExampleAlertAlwaysFiring", time.Duration(0)),
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.TODO())
|
|
m := &manager{groups: make(map[uint64]*Group)}
|
|
path := []string{tc.initPath}
|
|
if err := m.update(ctx, path, true, false); err != nil {
|
|
t.Fatalf("failed to complete initial rules update: %s", err)
|
|
}
|
|
|
|
path = []string{tc.updatePath}
|
|
_ = m.update(ctx, path, true, false)
|
|
if len(tc.want) != len(m.groups) {
|
|
t.Fatalf("\nwant number of groups: %d;\ngot: %d ", len(tc.want), len(m.groups))
|
|
}
|
|
|
|
for _, wantG := range tc.want {
|
|
gotG, ok := m.groups[wantG.ID()]
|
|
if !ok {
|
|
t.Fatalf("expected to have group %q", wantG.Name)
|
|
}
|
|
compareGroups(t, gotG, wantG)
|
|
}
|
|
|
|
cancel()
|
|
m.close()
|
|
})
|
|
}
|
|
}
|
|
|
|
func compareGroups(t *testing.T, a, b *Group) {
|
|
t.Helper()
|
|
if len(a.Rules) != len(b.Rules) {
|
|
t.Fatalf("expected group %s to have %d rules; got: %d",
|
|
a.Name, len(a.Rules), len(b.Rules))
|
|
}
|
|
for i, r := range a.Rules {
|
|
got, want := r, b.Rules[i]
|
|
if got.Name != want.Name {
|
|
t.Fatalf("expected to have rule %q; got %q", want.Name, got.Name)
|
|
}
|
|
}
|
|
}
|