Alerting: Start splitting apart ngalert/api package. (#102075)
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
ngalertapi "github.com/grafana/grafana/pkg/services/ngalert/api"
|
||||
ngalertapi "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
apiprometheus "github.com/grafana/grafana/pkg/services/ngalert/api/prometheus"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/backtesting"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
@@ -64,7 +65,7 @@ type API struct {
|
||||
DataProxy *datasourceproxy.DataSourceProxyService
|
||||
MultiOrgAlertmanager *notifier.MultiOrgAlertmanager
|
||||
StateManager *state.Manager
|
||||
Scheduler StatusReader
|
||||
Scheduler apiprometheus.StatusReader
|
||||
AccessControl ac.AccessControl
|
||||
Policies *provisioning.NotificationPolicyService
|
||||
ReceiverService *notifier.ReceiverService
|
||||
@@ -119,7 +120,7 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
|
||||
api.RegisterPrometheusApiEndpoints(NewForkingProm(
|
||||
api.DatasourceCache,
|
||||
NewLotexProm(proxy, logger),
|
||||
&PrometheusSrv{log: logger, manager: api.StateManager, status: api.Scheduler, store: api.RuleStore, authz: ruleAuthzService},
|
||||
apiprometheus.NewPrometheusSrv(logger, api.StateManager, api.Scheduler, api.RuleStore, ruleAuthzService),
|
||||
), m)
|
||||
// Register endpoints for proxying to Cortex Ruler-compatible backends.
|
||||
api.RegisterRulerApiEndpoints(NewForkingRuler(
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
|
||||
@@ -32,6 +32,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/prometheus"
|
||||
)
|
||||
|
||||
func Test_FormatValues(t *testing.T) {
|
||||
@@ -79,7 +81,7 @@ func Test_FormatValues(t *testing.T) {
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.expected, formatValues(tt.alertState))
|
||||
require.Equal(t, tt.expected, FormatValues(tt.alertState))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -503,13 +505,13 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
rules := gen.With(gen.WithGroupKey(groupKey), gen.WithUniqueGroupIndex()).GenerateManyRef(5, 10)
|
||||
ruleStore.PutRule(context.Background(), rules...)
|
||||
|
||||
api := PrometheusSrv{
|
||||
log: log.NewNopLogger(),
|
||||
manager: fakeAIM,
|
||||
status: fakeSch,
|
||||
store: ruleStore,
|
||||
authz: &fakeRuleAccessControlService{},
|
||||
}
|
||||
api := NewPrometheusSrv(
|
||||
log.NewNopLogger(),
|
||||
fakeAIM,
|
||||
fakeSch,
|
||||
ruleStore,
|
||||
&fakeRuleAccessControlService{},
|
||||
)
|
||||
|
||||
response := api.RouteGetRuleStatuses(c)
|
||||
require.Equal(t, http.StatusOK, response.Status())
|
||||
@@ -565,13 +567,13 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
ruleStore.PutRule(context.Background(), rulesInGroup2...)
|
||||
ruleStore.PutRule(context.Background(), rulesInGroup3...)
|
||||
|
||||
api := PrometheusSrv{
|
||||
log: log.NewNopLogger(),
|
||||
manager: fakeAIM,
|
||||
status: newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
store: ruleStore,
|
||||
authz: accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
}
|
||||
api := NewPrometheusSrv(
|
||||
log.NewNopLogger(),
|
||||
fakeAIM,
|
||||
newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
ruleStore,
|
||||
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
)
|
||||
|
||||
permissions := createPermissionsForRules(slices.Concat(rulesInGroup1, rulesInGroup2, rulesInGroup3), orgID)
|
||||
user := &user.SignedInUser{
|
||||
@@ -663,7 +665,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
require.NoError(t, json.Unmarshal(resp.Body(), result))
|
||||
|
||||
require.Len(t, result.Data.RuleGroups, 1)
|
||||
folder, err := api.store.GetNamespaceByUID(context.Background(), "folder-1", orgID, user)
|
||||
folder, err := ruleStore.GetNamespaceByUID(context.Background(), "folder-1", orgID, user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, folder.Fullpath, result.Data.RuleGroups[0].File)
|
||||
require.Equal(t, "rule-group-3", result.Data.RuleGroups[0].Name)
|
||||
@@ -691,13 +693,13 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
ruleStore.PutRule(context.Background(), rules...)
|
||||
}
|
||||
|
||||
api := PrometheusSrv{
|
||||
log: log.NewNopLogger(),
|
||||
manager: fakeAIM,
|
||||
status: newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
store: ruleStore,
|
||||
authz: accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
}
|
||||
api := NewPrometheusSrv(
|
||||
log.NewNopLogger(),
|
||||
fakeAIM,
|
||||
newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
ruleStore,
|
||||
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
)
|
||||
|
||||
permissions := createPermissionsForRules(allRules, orgID)
|
||||
user := &user.SignedInUser{
|
||||
@@ -722,7 +724,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
require.Len(t, result.Data.RuleGroups, 9)
|
||||
require.NotZero(t, len(result.Data.Totals))
|
||||
for i := 0; i < 9; i++ {
|
||||
folder, err := api.store.GetNamespaceByUID(context.Background(), fmt.Sprintf("namespace_%d", i/9), orgID, user)
|
||||
folder, err := ruleStore.GetNamespaceByUID(context.Background(), fmt.Sprintf("namespace_%d", i/9), orgID, user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, folder.Fullpath, result.Data.RuleGroups[i].File)
|
||||
require.Equal(t, fmt.Sprintf("rule_group_%d", i), result.Data.RuleGroups[i].Name)
|
||||
@@ -783,7 +785,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
require.Empty(t, result.Data.NextToken)
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
folder, err := api.store.GetNamespaceByUID(context.Background(), fmt.Sprintf("namespace_%d", i/9), orgID, user)
|
||||
folder, err := ruleStore.GetNamespaceByUID(context.Background(), fmt.Sprintf("namespace_%d", i/9), orgID, user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, folder.Fullpath, returnedGroups[i].File)
|
||||
require.Equal(t, fmt.Sprintf("rule_group_%d", i), returnedGroups[i].Name)
|
||||
@@ -805,7 +807,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
require.Len(t, result.Data.Totals, 0)
|
||||
require.NotEmpty(t, result.Data.NextToken)
|
||||
|
||||
folder, err := api.store.GetNamespaceByUID(context.Background(), "namespace_0", orgID, user)
|
||||
folder, err := ruleStore.GetNamespaceByUID(context.Background(), "namespace_0", orgID, user)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, folder.Fullpath, result.Data.RuleGroups[0].File)
|
||||
require.Equal(t, "rule_group_0", result.Data.RuleGroups[0].Name)
|
||||
@@ -835,13 +837,13 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
ruleStore.PutRule(context.Background(), rules...)
|
||||
ruleStore.PutRule(context.Background(), gen.GenerateManyRef(2, 6)...)
|
||||
|
||||
api := PrometheusSrv{
|
||||
log: log.NewNopLogger(),
|
||||
manager: fakeAIM,
|
||||
status: newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
store: ruleStore,
|
||||
authz: accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
}
|
||||
api := NewPrometheusSrv(
|
||||
log.NewNopLogger(),
|
||||
fakeAIM,
|
||||
newFakeSchedulerReader(t).setupStates(fakeAIM),
|
||||
ruleStore,
|
||||
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
|
||||
)
|
||||
|
||||
c := &contextmodel.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &user.SignedInUser{OrgID: orgID, Permissions: createPermissionsForRules(rules, orgID)}}
|
||||
|
||||
@@ -1523,13 +1525,13 @@ func setupAPI(t *testing.T) (*fakes.RuleStore, *fakeAlertInstanceManager, Promet
|
||||
fakeSch := newFakeSchedulerReader(t).setupStates(fakeAIM)
|
||||
fakeAuthz := &fakeRuleAccessControlService{}
|
||||
|
||||
api := PrometheusSrv{
|
||||
log: log.NewNopLogger(),
|
||||
manager: fakeAIM,
|
||||
status: fakeSch,
|
||||
store: fakeStore,
|
||||
authz: fakeAuthz,
|
||||
}
|
||||
api := *NewPrometheusSrv(
|
||||
log.NewNopLogger(),
|
||||
fakeAIM,
|
||||
fakeSch,
|
||||
fakeStore,
|
||||
fakeAuthz,
|
||||
)
|
||||
|
||||
return fakeStore, fakeAIM, api
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/hcl"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
alerting_models "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,7 +23,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
authz "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
apivalidation "github.com/grafana/grafana/pkg/services/ngalert/api/validation"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
@@ -390,7 +394,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *contextmodel.ReqContext, ruleGro
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
|
||||
rules, err := ValidateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace.UID, RuleLimitsFromConfig(srv.cfg, srv.featureManager))
|
||||
rules, err := apivalidation.ValidateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace.UID, apivalidation.RuleLimitsFromConfig(srv.cfg, srv.featureManager))
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
@@ -813,3 +817,10 @@ func (srv RulerSrv) resolveUserIdToNameFn(ctx context.Context) userIDToUserInfoF
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func getPanelIDFromQuery(v url.Values) (int64, error) {
|
||||
if s := strings.TrimSpace(v.Get("panel_id")); s != "" {
|
||||
return strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
|
||||
authz "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
apivalidation "github.com/grafana/grafana/pkg/services/ngalert/api/validation"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
@@ -21,7 +23,7 @@ func (srv RulerSrv) ExportFromPayload(c *contextmodel.ReqContext, ruleGroupConfi
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
|
||||
rulesWithOptionals, err := ValidateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace.UID, RuleLimitsFromConfig(srv.cfg, srv.featureManager))
|
||||
rulesWithOptionals, err := apivalidation.ValidateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace.UID, apivalidation.RuleLimitsFromConfig(srv.cfg, srv.featureManager))
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
folder2 "github.com/grafana/grafana/pkg/services/folder"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
|
||||
@@ -14,11 +14,14 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/validation"
|
||||
)
|
||||
|
||||
var allNoData = []apimodels.NoDataState{
|
||||
@@ -187,7 +190,7 @@ func TestValidateCondition(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateCondition(tc.condition, tc.data, false)
|
||||
err := ValidateCondition(tc.condition, tc.data, false)
|
||||
if tc.errorMsg == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
@@ -511,7 +514,7 @@ func TestValidateRuleNode_NoUID(t *testing.T) {
|
||||
lim = *testCase.limits
|
||||
}
|
||||
|
||||
alert, err := validateRuleNode(r, name, interval, orgId, folder.UID, lim)
|
||||
alert, err := ValidateRuleNode(r, name, interval, orgId, folder.UID, lim)
|
||||
require.NoError(t, err)
|
||||
testCase.assert(t, r, alert)
|
||||
})
|
||||
@@ -519,7 +522,7 @@ func TestValidateRuleNode_NoUID(t *testing.T) {
|
||||
|
||||
t.Run("accepts empty group name", func(t *testing.T) {
|
||||
r := validRule()
|
||||
alert, err := validateRuleNode(&r, "", interval, orgId, folder.UID, limits)
|
||||
alert, err := ValidateRuleNode(&r, "", interval, orgId, folder.UID, limits)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", alert.RuleGroup)
|
||||
})
|
||||
@@ -739,7 +742,7 @@ func TestValidateRuleNodeFailures_NoUID(t *testing.T) {
|
||||
lim = *testCase.limits
|
||||
}
|
||||
|
||||
_, err := validateRuleNode(r, "", interval, orgId, folder.UID, lim)
|
||||
_, err := ValidateRuleNode(r, "", interval, orgId, folder.UID, lim)
|
||||
require.Error(t, err)
|
||||
if testCase.expErr != "" {
|
||||
require.ErrorContains(t, err, testCase.expErr)
|
||||
@@ -835,7 +838,7 @@ func TestValidateRuleNode_UID(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
r := testCase.rule()
|
||||
alert, err := validateRuleNode(r, name, interval, orgId, folder.UID, limits)
|
||||
alert, err := ValidateRuleNode(r, name, interval, orgId, folder.UID, limits)
|
||||
require.NoError(t, err)
|
||||
testCase.assert(t, r, alert)
|
||||
})
|
||||
@@ -843,7 +846,7 @@ func TestValidateRuleNode_UID(t *testing.T) {
|
||||
|
||||
t.Run("accepts empty group name", func(t *testing.T) {
|
||||
r := validRule()
|
||||
alert, err := validateRuleNode(&r, "", interval, orgId, folder.UID, limits)
|
||||
alert, err := ValidateRuleNode(&r, "", interval, orgId, folder.UID, limits)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", alert.RuleGroup)
|
||||
})
|
||||
@@ -939,7 +942,7 @@ func TestValidateRuleNodeFailures_UID(t *testing.T) {
|
||||
interval = *testCase.interval
|
||||
}
|
||||
|
||||
_, err := validateRuleNode(r, "", interval, orgId, folder.UID, limits)
|
||||
_, err := ValidateRuleNode(r, "", interval, orgId, folder.UID, limits)
|
||||
require.Error(t, err)
|
||||
if testCase.assert != nil {
|
||||
testCase.assert(t, r, err)
|
||||
@@ -973,7 +976,7 @@ func TestValidateRuleNodeIntervalFailures(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
r := validRule()
|
||||
_, err := validateRuleNode(&r, util.GenerateShortUID(), testCase.interval, rand.Int63(), randFolder().UID, limits)
|
||||
_, err := ValidateRuleNode(&r, util.GenerateShortUID(), testCase.interval, rand.Int63(), randFolder().UID, limits)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
@@ -1064,7 +1067,7 @@ func TestValidateRuleNodeNotificationSettings(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := validRule()
|
||||
r.GrafanaManagedAlert.NotificationSettings = AlertRuleNotificationSettingsFromNotificationSettings([]models.NotificationSettings{tt.notificationSettings})
|
||||
_, err := validateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
_, err := ValidateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
|
||||
if tt.expErrorContains != "" {
|
||||
require.Error(t, err)
|
||||
@@ -1099,7 +1102,7 @@ func TestValidateRuleNodeEditorSettings(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := validRule()
|
||||
r.GrafanaManagedAlert.Metadata = AlertRuleMetadataFromModelMetadata(models.AlertRuleMetadata{EditorSettings: tt.editorSettings})
|
||||
newRule, err := validateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
newRule, err := ValidateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.editorSettings, newRule.Metadata.EditorSettings)
|
||||
})
|
||||
@@ -1116,7 +1119,7 @@ func TestValidateRuleNodeReservedLabels(t *testing.T) {
|
||||
r.ApiRuleNode.Labels = map[string]string{
|
||||
label: "true",
|
||||
}
|
||||
_, err := validateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
_, err := ValidateRuleNode(&r, util.GenerateShortUID(), cfg.BaseInterval*time.Duration(rand.Int63n(10)+1), rand.Int63(), randFolder().UID, limits)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, label)
|
||||
})
|
||||
|
||||
@@ -25,7 +25,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
apivalidation "github.com/grafana/grafana/pkg/services/ngalert/api/validation"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/backtesting"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@@ -61,13 +63,13 @@ func (srv TestingApiSrv) RouteTestGrafanaRuleConfig(c *contextmodel.ReqContext,
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||
}
|
||||
rule, err := validateRuleNode(
|
||||
rule, err := apivalidation.ValidateRuleNode(
|
||||
&body.Rule,
|
||||
body.RuleGroup,
|
||||
srv.cfg.BaseInterval,
|
||||
c.SignedInUser.GetOrgID(),
|
||||
folder.UID,
|
||||
RuleLimitsFromConfig(srv.cfg, srv.featureManager),
|
||||
apivalidation.RuleLimitsFromConfig(srv.cfg, srv.featureManager),
|
||||
)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
@@ -239,7 +241,7 @@ func (srv TestingApiSrv) BacktestAlertRule(c *contextmodel.ReqContext, cmd apimo
|
||||
return ErrResp(400, nil, "Bad For interval")
|
||||
}
|
||||
|
||||
intervalSeconds, err := validateInterval(time.Duration(cmd.Interval), srv.cfg.BaseInterval)
|
||||
intervalSeconds, err := apivalidation.ValidateInterval(time.Duration(cmd.Interval), srv.cfg.BaseInterval)
|
||||
if err != nil {
|
||||
return ErrResp(400, err, "")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval/eval_mocks"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
receiversTesting "github.com/grafana/alerting/receivers/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
apicompat "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
@@ -38,7 +39,7 @@ func TestContactPointFromContactPointExports(t *testing.T) {
|
||||
return string(d)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
ex, err := ReceiverExportFromEmbeddedContactPoint(emb)
|
||||
ex, err := apicompat.ReceiverExportFromEmbeddedContactPoint(emb)
|
||||
require.NoError(t, err)
|
||||
export = append(export, ex)
|
||||
}
|
||||
|
||||
@@ -5,16 +5,18 @@ import (
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
|
||||
apiprometheus "github.com/grafana/grafana/pkg/services/ngalert/api/prometheus"
|
||||
)
|
||||
|
||||
type PrometheusApiHandler struct {
|
||||
ProxySvc *LotexProm
|
||||
GrafanaSvc *PrometheusSrv
|
||||
GrafanaSvc *apiprometheus.PrometheusSrv
|
||||
DatasourceCache datasources.CacheService
|
||||
}
|
||||
|
||||
// NewForkingProm implements a set of routes that proxy to various Prometheus-compatible backends.
|
||||
func NewForkingProm(datasourceCache datasources.CacheService, proxy *LotexProm, grafana *PrometheusSrv) *PrometheusApiHandler {
|
||||
func NewForkingProm(datasourceCache datasources.CacheService, proxy *LotexProm, grafana *apiprometheus.PrometheusSrv) *PrometheusApiHandler {
|
||||
return &PrometheusApiHandler{
|
||||
ProxySvc: proxy,
|
||||
GrafanaSvc: grafana,
|
||||
|
||||
+26
-5
@@ -16,8 +16,10 @@ import (
|
||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@@ -25,6 +27,15 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type RuleStoreReader interface {
|
||||
GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error)
|
||||
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
|
||||
}
|
||||
|
||||
type RuleGroupAccessControlService interface {
|
||||
HasAccessToRuleGroup(ctx context.Context, user identity.Requester, rules ngmodels.RulesGroup) (bool, error)
|
||||
}
|
||||
|
||||
type StatusReader interface {
|
||||
Status(key ngmodels.AlertRuleKey) (ngmodels.RuleStatus, bool)
|
||||
}
|
||||
@@ -33,8 +44,18 @@ type PrometheusSrv struct {
|
||||
log log.Logger
|
||||
manager state.AlertInstanceManager
|
||||
status StatusReader
|
||||
store RuleStore
|
||||
authz RuleAccessControlService
|
||||
store RuleStoreReader
|
||||
authz RuleGroupAccessControlService
|
||||
}
|
||||
|
||||
func NewPrometheusSrv(log log.Logger, manager state.AlertInstanceManager, status StatusReader, store RuleStoreReader, authz RuleGroupAccessControlService) *PrometheusSrv {
|
||||
return &PrometheusSrv{
|
||||
log: log,
|
||||
manager: manager,
|
||||
status: status,
|
||||
store: store,
|
||||
authz: authz,
|
||||
}
|
||||
}
|
||||
|
||||
const queryIncludeInternalLabels = "includeInternalLabels"
|
||||
@@ -99,7 +120,7 @@ func PrepareAlertStatuses(manager state.AlertInstanceManager, opts AlertStatuses
|
||||
valString := ""
|
||||
|
||||
if alertState.State == eval.Alerting || alertState.State == eval.Pending {
|
||||
valString = formatValues(alertState)
|
||||
valString = FormatValues(alertState)
|
||||
}
|
||||
|
||||
alertResponse.Data.Alerts = append(alertResponse.Data.Alerts, &apimodels.Alert{
|
||||
@@ -117,7 +138,7 @@ func PrepareAlertStatuses(manager state.AlertInstanceManager, opts AlertStatuses
|
||||
return alertResponse
|
||||
}
|
||||
|
||||
func formatValues(alertState *state.State) string {
|
||||
func FormatValues(alertState *state.State) string {
|
||||
var fv string
|
||||
values := alertState.GetLastEvaluationValuesForCondition()
|
||||
|
||||
@@ -546,7 +567,7 @@ func toRuleGroup(log log.Logger, manager state.AlertInstanceManager, sr StatusRe
|
||||
activeAt := alertState.StartsAt
|
||||
valString := ""
|
||||
if alertState.State == eval.Alerting || alertState.State == eval.Pending {
|
||||
valString = formatValues(alertState)
|
||||
valString = FormatValues(alertState)
|
||||
}
|
||||
stateKey := strings.ToLower(alertState.State.String())
|
||||
totals[stateKey] += 1
|
||||
+10
-9
@@ -1,4 +1,4 @@
|
||||
package api
|
||||
package validation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
. "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
@@ -32,15 +33,15 @@ func RuleLimitsFromConfig(cfg *setting.UnifiedAlertingSettings, toggles featurem
|
||||
}
|
||||
}
|
||||
|
||||
// validateRuleNode validates API model (definitions.PostableExtendedRuleNode) and converts it to models.AlertRule
|
||||
func validateRuleNode(
|
||||
// ValidateRuleNode validates API model (definitions.PostableExtendedRuleNode) and converts it to models.AlertRule
|
||||
func ValidateRuleNode(
|
||||
ruleNode *apimodels.PostableExtendedRuleNode,
|
||||
groupName string,
|
||||
interval time.Duration,
|
||||
orgId int64,
|
||||
namespaceUID string,
|
||||
limits RuleLimits) (*ngmodels.AlertRule, error) {
|
||||
intervalSeconds, err := validateInterval(interval, limits.BaseInterval)
|
||||
intervalSeconds, err := ValidateInterval(interval, limits.BaseInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,7 +133,7 @@ func validateAlertingRuleFields(in *apimodels.PostableExtendedRuleNode, newRule
|
||||
}
|
||||
newRule.ExecErrState = errorState
|
||||
|
||||
err = validateCondition(in.GrafanaManagedAlert.Condition, in.GrafanaManagedAlert.Data, canPatch)
|
||||
err = ValidateCondition(in.GrafanaManagedAlert.Condition, in.GrafanaManagedAlert.Data, canPatch)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRule{}, err
|
||||
}
|
||||
@@ -166,7 +167,7 @@ func validateRecordingRuleFields(in *apimodels.PostableExtendedRuleNode, newRule
|
||||
return ngmodels.AlertRule{}, fmt.Errorf("%w: recording rules cannot be created on this instance", ngmodels.ErrAlertRuleFailedValidation)
|
||||
}
|
||||
|
||||
err := validateCondition(in.GrafanaManagedAlert.Record.From, in.GrafanaManagedAlert.Data, canPatch)
|
||||
err := ValidateCondition(in.GrafanaManagedAlert.Record.From, in.GrafanaManagedAlert.Data, canPatch)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRule{}, fmt.Errorf("%w: %s", ngmodels.ErrAlertRuleFailedValidation, err.Error())
|
||||
}
|
||||
@@ -198,7 +199,7 @@ func validateLabels(l map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCondition(condition string, queries []apimodels.AlertQuery, canPatch bool) error {
|
||||
func ValidateCondition(condition string, queries []apimodels.AlertQuery, canPatch bool) error {
|
||||
if canPatch {
|
||||
// Patch requests may leave both query and condition blank. If a request supplies one, it must supply the other.
|
||||
if len(queries) == 0 && condition == "" {
|
||||
@@ -240,7 +241,7 @@ func validateCondition(condition string, queries []apimodels.AlertQuery, canPatc
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateInterval(interval, baseInterval time.Duration) (int64, error) {
|
||||
func ValidateInterval(interval, baseInterval time.Duration) (int64, error) {
|
||||
intervalSeconds := int64(interval.Seconds())
|
||||
|
||||
baseIntervalSeconds := int64(baseInterval.Seconds())
|
||||
@@ -302,7 +303,7 @@ func ValidateRuleGroup(
|
||||
result := make([]*ngmodels.AlertRuleWithOptionals, 0, len(ruleGroupConfig.Rules))
|
||||
uids := make(map[string]int, cap(result))
|
||||
for idx := range ruleGroupConfig.Rules {
|
||||
rule, err := validateRuleNode(&ruleGroupConfig.Rules[idx], ruleGroupConfig.Name, interval, orgId, namespaceUID, limits)
|
||||
rule, err := ValidateRuleNode(&ruleGroupConfig.Rules[idx], ruleGroupConfig.Name, interval, orgId, namespaceUID, limits)
|
||||
// TODO do not stop on the first failure but return all failures
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid rule specification at index [%d]: %w", idx, err)
|
||||
Reference in New Issue
Block a user