Alerting: Cleanup and move legacy to a legacy file (#32803)

* Alerting: Cleanup and move legacy to a legacy file

A quick cleanup of the ngalert/api directory, optimising for an easy
removal of what is will be considered legacy at some point. A quick
summary of what's done is:

- Add a prefix `generated` prefix to files that are auto-generated by
  our swagger definitions.
- Create a legacy file to place all the legacy API routes implementation
  and helpers. Deleting files that where no longer needed after this
move.
- Rename the `lotex` file to `lotex_ruler`
- Adding a couple of comments here and there.

With this, I hope to organise our code in this directory a bit better
given there's a lot going on.
This commit is contained in:
gotjosh 2021-04-09 10:55:41 +01:00 committed by GitHub
parent 51e4106d1d
commit c9e5088e8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 377 additions and 610 deletions

1
go.sum
View File

@ -1041,6 +1041,7 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=

View File

@ -1,7 +1,6 @@
package api
import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/services/ngalert/state"
@ -9,21 +8,16 @@ import (
"github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/datasourceproxy"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/util"
)
// timeNow makes it possible to test usage of time
@ -65,21 +59,25 @@ func (api *API) RegisterAPIEndpoints() {
proxy := &AlertingProxy{
DataProxy: api.DataProxy,
}
// Register endpoints for proxing to Alertmanager-compatible backends.
api.RegisterAlertmanagerApiEndpoints(NewForkedAM(
api.DatasourceCache,
NewLotexAM(proxy, logger),
AlertmanagerSrv{store: api.AlertingStore, am: api.Alertmanager, log: logger},
))
// Register endpoints for proxing to Prometheus-compatible backends.
api.RegisterPrometheusApiEndpoints(NewForkedProm(
api.DatasourceCache,
NewLotexProm(proxy, logger),
PrometheusSrv{log: logger, stateTracker: api.StateTracker},
))
// Register endpoints for proxing to Cortex Ruler-compatible backends.
api.RegisterRulerApiEndpoints(NewForkedRuler(
api.DatasourceCache,
NewLotexRuler(proxy, logger),
RulerSrv{store: api.RuleStore, log: logger},
))
// Register endpoints for testing evaluation of rules and notification channels.
api.RegisterTestingApiEndpoints(TestingApiMock{log: logger})
// Legacy routes; they will be removed in v8
@ -119,238 +117,3 @@ func (api *API) RegisterAPIEndpoints() {
alertInstances.Get("", middleware.ReqSignedIn, routing.Wrap(api.listAlertInstancesEndpoint))
})
}
// conditionEvalEndpoint handles POST /api/alert-definitions/eval.
func (api *API) conditionEvalEndpoint(c *models.ReqContext, cmd ngmodels.EvalAlertConditionCommand) response.Response {
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
now := cmd.Now
if now.IsZero() {
now = timeNow()
}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(&evalCond, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate conditions", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
}
// alertDefinitionEvalEndpoint handles GET /api/alert-definitions/eval/:alertDefinitionUID.
func (api *API) alertDefinitionEvalEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
condition, err := api.LoadAlertCondition(alertDefinitionUID, c.SignedInUser.OrgId)
if err != nil {
return response.Error(400, "Failed to load alert definition conditions", err)
}
if err := api.validateCondition(*condition, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(condition, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate alert", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
}
// getAlertDefinitionEndpoint handles GET /api/alert-definitions/:alertDefinitionUID.
func (api *API) getAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
query := ngmodels.GetAlertDefinitionByUIDQuery{
UID: alertDefinitionUID,
OrgID: c.SignedInUser.OrgId,
}
if err := api.Store.GetAlertDefinitionByUID(&query); err != nil {
return response.Error(500, "Failed to get alert definition", err)
}
return response.JSON(200, &query.Result)
}
// deleteAlertDefinitionEndpoint handles DELETE /api/alert-definitions/:alertDefinitionUID.
func (api *API) deleteAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
cmd := ngmodels.DeleteAlertDefinitionByUIDCommand{
UID: alertDefinitionUID,
OrgID: c.SignedInUser.OrgId,
}
if err := api.Store.DeleteAlertDefinitionByUID(&cmd); err != nil {
return response.Error(500, "Failed to delete alert definition", err)
}
return response.Success("Alert definition deleted")
}
// updateAlertDefinitionEndpoint handles PUT /api/alert-definitions/:alertDefinitionUID.
func (api *API) updateAlertDefinitionEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionCommand) response.Response {
cmd.UID = c.Params(":alertDefinitionUID")
cmd.OrgID = c.SignedInUser.OrgId
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
if err := api.Store.UpdateAlertDefinition(&cmd); err != nil {
return response.Error(500, "Failed to update alert definition", err)
}
return response.JSON(200, cmd.Result)
}
// createAlertDefinitionEndpoint handles POST /api/alert-definitions.
func (api *API) createAlertDefinitionEndpoint(c *models.ReqContext, cmd ngmodels.SaveAlertDefinitionCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
if err := api.Store.SaveAlertDefinition(&cmd); err != nil {
return response.Error(500, "Failed to create alert definition", err)
}
return response.JSON(200, cmd.Result)
}
// listAlertDefinitions handles GET /api/alert-definitions.
func (api *API) listAlertDefinitions(c *models.ReqContext) response.Response {
query := ngmodels.ListAlertDefinitionsQuery{OrgID: c.SignedInUser.OrgId}
if err := api.Store.GetOrgAlertDefinitions(&query); err != nil {
return response.Error(500, "Failed to list alert definitions", err)
}
return response.JSON(200, util.DynMap{"results": query.Result})
}
func (api *API) pauseScheduler() response.Response {
err := api.Schedule.Pause()
if err != nil {
return response.Error(500, "Failed to pause scheduler", err)
}
return response.JSON(200, util.DynMap{"message": "alert definition scheduler paused"})
}
func (api *API) unpauseScheduler() response.Response {
err := api.Schedule.Unpause()
if err != nil {
return response.Error(500, "Failed to unpause scheduler", err)
}
return response.JSON(200, util.DynMap{"message": "alert definition scheduler unpaused"})
}
// alertDefinitionPauseEndpoint handles POST /api/alert-definitions/pause.
func (api *API) alertDefinitionPauseEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionPausedCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
cmd.Paused = true
err := api.Store.UpdateAlertDefinitionPaused(&cmd)
if err != nil {
return response.Error(500, "Failed to pause alert definition", err)
}
return response.JSON(200, util.DynMap{"message": fmt.Sprintf("%d alert definitions paused", cmd.ResultCount)})
}
// alertDefinitionUnpauseEndpoint handles POST /api/alert-definitions/unpause.
func (api *API) alertDefinitionUnpauseEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionPausedCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
cmd.Paused = false
err := api.Store.UpdateAlertDefinitionPaused(&cmd)
if err != nil {
return response.Error(500, "Failed to unpause alert definition", err)
}
return response.JSON(200, util.DynMap{"message": fmt.Sprintf("%d alert definitions unpaused", cmd.ResultCount)})
}
// LoadAlertCondition returns a Condition object for the given alertDefinitionID.
func (api *API) LoadAlertCondition(alertDefinitionUID string, orgID int64) (*ngmodels.Condition, error) {
q := ngmodels.GetAlertDefinitionByUIDQuery{UID: alertDefinitionUID, OrgID: orgID}
if err := api.Store.GetAlertDefinitionByUID(&q); err != nil {
return nil, err
}
alertDefinition := q.Result
err := api.Store.ValidateAlertDefinition(alertDefinition, true)
if err != nil {
return nil, err
}
return &ngmodels.Condition{
Condition: alertDefinition.Condition,
OrgID: alertDefinition.OrgID,
Data: alertDefinition.Data,
}, nil
}
func (api *API) validateCondition(c ngmodels.Condition, user *models.SignedInUser, skipCache bool) error {
var refID string
if len(c.Data) == 0 {
return nil
}
for _, query := range c.Data {
if c.Condition == query.RefID {
refID = c.Condition
}
datasourceUID, err := query.GetDatasource()
if err != nil {
return err
}
isExpression, err := query.IsExpression()
if err != nil {
return err
}
if isExpression {
continue
}
_, err = api.DatasourceCache.GetDatasourceByUID(datasourceUID, user, skipCache)
if err != nil {
return fmt.Errorf("failed to get datasource: %s: %w", datasourceUID, err)
}
}
if refID == "" {
return fmt.Errorf("condition %s not found in any query or expression", c.Condition)
}
return nil
}

View File

@ -1,118 +0,0 @@
/*Package api contains base API implementation of unified alerting
*
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
* Need to remove unused imports.
*/
package api
import (
"net/http"
"github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
type AlertmanagerApiService interface {
RouteCreateSilence(*models.ReqContext, apimodels.PostableSilence) response.Response
RouteDeleteAlertingConfig(*models.ReqContext) response.Response
RouteDeleteSilence(*models.ReqContext) response.Response
RouteGetAMAlertGroups(*models.ReqContext) response.Response
RouteGetAMAlerts(*models.ReqContext) response.Response
RouteGetAlertingConfig(*models.ReqContext) response.Response
RouteGetSilence(*models.ReqContext) response.Response
RouteGetSilences(*models.ReqContext) response.Response
RoutePostAMAlerts(*models.ReqContext, apimodels.PostableAlerts) response.Response
RoutePostAlertingConfig(*models.ReqContext, apimodels.PostableUserConfig) response.Response
}
type AlertmanagerApiBase struct {
log log.Logger
}
func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.PostableSilence{}), routing.Wrap(srv.RouteCreateSilence))
group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig))
group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences))
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts))
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig))
}, middleware.ReqSignedIn)
}
func (base AlertmanagerApiBase) RouteCreateSilence(c *models.ReqContext, body apimodels.PostableSilence) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteCreateSilence: ", "Recipient", recipient)
base.log.Info("RouteCreateSilence: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteDeleteSilence(c *models.ReqContext) response.Response {
silenceId := c.Params(":SilenceId")
base.log.Info("RouteDeleteSilence: ", "SilenceId", silenceId)
recipient := c.Params(":Recipient")
base.log.Info("RouteDeleteSilence: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteGetAMAlertGroups(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetAMAlertGroups: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteGetAMAlerts(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetAMAlerts: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteGetAlertingConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetAlertingConfig: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteGetSilence(c *models.ReqContext) response.Response {
silenceId := c.Params(":SilenceId")
base.log.Info("RouteGetSilence: ", "SilenceId", silenceId)
recipient := c.Params(":Recipient")
base.log.Info("RouteGetSilence: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RouteGetSilences(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetSilences: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RoutePostAMAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RoutePostAMAlerts: ", "Recipient", recipient)
base.log.Info("RoutePostAMAlerts: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base AlertmanagerApiBase) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RoutePostAlertingConfig: ", "Recipient", recipient)
base.log.Info("RoutePostAlertingConfig: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}

View File

@ -1,94 +0,0 @@
/*Package api contains base API implementation of unified alerting
*
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
* Need to remove unused imports.
*/
package api
import (
"net/http"
"github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
type RulerApiService interface {
RouteDeleteNamespaceRulesConfig(*models.ReqContext) response.Response
RouteDeleteRuleGroupConfig(*models.ReqContext) response.Response
RouteGetNamespaceRulesConfig(*models.ReqContext) response.Response
RouteGetRulegGroupConfig(*models.ReqContext) response.Response
RouteGetRulesConfig(*models.ReqContext) response.Response
RoutePostNameRulesConfig(*models.ReqContext, apimodels.PostableRuleGroupConfig) response.Response
}
type RulerApiBase struct {
log log.Logger
}
func (api *API) RegisterRulerApiEndpoints(srv RulerApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteDeleteNamespaceRulesConfig))
group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteDeleteRuleGroupConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteGetNamespaceRulesConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteGetRulegGroupConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRulesConfig))
group.Post(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), binding.Bind(apimodels.PostableRuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig))
}, middleware.ReqSignedIn)
}
func (base RulerApiBase) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteDeleteNamespaceRulesConfig: ", "Recipient", recipient)
namespace := c.Params(":Namespace")
base.log.Info("RouteDeleteNamespaceRulesConfig: ", "Namespace", namespace)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base RulerApiBase) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteDeleteRuleGroupConfig: ", "Recipient", recipient)
namespace := c.Params(":Namespace")
base.log.Info("RouteDeleteRuleGroupConfig: ", "Namespace", namespace)
groupname := c.Params(":Groupname")
base.log.Info("RouteDeleteRuleGroupConfig: ", "Groupname", groupname)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base RulerApiBase) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetNamespaceRulesConfig: ", "Recipient", recipient)
namespace := c.Params(":Namespace")
base.log.Info("RouteGetNamespaceRulesConfig: ", "Namespace", namespace)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base RulerApiBase) RouteGetRulegGroupConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetRulegGroupConfig: ", "Recipient", recipient)
namespace := c.Params(":Namespace")
base.log.Info("RouteGetRulegGroupConfig: ", "Namespace", namespace)
groupname := c.Params(":Groupname")
base.log.Info("RouteGetRulegGroupConfig: ", "Groupname", groupname)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base RulerApiBase) RouteGetRulesConfig(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetRulesConfig: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base RulerApiBase) RoutePostNameRulesConfig(c *models.ReqContext, body apimodels.PostableRuleGroupConfig) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RoutePostNameRulesConfig: ", "Recipient", recipient)
namespace := c.Params(":Namespace")
base.log.Info("RoutePostNameRulesConfig: ", "Namespace", namespace)
base.log.Info("RoutePostNameRulesConfig: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}

View File

@ -15,6 +15,7 @@ type ForkedRuler struct {
DatasourceCache datasources.CacheService
}
// NewForkedRuler implements a set of routes that proxy to various Cortex Ruler-compatible backends.
func NewForkedRuler(datasourceCache datasources.CacheService, lotex, grafana RulerApiService) *ForkedRuler {
return &ForkedRuler{
LotexRuler: lotex,

View File

@ -14,6 +14,7 @@ type ForkedAMSvc struct {
DatasourceCache datasources.CacheService
}
// NewForkedAM implements a set of routes that proxy to various Alertmanager-compatible backends.
func NewForkedAM(datasourceCache datasources.CacheService, proxy, grafana AlertmanagerApiService) *ForkedAMSvc {
return &ForkedAMSvc{
AMSvc: proxy,

View File

@ -14,6 +14,7 @@ type ForkedPromSvc struct {
DatasourceCache datasources.CacheService
}
// NewForkedProm implements a set of routes that proxy to various Prometheus-compatible backends.
func NewForkedProm(datasourceCache datasources.CacheService, proxy, grafana PrometheusApiService) *ForkedPromSvc {
return &ForkedPromSvc{
ProxySvc: proxy,

View File

@ -0,0 +1,45 @@
/*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 (
"github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
type AlertmanagerApiService interface {
RouteCreateSilence(*models.ReqContext, apimodels.PostableSilence) response.Response
RouteDeleteAlertingConfig(*models.ReqContext) response.Response
RouteDeleteSilence(*models.ReqContext) response.Response
RouteGetAMAlertGroups(*models.ReqContext) response.Response
RouteGetAMAlerts(*models.ReqContext) response.Response
RouteGetAlertingConfig(*models.ReqContext) response.Response
RouteGetSilence(*models.ReqContext) response.Response
RouteGetSilences(*models.ReqContext) response.Response
RoutePostAMAlerts(*models.ReqContext, apimodels.PostableAlerts) response.Response
RoutePostAlertingConfig(*models.ReqContext, apimodels.PostableUserConfig) response.Response
}
func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.PostableSilence{}), routing.Wrap(srv.RouteCreateSilence))
group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig))
group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence))
group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences))
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts))
group.Post(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig))
}, middleware.ReqSignedIn)
}

View File

@ -1,17 +1,14 @@
/*Package api contains base API implementation of unified alerting
*
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
* Need to remove unused imports.
*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/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
@ -21,25 +18,9 @@ type PrometheusApiService interface {
RouteGetRuleStatuses(*models.ReqContext) response.Response
}
type PrometheusApiBase struct {
log log.Logger
}
func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Get(toMacaronPath("/api/prometheus/{Recipient}/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertStatuses))
group.Get(toMacaronPath("/api/prometheus/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRuleStatuses))
}, middleware.ReqSignedIn)
}
func (base PrometheusApiBase) RouteGetAlertStatuses(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetAlertStatuses: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base PrometheusApiBase) RouteGetRuleStatuses(c *models.ReqContext) response.Response {
recipient := c.Params(":Recipient")
base.log.Info("RouteGetRuleStatuses: ", "Recipient", recipient)
return response.Error(http.StatusNotImplemented, "", nil)
}

View File

@ -0,0 +1,37 @@
/*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 (
"github.com/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
type RulerApiService interface {
RouteDeleteNamespaceRulesConfig(*models.ReqContext) response.Response
RouteDeleteRuleGroupConfig(*models.ReqContext) response.Response
RouteGetNamespaceRulesConfig(*models.ReqContext) response.Response
RouteGetRulegGroupConfig(*models.ReqContext) response.Response
RouteGetRulesConfig(*models.ReqContext) response.Response
RoutePostNameRulesConfig(*models.ReqContext, apimodels.PostableRuleGroupConfig) response.Response
}
func (api *API) RegisterRulerApiEndpoints(srv RulerApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteDeleteNamespaceRulesConfig))
group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteDeleteRuleGroupConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteGetNamespaceRulesConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteGetRulegGroupConfig))
group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRulesConfig))
group.Post(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), binding.Bind(apimodels.PostableRuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig))
}, middleware.ReqSignedIn)
}

View File

@ -1,19 +1,17 @@
/*Package api contains base API implementation of unified alerting
*
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
* Need to remove unused imports.
*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/go-macaron/binding"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
)
@ -23,23 +21,9 @@ type TestingApiService interface {
RouteTestRuleConfig(*models.ReqContext, apimodels.TestRulePayload) response.Response
}
type TestingApiBase struct {
log log.Logger
}
func (api *API) RegisterTestingApiEndpoints(srv TestingApiService) {
api.RouteRegister.Group("", func(group routing.RouteRegister) {
group.Post(toMacaronPath("/api/v1/receiver/test"), binding.Bind(apimodels.ExtendedReceiver{}), routing.Wrap(srv.RouteTestReceiverConfig))
group.Post(toMacaronPath("/api/v1/rule/test"), binding.Bind(apimodels.TestRulePayload{}), routing.Wrap(srv.RouteTestRuleConfig))
}, middleware.ReqSignedIn)
}
func (base TestingApiBase) RouteTestReceiverConfig(c *models.ReqContext, body apimodels.ExtendedReceiver) response.Response {
base.log.Info("RouteTestReceiverConfig: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}
func (base TestingApiBase) RouteTestRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload) response.Response {
base.log.Info("RouteTestRuleConfig: ", "body", body)
return response.Error(http.StatusNotImplemented, "", nil)
}

View File

@ -1,19 +0,0 @@
package api
import (
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
)
// listAlertInstancesEndpoint handles GET /api/alert-instances.
func (api *API) listAlertInstancesEndpoint(c *models.ReqContext) response.Response {
cmd := ngmodels.ListAlertInstancesQuery{DefinitionOrgID: c.SignedInUser.OrgId}
if err := api.Store.ListAlertInstances(&cmd); err != nil {
return response.Error(500, "Failed to list alert instances", err)
}
return response.JSON(200, cmd.Result)
}

View File

@ -0,0 +1,274 @@
package api
import (
"fmt"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/util"
)
// listAlertInstancesEndpoint handles GET /api/alert-instances.
func (api *API) listAlertInstancesEndpoint(c *models.ReqContext) response.Response {
cmd := ngmodels.ListAlertInstancesQuery{DefinitionOrgID: c.SignedInUser.OrgId}
if err := api.Store.ListAlertInstances(&cmd); err != nil {
return response.Error(500, "Failed to list alert instances", err)
}
return response.JSON(200, cmd.Result)
}
// conditionEvalEndpoint handles POST /api/alert-definitions/eval.
func (api *API) conditionEvalEndpoint(c *models.ReqContext, cmd ngmodels.EvalAlertConditionCommand) response.Response {
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
now := cmd.Now
if now.IsZero() {
now = timeNow()
}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(&evalCond, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate conditions", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
}
// alertDefinitionEvalEndpoint handles GET /api/alert-definitions/eval/:alertDefinitionUID.
func (api *API) alertDefinitionEvalEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
condition, err := api.LoadAlertCondition(alertDefinitionUID, c.SignedInUser.OrgId)
if err != nil {
return response.Error(400, "Failed to load alert definition conditions", err)
}
if err := api.validateCondition(*condition, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(condition, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate alert", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
}
// getAlertDefinitionEndpoint handles GET /api/alert-definitions/:alertDefinitionUID.
func (api *API) getAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
query := ngmodels.GetAlertDefinitionByUIDQuery{
UID: alertDefinitionUID,
OrgID: c.SignedInUser.OrgId,
}
if err := api.Store.GetAlertDefinitionByUID(&query); err != nil {
return response.Error(500, "Failed to get alert definition", err)
}
return response.JSON(200, &query.Result)
}
// deleteAlertDefinitionEndpoint handles DELETE /api/alert-definitions/:alertDefinitionUID.
func (api *API) deleteAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.Params(":alertDefinitionUID")
cmd := ngmodels.DeleteAlertDefinitionByUIDCommand{
UID: alertDefinitionUID,
OrgID: c.SignedInUser.OrgId,
}
if err := api.Store.DeleteAlertDefinitionByUID(&cmd); err != nil {
return response.Error(500, "Failed to delete alert definition", err)
}
return response.Success("Alert definition deleted")
}
// updateAlertDefinitionEndpoint handles PUT /api/alert-definitions/:alertDefinitionUID.
func (api *API) updateAlertDefinitionEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionCommand) response.Response {
cmd.UID = c.Params(":alertDefinitionUID")
cmd.OrgID = c.SignedInUser.OrgId
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
if err := api.Store.UpdateAlertDefinition(&cmd); err != nil {
return response.Error(500, "Failed to update alert definition", err)
}
return response.JSON(200, cmd.Result)
}
// createAlertDefinitionEndpoint handles POST /api/alert-definitions.
func (api *API) createAlertDefinitionEndpoint(c *models.ReqContext, cmd ngmodels.SaveAlertDefinitionCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
evalCond := ngmodels.Condition{
Condition: cmd.Condition,
OrgID: c.SignedInUser.OrgId,
Data: cmd.Data,
}
if err := api.validateCondition(evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
if err := api.Store.SaveAlertDefinition(&cmd); err != nil {
return response.Error(500, "Failed to create alert definition", err)
}
return response.JSON(200, cmd.Result)
}
// listAlertDefinitions handles GET /api/alert-definitions.
func (api *API) listAlertDefinitions(c *models.ReqContext) response.Response {
query := ngmodels.ListAlertDefinitionsQuery{OrgID: c.SignedInUser.OrgId}
if err := api.Store.GetOrgAlertDefinitions(&query); err != nil {
return response.Error(500, "Failed to list alert definitions", err)
}
return response.JSON(200, util.DynMap{"results": query.Result})
}
func (api *API) pauseScheduler() response.Response {
err := api.Schedule.Pause()
if err != nil {
return response.Error(500, "Failed to pause scheduler", err)
}
return response.JSON(200, util.DynMap{"message": "alert definition scheduler paused"})
}
func (api *API) unpauseScheduler() response.Response {
err := api.Schedule.Unpause()
if err != nil {
return response.Error(500, "Failed to unpause scheduler", err)
}
return response.JSON(200, util.DynMap{"message": "alert definition scheduler unpaused"})
}
// alertDefinitionPauseEndpoint handles POST /api/alert-definitions/pause.
func (api *API) alertDefinitionPauseEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionPausedCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
cmd.Paused = true
err := api.Store.UpdateAlertDefinitionPaused(&cmd)
if err != nil {
return response.Error(500, "Failed to pause alert definition", err)
}
return response.JSON(200, util.DynMap{"message": fmt.Sprintf("%d alert definitions paused", cmd.ResultCount)})
}
// alertDefinitionUnpauseEndpoint handles POST /api/alert-definitions/unpause.
func (api *API) alertDefinitionUnpauseEndpoint(c *models.ReqContext, cmd ngmodels.UpdateAlertDefinitionPausedCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
cmd.Paused = false
err := api.Store.UpdateAlertDefinitionPaused(&cmd)
if err != nil {
return response.Error(500, "Failed to unpause alert definition", err)
}
return response.JSON(200, util.DynMap{"message": fmt.Sprintf("%d alert definitions unpaused", cmd.ResultCount)})
}
// LoadAlertCondition returns a Condition object for the given alertDefinitionID.
func (api *API) LoadAlertCondition(alertDefinitionUID string, orgID int64) (*ngmodels.Condition, error) {
q := ngmodels.GetAlertDefinitionByUIDQuery{UID: alertDefinitionUID, OrgID: orgID}
if err := api.Store.GetAlertDefinitionByUID(&q); err != nil {
return nil, err
}
alertDefinition := q.Result
err := api.Store.ValidateAlertDefinition(alertDefinition, true)
if err != nil {
return nil, err
}
return &ngmodels.Condition{
Condition: alertDefinition.Condition,
OrgID: alertDefinition.OrgID,
Data: alertDefinition.Data,
}, nil
}
func (api *API) validateCondition(c ngmodels.Condition, user *models.SignedInUser, skipCache bool) error {
var refID string
if len(c.Data) == 0 {
return nil
}
for _, query := range c.Data {
if c.Condition == query.RefID {
refID = c.Condition
}
datasourceUID, err := query.GetDatasource()
if err != nil {
return err
}
isExpression, err := query.IsExpression()
if err != nil {
return err
}
if isExpression {
continue
}
_, err = api.DatasourceCache.GetDatasourceByUID(datasourceUID, user, skipCache)
if err != nil {
return fmt.Errorf("failed to get datasource: %s: %w", datasourceUID, err)
}
}
if refID == "" {
return fmt.Errorf("condition %s not found in any query or expression", c.Condition)
}
return nil
}
func (api *API) validateOrgAlertDefinition(c *models.ReqContext) {
uid := c.ParamsEscape(":alertDefinitionUID")
if uid == "" {
c.JsonApiErr(403, "Permission denied", nil)
return
}
query := ngmodels.GetAlertDefinitionByUIDQuery{UID: uid, OrgID: c.SignedInUser.OrgId}
if err := api.Store.GetAlertDefinitionByUID(&query); err != nil {
c.JsonApiErr(404, "Alert definition not found", nil)
return
}
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
"time"
"github.com/prometheus/common/model"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api/response"
@ -14,7 +16,6 @@ import (
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/util"
"github.com/prometheus/common/model"
)
// conditionEvalEndpoint handles POST /api/alert-definitions/evalOld.
@ -27,24 +28,19 @@ func (api *API) conditionEvalOldEndpoint(c *models.ReqContext) response.Response
if err != nil {
return response.Error(400, "Failed to translate alert conditions", err)
}
if err := api.validateCondition(*evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
//now := cmd.Now
//if now.IsZero() {
//now := timeNow()
//}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(evalCond, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate conditions", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
@ -56,48 +52,37 @@ func (api *API) conditionEvalOldEndpointByID(c *models.ReqContext) response.Resp
if id == 0 {
return response.Error(400, "missing id", nil)
}
getAlert := &models.GetAlertByIdQuery{
Id: id,
}
if err := bus.Dispatch(getAlert); err != nil {
return response.Error(400, fmt.Sprintf("could find alert with id %v", id), err)
}
if getAlert.Result.OrgId != c.SignedInUser.OrgId {
return response.Error(403, "alert does not match organization of user", nil)
}
settings := getAlert.Result.Settings
sb, err := settings.ToDB()
if err != nil {
return response.Error(400, "failed to marshal alert settings", err)
}
evalCond, err := translate.DashboardAlertConditions(sb, c.OrgId)
if err != nil {
return response.Error(400, "Failed to translate alert conditions", err)
}
if err := api.validateCondition(*evalCond, c.SignedInUser, c.SkipCache); err != nil {
return response.Error(400, "invalid condition", err)
}
//now := cmd.Now
//if now.IsZero() {
//now := timeNow()
//}
evaluator := eval.Evaluator{Cfg: api.Cfg}
evalResults, err := evaluator.ConditionEval(evalCond, timeNow(), api.DataService)
if err != nil {
return response.Error(400, "Failed to evaluate conditions", err)
}
frame := evalResults.AsDataFrame()
return response.JSONStreaming(200, util.DynMap{
"instances": []*data.Frame{&frame},
})
@ -109,31 +94,24 @@ func (api *API) conditionOldEndpointByID(c *models.ReqContext) response.Response
if id == 0 {
return response.Error(400, "missing id", nil)
}
getAlert := &models.GetAlertByIdQuery{
Id: id,
}
if err := bus.Dispatch(getAlert); err != nil {
return response.Error(400, fmt.Sprintf("could find alert with id %v", id), err)
}
if getAlert.Result.OrgId != c.SignedInUser.OrgId {
return response.Error(403, "alert does not match organization of user", nil)
}
settings := getAlert.Result.Settings
sb, err := settings.ToDB()
if err != nil {
return response.Error(400, "failed to marshal alert settings", err)
}
evalCond, err := translate.DashboardAlertConditions(sb, c.OrgId)
if err != nil {
return response.Error(400, "Failed to translate alert conditions", err)
}
return response.JSON(200, evalCond)
}
@ -143,32 +121,25 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
if id == 0 {
return response.Error(400, "missing id", nil)
}
save := c.Query("save") == "true"
// Get dashboard alert definition from database.
oldAlert, status, err := transGetAlertById(id, *c.SignedInUser)
if err != nil {
return response.Error(status, "failed to get alert", fmt.Errorf("failed to get alert for alert id %v: %w", id, err))
}
// Translate the dashboard's alerts conditions into SSE queries and conditions.
sseCond, err := transToSSECondition(oldAlert, *c.SignedInUser)
if err != nil {
return response.Error(400, "failed to translate alert conditions",
fmt.Errorf("failed to translate alert conditions for alert id %v: %w", id, err))
}
// Get the dashboard that contains the dashboard Alert.
oldAlertsDash, status, err := transGetAlertsDashById(oldAlert.DashboardId, *c.SignedInUser)
if err != nil {
return response.Error(status, "failed to get alert's dashboard", fmt.Errorf("failed to get dashboard for alert id %v, %w", id, err))
}
isGeneralFolder := oldAlertsDash.FolderId == 0 && !oldAlertsDash.IsFolder
var namespaceUID string
if isGeneralFolder {
namespaceUID = "General"
} else {
@ -180,19 +151,15 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
if err := bus.Dispatch(getFolder); err != nil {
return response.Error(400, fmt.Sprintf("could find folder %v for alert with id %v", getFolder.Id, id), err)
}
namespaceUID = getFolder.Result.Uid
}
noDataSetting, execErrSetting, err := transNoDataExecSettings(oldAlert, *c.SignedInUser)
if err != nil {
return response.Error(400, "unable to translate nodata/exec error settings",
fmt.Errorf("unable to translate nodata/exec error settings for alert id %v: %w", id, err))
}
// TODO: What to do with Rule Tags
// ruleTags := map[string]string{}
// for k, v := range oldAlert.Settings.Get("alertRuleTags").MustMap() {
// sV, ok := v.(string)
// if !ok {
@ -201,9 +168,7 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
// }
// ruleTags[k] = sV
// }
// TODO: Need place to put FOR duration
rule := ngmodels.AlertRule{
Title: oldAlert.Name,
Data: sseCond.Data,
@ -211,7 +176,6 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
NoDataState: *noDataSetting,
ExecErrState: *execErrSetting,
}
rgc := apimodels.PostableRuleGroupConfig{
// TODO? Generate new name on conflict?
Name: oldAlert.Name,
@ -220,21 +184,17 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
toPostableExtendedRuleNode(rule),
},
}
cmd := store.UpdateRuleGroupCmd{
OrgID: oldAlert.OrgId,
NamespaceUID: namespaceUID,
RuleGroupConfig: rgc,
}
if !save {
return response.JSON(200, cmd)
}
// note: Update rule group will set the Interval within the grafana_alert from
// the interval of the group.
err = api.RuleStore.UpdateRuleGroup(cmd)
if err != nil {
return response.JSON(400, util.DynMap{
"message:": "failed to save alert rule",
@ -242,10 +202,8 @@ func (api *API) ruleGroupByOldID(c *models.ReqContext) response.Response {
"cmd": cmd,
})
}
return response.JSON(200, cmd)
}
func transAdjustInterval(freq int64) model.Duration {
// 10 corresponds to the SchedulerCfg, but TODO not worrying about fetching for now.
var baseFreq int64 = 10
@ -254,23 +212,18 @@ func transAdjustInterval(freq int64) model.Duration {
}
return model.Duration(time.Duration((freq - (freq % baseFreq))) * time.Second)
}
func transGetAlertById(id int64, user models.SignedInUser) (*models.Alert, int, error) {
getAlert := &models.GetAlertByIdQuery{
Id: id,
}
if err := bus.Dispatch(getAlert); err != nil {
return nil, 400, fmt.Errorf("could find alert with id %v: %w", id, err)
}
if getAlert.Result.OrgId != user.OrgId {
return nil, 403, fmt.Errorf("alert does not match organization of user")
}
return getAlert.Result, 0, nil
}
func transGetAlertsDashById(dashboardId int64, user models.SignedInUser) (*models.Dashboard, int, error) {
getDash := &models.GetDashboardQuery{
Id: dashboardId,
@ -281,27 +234,23 @@ func transGetAlertsDashById(dashboardId int64, user models.SignedInUser) (*model
}
return getDash.Result, 0, nil
}
func transToSSECondition(m *models.Alert, user models.SignedInUser) (*ngmodels.Condition, error) {
sb, err := m.Settings.ToDB()
if err != nil {
return nil, fmt.Errorf("failed to marshal alert settings: %w", err)
}
evalCond, err := translate.DashboardAlertConditions(sb, user.OrgId)
if err != nil {
return nil, fmt.Errorf("failed to translate dashboard alert to SSE conditions: %w", err)
}
return evalCond, nil
}
func transNoDataExecSettings(m *models.Alert, user models.SignedInUser) (*ngmodels.NoDataState, *ngmodels.ExecutionErrorState, error) {
oldNoData := m.Settings.Get("noDataState").MustString()
noDataSetting, err := transNoData(oldNoData)
if err != nil {
return nil, nil, err
}
oldExecErr := m.Settings.Get("executionErrorState").MustString()
execErrSetting, err := transExecErr(oldExecErr)
if err != nil {
@ -309,7 +258,6 @@ func transNoDataExecSettings(m *models.Alert, user models.SignedInUser) (*ngmode
}
return &noDataSetting, &execErrSetting, nil
}
func transNoData(s string) (ngmodels.NoDataState, error) {
switch s {
case "ok":
@ -323,7 +271,6 @@ func transNoData(s string) (ngmodels.NoDataState, error) {
}
return ngmodels.NoData, fmt.Errorf("unrecognized No Data setting %v", s)
}
func transExecErr(s string) (ngmodels.ExecutionErrorState, error) {
switch s {
case "alerting":

View File

@ -1,23 +0,0 @@
package api
import (
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/models"
)
func (api *API) validateOrgAlertDefinition(c *models.ReqContext) {
uid := c.ParamsEscape(":alertDefinitionUID")
if uid == "" {
c.JsonApiErr(403, "Permission denied", nil)
return
}
query := ngmodels.GetAlertDefinitionByUIDQuery{UID: uid, OrgID: c.SignedInUser.OrgId}
if err := api.Store.GetAlertDefinitionByUID(&query); err != nil {
c.JsonApiErr(404, "Alert definition not found", nil)
return
}
}

View File

@ -18,9 +18,8 @@ fix:
sed -i -e 's/apimodels\.\[\]PostableAlert/apimodels.PostableAlerts/' ../go/*.go
sed -i -e 's/apimodels\.\[\]UpdateDashboardAclCommand/apimodels.Permissions/' ../go/*.go
goimports -w -v ../go/*.go
rm ../go/*.go-e
clean:
rm -rf ../go
all: swagger-codegen-api fix copy-files clean
all: swagger-codegen-api fix copy-files clean

View File

@ -4,4 +4,4 @@ files = os.listdir(path)
dest_dir = "../"
for index, file in enumerate(files):
os.rename(os.path.join(path, file), os.path.join(dest_dir, ''.join([file.split('.')[0], '_base.go'])))
os.rename(os.path.join(path, file), os.path.join(dest_dir, ''.join(['generated_base_',file.split('.')[0], '.go'])))

View File

@ -3,13 +3,11 @@ package {{packageName}}
{{#operations}}
import (
"net/http"
"github.com/go-macaron/binding"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/infra/log"
apimodels "github.com/grafana/alerting-api/pkg/api"
"github.com/grafana/grafana/pkg/middleware"
)
@ -18,22 +16,11 @@ type {{classname}}Service interface { {{#operation}}
{{nickname}}(*models.ReqContext{{#bodyParams}}, apimodels.{{dataType}}{{/bodyParams}}) response.Response{{/operation}}
}
type {{classname}}Base struct {
log log.Logger
}
func (api *API) Register{{classname}}Endpoints(srv {{classname}}Service) {
api.RouteRegister.Group("", func(group routing.RouteRegister){ {{#operations}}{{#operation}}
group.{{httpMethod}}(toMacaronPath("{{{path}}}"){{#bodyParams}}, binding.Bind(apimodels.{{dataType}}{}){{/bodyParams}}, routing.Wrap(srv.{{nickname}})){{/operation}}{{/operations}}
}, middleware.ReqSignedIn)
}{{#operation}}
func (base {{classname}}Base) {{nickname}}(c *models.ReqContext{{#bodyParams}}, {{paramName}} apimodels.{{dataType}}{{/bodyParams}}) response.Response { {{#pathParams}}
{{paramName}} := c.Params(":{{baseName}}")
base.log.Info("{{nickname}}: ", "{{baseName}}", {{paramName}}){{/pathParams}}{{#bodyParams}}
base.log.Info("{{nickname}}: ", "{{baseName}}", {{paramName}}){{/bodyParams}}
return response.Error(http.StatusNotImplemented, "", nil)
}{{/operation}}{{/operations}}
{{/operation}}{{/operations}}

View File

@ -1,6 +1,6 @@
/*Package api contains base API implementation of unified alerting
*
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*
* Need to remove unused imports.
*Do not manually edit these files, please find ngalert/api/swagger-codegen/ for commands on how to generate them.
*/