Alerting: Consistently return Prometheus-style responses from rules APIs. (#86600)
* Alerting: Consistently return Prometheus-style responses from rules APIs. This commit is part refactor and part fix. The /rules API occasionally returns error responses which are inconsistent with other error responses. This fixes that, and adds a function to map from Prometheus error type and HTTP code. * Fix integration tests * Linter happiness * Make linter more happy * Fix up one more place returning non-Prometheus responses
This commit is contained in:
@@ -95,7 +95,7 @@ func (srv PrometheusSrv) RouteGetAlertStatuses(c *contextmodel.ReqContext) respo
|
||||
})
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, alertResponse)
|
||||
return response.JSON(alertResponse.HTTPStatusCode(), alertResponse)
|
||||
}
|
||||
|
||||
func formatValues(alertState *state.State) string {
|
||||
@@ -175,31 +175,6 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
// As we are using req.Form directly, this triggers a call to ParseForm() if needed.
|
||||
c.Query("")
|
||||
|
||||
dashboardUID := c.Query("dashboard_uid")
|
||||
panelID, err := getPanelIDFromRequest(c.Req)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "invalid panel_id")
|
||||
}
|
||||
if dashboardUID == "" && panelID != 0 {
|
||||
return ErrResp(http.StatusBadRequest, errors.New("panel_id must be set with dashboard_uid"), "")
|
||||
}
|
||||
|
||||
limitGroups := getInt64WithDefault(c.Req.Form, "limit", -1)
|
||||
limitRulesPerGroup := getInt64WithDefault(c.Req.Form, "limit_rules", -1)
|
||||
limitAlertsPerRule := getInt64WithDefault(c.Req.Form, "limit_alerts", -1)
|
||||
matchers, err := getMatchersFromRequest(c.Req)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
withStates, err := getStatesFromRequest(c.Req)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
withStatesFast := make(map[eval.State]struct{})
|
||||
for _, state := range withStates {
|
||||
withStatesFast[state] = struct{}{}
|
||||
}
|
||||
|
||||
ruleResponse := apimodels.RuleResponse{
|
||||
DiscoveryBase: apimodels.DiscoveryBase{
|
||||
Status: "success",
|
||||
@@ -209,6 +184,43 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
},
|
||||
}
|
||||
|
||||
dashboardUID := c.Query("dashboard_uid")
|
||||
panelID, err := getPanelIDFromRequest(c.Req)
|
||||
if err != nil {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("invalid panel_id: %s", err.Error())
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrBadData
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
if dashboardUID == "" && panelID != 0 {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = "panel_id must be set with dashboard_uid"
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrBadData
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
|
||||
limitGroups := getInt64WithDefault(c.Req.Form, "limit", -1)
|
||||
limitRulesPerGroup := getInt64WithDefault(c.Req.Form, "limit_rules", -1)
|
||||
limitAlertsPerRule := getInt64WithDefault(c.Req.Form, "limit_alerts", -1)
|
||||
matchers, err := getMatchersFromRequest(c.Req)
|
||||
if err != nil {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = err.Error()
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrBadData
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
withStates, err := getStatesFromRequest(c.Req)
|
||||
if err != nil {
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = err.Error()
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrBadData
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
withStatesFast := make(map[eval.State]struct{})
|
||||
for _, state := range withStates {
|
||||
withStatesFast[state] = struct{}{}
|
||||
}
|
||||
|
||||
var labelOptions []ngmodels.LabelOption
|
||||
if !getBoolWithDefault(c.Req.Form, queryIncludeInternalLabels, false) {
|
||||
labelOptions = append(labelOptions, ngmodels.WithoutInternalLabels())
|
||||
@@ -216,12 +228,15 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
|
||||
namespaceMap, err := srv.store.GetUserVisibleNamespaces(c.Req.Context(), c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get namespaces visible to the user")
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failed to get namespaces visible to the user: %s", err.Error())
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
|
||||
if len(namespaceMap) == 0 {
|
||||
srv.log.Debug("User does not have access to any namespaces")
|
||||
return response.JSON(http.StatusOK, ruleResponse)
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
|
||||
namespaceUIDs := make([]string, len(namespaceMap))
|
||||
@@ -240,7 +255,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting rules: %s", err.Error())
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
|
||||
return response.JSON(http.StatusInternalServerError, ruleResponse)
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
|
||||
// Group rules together by Namespace and Rule Group. Rules are also grouped by Org ID,
|
||||
@@ -267,7 +282,10 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
}
|
||||
ok, err := srv.authz.HasAccessToRuleGroup(c.Req.Context(), c.SignedInUser, rules)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "cannot authorize access to rule group", err)
|
||||
ruleResponse.DiscoveryBase.Status = "error"
|
||||
ruleResponse.DiscoveryBase.Error = fmt.Sprintf("cannot authorize access to rule group: %s", err.Error())
|
||||
ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
@@ -317,7 +335,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
ruleResponse.Data.RuleGroups = ruleResponse.Data.RuleGroups[0:limitGroups]
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, ruleResponse)
|
||||
return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse)
|
||||
}
|
||||
|
||||
// This is the same as matchers.Matches but avoids the need to create a LabelSet
|
||||
|
||||
Reference in New Issue
Block a user