2020-06-21 10:32:46 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-05-25 13:27:22 +00:00
|
|
|
"context"
|
2020-06-21 10:32:46 +00:00
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"testing"
|
2021-05-25 13:27:22 +00:00
|
|
|
"time"
|
2020-06-21 10:32:46 +00:00
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/notifier"
|
2021-11-29 23:18:48 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/remotewrite"
|
2021-05-25 13:27:22 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
|
2020-06-21 10:32:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestGetExternalURL(t *testing.T) {
|
2023-12-15 10:13:56 +00:00
|
|
|
invalidURL := "victoriametrics.com/path"
|
|
|
|
_, err := getExternalURL(invalidURL)
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("expected error, got nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
expURL := "https://victoriametrics.com/path"
|
|
|
|
u, err := getExternalURL(expURL)
|
2020-06-21 10:32:46 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error %s", err)
|
|
|
|
}
|
|
|
|
if u.String() != expURL {
|
2023-12-15 10:13:56 +00:00
|
|
|
t.Errorf("unexpected url: want %q, got %s", expURL, u.String())
|
2020-06-21 10:32:46 +00:00
|
|
|
}
|
2023-12-15 10:13:56 +00:00
|
|
|
|
2020-06-21 10:32:46 +00:00
|
|
|
h, _ := os.Hostname()
|
2023-12-15 10:13:56 +00:00
|
|
|
expURL = fmt.Sprintf("http://%s:8880", h)
|
|
|
|
u, err = getExternalURL("")
|
2020-06-21 10:32:46 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error %s", err)
|
|
|
|
}
|
|
|
|
if u.String() != expURL {
|
2023-12-15 10:13:56 +00:00
|
|
|
t.Errorf("unexpected url: want %s, got %s", expURL, u.String())
|
2020-06-21 10:32:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetAlertURLGenerator(t *testing.T) {
|
2022-10-05 17:25:03 +00:00
|
|
|
testAlert := notifier.Alert{GroupID: 42, ID: 2, Value: 4, Labels: map[string]string{"tenant": "baz"}}
|
2020-06-21 10:32:46 +00:00
|
|
|
u, _ := url.Parse("https://victoriametrics.com/path")
|
|
|
|
fn, err := getAlertURLGenerator(u, "", false)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error %s", err)
|
|
|
|
}
|
2022-08-17 12:46:28 +00:00
|
|
|
exp := fmt.Sprintf("https://victoriametrics.com/path/vmalert/alert?%s=42&%s=2", paramGroupID, paramAlertID)
|
2022-07-08 08:26:13 +00:00
|
|
|
if exp != fn(testAlert) {
|
2020-06-21 10:32:46 +00:00
|
|
|
t.Errorf("unexpected url want %s, got %s", exp, fn(testAlert))
|
|
|
|
}
|
|
|
|
_, err = getAlertURLGenerator(nil, "foo?{{invalid}}", true)
|
|
|
|
if err == nil {
|
2022-07-18 09:02:51 +00:00
|
|
|
t.Errorf("expected template validation error got nil")
|
2020-06-21 10:32:46 +00:00
|
|
|
}
|
2022-10-05 17:25:03 +00:00
|
|
|
fn, err = getAlertURLGenerator(u, "foo?query={{$value}}&ds={{ $labels.tenant }}", true)
|
2020-06-21 10:32:46 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error %s", err)
|
|
|
|
}
|
2022-10-27 19:30:27 +00:00
|
|
|
if exp := "https://victoriametrics.com/path/foo?query=4&ds=baz"; exp != fn(testAlert) {
|
2020-06-21 10:32:46 +00:00
|
|
|
t.Errorf("unexpected url want %s, got %s", exp, fn(testAlert))
|
|
|
|
}
|
|
|
|
}
|
2021-05-25 13:27:22 +00:00
|
|
|
|
|
|
|
func TestConfigReload(t *testing.T) {
|
|
|
|
originalRulePath := *rulePath
|
|
|
|
defer func() {
|
|
|
|
*rulePath = originalRulePath
|
|
|
|
}()
|
|
|
|
|
|
|
|
const (
|
|
|
|
rules1 = `
|
|
|
|
groups:
|
|
|
|
- name: group-1
|
|
|
|
rules:
|
|
|
|
- alert: ExampleAlertAlwaysFiring
|
|
|
|
expr: sum by(job) (up == 1)
|
|
|
|
- record: handler:requests:rate5m
|
|
|
|
expr: sum(rate(prometheus_http_requests_total[5m])) by (handler)
|
|
|
|
`
|
|
|
|
rules2 = `
|
|
|
|
groups:
|
|
|
|
- name: group-1
|
|
|
|
rules:
|
|
|
|
- alert: ExampleAlertAlwaysFiring
|
|
|
|
expr: sum by(job) (up == 1)
|
|
|
|
- name: group-2
|
|
|
|
rules:
|
|
|
|
- record: handler:requests:rate5m
|
|
|
|
expr: sum(rate(prometheus_http_requests_total[5m])) by (handler)
|
|
|
|
`
|
|
|
|
)
|
|
|
|
|
2022-08-21 21:20:55 +00:00
|
|
|
f, err := os.CreateTemp("", "")
|
2021-05-25 13:27:22 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2023-08-09 19:16:00 +00:00
|
|
|
defer func() { _ = os.Remove(f.Name()) }()
|
2021-05-25 13:27:22 +00:00
|
|
|
writeToFile(t, f.Name(), rules1)
|
|
|
|
|
2023-07-31 14:39:57 +00:00
|
|
|
*configCheckInterval = 200 * time.Millisecond
|
2021-05-25 13:27:22 +00:00
|
|
|
*rulePath = []string{f.Name()}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
m := &manager{
|
|
|
|
querierBuilder: &fakeQuerier{},
|
|
|
|
groups: make(map[uint64]*Group),
|
|
|
|
labels: map[string]string{},
|
2022-02-02 12:11:41 +00:00
|
|
|
notifiers: func() []notifier.Notifier { return []notifier.Notifier{&fakeNotifier{}} },
|
2021-11-29 23:18:48 +00:00
|
|
|
rw: &remotewrite.Client{},
|
2021-05-25 13:27:22 +00:00
|
|
|
}
|
2021-09-13 12:48:18 +00:00
|
|
|
|
|
|
|
syncCh := make(chan struct{})
|
2021-10-19 13:35:27 +00:00
|
|
|
sighupCh := procutil.NewSighupChan()
|
2021-09-13 12:48:18 +00:00
|
|
|
go func() {
|
2021-10-19 13:35:27 +00:00
|
|
|
configReload(ctx, m, nil, sighupCh)
|
2021-09-13 12:48:18 +00:00
|
|
|
close(syncCh)
|
|
|
|
}()
|
2021-05-25 13:27:22 +00:00
|
|
|
|
|
|
|
lenLocked := func(m *manager) int {
|
|
|
|
m.groupsMu.RLock()
|
|
|
|
defer m.groupsMu.RUnlock()
|
|
|
|
return len(m.groups)
|
|
|
|
}
|
|
|
|
|
2023-08-07 19:58:40 +00:00
|
|
|
checkCfg := func(err error) {
|
|
|
|
cErr := getLastConfigError()
|
|
|
|
cfgSuc := configSuccess.Get()
|
|
|
|
if err != nil {
|
|
|
|
if cErr == nil {
|
|
|
|
t.Fatalf("expected to have config error %s; got nil instead", cErr)
|
|
|
|
}
|
|
|
|
if cfgSuc != 0 {
|
|
|
|
t.Fatalf("expected to have metric configSuccess to be set to 0; got %d instead", cfgSuc)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if cErr != nil {
|
|
|
|
t.Fatalf("unexpected config error: %s", cErr)
|
|
|
|
}
|
|
|
|
if cfgSuc != 1 {
|
|
|
|
t.Fatalf("expected to have metric configSuccess to be set to 1; got %d instead", cfgSuc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-31 14:39:57 +00:00
|
|
|
time.Sleep(*configCheckInterval * 2)
|
2023-08-07 19:58:40 +00:00
|
|
|
checkCfg(nil)
|
2021-05-25 13:27:22 +00:00
|
|
|
groupsLen := lenLocked(m)
|
|
|
|
if groupsLen != 1 {
|
|
|
|
t.Fatalf("expected to have exactly 1 group loaded; got %d", groupsLen)
|
|
|
|
}
|
|
|
|
|
|
|
|
writeToFile(t, f.Name(), rules2)
|
2023-07-31 14:39:57 +00:00
|
|
|
time.Sleep(*configCheckInterval * 2)
|
2023-08-07 19:58:40 +00:00
|
|
|
checkCfg(nil)
|
2021-05-25 13:27:22 +00:00
|
|
|
groupsLen = lenLocked(m)
|
|
|
|
if groupsLen != 2 {
|
|
|
|
fmt.Println(m.groups)
|
|
|
|
t.Fatalf("expected to have exactly 2 groups loaded; got %d", groupsLen)
|
|
|
|
}
|
|
|
|
|
|
|
|
writeToFile(t, f.Name(), rules1)
|
|
|
|
procutil.SelfSIGHUP()
|
2023-07-31 14:39:57 +00:00
|
|
|
time.Sleep(*configCheckInterval / 2)
|
2023-08-07 19:58:40 +00:00
|
|
|
checkCfg(nil)
|
2021-05-25 13:27:22 +00:00
|
|
|
groupsLen = lenLocked(m)
|
|
|
|
if groupsLen != 1 {
|
|
|
|
t.Fatalf("expected to have exactly 1 group loaded; got %d", groupsLen)
|
|
|
|
}
|
|
|
|
|
|
|
|
writeToFile(t, f.Name(), `corrupted`)
|
|
|
|
procutil.SelfSIGHUP()
|
2023-07-31 14:39:57 +00:00
|
|
|
time.Sleep(*configCheckInterval / 2)
|
2023-08-07 19:58:40 +00:00
|
|
|
checkCfg(fmt.Errorf("config error"))
|
2021-05-25 13:27:22 +00:00
|
|
|
groupsLen = lenLocked(m)
|
|
|
|
if groupsLen != 1 { // should remain unchanged
|
|
|
|
t.Fatalf("expected to have exactly 1 group loaded; got %d", groupsLen)
|
|
|
|
}
|
2021-09-13 12:48:18 +00:00
|
|
|
|
|
|
|
cancel()
|
|
|
|
<-syncCh
|
2021-05-25 13:27:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writeToFile(t *testing.T, file, b string) {
|
|
|
|
t.Helper()
|
2022-08-21 20:51:13 +00:00
|
|
|
err := os.WriteFile(file, []byte(b), 0644)
|
2021-05-25 13:27:22 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|