Files

201 lines
5.3 KiB
Go

package libprometheus_test
import (
"context"
"os"
"testing"
"time"
"github.com/portainer/portainer/api/filesystem"
libprom "github.com/portainer/portainer/pkg/libprometheus"
pkgmetrics "github.com/portainer/portainer/pkg/metrics"
prometheusreg "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/prometheus/rules"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewRuleManager(t *testing.T) {
reg := prometheusreg.NewRegistry()
db, err := libprom.NewInMemoryTSDB(reg)
require.NoError(t, err)
defer func() { require.NoError(t, db.Close()) }()
engine := libprom.NewEngine()
var notified bool
mgr := libprom.NewRuleManager(libprom.RuleManagerConfig{
Engine: engine,
Queryable: db,
Appendable: db,
NotifyFunc: func(_ context.Context, _ string, alerts ...*rules.Alert) {
notified = true
},
Context: context.Background(),
Registerer: reg,
})
require.NotNil(t, mgr)
_ = notified // used by rule evaluation, not directly testable without running the manager
}
func TestReloadRules(t *testing.T) {
reg := prometheusreg.NewRegistry()
db, err := libprom.NewInMemoryTSDB(reg)
require.NoError(t, err)
defer func() { require.NoError(t, db.Close()) }()
engine := libprom.NewEngine()
mgr := libprom.NewRuleManager(libprom.RuleManagerConfig{
Engine: engine,
Queryable: db,
Appendable: db,
NotifyFunc: func(_ context.Context, _ string, _ ...*rules.Alert) {},
Context: context.Background(),
Registerer: reg,
})
t.Run("empty path clears rules", func(t *testing.T) {
err := libprom.ReloadRules(mgr, 15*time.Second, "")
require.NoError(t, err)
assert.Empty(t, mgr.RuleGroups())
})
t.Run("valid rule file loads successfully", func(t *testing.T) {
dir := t.TempDir()
alertsFile := filesystem.JoinPaths(dir, "alerts.yaml")
rulesYAML := `groups:
- name: test-group
rules:
- alert: TestAlert
expr: up == 0
for: 1m
labels:
alert_rule_id: "42"
severity: critical
`
require.NoError(t, os.WriteFile(alertsFile, []byte(rulesYAML), 0o644))
err := libprom.ReloadRules(mgr, 15*time.Second, alertsFile)
require.NoError(t, err)
assert.Len(t, mgr.RuleGroups(), 1)
})
t.Run("invalid file returns error", func(t *testing.T) {
err := libprom.ReloadRules(mgr, 15*time.Second, "/nonexistent/alerts.yaml")
require.Error(t, err)
})
}
func TestExtractAlertStates(t *testing.T) {
newTestRuleManager := func(t *testing.T) *rules.Manager {
t.Helper()
reg := prometheusreg.NewRegistry()
db, err := libprom.NewInMemoryTSDB(reg)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, db.Close())
})
engine := libprom.NewEngine()
return libprom.NewRuleManager(libprom.RuleManagerConfig{
Engine: engine,
Queryable: db,
Appendable: db,
NotifyFunc: func(_ context.Context, _ string, _ ...*rules.Alert) {},
Context: context.Background(),
Registerer: reg,
})
}
t.Run("no rules returns nil", func(t *testing.T) {
mgr := newTestRuleManager(t)
states := libprom.ExtractAlertStates(mgr)
assert.Nil(t, states)
})
t.Run("loaded rules return states", func(t *testing.T) {
mgr := newTestRuleManager(t)
dir := t.TempDir()
alertsFile := filesystem.JoinPaths(dir, "alerts.yaml")
rulesYAML := `groups:
- name: test-group
rules:
- alert: TestAlert
expr: up == 0
for: 1m
labels:
alert_rule_id: "7"
severity: warning
`
require.NoError(t, os.WriteFile(alertsFile, []byte(rulesYAML), 0o644))
require.NoError(t, libprom.ReloadRules(mgr, 15*time.Second, alertsFile))
states := libprom.ExtractAlertStates(mgr)
require.Len(t, states, 1)
assert.Equal(t, 7, states[0].RuleID)
assert.Equal(t, pkgmetrics.AlertRuleStateOK, states[0].State)
})
t.Run("duplicate alert_rule_id values are aggregated", func(t *testing.T) {
mgr := newTestRuleManager(t)
dir := t.TempDir()
alertsFile := filesystem.JoinPaths(dir, "alerts.yaml")
rulesYAML := `groups:
- name: test-group
rules:
- alert: CpuWarning
expr: up == 0
labels:
alert_rule_id: "42"
severity: warning
- alert: CpuCritical
expr: up >= 0
labels:
alert_rule_id: "42"
severity: critical
`
require.NoError(t, os.WriteFile(alertsFile, []byte(rulesYAML), 0o644))
require.NoError(t, libprom.ReloadRules(mgr, 15*time.Second, alertsFile))
states := libprom.ExtractAlertStates(mgr)
require.Len(t, states, 1)
assert.Equal(t, 42, states[0].RuleID)
assert.Equal(t, pkgmetrics.AlertRuleStateOK, states[0].State)
})
t.Run("states are returned in stable rule ID order", func(t *testing.T) {
mgr := newTestRuleManager(t)
dir := t.TempDir()
alertsFile := filesystem.JoinPaths(dir, "alerts.yaml")
rulesYAML := `groups:
- name: test-group
rules:
- alert: RuleTen
expr: up == 0
labels:
alert_rule_id: "10"
severity: warning
- alert: RuleThree
expr: up >= 0
labels:
alert_rule_id: "3"
severity: critical
`
require.NoError(t, os.WriteFile(alertsFile, []byte(rulesYAML), 0o644))
require.NoError(t, libprom.ReloadRules(mgr, 15*time.Second, alertsFile))
states := libprom.ExtractAlertStates(mgr)
require.Len(t, states, 2)
assert.Equal(t, 3, states[0].RuleID)
assert.Equal(t, 10, states[1].RuleID)
})
}