Alerting: Include annotations in prometheus Alert response. (#45970)

* Alerting: Include annotations in prometheus Alert response.

* add tests

* re-order depedencies
This commit is contained in:
gotjosh 2022-03-09 18:20:29 +00:00 committed by GitHub
parent b1e6f7126a
commit 8d4a0a0396
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 180 additions and 2 deletions

View File

@ -24,7 +24,7 @@ import (
type PrometheusSrv struct {
log log.Logger
manager *state.Manager
manager state.AlertInstanceManager
store store.RuleStore
}
@ -43,14 +43,16 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Re
if alertState.State == eval.Alerting {
valString = alertState.LastEvaluationString
}
alertResponse.Data.Alerts = append(alertResponse.Data.Alerts, &apimodels.Alert{
Labels: map[string]string(alertState.Labels),
Annotations: map[string]string{}, //TODO: Once annotations are added to the evaluation result, set them here
Annotations: alertState.Annotations,
State: alertState.State.String(),
ActiveAt: &startsAt,
Value: valString,
})
}
return response.JSON(http.StatusOK, alertResponse)
}

View File

@ -0,0 +1,80 @@
package api
import (
"net/http"
"testing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/stretchr/testify/require"
)
func TestRouteGetAlertStatuses(t *testing.T) {
fakeStore := store.NewFakeRuleStore(t)
fakeAlertInstanceManager := NewFakeAlertInstanceManager(t)
orgID := int64(1)
server := PrometheusSrv{
log: log.NewNopLogger(),
manager: fakeAlertInstanceManager,
store: fakeStore,
}
c := &models.ReqContext{SignedInUser: &models.SignedInUser{OrgId: orgID}}
t.Run("with no alerts", func(t *testing.T) {
r := server.RouteGetAlertStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
{
"status": "success",
"data": {
"alerts": []
}
}
`, string(r.Body()))
})
t.Run("with two alerts", func(t *testing.T) {
fakeAlertInstanceManager.GenerateAlertInstances(1, 2)
r := server.RouteGetAlertStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
require.JSONEq(t, `
{
"status": "success",
"data": {
"alerts": [{
"labels": {
"__alert_rule_namespace_uid__": "test_namespace_uid",
"__alert_rule_uid__": "test_alert_rule_uid_0",
"alertname": "test_title_0",
"instance_label": "test",
"label": "test"
},
"annotations": {
"annotation": "test"
},
"state": "Normal",
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}, {
"labels": {
"__alert_rule_namespace_uid__": "test_namespace_uid",
"__alert_rule_uid__": "test_alert_rule_uid_1",
"alertname": "test_title_1",
"instance_label": "test",
"label": "test"
},
"annotations": {
"annotation": "test"
},
"state": "Normal",
"activeAt": "0001-01-01T00:00:00Z",
"value": ""
}]
}
}`, string(r.Body()))
})
}

View File

@ -2,10 +2,18 @@ package api
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana-plugin-sdk-go/data"
)
type FakeAlertingStore struct {
@ -30,3 +38,85 @@ func (f FakeAlertingStore) GetLatestAlertmanagerConfiguration(_ context.Context,
}
return store.ErrNoAlertmanagerConfiguration
}
type fakeAlertInstanceManager struct {
mtx sync.Mutex
// orgID -> RuleID -> States
states map[int64]map[string][]*state.State
}
func NewFakeAlertInstanceManager(t *testing.T) *fakeAlertInstanceManager {
t.Helper()
return &fakeAlertInstanceManager{
states: map[int64]map[string][]*state.State{},
}
}
func (f *fakeAlertInstanceManager) GetAll(orgID int64) []*state.State {
f.mtx.Lock()
defer f.mtx.Unlock()
var s []*state.State
for orgID := range f.states {
for _, states := range f.states[orgID] {
s = append(s, states...)
}
}
return s
}
func (f *fakeAlertInstanceManager) GetStatesForRuleUID(orgID int64, alertRuleUID string) []*state.State {
f.mtx.Lock()
defer f.mtx.Unlock()
return f.states[orgID][alertRuleUID]
}
func (f *fakeAlertInstanceManager) GenerateAlertInstances(orgID int64, count int) {
f.mtx.Lock()
defer f.mtx.Unlock()
evaluationTime := time.Now()
evaluationDuration := 1 * time.Minute
alertRuleUID := util.GenerateShortUID()
for i := 0; i < count; i++ {
_, ok := f.states[orgID]
if !ok {
f.states[orgID] = map[string][]*state.State{}
}
_, ok = f.states[orgID][alertRuleUID]
if !ok {
f.states[orgID][alertRuleUID] = []*state.State{}
}
f.states[orgID][alertRuleUID] = append(f.states[orgID][alertRuleUID], &state.State{
AlertRuleUID: fmt.Sprintf("alert_rule_%v", i),
OrgID: 1,
Labels: data.Labels{
"__alert_rule_namespace_uid__": "test_namespace_uid",
"__alert_rule_uid__": fmt.Sprintf("test_alert_rule_uid_%v", i),
"alertname": fmt.Sprintf("test_title_%v", i),
"label": "test",
"instance_label": "test",
},
State: eval.Normal,
Results: []state.Evaluation{
{
EvaluationTime: evaluationTime,
EvaluationState: eval.Normal,
Values: make(map[string]*float64),
},
{
EvaluationTime: evaluationTime.Add(1 * time.Minute),
EvaluationState: eval.Normal,
Values: make(map[string]*float64),
},
},
LastEvaluationTime: evaluationTime.Add(1 * time.Minute),
EvaluationDuration: evaluationDuration,
Annotations: map[string]string{"annotation": "test"},
})
}
}

View File

@ -22,6 +22,12 @@ import (
var ResendDelay = 30 * time.Second
// AlertInstanceManager defines the interface for querying the current alert instances.
type AlertInstanceManager interface {
GetAll(orgID int64) []*State
GetStatesForRuleUID(orgID int64, alertRuleUID string) []*State
}
type Manager struct {
log log.Logger
metrics *metrics.State