mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
2f218ab928
commit
6ad1cfef38
@ -84,6 +84,7 @@ type API struct {
|
|||||||
AlertsRouter *sender.AlertsRouter
|
AlertsRouter *sender.AlertsRouter
|
||||||
EvaluatorFactory eval.EvaluatorFactory
|
EvaluatorFactory eval.EvaluatorFactory
|
||||||
FeatureManager featuremgmt.FeatureToggles
|
FeatureManager featuremgmt.FeatureToggles
|
||||||
|
Historian Historian
|
||||||
|
|
||||||
AppUrl *url.URL
|
AppUrl *url.URL
|
||||||
}
|
}
|
||||||
@ -152,6 +153,11 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
|||||||
muteTimings: api.MuteTimings,
|
muteTimings: api.MuteTimings,
|
||||||
alertRules: api.AlertRules,
|
alertRules: api.AlertRules,
|
||||||
}), m)
|
}), m)
|
||||||
|
|
||||||
|
api.RegisterHistoryApiEndpoints(NewStateHistoryApi(&HistorySrv{
|
||||||
|
logger: logger,
|
||||||
|
hist: api.Historian,
|
||||||
|
}), m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
func (api *API) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
||||||
|
40
pkg/services/ngalert/api/api_ruler_history.go
Normal file
40
pkg/services/ngalert/api/api_ruler_history.go
Normal 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)
|
||||||
|
}
|
@ -58,6 +58,10 @@ func (api *API) authorize(method, path string) web.Handler {
|
|||||||
ac.EvalPermission(ac.ActionAlertingRuleCreate, scope),
|
ac.EvalPermission(ac.ActionAlertingRuleCreate, scope),
|
||||||
ac.EvalPermission(ac.ActionAlertingRuleDelete, 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
|
// Grafana, Prometheus-compatible Paths
|
||||||
case http.MethodGet + "/api/prometheus/grafana/api/v1/rules":
|
case http.MethodGet + "/api/prometheus/grafana/api/v1/rules":
|
||||||
|
@ -49,7 +49,7 @@ func TestAuthorize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
paths[p] = methods
|
paths[p] = methods
|
||||||
}
|
}
|
||||||
require.Len(t, paths, 44)
|
require.Len(t, paths, 45)
|
||||||
|
|
||||||
ac := acmock.New()
|
ac := acmock.New()
|
||||||
api := &API{AccessControl: ac}
|
api := &API{AccessControl: ac}
|
||||||
|
40
pkg/services/ngalert/api/generated_base_api_history.go
Normal file
40
pkg/services/ngalert/api/generated_base_api_history.go
Normal 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)
|
||||||
|
}
|
20
pkg/services/ngalert/api/ruler_history.go
Normal file
20
pkg/services/ngalert/api/ruler_history.go
Normal 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)
|
||||||
|
}
|
@ -3931,7 +3931,6 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"receiver": {
|
"receiver": {
|
||||||
"description": "Receiver receiver",
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"active": {
|
"active": {
|
||||||
"description": "active",
|
"description": "active",
|
||||||
@ -4885,4 +4884,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"swagger": "2.0"
|
"swagger": "2.0"
|
||||||
}
|
}
|
@ -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"`
|
||||||
|
}
|
@ -6642,6 +6642,23 @@
|
|||||||
"testing"
|
"testing"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/rules/history": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "RouteGetStateHistory",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/StateHistory"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"summary": "Query state history.",
|
||||||
|
"tags": [
|
||||||
|
"history"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -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": {
|
"definitions": {
|
||||||
@ -6330,6 +6347,7 @@
|
|||||||
"$ref": "#/definitions/gettableAlert"
|
"$ref": "#/definitions/gettableAlert"
|
||||||
},
|
},
|
||||||
"gettableAlerts": {
|
"gettableAlerts": {
|
||||||
|
"description": "GettableAlerts gettable alerts",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/gettableAlert"
|
"$ref": "#/definitions/gettableAlert"
|
||||||
@ -6708,4 +6726,4 @@
|
|||||||
"type": "basic"
|
"type": "basic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,17 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
)
|
||||||
|
|
||||||
// HistoryQuery represents a query for alert state history.
|
// HistoryQuery represents a query for alert state history.
|
||||||
type HistoryQuery struct {
|
type HistoryQuery struct {
|
||||||
RuleUID string
|
RuleUID string
|
||||||
OrgID int64
|
OrgID int64
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
From time.Time
|
From time.Time
|
||||||
To time.Time
|
To time.Time
|
||||||
|
SignedInUser *user.SignedInUser
|
||||||
}
|
}
|
||||||
|
@ -268,6 +268,7 @@ func (ng *AlertNG) init() error {
|
|||||||
EvaluatorFactory: evalFactory,
|
EvaluatorFactory: evalFactory,
|
||||||
FeatureManager: ng.FeatureToggles,
|
FeatureManager: ng.FeatureToggles,
|
||||||
AppUrl: appUrl,
|
AppUrl: appUrl,
|
||||||
|
Historian: history,
|
||||||
}
|
}
|
||||||
api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics())
|
api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics())
|
||||||
|
|
||||||
@ -383,7 +384,12 @@ func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
|||||||
return limits, nil
|
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 {
|
if !cfg.Enabled {
|
||||||
return historian.NewNopHistorian(), nil
|
return historian.NewNopHistorian(), nil
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,11 @@ func (h *AnnotationBackend) QueryStates(ctx context.Context, query ngmodels.Hist
|
|||||||
}
|
}
|
||||||
|
|
||||||
q := annotations.ItemQuery{
|
q := annotations.ItemQuery{
|
||||||
AlertId: rq.Result.ID,
|
AlertId: rq.Result.ID,
|
||||||
OrgId: query.OrgID,
|
OrgId: query.OrgID,
|
||||||
From: query.From.Unix(),
|
From: query.From.Unix(),
|
||||||
To: query.To.Unix(),
|
To: query.To.Unix(),
|
||||||
|
SignedInUser: query.SignedInUser,
|
||||||
}
|
}
|
||||||
items, err := h.annotations.Find(ctx, &q)
|
items, err := h.annotations.Find(ctx, &q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,6 +3,8 @@ package historian
|
|||||||
import (
|
import (
|
||||||
"context"
|
"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"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
history_model "github.com/grafana/grafana/pkg/services/ngalert/state/historian/model"
|
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)
|
close(errCh)
|
||||||
return errCh
|
return errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *NoOpHistorian) QueryStates(ctx context.Context, query models.HistoryQuery) (*data.Frame, error) {
|
||||||
|
return data.NewFrame("states"), nil
|
||||||
|
}
|
||||||
|
@ -19076,6 +19076,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gettableAlerts": {
|
"gettableAlerts": {
|
||||||
|
"description": "GettableAlerts gettable alerts",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/gettableAlert"
|
"$ref": "#/definitions/gettableAlert"
|
||||||
|
Loading…
Reference in New Issue
Block a user