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:
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user