mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: update rule versions on folder move (#88376)
* Alerting: update rule versions on folder move (#88361) * Add tracing to folder.Move and folder.Update
This commit is contained in:
committed by
GitHub
parent
8044cb50f1
commit
b2eeb0dd6e
@@ -834,7 +834,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||||||
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
||||||
|
|
||||||
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()),
|
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()),
|
||||||
dashboardStore, folderStore, db.InitTestDB(t), features, supportbundlestest.NewFakeBundleService(), nil)
|
dashboardStore, folderStore, db.InitTestDB(t), features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
if dashboardService == nil {
|
if dashboardService == nil {
|
||||||
dashboardService, err = service.ProvideDashboardServiceImpl(
|
dashboardService, err = service.ProvideDashboardServiceImpl(
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog
|
|||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sc.db.DB())
|
folderStore := folderimpl.ProvideDashboardFolderStore(sc.db.DB())
|
||||||
|
|
||||||
ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())
|
ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())
|
||||||
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, sc.db.DB(), features, supportbundlestest.NewFakeBundleService(), nil)
|
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, sc.db.DB(), features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
actionSets := resourcepermissions.NewActionSetService(features)
|
actionSets := resourcepermissions.NewActionSetService(features)
|
||||||
|
|||||||
@@ -71,10 +71,13 @@ type DataSourceCreated struct {
|
|||||||
OrgID int64 `json:"org_id"`
|
OrgID int64 `json:"org_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FolderTitleUpdated struct {
|
// FolderFullPathUpdated is emitted when the full path of the folder(s) is updated.
|
||||||
|
// For example, when the folder is renamed or moved to another folder.
|
||||||
|
// It does not contain the full path of the folders because calculating
|
||||||
|
// it requires more resources and not needed in the event at the moment.
|
||||||
|
type FolderFullPathUpdated struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
Title string `json:"name"`
|
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
UID string `json:"uid"`
|
UIDs []string `json:"uids"`
|
||||||
OrgID int64 `json:"org_id"`
|
OrgID int64 `json:"org_id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ func TestIntegrationAnnotationListingWithInheritedRBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
||||||
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(sql), sql, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(sql), sql, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
cfg.AnnotationMaximumTagsLength = 60
|
cfg.AnnotationMaximumTagsLength = 60
|
||||||
|
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ func TestIntegrationDashboardInheritedFolderRBAC(t *testing.T) {
|
|||||||
guardian.New = origNewGuardian
|
guardian.New = origNewGuardian
|
||||||
})
|
})
|
||||||
|
|
||||||
folderSvc := folderimpl.ProvideService(mock.New(), bus.ProvideBus(tracer), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(sqlStore.DB()), sqlStore.DB(), features, supportbundlestest.NewFakeBundleService(), nil)
|
folderSvc := folderimpl.ProvideService(mock.New(), bus.ProvideBus(tracer), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(sqlStore.DB()), sqlStore.DB(), features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
parentUID := ""
|
parentUID := ""
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
|||||||
|
|
||||||
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||||
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
user := &user.SignedInUser{
|
user := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
@@ -948,7 +948,7 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
|||||||
|
|
||||||
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
ac := acimpl.ProvideAccessControl(features, zanzana.NewNoopClient())
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||||
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
user := &user.SignedInUser{
|
user := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/dskit/concurrency"
|
"github.com/grafana/dskit/concurrency"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
@@ -20,6 +22,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/events"
|
"github.com/grafana/grafana/pkg/events"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||||
@@ -44,12 +47,14 @@ type Service struct {
|
|||||||
dashboardFolderStore folder.FolderStore
|
dashboardFolderStore folder.FolderStore
|
||||||
features featuremgmt.FeatureToggles
|
features featuremgmt.FeatureToggles
|
||||||
accessControl accesscontrol.AccessControl
|
accessControl accesscontrol.AccessControl
|
||||||
// bus is currently used to publish event in case of title change
|
// bus is currently used to publish event in case of folder full path change.
|
||||||
|
// For example when a folder is moved to another folder or when a folder is renamed.
|
||||||
bus bus.Bus
|
bus bus.Bus
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
registry map[string]folder.RegistryService
|
registry map[string]folder.RegistryService
|
||||||
metrics *foldersMetrics
|
metrics *foldersMetrics
|
||||||
|
tracer tracing.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(
|
func ProvideService(
|
||||||
@@ -61,6 +66,7 @@ func ProvideService(
|
|||||||
features featuremgmt.FeatureToggles,
|
features featuremgmt.FeatureToggles,
|
||||||
supportBundles supportbundles.Service,
|
supportBundles supportbundles.Service,
|
||||||
r prometheus.Registerer,
|
r prometheus.Registerer,
|
||||||
|
tracer tracing.Tracer,
|
||||||
) folder.Service {
|
) folder.Service {
|
||||||
store := ProvideStore(db)
|
store := ProvideStore(db)
|
||||||
srv := &Service{
|
srv := &Service{
|
||||||
@@ -74,6 +80,7 @@ func ProvideService(
|
|||||||
db: db,
|
db: db,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(r),
|
metrics: newFoldersMetrics(r),
|
||||||
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
srv.DBMigration(db)
|
srv.DBMigration(db)
|
||||||
|
|
||||||
@@ -655,6 +662,9 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
|
ctx, span := s.tracer.Start(ctx, "folder.Update")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if cmd.SignedInUser == nil {
|
if cmd.SignedInUser == nil {
|
||||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||||
}
|
}
|
||||||
@@ -679,14 +689,8 @@ func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (
|
|||||||
|
|
||||||
if cmd.NewTitle != nil {
|
if cmd.NewTitle != nil {
|
||||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
|
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
|
||||||
if err := s.bus.Publish(ctx, &events.FolderTitleUpdated{
|
|
||||||
Timestamp: foldr.Updated,
|
if err := s.publishFolderFullPathUpdatedEvent(ctx, foldr.Updated, cmd.OrgID, cmd.UID); err != nil {
|
||||||
Title: foldr.Title,
|
|
||||||
ID: dashFolder.ID, // nolint:staticcheck
|
|
||||||
UID: dashFolder.UID,
|
|
||||||
OrgID: cmd.OrgID,
|
|
||||||
}); err != nil {
|
|
||||||
s.log.ErrorContext(ctx, "failed to publish FolderTitleUpdated event", "folder", foldr.Title, "user", cmd.SignedInUser.GetID(), "error", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -873,6 +877,9 @@ func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||||
|
ctx, span := s.tracer.Start(ctx, "folder.Move")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if cmd.SignedInUser == nil {
|
if cmd.SignedInUser == nil {
|
||||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||||
}
|
}
|
||||||
@@ -947,6 +954,10 @@ func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*fol
|
|||||||
return folder.ErrInternal.Errorf("failed to move legacy folder: %w", err)
|
return folder.ErrInternal.Errorf("failed to move legacy folder: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.publishFolderFullPathUpdatedEvent(ctx, f.Updated, cmd.OrgID, cmd.UID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -954,6 +965,36 @@ func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*fol
|
|||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) publishFolderFullPathUpdatedEvent(ctx context.Context, timestamp time.Time, orgID int64, folderUID string) error {
|
||||||
|
ctx, span := s.tracer.Start(ctx, "folder.publishFolderFullPathUpdatedEvent")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
descFolders, err := s.store.GetDescendants(ctx, orgID, folderUID)
|
||||||
|
if err != nil {
|
||||||
|
s.log.ErrorContext(ctx, "Failed to get descendants of the folder", "folderUID", folderUID, "orgID", orgID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uids := make([]string, 0, len(descFolders)+1)
|
||||||
|
uids = append(uids, folderUID)
|
||||||
|
for _, f := range descFolders {
|
||||||
|
uids = append(uids, f.UID)
|
||||||
|
}
|
||||||
|
span.AddEvent("found folder descendants", trace.WithAttributes(
|
||||||
|
attribute.Int64("folders", int64(len(uids))),
|
||||||
|
))
|
||||||
|
|
||||||
|
if err := s.bus.Publish(ctx, &events.FolderFullPathUpdated{
|
||||||
|
Timestamp: timestamp,
|
||||||
|
UIDs: uids,
|
||||||
|
OrgID: orgID,
|
||||||
|
}); err != nil {
|
||||||
|
s.log.ErrorContext(ctx, "Failed to publish FolderFullPathUpdated event", "folderUID", folderUID, "orgID", orgID, "descendantsUIDs", uids, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) canMove(ctx context.Context, cmd *folder.MoveFolderCommand) (bool, error) {
|
func (s *Service) canMove(ctx context.Context, cmd *folder.MoveFolderCommand) (bool, error) {
|
||||||
// Check that the user is allowed to move the folder to the destination folder
|
// Check that the user is allowed to move the folder to the destination folder
|
||||||
var evaluator accesscontrol.Evaluator
|
var evaluator accesscontrol.Evaluator
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func TestIntegrationProvideFolderService(t *testing.T) {
|
|||||||
t.Run("should register scope resolvers", func(t *testing.T) {
|
t.Run("should register scope resolvers", func(t *testing.T) {
|
||||||
ac := acmock.New()
|
ac := acmock.New()
|
||||||
db := db.InitTestDB(t)
|
db := db.InitTestDB(t)
|
||||||
ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil)
|
ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), nil, nil, db, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 3)
|
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 3)
|
||||||
})
|
})
|
||||||
@@ -100,6 +100,7 @@ func TestIntegrationFolderService(t *testing.T) {
|
|||||||
accessControl: acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()),
|
accessControl: acimpl.ProvideAccessControl(features, zanzana.NewNoopClient()),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, service.RegisterService(alertingStore))
|
require.NoError(t, service.RegisterService(alertingStore))
|
||||||
@@ -440,6 +441,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
signedInUser := user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{
|
signedInUser := user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{
|
||||||
@@ -553,6 +555,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||||||
db: db,
|
db: db,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
origNewGuardian := guardian.New
|
origNewGuardian := guardian.New
|
||||||
@@ -630,6 +633,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||||||
db: db,
|
db: db,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@@ -805,6 +809,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) {
|
|||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
|
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
|
||||||
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
|
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
t.Run("create folder", func(t *testing.T) {
|
t.Run("create folder", func(t *testing.T) {
|
||||||
nestedFolderStore.ExpectedFolder = &folder.Folder{ParentUID: util.GenerateShortUID()}
|
nestedFolderStore.ExpectedFolder = &folder.Folder{ParentUID: util.GenerateShortUID()}
|
||||||
@@ -841,6 +846,7 @@ func TestFolderServiceDualWrite(t *testing.T) {
|
|||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
|
features: featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders),
|
||||||
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
|
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
bus: bus.ProvideBus(tracing.InitializeTracerForTest()),
|
bus: bus.ProvideBus(tracing.InitializeTracerForTest()),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1475,6 +1481,7 @@ func TestIntegrationNestedFolderSharedWithMe(t *testing.T) {
|
|||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardPermissions := acmock.NewMockedPermissionsService()
|
dashboardPermissions := acmock.NewMockedPermissionsService()
|
||||||
@@ -1977,6 +1984,7 @@ func TestFolderServiceGetFolders(t *testing.T) {
|
|||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
signedInAdminUser := user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{
|
signedInAdminUser := user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{
|
||||||
@@ -2063,6 +2071,7 @@ func TestGetChildrenFilterByPermission(t *testing.T) {
|
|||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
origGuardian := guardian.New
|
origGuardian := guardian.New
|
||||||
@@ -2523,6 +2532,7 @@ func setup(t *testing.T, dashStore dashboards.Store, dashboardFolderStore folder
|
|||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
db: db,
|
db: db,
|
||||||
metrics: newFoldersMetrics(nil),
|
metrics: newFoldersMetrics(nil),
|
||||||
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
|
||||||
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
t.Logf("Creating folder with title and UID %q", title)
|
t.Logf("Creating folder with title and UID %q", title)
|
||||||
ctx := identity.WithRequester(context.Background(), &sc.user)
|
ctx := identity.WithRequester(context.Background(), &sc.user)
|
||||||
folder, err := s.Create(ctx, &folder.CreateFolderCommand{
|
folder, err := s.Create(ctx, &folder.CreateFolderCommand{
|
||||||
@@ -463,7 +463,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
features: featuremgmt.WithFeatures(),
|
features: featuremgmt.WithFeatures(),
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracer), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil),
|
folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracer), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// deliberate difference between signed in user and user in db to make it crystal clear
|
// deliberate difference between signed in user and user in db to make it crystal clear
|
||||||
|
|||||||
@@ -753,7 +753,7 @@ func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder
|
|||||||
dashboardStore, err := database.ProvideDashboardStore(sc.replStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService)
|
dashboardStore, err := database.ProvideDashboardStore(sc.replStore, cfg, features, tagimpl.ProvideService(sc.sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
|
||||||
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sc.sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
t.Logf("Creating folder with title and UID %q", title)
|
t.Logf("Creating folder with title and UID %q", title)
|
||||||
ctx := identity.WithRequester(context.Background(), sc.user)
|
ctx := identity.WithRequester(context.Background(), sc.user)
|
||||||
@@ -836,7 +836,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
dashboardStore, err := database.ProvideDashboardStore(replStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
dashboardStore, err := database.ProvideDashboardStore(replStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
features := featuremgmt.WithFeatures()
|
features := featuremgmt.WithFeatures()
|
||||||
folderService := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderService := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService, featuremgmt.WithFeatures(), ac)
|
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService, featuremgmt.WithFeatures(), ac)
|
||||||
service := LibraryPanelService{
|
service := LibraryPanelService{
|
||||||
|
|||||||
@@ -1819,7 +1819,7 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||||
folderService := folderimpl.ProvideService(actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil)
|
folderService := folderimpl.ProvideService(actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
store := store.DBstore{
|
store := store.DBstore{
|
||||||
Logger: log,
|
Logger: log,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ type RuleStore interface {
|
|||||||
UpdateAlertRules(ctx context.Context, rule []ngmodels.UpdateRule) error
|
UpdateAlertRules(ctx context.Context, rule []ngmodels.UpdateRule) error
|
||||||
DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUID ...string) error
|
DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUID ...string) error
|
||||||
|
|
||||||
// IncreaseVersionForAllRulesInNamespace Increases version for all rules that have specified namespace. Returns all rules that belong to the namespace
|
// IncreaseVersionForAllRulesInNamespaces Increases version for all rules that have specified namespace uids
|
||||||
IncreaseVersionForAllRulesInNamespace(ctx context.Context, orgID int64, namespaceUID string) ([]ngmodels.AlertRuleKeyWithVersion, error)
|
IncreaseVersionForAllRulesInNamespaces(ctx context.Context, orgID int64, namespaceUIDs []string) ([]ngmodels.AlertRuleKeyWithVersion, error)
|
||||||
|
|
||||||
accesscontrol.RuleUIDToNamespaceStore
|
accesscontrol.RuleUIDToNamespaceStore
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -487,15 +487,16 @@ func (ng *AlertNG) init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToFolderChanges(logger log.Logger, bus bus.Bus, dbStore api.RuleStore) {
|
func subscribeToFolderChanges(logger log.Logger, bus bus.Bus, dbStore api.RuleStore) {
|
||||||
// if folder title is changed, we update all alert rules in that folder to make sure that all peers (in HA mode) will update folder title and
|
// if full path to the folder is changed, we update all alert rules in that folder to make sure that all peers (in HA mode) will update folder title and
|
||||||
// clean up the current state
|
// clean up the current state
|
||||||
bus.AddEventListener(func(ctx context.Context, evt *events.FolderTitleUpdated) error {
|
bus.AddEventListener(func(ctx context.Context, evt *events.FolderFullPathUpdated) error {
|
||||||
logger.Info("Got folder title updated event. updating rules in the folder", "folderUID", evt.UID)
|
logger.Info("Got folder full path updated event. updating rules in the folders", "folderUIDs", evt.UIDs)
|
||||||
_, err := dbStore.IncreaseVersionForAllRulesInNamespace(ctx, evt.OrgID, evt.UID)
|
updatedKeys, err := dbStore.IncreaseVersionForAllRulesInNamespaces(ctx, evt.OrgID, evt.UIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to update alert rules in the folder after its title was changed", "error", err, "folderUID", evt.UID, "folder", evt.Title)
|
logger.Error("Failed to update alert rules in the folders after their full paths were changed", "error", err, "folderUIDs", evt.UIDs, "orgID", evt.OrgID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
logger.Info("Updated version for alert rules", "keys", updatedKeys)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
@@ -25,37 +26,52 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_subscribeToFolderChanges(t *testing.T) {
|
func Test_subscribeToFolderChanges(t *testing.T) {
|
||||||
|
getRecordedCommand := func(ruleStore *fakes.RuleStore) []fakes.GenericRecordedQuery {
|
||||||
|
results := ruleStore.GetRecordedCommands(func(cmd any) (any, bool) {
|
||||||
|
c, ok := cmd.(fakes.GenericRecordedQuery)
|
||||||
|
if !ok || c.Name != "IncreaseVersionForAllRulesInNamespaces" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return c, ok
|
||||||
|
})
|
||||||
|
var result []fakes.GenericRecordedQuery
|
||||||
|
for _, cmd := range results {
|
||||||
|
result = append(result, cmd.(fakes.GenericRecordedQuery))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
folder := &folder.Folder{
|
folder1 := &folder.Folder{
|
||||||
|
UID: util.GenerateShortUID(),
|
||||||
|
Title: "Folder" + util.GenerateShortUID(),
|
||||||
|
}
|
||||||
|
folder2 := &folder.Folder{
|
||||||
UID: util.GenerateShortUID(),
|
UID: util.GenerateShortUID(),
|
||||||
Title: "Folder" + util.GenerateShortUID(),
|
Title: "Folder" + util.GenerateShortUID(),
|
||||||
}
|
}
|
||||||
gen := models.RuleGen
|
gen := models.RuleGen
|
||||||
rules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder)).GenerateManyRef(5)
|
rules := gen.With(gen.WithOrgID(orgID), gen.WithNamespace(folder1)).GenerateManyRef(5)
|
||||||
|
|
||||||
bus := bus.ProvideBus(tracing.InitializeTracerForTest())
|
bus := bus.ProvideBus(tracing.InitializeTracerForTest())
|
||||||
db := fakes.NewRuleStore(t)
|
db := fakes.NewRuleStore(t)
|
||||||
db.Folders[orgID] = append(db.Folders[orgID], folder)
|
db.Folders[orgID] = append(db.Folders[orgID], folder1)
|
||||||
db.PutRule(context.Background(), rules...)
|
db.PutRule(context.Background(), rules...)
|
||||||
|
|
||||||
subscribeToFolderChanges(log.New("test"), bus, db)
|
subscribeToFolderChanges(log.New("test"), bus, db)
|
||||||
|
|
||||||
err := bus.Publish(context.Background(), &events.FolderTitleUpdated{
|
err := bus.Publish(context.Background(), &events.FolderFullPathUpdated{
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Title: "Folder" + util.GenerateShortUID(),
|
UIDs: []string{folder1.UID, folder2.UID},
|
||||||
UID: folder.UID,
|
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Eventuallyf(t, func() bool {
|
require.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||||
return len(db.GetRecordedCommands(func(cmd any) (any, bool) {
|
recordedCommands := getRecordedCommand(db)
|
||||||
c, ok := cmd.(fakes.GenericRecordedQuery)
|
require.Len(c, recordedCommands, 1)
|
||||||
if !ok || c.Name != "IncreaseVersionForAllRulesInNamespace" {
|
require.Equal(c, recordedCommands[0].Params[0].(int64), orgID)
|
||||||
return nil, false
|
require.ElementsMatch(c, recordedCommands[0].Params[1].([]string), []string{folder1.UID, folder2.UID})
|
||||||
}
|
|
||||||
return c, true
|
|
||||||
})) > 0
|
|
||||||
}, time.Second, 10*time.Millisecond, "expected to call db store method but nothing was called")
|
}, time.Second, 10*time.Millisecond, "expected to call db store method but nothing was called")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1479,7 +1479,7 @@ func TestProvisiongWithFullpath(t *testing.T) {
|
|||||||
_, dashboardStore := testutil.SetupDashboardService(t, sqlStore, folderStore, cfg)
|
_, dashboardStore := testutil.SetupDashboardService(t, sqlStore, folderStore, cfg)
|
||||||
ac := acmock.New()
|
ac := acmock.New()
|
||||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||||
folderService := folderimpl.ProvideService(ac, inProcBus, dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderService := folderimpl.ProvideService(ac, inProcBus, dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
ruleService := createAlertRuleService(t, folderService)
|
ruleService := createAlertRuleService(t, folderService)
|
||||||
var orgID int64 = 1
|
var orgID int64 = 1
|
||||||
|
|||||||
@@ -75,16 +75,26 @@ func (st DBstore) DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUI
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// IncreaseVersionForAllRulesInNamespace Increases version for all rules that have specified namespace. Returns all rules that belong to the namespace
|
// IncreaseVersionForAllRulesInNamespaces Increases version for all rules that have specified namespace. Returns all rules that belong to the namespaces
|
||||||
func (st DBstore) IncreaseVersionForAllRulesInNamespace(ctx context.Context, orgID int64, namespaceUID string) ([]ngmodels.AlertRuleKeyWithVersion, error) {
|
func (st DBstore) IncreaseVersionForAllRulesInNamespaces(ctx context.Context, orgID int64, namespaceUIDs []string) ([]ngmodels.AlertRuleKeyWithVersion, error) {
|
||||||
var keys []ngmodels.AlertRuleKeyWithVersion
|
var keys []ngmodels.AlertRuleKeyWithVersion
|
||||||
err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||||
now := TimeNow()
|
now := TimeNow()
|
||||||
_, err := sess.Exec("UPDATE alert_rule SET version = version + 1, updated = ? WHERE namespace_uid = ? AND org_id = ?", now, namespaceUID, orgID)
|
namespaceUIDsArgs, in := getINSubQueryArgs(namespaceUIDs)
|
||||||
|
sql := fmt.Sprintf(
|
||||||
|
"UPDATE alert_rule SET version = version + 1, updated = ? WHERE org_id = ? AND namespace_uid IN (%s)",
|
||||||
|
strings.Join(in, ","),
|
||||||
|
)
|
||||||
|
args := make([]interface{}, 0, 3+len(namespaceUIDsArgs))
|
||||||
|
args = append(args, sql, now, orgID)
|
||||||
|
args = append(args, namespaceUIDsArgs...)
|
||||||
|
|
||||||
|
_, err := sess.Exec(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sess.Table(ngmodels.AlertRule{}).Where("namespace_uid = ? AND org_id = ?", namespaceUID, orgID).Find(&keys)
|
|
||||||
|
return sess.Table(ngmodels.AlertRule{}).Where("org_id = ?", orgID).In("namespace_uid", namespaceUIDs).Find(&keys)
|
||||||
})
|
})
|
||||||
return keys, err
|
return keys, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1184,6 +1184,58 @@ func TestIntegrationRuleGroupsCaseSensitive(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncreaseVersionForAllRulesInNamespaces(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
cfg := setting.NewCfg()
|
||||||
|
cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{BaseInterval: time.Duration(rand.Int63n(100)+1) * time.Second}
|
||||||
|
sqlStore := db.InitTestReplDB(t)
|
||||||
|
store := &DBstore{
|
||||||
|
SQLStore: sqlStore,
|
||||||
|
Cfg: cfg.UnifiedAlerting,
|
||||||
|
FolderService: setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()),
|
||||||
|
Logger: &logtest.Fake{},
|
||||||
|
}
|
||||||
|
orgID := int64(1)
|
||||||
|
gen := models.RuleGen
|
||||||
|
gen = gen.With(gen.WithIntervalMatching(store.Cfg.BaseInterval)).With(gen.WithOrgID(orgID))
|
||||||
|
|
||||||
|
alertRules := []*models.AlertRule{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
alertRules = append(alertRules, createRule(t, store, gen))
|
||||||
|
}
|
||||||
|
alertRuleNamespaceUIDs := make([]string, 0, len(alertRules))
|
||||||
|
for _, rule := range alertRules {
|
||||||
|
alertRuleNamespaceUIDs = append(alertRuleNamespaceUIDs, rule.NamespaceUID)
|
||||||
|
}
|
||||||
|
alertRuleInAnotherNamespace := createRule(t, store, gen)
|
||||||
|
|
||||||
|
requireAlertRuleVersion := func(t *testing.T, ruleID int64, orgID int64, expectedVersion int64) {
|
||||||
|
t.Helper()
|
||||||
|
dbrule := &models.AlertRule{}
|
||||||
|
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||||
|
exist, err := sess.Table(models.AlertRule{}).ID(ruleID).Get(dbrule)
|
||||||
|
require.Truef(t, exist, fmt.Sprintf("rule with ID %d does not exist", ruleID))
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedVersion, dbrule.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("should increase version for all rules", func(t *testing.T) {
|
||||||
|
_, err := store.IncreaseVersionForAllRulesInNamespaces(context.Background(), orgID, alertRuleNamespaceUIDs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, rule := range alertRules {
|
||||||
|
requireAlertRuleVersion(t, rule.ID, orgID, rule.Version+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this rule's version should not be changed
|
||||||
|
requireAlertRuleVersion(t, alertRuleInAnotherNamespace.ID, orgID, alertRuleInAnotherNamespace.Version)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// createAlertRule creates an alert rule in the database and returns it.
|
// createAlertRule creates an alert rule in the database and returns it.
|
||||||
// If a generator is not specified, uniqueness of primary key is not guaranteed.
|
// If a generator is not specified, uniqueness of primary key is not guaranteed.
|
||||||
func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerator) *models.AlertRule {
|
func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerator) *models.AlertRule {
|
||||||
|
|||||||
@@ -315,19 +315,24 @@ func (f *RuleStore) UpdateRuleGroup(ctx context.Context, orgID int64, namespaceU
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) IncreaseVersionForAllRulesInNamespace(_ context.Context, orgID int64, namespaceUID string) ([]models.AlertRuleKeyWithVersion, error) {
|
func (f *RuleStore) IncreaseVersionForAllRulesInNamespaces(_ context.Context, orgID int64, namespaceUIDs []string) ([]models.AlertRuleKeyWithVersion, error) {
|
||||||
f.mtx.Lock()
|
f.mtx.Lock()
|
||||||
defer f.mtx.Unlock()
|
defer f.mtx.Unlock()
|
||||||
|
|
||||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||||
Name: "IncreaseVersionForAllRulesInNamespace",
|
Name: "IncreaseVersionForAllRulesInNamespaces",
|
||||||
Params: []any{orgID, namespaceUID},
|
Params: []any{orgID, namespaceUIDs},
|
||||||
})
|
})
|
||||||
|
|
||||||
var result []models.AlertRuleKeyWithVersion
|
var result []models.AlertRuleKeyWithVersion
|
||||||
|
|
||||||
|
namespaceUIDsMap := make(map[string]struct{}, len(namespaceUIDs))
|
||||||
|
for _, namespaceUID := range namespaceUIDs {
|
||||||
|
namespaceUIDsMap[namespaceUID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
for _, rule := range f.Rules[orgID] {
|
for _, rule := range f.Rules[orgID] {
|
||||||
if rule.NamespaceUID == namespaceUID && rule.OrgID == orgID {
|
if _, ok := namespaceUIDsMap[rule.NamespaceUID]; ok && rule.OrgID == orgID {
|
||||||
rule.Version++
|
rule.Version++
|
||||||
rule.Updated = time.Now()
|
rule.Updated = time.Now()
|
||||||
result = append(result, models.AlertRuleKeyWithVersion{
|
result = append(result, models.AlertRuleKeyWithVersion{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@@ -26,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func SetupFolderService(tb testing.TB, cfg *setting.Cfg, db db.DB, dashboardStore dashboards.Store, folderStore *folderimpl.DashboardFolderStoreImpl, bus *bus.InProcBus, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
|
func SetupFolderService(tb testing.TB, cfg *setting.Cfg, db db.DB, dashboardStore dashboards.Store, folderStore *folderimpl.DashboardFolderStoreImpl, bus *bus.InProcBus, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl) folder.Service {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
return folderimpl.ProvideService(ac, bus, dashboardStore, folderStore, db, features, supportbundlestest.NewFakeBundleService(), nil)
|
return folderimpl.ProvideService(ac, bus, dashboardStore, folderStore, db, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupDashboardService(tb testing.TB, sqlStore db.ReplDB, fs *folderimpl.DashboardFolderStoreImpl, cfg *setting.Cfg) (*dashboardservice.DashboardServiceImpl, dashboards.Store) {
|
func SetupDashboardService(tb testing.TB, sqlStore db.ReplDB, fs *folderimpl.DashboardFolderStoreImpl, cfg *setting.Cfg) (*dashboardservice.DashboardServiceImpl, dashboards.Store) {
|
||||||
|
|||||||
@@ -822,7 +822,7 @@ func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol
|
|||||||
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db), quotatest.New(false, nil))
|
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
folderSvc := folderimpl.ProvideService(actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(db), db, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderSvc := folderimpl.ProvideService(actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(db), db, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
// create parent folder
|
// create parent folder
|
||||||
parent, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
parent, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func setupBenchMark(b *testing.B, usr user.SignedInUser, features featuremgmt.Fe
|
|||||||
dashboardWriteStore, err := database.ProvideDashboardStore(store, cfg, features, tagimpl.ProvideService(store), quotaService)
|
dashboardWriteStore, err := database.ProvideDashboardStore(store, cfg, features, tagimpl.ProvideService(store), quotaService)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
folderSvc := folderimpl.ProvideService(mock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(store), store, features, supportbundlestest.NewFakeBundleService(), nil)
|
folderSvc := folderimpl.ProvideService(mock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardWriteStore, folderimpl.ProvideDashboardFolderStore(store), store, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest())
|
||||||
|
|
||||||
origNewGuardian := guardian.New
|
origNewGuardian := guardian.New
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true, CanSaveValue: true})
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true, CanSaveValue: true})
|
||||||
|
|||||||
Reference in New Issue
Block a user