Alerting: Return cached alerts for prometheus/api/v1/alerts (#32654)

* Return cached alerts for prometheus/api/v1/alerts

* Return not implemented for /prometheus/grafana/api/v1/rules

* Set StartsAt for already alerting states

* Fix tests
This commit is contained in:
David Parrott 2021-04-05 15:05:39 -07:00 committed by GitHub
parent ec624aa1ef
commit c0d83fc01e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 136 deletions

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/grafana/grafana/pkg/services/ngalert/state"
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api" apimodels "github.com/grafana/alerting-api/pkg/api"
@ -47,6 +49,7 @@ type API struct {
AlertingStore store.AlertingStore AlertingStore store.AlertingStore
DataProxy *datasourceproxy.DatasourceProxyService DataProxy *datasourceproxy.DatasourceProxyService
Alertmanager Alertmanager Alertmanager Alertmanager
StateTracker *state.StateTracker
} }
// RegisterAPIEndpoints registers API handlers // RegisterAPIEndpoints registers API handlers
@ -63,7 +66,7 @@ func (api *API) RegisterAPIEndpoints() {
api.RegisterPrometheusApiEndpoints(NewForkedProm( api.RegisterPrometheusApiEndpoints(NewForkedProm(
api.DatasourceCache, api.DatasourceCache,
NewLotexProm(proxy, logger), NewLotexProm(proxy, logger),
PrometheusApiMock{log: logger}, PrometheusSrv{log: logger, stateTracker: api.StateTracker},
)) ))
api.RegisterRulerApiEndpoints(NewForkedRuler( api.RegisterRulerApiEndpoints(NewForkedRuler(
api.DatasourceCache, api.DatasourceCache,

View File

@ -0,0 +1,44 @@
package api
import (
"net/http"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
)
type PrometheusSrv struct {
log log.Logger
stateTracker *state.StateTracker
}
func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Response {
alertResponse := apimodels.AlertResponse{
DiscoveryBase: apimodels.DiscoveryBase{
Status: "success",
},
Data: apimodels.AlertDiscovery{
Alerts: []*apimodels.Alert{},
},
}
for _, alertState := range srv.stateTracker.GetAll() {
startsAt := alertState.StartsAt
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
State: alertState.State.String(),
ActiveAt: &startsAt,
Value: "", //TODO: once the result of the evaluation is added to the evaluation result, set it here
})
}
return response.JSON(http.StatusOK, alertResponse)
}
func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
srv.log.Info("RouteGetRuleStatuses: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}

View File

@ -1,133 +0,0 @@
package api
import (
"net/http"
"time"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
)
type PrometheusApiMock struct {
log log.Logger
}
func (mock PrometheusApiMock) RouteGetAlertStatuses(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
mock.log.Info("RouteGetAlertStatuses: ", "Recipient", recipient)
now := time.Now()
result := apimodels.AlertResponse{
Data: apimodels.AlertDiscovery{
Alerts: []*apimodels.Alert{
{
Labels: map[string]string{
"label 1": "value 1",
"label 2": "value 2",
},
Annotations: map[string]string{
"alert annotation 1": "value 1",
"alert annotation 2": "value 2",
},
State: "firing",
ActiveAt: &now,
Value: "",
},
{
Labels: map[string]string{
"label 1-2": "value 1",
"label 2-2": "value 2",
},
Annotations: map[string]string{
"alert annotation 1-2": "value 1",
"alert annotation 2-2": "value 2",
},
State: "inactive",
ActiveAt: &now,
Value: "",
},
},
},
}
return response.JSON(http.StatusOK, result)
}
func (mock PrometheusApiMock) RouteGetRuleStatuses(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
mock.log.Info("RouteGetRuleStatuses: ", "Recipient", recipient)
now := time.Now()
result := apimodels.RuleResponse{
Data: apimodels.RuleDiscovery{
RuleGroups: []*apimodels.RuleGroup{
{
Name: "group1",
Interval: 60,
LastEvaluation: now.Add(-time.Minute),
EvaluationTime: 1,
Rules: []apimodels.AlertingRule{
{
State: "firing",
Name: "rule 1-1",
Query: `{
"title": "rule 1-1",
"condition": "A",
"data": [
{
"refId": "A",
"queryType": "",
"relativeTimeRange": {
"from": 18000,
"to": 10800
},
"model": {
"datasource": "__expr__",
"type": "math",
"expression": "2 + 2 > 1"
}
}
],
"no_data_state": "NoData",
"exec_err_state": "Alerting"
}`,
Duration: 600,
Annotations: map[string]string{
"annotation 1": "value 1",
"annotation 2": "value 2",
},
Alerts: []*apimodels.Alert{
{
Labels: map[string]string{
"label 1": "value 1",
"label 2": "value 2",
},
Annotations: map[string]string{
"alert annotation 1": "value 1",
"alert annotation 2": "value 2",
},
State: "firing",
ActiveAt: &now,
Value: "",
},
{
Labels: map[string]string{
"label 1-2": "value 1",
"label 2-2": "value 2",
},
Annotations: map[string]string{
"alert annotation 1-2": "value 1",
"alert annotation 2-2": "value 2",
},
State: "inactive",
ActiveAt: &now,
Value: "",
},
},
},
},
},
},
},
}
return response.JSON(http.StatusOK, result)
}

View File

@ -85,6 +85,7 @@ func (ng *AlertNG) Init() error {
RuleStore: store, RuleStore: store,
AlertingStore: store, AlertingStore: store,
Alertmanager: ng.Alertmanager, Alertmanager: ng.Alertmanager,
StateTracker: ng.stateTracker,
} }
api.RegisterAPIEndpoints() api.RegisterAPIEndpoints()

View File

@ -70,6 +70,9 @@ func (st *StateTracker) getOrCreate(uid string, orgId int64, result eval.Result)
State: result.State, State: result.State,
Results: []StateEvaluation{}, Results: []StateEvaluation{},
} }
if result.State == eval.Alerting {
newState.StartsAt = result.EvaluatedAt
}
st.stateCache.cacheMap[idString] = newState st.stateCache.cacheMap[idString] = newState
return newState return newState
} }

View File

@ -133,7 +133,7 @@ func TestProcessEvalResults(t *testing.T) {
{EvaluationTime: evaluationTime, EvaluationState: eval.Alerting}, {EvaluationTime: evaluationTime, EvaluationState: eval.Alerting},
{EvaluationTime: evaluationTime.Add(1 * time.Minute), EvaluationState: eval.Normal}, {EvaluationTime: evaluationTime.Add(1 * time.Minute), EvaluationState: eval.Normal},
}, },
StartsAt: time.Time{}, StartsAt: evaluationTime,
EndsAt: evaluationTime.Add(1 * time.Minute), EndsAt: evaluationTime.Add(1 * time.Minute),
LastEvaluationTime: evaluationTime.Add(1 * time.Minute), LastEvaluationTime: evaluationTime.Add(1 * time.Minute),
}, },
@ -172,7 +172,7 @@ func TestProcessEvalResults(t *testing.T) {
{EvaluationTime: evaluationTime, EvaluationState: eval.Alerting}, {EvaluationTime: evaluationTime, EvaluationState: eval.Alerting},
{EvaluationTime: evaluationTime.Add(1 * time.Minute), EvaluationState: eval.Alerting}, {EvaluationTime: evaluationTime.Add(1 * time.Minute), EvaluationState: eval.Alerting},
}, },
StartsAt: time.Time{}, StartsAt: evaluationTime,
EndsAt: evaluationTime.Add(100 * time.Second), EndsAt: evaluationTime.Add(100 * time.Second),
LastEvaluationTime: evaluationTime.Add(1 * time.Minute), LastEvaluationTime: evaluationTime.Add(1 * time.Minute),
}, },