Alerting: Return RuleResponse for api/prometheus/grafana/api/v1/rules (#32919)

* Return RuleResponse for api/prometheus/grafana/api/v1/rules

* change TODO to note

Co-authored-by: gotjosh <josue@grafana.com>

* pr feedback

* test fixup

Co-authored-by: gotjosh <josue@grafana.com>
This commit is contained in:
David Parrott
2021-04-13 17:38:09 -04:00
committed by GitHub
co-authored by gotjosh
parent 50ab6155ff
commit 567a6a09bd
9 changed files with 147 additions and 23 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ func (api *API) RegisterAPIEndpoints() {
api.RegisterPrometheusApiEndpoints(NewForkedProm(
api.DatasourceCache,
NewLotexProm(proxy, logger),
PrometheusSrv{log: logger, stateTracker: api.StateTracker},
PrometheusSrv{log: logger, stateTracker: api.StateTracker, store: api.RuleStore},
))
// Register endpoints for proxing to Cortex Ruler-compatible backends.
api.RegisterRulerApiEndpoints(NewForkedRuler(
+112 -3
View File
@@ -1,7 +1,14 @@
package api
import (
"fmt"
"net/http"
"time"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
@@ -13,6 +20,7 @@ import (
type PrometheusSrv struct {
log log.Logger
stateTracker *state.StateTracker
store store.RuleStore
}
func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Response {
@@ -38,7 +46,108 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *models.ReqContext) response.Re
}
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)
ruleResponse := apimodels.RuleResponse{
DiscoveryBase: apimodels.DiscoveryBase{
Status: "success",
},
Data: apimodels.RuleDiscovery{},
}
ruleGroupQuery := ngmodels.ListOrgRuleGroupsQuery{
OrgID: c.SignedInUser.OrgId,
}
if err := srv.store.GetOrgRuleGroups(&ruleGroupQuery); err != nil {
ruleResponse.DiscoveryBase.Status = "error"
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting rule groups: %s", err.Error())
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
return response.JSON(http.StatusInternalServerError, ruleResponse)
}
for _, groupId := range ruleGroupQuery.Result {
alertRuleQuery := ngmodels.ListRuleGroupAlertRulesQuery{OrgID: c.SignedInUser.OrgId, RuleGroup: groupId}
if err := srv.store.GetRuleGroupAlertRules(&alertRuleQuery); err != nil {
ruleResponse.DiscoveryBase.Status = "error"
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting rules for group %s: %s", groupId, err.Error())
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
return response.JSON(http.StatusInternalServerError, ruleResponse)
}
newGroup := &apimodels.RuleGroup{
Name: groupId,
File: "", // This doesn't make sense in our architecture but would be a good use case for provisioned alerts.
LastEvaluation: time.Time{},
EvaluationTime: 0, // TODO: see if we are able to pass this along with evaluation results
}
for _, rule := range alertRuleQuery.Result {
instanceQuery := ngmodels.ListAlertInstancesQuery{
DefinitionOrgID: c.SignedInUser.OrgId,
DefinitionUID: rule.UID,
}
if err := srv.store.ListAlertInstances(&instanceQuery); err != nil {
ruleResponse.DiscoveryBase.Status = "error"
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting alerts for rule %s: %s", rule.UID, err.Error())
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
return response.JSON(http.StatusInternalServerError, ruleResponse)
}
alertingRule := apimodels.AlertingRule{
State: "inactive",
Name: rule.Title,
Query: "", // TODO: get this from parsing AlertRule.Data
Duration: time.Duration(rule.For).Seconds(),
Annotations: rule.Annotations,
}
newRule := apimodels.Rule{
Name: rule.Title,
Labels: nil, // TODO: NG AlertRule does not have labels but does have annotations
Health: "ok", // TODO: update this in the future when error and noData states are being evaluated and set
Type: apiv1.RuleTypeAlerting,
LastEvaluation: time.Time{}, // TODO: set this to be rule evaluation time once it is being set
EvaluationTime: 0, // TODO: set this once we are saving it or adding it to evaluation results
}
for _, instance := range instanceQuery.Result {
activeAt := instance.CurrentStateSince
alert := &apimodels.Alert{
Labels: map[string]string(instance.Labels),
Annotations: nil, // TODO: set these once they are added to evaluation results
State: translateInstanceState(instance.CurrentState),
ActiveAt: &activeAt,
Value: "", // TODO: set this once it is added to the evaluation results
}
if instance.LastEvalTime.After(newRule.LastEvaluation) {
newRule.LastEvaluation = instance.LastEvalTime
newGroup.LastEvaluation = instance.LastEvalTime
}
switch alert.State {
case "pending":
if alertingRule.State == "inactive" {
alertingRule.State = "pending"
}
case "firing":
alertingRule.State = "firing"
}
alertingRule.Alerts = append(alertingRule.Alerts, alert)
}
alertingRule.Rule = newRule
newGroup.Rules = append(newGroup.Rules, alertingRule)
newGroup.Interval = float64(rule.IntervalSeconds)
}
ruleResponse.Data.RuleGroups = append(ruleResponse.Data.RuleGroups, newGroup)
}
return response.JSON(http.StatusOK, ruleResponse)
}
func translateInstanceState(state ngmodels.InstanceStateType) string {
switch {
case state == ngmodels.InstanceStateFiring:
return "firing"
case state == ngmodels.InstanceStateNormal:
return "inactive"
case state == ngmodels.InstanceStatePending:
return "pending"
default:
return "inactive"
}
}