Alerting: Add endpoint for querying state history (#62166)

* Define endpoint and generate

* Wire up and register endpoint

* Cleanup, define authorization

* Forgot the leading slash

* Wire up query and SignedInUser

* Wire up timerange query params

* Add todo for label queries

* Drop comment

* Update path to rules subtree
This commit is contained in:
Alexander Weaver 2023-02-02 11:34:00 -06:00 committed by GitHub
parent 2f218ab928
commit 6ad1cfef38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 195 additions and 15 deletions

View File

@ -84,6 +84,7 @@ type API struct {
AlertsRouter *sender.AlertsRouter
EvaluatorFactory eval.EvaluatorFactory
FeatureManager featuremgmt.FeatureToggles
Historian Historian
AppUrl *url.URL
}
@ -152,6 +153,11 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
muteTimings: api.MuteTimings,
alertRules: api.AlertRules,
}), m)
api.RegisterHistoryApiEndpoints(NewStateHistoryApi(&HistorySrv{
logger: logger,
hist: api.Historian,
}), m)
}
func (api *API) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {

View File

@ -0,0 +1,40 @@
package api
import (
"context"
"net/http"
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
type Historian interface {
QueryStates(ctx context.Context, query models.HistoryQuery) (*data.Frame, error)
}
type HistorySrv struct {
logger log.Logger
hist Historian
}
func (srv *HistorySrv) RouteQueryStateHistory(c *contextmodel.ReqContext) response.Response {
from := c.QueryInt64("from")
to := c.QueryInt64("to")
query := models.HistoryQuery{
RuleUID: c.Query("ruleUID"),
OrgID: c.OrgID,
SignedInUser: c.SignedInUser,
From: time.Unix(from, 0),
To: time.Unix(to, 0),
Labels: map[string]string{}, // TODO, not supported by all backends yet.
}
frame, err := srv.hist.QueryStates(c.Req.Context(), query)
if err != nil {
return ErrResp(http.StatusInternalServerError, err, "")
}
return response.JSON(http.StatusOK, frame)
}

View File

@ -58,6 +58,10 @@ func (api *API) authorize(method, path string) web.Handler {
ac.EvalPermission(ac.ActionAlertingRuleCreate, scope),
ac.EvalPermission(ac.ActionAlertingRuleDelete, scope),
)
// Grafana rule state history paths
case http.MethodGet + "/api/v1/rules/history":
fallback = middleware.ReqSignedIn
eval = ac.EvalPermission(ac.ActionAlertingRuleRead)
// Grafana, Prometheus-compatible Paths
case http.MethodGet + "/api/prometheus/grafana/api/v1/rules":

View File

@ -49,7 +49,7 @@ func TestAuthorize(t *testing.T) {
}
paths[p] = methods
}
require.Len(t, paths, 44)
require.Len(t, paths, 45)
ac := acmock.New()
api := &API{AccessControl: ac}

View File

@ -0,0 +1,40 @@
/*Package api contains base API implementation of unified alerting
*
*Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
*Do not manually edit these files, please find ngalert/api/swagger-codegen/ for commands on how to generate them.
*/
package api
import (
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
)
type HistoryApi interface {
RouteGetStateHistory(*contextmodel.ReqContext) response.Response
}
func (f *HistoryApiHandler) RouteGetStateHistory(ctx *contextmodel.ReqContext) response.Response {
return f.handleRouteGetStateHistory(ctx)
}
func (api *API) RegisterHistoryApiEndpoints(srv HistoryApi, m *metrics.API) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Get(
toMacaronPath("/api/v1/rules/history"),
api.authorize(http.MethodGet, "/api/v1/rules/history"),
metrics.Instrument(
http.MethodGet,
"/api/v1/rules/history",
srv.RouteGetStateHistory,
m,
),
)
}, middleware.ReqSignedIn)
}

View File

@ -0,0 +1,20 @@
package api
import (
"github.com/grafana/grafana/pkg/api/response"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
)
type HistoryApiHandler struct {
svc *HistorySrv
}
func NewStateHistoryApi(svc *HistorySrv) *HistoryApiHandler {
return &HistoryApiHandler{
svc: svc,
}
}
func (f *HistoryApiHandler) handleRouteGetStateHistory(ctx *contextmodel.ReqContext) response.Response {
return f.svc.RouteQueryStateHistory(ctx)
}

View File

@ -3931,7 +3931,6 @@
"type": "object"
},
"receiver": {
"description": "Receiver receiver",
"properties": {
"active": {
"description": "active",
@ -4885,4 +4884,4 @@
}
},
"swagger": "2.0"
}
}

View File

@ -0,0 +1,17 @@
package definitions
import "github.com/grafana/grafana-plugin-sdk-go/data"
// swagger:route GET /api/v1/rules/history history RouteGetStateHistory
//
// Query state history.
//
// Produces:
// - application/json
//
// Responses:
// 200: StateHistory
type StateHistory struct {
Results *data.Frame `json:"results"`
}

View File

@ -6642,6 +6642,23 @@
"testing"
]
}
},
"/api/v1/rules/history": {
"get": {
"operationId": "RouteGetStateHistory",
"produces": [
"application/json"
],
"responses": {
"200": {
"$ref": "#/responses/StateHistory"
}
},
"summary": "Query state history.",
"tags": [
"history"
]
}
}
},
"produces": [

View File

@ -2632,6 +2632,23 @@
}
}
}
},
"/api/v1/rules/history": {
"get": {
"produces": [
"application/json"
],
"tags": [
"history"
],
"summary": "Query state history.",
"operationId": "RouteGetStateHistory",
"responses": {
"200": {
"$ref": "#/responses/StateHistory"
}
}
}
}
},
"definitions": {
@ -6330,6 +6347,7 @@
"$ref": "#/definitions/gettableAlert"
},
"gettableAlerts": {
"description": "GettableAlerts gettable alerts",
"type": "array",
"items": {
"$ref": "#/definitions/gettableAlert"
@ -6708,4 +6726,4 @@
"type": "basic"
}
}
}
}

View File

@ -1,12 +1,17 @@
package models
import "time"
import (
"time"
"github.com/grafana/grafana/pkg/services/user"
)
// HistoryQuery represents a query for alert state history.
type HistoryQuery struct {
RuleUID string
OrgID int64
Labels map[string]string
From time.Time
To time.Time
RuleUID string
OrgID int64
Labels map[string]string
From time.Time
To time.Time
SignedInUser *user.SignedInUser
}

View File

@ -268,6 +268,7 @@ func (ng *AlertNG) init() error {
EvaluatorFactory: evalFactory,
FeatureManager: ng.FeatureToggles,
AppUrl: appUrl,
Historian: history,
}
api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics())
@ -383,7 +384,12 @@ func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
return limits, nil
}
func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingStateHistorySettings, ar annotations.Repository, ds dashboards.DashboardService, rs historian.RuleStore) (state.Historian, error) {
type Historian interface {
api.Historian
state.Historian
}
func configureHistorianBackend(ctx context.Context, cfg setting.UnifiedAlertingStateHistorySettings, ar annotations.Repository, ds dashboards.DashboardService, rs historian.RuleStore) (Historian, error) {
if !cfg.Enabled {
return historian.NewNopHistorian(), nil
}

View File

@ -79,10 +79,11 @@ func (h *AnnotationBackend) QueryStates(ctx context.Context, query ngmodels.Hist
}
q := annotations.ItemQuery{
AlertId: rq.Result.ID,
OrgId: query.OrgID,
From: query.From.Unix(),
To: query.To.Unix(),
AlertId: rq.Result.ID,
OrgId: query.OrgID,
From: query.From.Unix(),
To: query.To.Unix(),
SignedInUser: query.SignedInUser,
}
items, err := h.annotations.Find(ctx, &q)
if err != nil {

View File

@ -3,6 +3,8 @@ package historian
import (
"context"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/state"
history_model "github.com/grafana/grafana/pkg/services/ngalert/state/historian/model"
)
@ -19,3 +21,7 @@ func (f *NoOpHistorian) RecordStatesAsync(ctx context.Context, _ history_model.R
close(errCh)
return errCh
}
func (f *NoOpHistorian) QueryStates(ctx context.Context, query models.HistoryQuery) (*data.Frame, error) {
return data.NewFrame("states"), nil
}

View File

@ -19076,6 +19076,7 @@
}
},
"gettableAlerts": {
"description": "GettableAlerts gettable alerts",
"type": "array",
"items": {
"$ref": "#/definitions/gettableAlert"