Revert "Chore: Refactor quota service (#57586)" (#58394)

This reverts commit 326ea86a57.
This commit is contained in:
Sofia Papagiannaki 2022-11-08 11:52:07 +02:00 committed by GitHub
parent 228ec4c0f3
commit 96cdf77995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 1398 additions and 2596 deletions

View File

@ -245,7 +245,7 @@ func (hs *HTTPServer) AdminDeleteUser(c *models.ReqContext) response.Response {
return nil return nil
}) })
g.Go(func() error { g.Go(func() error {
if err := hs.QuotaService.DeleteQuotaForUser(ctx, cmd.UserID); err != nil { if err := hs.QuotaService.DeleteByUser(ctx, cmd.UserID); err != nil {
return err return err
} }
return nil return nil

View File

@ -36,16 +36,12 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/correlations" "github.com/grafana/grafana/pkg/services/correlations"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
publicdashboardsapi "github.com/grafana/grafana/pkg/services/publicdashboards/api" publicdashboardsapi "github.com/grafana/grafana/pkg/services/publicdashboards/api"
"github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
@ -73,8 +69,8 @@ func (hs *HTTPServer) registerRoutes() {
// not logged in views // not logged in views
r.Get("/logout", hs.Logout) r.Get("/logout", hs.Logout)
r.Post("/login", quota(string(auth.QuotaTargetSrv)), routing.Wrap(hs.LoginPost)) r.Post("/login", quota("session"), routing.Wrap(hs.LoginPost))
r.Get("/login/:name", quota(string(auth.QuotaTargetSrv)), hs.OAuthLogin) r.Get("/login/:name", quota("session"), hs.OAuthLogin)
r.Get("/login", hs.LoginView) r.Get("/login", hs.LoginView)
r.Get("/invite/:code", hs.Index) r.Get("/invite/:code", hs.Index)
@ -177,7 +173,7 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/verify", hs.Index) r.Get("/verify", hs.Index)
r.Get("/signup", hs.Index) r.Get("/signup", hs.Index)
r.Get("/api/user/signup/options", routing.Wrap(GetSignUpOptions)) r.Get("/api/user/signup/options", routing.Wrap(GetSignUpOptions))
r.Post("/api/user/signup", quota(user.QuotaTargetSrv), quota(org.QuotaTargetSrv), routing.Wrap(hs.SignUp)) r.Post("/api/user/signup", quota("user"), routing.Wrap(hs.SignUp))
r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2)) r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2))
// invited // invited
@ -196,7 +192,7 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/dashboard/snapshots/", reqSignedIn, hs.Index) r.Get("/dashboard/snapshots/", reqSignedIn, hs.Index)
// api renew session based on cookie // api renew session based on cookie
r.Get("/api/login/ping", quota(string(auth.QuotaTargetSrv)), routing.Wrap(hs.LoginAPIPing)) r.Get("/api/login/ping", quota("session"), routing.Wrap(hs.LoginAPIPing))
// expose plugin file system assets // expose plugin file system assets
r.Get("/public/plugins/:pluginId/*", hs.getPluginAssets) r.Get("/public/plugins/:pluginId/*", hs.getPluginAssets)
@ -302,13 +298,13 @@ func (hs *HTTPServer) registerRoutes() {
orgRoute.Put("/address", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgsWrite)), routing.Wrap(hs.UpdateCurrentOrgAddress)) orgRoute.Put("/address", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgsWrite)), routing.Wrap(hs.UpdateCurrentOrgAddress))
orgRoute.Get("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsersForCurrentOrg)) orgRoute.Get("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Get("/users/search", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.SearchOrgUsersWithPaging)) orgRoute.Get("/users/search", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.SearchOrgUsersWithPaging))
orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota(user.QuotaTargetSrv), quota(org.QuotaTargetSrv), routing.Wrap(hs.AddOrgUserToCurrentOrg)) orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota("user"), routing.Wrap(hs.AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersWrite, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg)) orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersWrite, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUserForCurrentOrg)) orgRoute.Delete("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUserForCurrentOrg))
// invites // invites
orgRoute.Get("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), routing.Wrap(hs.GetPendingOrgInvites)) orgRoute.Get("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), routing.Wrap(hs.GetPendingOrgInvites))
orgRoute.Post("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), quota(user.QuotaTargetSrv), quota(user.QuotaTargetSrv), routing.Wrap(hs.AddOrgInvite)) orgRoute.Post("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), quota("user"), routing.Wrap(hs.AddOrgInvite))
orgRoute.Patch("/invites/:code/revoke", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), routing.Wrap(hs.RevokeInvite)) orgRoute.Patch("/invites/:code/revoke", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd)), routing.Wrap(hs.RevokeInvite))
// prefs // prefs
@ -335,7 +331,7 @@ func (hs *HTTPServer) registerRoutes() {
}) })
// create new org // create new org
apiRoute.Post("/orgs", authorizeInOrg(reqSignedIn, ac.UseGlobalOrg, ac.EvalPermission(ac.ActionOrgsCreate)), quota(org.QuotaTargetSrv), routing.Wrap(hs.CreateOrg)) apiRoute.Post("/orgs", authorizeInOrg(reqSignedIn, ac.UseGlobalOrg, ac.EvalPermission(ac.ActionOrgsCreate)), quota("org"), routing.Wrap(hs.CreateOrg))
// search all orgs // search all orgs
apiRoute.Get("/orgs", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, ac.EvalPermission(ac.ActionOrgsRead)), routing.Wrap(hs.SearchOrgs)) apiRoute.Get("/orgs", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, ac.EvalPermission(ac.ActionOrgsRead)), routing.Wrap(hs.SearchOrgs))
@ -362,7 +358,7 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) { apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) {
apikeyIDScope := ac.Scope("apikeys", "id", ac.Parameter(":id")) apikeyIDScope := ac.Scope("apikeys", "id", ac.Parameter(":id"))
keysRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyRead)), routing.Wrap(hs.GetAPIKeys)) keysRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyRead)), routing.Wrap(hs.GetAPIKeys))
keysRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyCreate)), quota(string(apikey.QuotaTargetSrv)), routing.Wrap(hs.AddAPIKey)) keysRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyCreate)), quota("api_key"), routing.Wrap(hs.AddAPIKey))
keysRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyDelete, apikeyIDScope)), routing.Wrap(hs.DeleteAPIKey)) keysRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyDelete, apikeyIDScope)), routing.Wrap(hs.DeleteAPIKey))
}) })
@ -377,7 +373,7 @@ func (hs *HTTPServer) registerRoutes() {
uidScope := datasources.ScopeProvider.GetResourceScopeUID(ac.Parameter(":uid")) uidScope := datasources.ScopeProvider.GetResourceScopeUID(ac.Parameter(":uid"))
nameScope := datasources.ScopeProvider.GetResourceScopeName(ac.Parameter(":name")) nameScope := datasources.ScopeProvider.GetResourceScopeName(ac.Parameter(":name"))
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSources)) datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSources))
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionCreate)), quota(string(datasources.QuotaTargetSrv)), routing.Wrap(hs.AddDataSource)) datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionCreate)), quota("data_source"), routing.Wrap(hs.AddDataSource))
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionWrite, idScope)), routing.Wrap(hs.UpdateDataSourceByID)) datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionWrite, idScope)), routing.Wrap(hs.UpdateDataSourceByID))
datasourceRoute.Put("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionWrite, uidScope)), routing.Wrap(hs.UpdateDataSourceByUID)) datasourceRoute.Put("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionWrite, uidScope)), routing.Wrap(hs.UpdateDataSourceByUID))
datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDelete, idScope)), routing.Wrap(hs.DeleteDataSourceById)) datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDelete, idScope)), routing.Wrap(hs.DeleteDataSourceById))

View File

@ -45,6 +45,7 @@ import (
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/preference/preftest" "github.com/grafana/grafana/pkg/services/preference/preftest"
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
@ -248,13 +249,15 @@ func (s *fakeRenderService) Init() error {
} }
func setupAccessControlScenarioContext(t *testing.T, cfg *setting.Cfg, url string, permissions []accesscontrol.Permission) (*scenarioContext, *HTTPServer) { func setupAccessControlScenarioContext(t *testing.T, cfg *setting.Cfg, url string, permissions []accesscontrol.Permission) (*scenarioContext, *HTTPServer) {
store := sqlstore.InitTestDB(t) cfg.Quota.Enabled = false
store := db.InitTestDB(t)
hs := &HTTPServer{ hs := &HTTPServer{
Cfg: cfg, Cfg: cfg,
Live: newTestLive(t, store), Live: newTestLive(t, store),
License: &licensing.OSSLicensingService{}, License: &licensing.OSSLicensingService{},
Features: featuremgmt.WithFeatures(), Features: featuremgmt.WithFeatures(),
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{Cfg: cfg},
RouteRegister: routing.NewRouteRegister(), RouteRegister: routing.NewRouteRegister(),
AccessControl: accesscontrolmock.New().WithPermissions(permissions), AccessControl: accesscontrolmock.New().WithPermissions(permissions),
searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()), searchUsersService: searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), usertest.NewUserServiceFake()),
@ -373,9 +376,7 @@ func setupHTTPServerWithCfgDb(
routeRegister := routing.NewRouteRegister() routeRegister := routing.NewRouteRegister()
teamService := teamimpl.ProvideService(db, cfg) teamService := teamimpl.ProvideService(db, cfg)
cfg.IsFeatureToggleEnabled = features.IsEnabled cfg.IsFeatureToggleEnabled = features.IsEnabled
quotaService := quotatest.New(false, nil) dashboardsStore := dashboardsstore.ProvideDashboardStore(db, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db, cfg))
dashboardsStore, err := dashboardsstore.ProvideDashboardStore(db, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db, cfg), quotaService)
require.NoError(t, err)
var acmock *accesscontrolmock.Mock var acmock *accesscontrolmock.Mock
var ac accesscontrol.AccessControl var ac accesscontrol.AccessControl
@ -401,8 +402,7 @@ func setupHTTPServerWithCfgDb(
acService, err = acimpl.ProvideService(cfg, db, routeRegister, localcache.ProvideService(), featuremgmt.WithFeatures()) acService, err = acimpl.ProvideService(cfg, db, routeRegister, localcache.ProvideService(), featuremgmt.WithFeatures())
require.NoError(t, err) require.NoError(t, err)
ac = acimpl.ProvideAccessControl(cfg) ac = acimpl.ProvideAccessControl(cfg)
userSvc, err = userimpl.ProvideService(db, nil, cfg, teamimpl.ProvideService(db, cfg), localcache.ProvideService(), quotatest.New(false, nil)) userSvc = userimpl.ProvideService(db, nil, cfg, teamimpl.ProvideService(db, cfg), localcache.ProvideService())
require.NoError(t, err)
} }
teamPermissionService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, routeRegister, db, ac, license, acService, teamService, userSvc) teamPermissionService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, routeRegister, db, ac, license, acService, teamService, userSvc)
require.NoError(t, err) require.NoError(t, err)
@ -412,7 +412,7 @@ func setupHTTPServerWithCfgDb(
Cfg: cfg, Cfg: cfg,
Features: features, Features: features,
Live: newTestLive(t, db), Live: newTestLive(t, db),
QuotaService: quotaService, QuotaService: &quotaimpl.Service{Cfg: cfg},
RouteRegister: routeRegister, RouteRegister: routeRegister,
SQLStore: store, SQLStore: store,
License: &licensing.OSSLicensingService{}, License: &licensing.OSSLicensingService{},
@ -497,7 +497,7 @@ func SetupAPITestServer(t *testing.T, opts ...APITestServerOption) *webtest.Serv
RouteRegister: routing.NewRouteRegister(), RouteRegister: routing.NewRouteRegister(),
License: &licensing.OSSLicensingService{}, License: &licensing.OSSLicensingService{},
Features: featuremgmt.WithFeatures(), Features: featuremgmt.WithFeatures(),
QuotaService: quotatest.New(false, nil), QuotaService: quotatest.NewQuotaServiceFake(),
searchUsersService: &searchusers.OSSService{}, searchUsersService: &searchusers.OSSService{},
} }

View File

@ -407,7 +407,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
dash := cmd.GetDashboardModel() dash := cmd.GetDashboardModel()
newDashboard := dash.Id == 0 newDashboard := dash.Id == 0
if newDashboard { if newDashboard {
limitReached, err := hs.QuotaService.QuotaReached(c, dashboards.QuotaTargetSrv) limitReached, err := hs.QuotaService.QuotaReached(c, "dashboard")
if err != nil { if err != nil {
return response.Error(500, "failed to get quota", err) return response.Error(500, "failed to get quota", err)
} }

View File

@ -39,7 +39,7 @@ import (
pref "github.com/grafana/grafana/pkg/services/preference" pref "github.com/grafana/grafana/pkg/services/preference"
"github.com/grafana/grafana/pkg/services/preference/preftest" "github.com/grafana/grafana/pkg/services/preference/preftest"
"github.com/grafana/grafana/pkg/services/provisioning" "github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
@ -150,7 +150,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
DashboardService: dashboardService, DashboardService: dashboardService,
dashboardVersionService: fakeDashboardVersionService, dashboardVersionService: fakeDashboardVersionService,
Coremodels: registry.NewBase(nil), Coremodels: registry.NewBase(nil),
QuotaService: quotatest.New(false, nil),
} }
setUp := func() { setUp := func() {
@ -991,12 +990,9 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
provisioningService = provisioning.NewProvisioningServiceMock(context.Background()) provisioningService = provisioning.NewProvisioningServiceMock(context.Background())
} }
var err error
if dashboardStore == nil { if dashboardStore == nil {
sql := db.InitTestDB(t) sql := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore = database.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg))
dashboardStore, err = database.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg), quotaService)
require.NoError(t, err)
} }
libraryPanelsService := mockLibraryPanelService{} libraryPanelsService := mockLibraryPanelService{}
@ -1035,7 +1031,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
require.Equal(sc.t, 200, sc.resp.Code) require.Equal(sc.t, 200, sc.resp.Code)
dash := dtos.DashboardFullWithMeta{} dash := dtos.DashboardFullWithMeta{}
err = json.NewDecoder(sc.resp.Body).Decode(&dash) err := json.NewDecoder(sc.resp.Body).Decode(&dash)
require.NoError(sc.t, err) require.NoError(sc.t, err)
return dash return dash
@ -1081,10 +1077,12 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) { t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
hs := HTTPServer{ hs := HTTPServer{
Cfg: cfg, Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()), ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
Live: newTestLive(t, db.InitTestDB(t)), Live: newTestLive(t, db.InitTestDB(t)),
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{
Cfg: cfg,
},
pluginStore: &plugins.FakePluginStore{}, pluginStore: &plugins.FakePluginStore{},
LibraryPanelService: &mockLibraryPanelService{}, LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{}, LibraryElementService: &mockLibraryElementService{},
@ -1118,7 +1116,7 @@ func postValidateScenario(t *testing.T, desc string, url string, routePattern st
Cfg: cfg, Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()), ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
Live: newTestLive(t, db.InitTestDB(t)), Live: newTestLive(t, db.InitTestDB(t)),
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{Cfg: cfg},
LibraryPanelService: &mockLibraryPanelService{}, LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{}, LibraryElementService: &mockLibraryElementService{},
SQLStore: sqlmock, SQLStore: sqlmock,
@ -1154,7 +1152,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
Cfg: cfg, Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()), ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
Live: newTestLive(t, db.InitTestDB(t)), Live: newTestLive(t, db.InitTestDB(t)),
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{Cfg: cfg},
LibraryPanelService: &mockLibraryPanelService{}, LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{}, LibraryElementService: &mockLibraryElementService{},
SQLStore: sqlmock, SQLStore: sqlmock,
@ -1192,7 +1190,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
Cfg: cfg, Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()), ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
Live: newTestLive(t, db.InitTestDB(t)), Live: newTestLive(t, db.InitTestDB(t)),
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{Cfg: cfg},
LibraryPanelService: &mockLibraryPanelService{}, LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{}, LibraryElementService: &mockLibraryElementService{},
DashboardService: mock, DashboardService: mock,

View File

@ -146,7 +146,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
server := SetupAPITestServer(t, func(hs *HTTPServer) { server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.folderService = folderService hs.folderService = folderService
hs.AccessControl = acmock.New() hs.AccessControl = acmock.New()
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
t.Run("Should attach access control metadata to multiple folders", func(t *testing.T) { t.Run("Should attach access control metadata to multiple folders", func(t *testing.T) {

View File

@ -94,12 +94,12 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) { serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds hs.queryDataService = qds
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, true) hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, true)
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
serverFeatureDisabled := SetupAPITestServer(t, func(hs *HTTPServer) { serverFeatureDisabled := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds hs.queryDataService = qds
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, false) hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagDatasourceQueryMultiStatus, false)
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
t.Run("Status code is 400 when data source response has an error and feature toggle is disabled", func(t *testing.T) { t.Run("Status code is 400 when data source response has an error and feature toggle is disabled", func(t *testing.T) {
@ -142,7 +142,7 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
) )
httpServer := SetupAPITestServer(t, func(hs *HTTPServer) { httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds hs.queryDataService = qds
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) { t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) {
@ -294,7 +294,7 @@ func TestDataSourceQueryError(t *testing.T) {
pluginClient.ProvideService(r, &config.Cfg{}), pluginClient.ProvideService(r, &config.Cfg{}),
&fakeOAuthTokenService{}, &fakeOAuthTokenService{},
) )
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
req := srv.NewPostRequest("/api/ds/query", strings.NewReader(tc.request)) req := srv.NewPostRequest("/api/ds/query", strings.NewReader(tc.request))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: org.RoleViewer}) webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, OrgRole: org.RoleViewer})

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -105,8 +104,7 @@ func TestAPIEndpoint_PutCurrentOrg_LegacyAccessControl(t *testing.T) {
}) })
setInitCtxSignedInOrgAdmin(sc.initCtx) setInitCtxSignedInOrgAdmin(sc.initCtx)
sc.hs.orgService, err = orgimpl.ProvideService(sc.db, sc.cfg, quotatest.New(false, nil)) sc.hs.orgService = orgimpl.ProvideService(sc.db, sc.cfg)
require.NoError(t, err)
t.Run("Admin can update current org", func(t *testing.T) { t.Run("Admin can update current org", func(t *testing.T) {
response := callAPI(sc.server, http.MethodPut, putCurrentOrgURL, input, t) response := callAPI(sc.server, http.MethodPut, putCurrentOrgURL, input, t)
assert.Equal(t, http.StatusOK, response.Code) assert.Equal(t, http.StatusOK, response.Code)
@ -120,8 +118,7 @@ func TestAPIEndpoint_PutCurrentOrg_AccessControl(t *testing.T) {
_, err := sc.db.CreateOrgWithMember("TestOrg", sc.initCtx.UserID) _, err := sc.db.CreateOrgWithMember("TestOrg", sc.initCtx.UserID)
require.NoError(t, err) require.NoError(t, err)
sc.hs.orgService, err = orgimpl.ProvideService(sc.db, sc.cfg, quotatest.New(false, nil)) sc.hs.orgService = orgimpl.ProvideService(sc.db, sc.cfg)
require.NoError(t, err)
input := strings.NewReader(testUpdateOrgNameForm) input := strings.NewReader(testUpdateOrgNameForm)
t.Run("AccessControl allows updating current org with correct permissions", func(t *testing.T) { t.Run("AccessControl allows updating current org with correct permissions", func(t *testing.T) {
@ -439,9 +436,7 @@ func TestAPIEndpoint_PutOrg_LegacyAccessControl(t *testing.T) {
cfg.RBACEnabled = false cfg.RBACEnabled = false
sc := setupHTTPServerWithCfg(t, true, cfg) sc := setupHTTPServerWithCfg(t, true, cfg)
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
var err error sc.hs.orgService = orgimpl.ProvideService(sc.db, sc.cfg)
sc.hs.orgService, err = orgimpl.ProvideService(sc.db, sc.cfg, quotatest.New(false, nil))
require.NoError(t, err)
// Create two orgs, to update another one than the logged in one // Create two orgs, to update another one than the logged in one
setupOrgsDBForAccessControlTests(t, sc.db, sc, 2) setupOrgsDBForAccessControlTests(t, sc.db, sc, 2)
@ -461,9 +456,7 @@ func TestAPIEndpoint_PutOrg_LegacyAccessControl(t *testing.T) {
func TestAPIEndpoint_PutOrg_AccessControl(t *testing.T) { func TestAPIEndpoint_PutOrg_AccessControl(t *testing.T) {
sc := setupHTTPServer(t, true) sc := setupHTTPServer(t, true)
var err error sc.hs.orgService = orgimpl.ProvideService(sc.db, sc.cfg)
sc.hs.orgService, err = orgimpl.ProvideService(sc.db, sc.cfg, quotatest.New(false, nil))
require.NoError(t, err)
// Create two orgs, to update another one than the logged in one // Create two orgs, to update another one than the logged in one
setupOrgsDBForAccessControlTests(t, sc.db, sc, 2) setupOrgsDBForAccessControlTests(t, sc.db, sc, 2)

View File

@ -22,7 +22,6 @@ import (
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
@ -390,13 +389,11 @@ func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
var err error
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService, err = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotatest.New(false, nil)) hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(),
require.NoError(t, err) )
hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotatest.New(false, nil)) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
require.NoError(t, err)
}) })
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
setInitCtxSignedInUser(sc.initCtx, tc.user) setInitCtxSignedInUser(sc.initCtx, tc.user)
@ -406,7 +403,7 @@ func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) {
require.Equal(t, tc.expectedCode, response.Code) require.Equal(t, tc.expectedCode, response.Code)
var userList []*models.OrgUserDTO var userList []*models.OrgUserDTO
err = json.NewDecoder(response.Body).Decode(&userList) err := json.NewDecoder(response.Body).Decode(&userList)
require.NoError(t, err) require.NoError(t, err)
if tc.expectedMetadata != nil { if tc.expectedMetadata != nil {
@ -496,14 +493,11 @@ func TestGetOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
var err error
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
quotaService := quotatest.New(false, nil) hs.userService = userimpl.ProvideService(
hs.userService, err = userimpl.ProvideService( hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(),
hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) )
require.NoError(t, err) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService)
require.NoError(t, err)
}) })
setInitCtxSignedInUser(sc.initCtx, tc.user) setInitCtxSignedInUser(sc.initCtx, tc.user)
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
@ -604,11 +598,10 @@ func TestPostOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
var err error
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
hs.userService, err = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotatest.New(false, nil)) hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(),
require.NoError(t, err) )
}) })
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
@ -723,12 +716,11 @@ func TestOrgUsersAPIEndpointWithSetPerms_AccessControl(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
var err error
sc := setupHTTPServer(t, true, func(hs *HTTPServer) { sc := setupHTTPServer(t, true, func(hs *HTTPServer) {
hs.tempUserService = tempuserimpl.ProvideService(hs.SQLStore) hs.tempUserService = tempuserimpl.ProvideService(hs.SQLStore)
hs.userService, err = userimpl.ProvideService( hs.userService = userimpl.ProvideService(
hs.SQLStore, nil, setting.NewCfg(), teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), setting.NewCfg()), localcache.ProvideService(), quotatest.New(false, nil)) hs.SQLStore, nil, setting.NewCfg(), teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), setting.NewCfg()), localcache.ProvideService(),
require.NoError(t, err) )
}) })
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
@ -843,14 +835,11 @@ func TestPatchOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
var err error
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
quotaService := quotatest.New(false, nil) hs.userService = userimpl.ProvideService(
hs.userService, err = userimpl.ProvideService( hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(),
hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) )
require.NoError(t, err) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService)
require.NoError(t, err)
}) })
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
setInitCtxSignedInUser(sc.initCtx, tc.user) setInitCtxSignedInUser(sc.initCtx, tc.user)
@ -973,14 +962,11 @@ func TestDeleteOrgUsersAPIEndpoint_AccessControl(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = tc.enableAccessControl cfg.RBACEnabled = tc.enableAccessControl
var err error
sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) { sc := setupHTTPServerWithCfg(t, false, cfg, func(hs *HTTPServer) {
quotaService := quotatest.New(false, nil) hs.userService = userimpl.ProvideService(
hs.userService, err = userimpl.ProvideService( hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(),
hs.SQLStore, nil, cfg, teamimpl.ProvideService(hs.SQLStore.(*sqlstore.SQLStore), cfg), localcache.ProvideService(), quotaService) )
require.NoError(t, err) hs.orgService = orgimpl.ProvideService(hs.SQLStore, cfg)
hs.orgService, err = orgimpl.ProvideService(hs.SQLStore, cfg, quotaService)
require.NoError(t, err)
}) })
setupOrgUsersDBForAccessControlTests(t, sc.db) setupOrgUsersDBForAccessControlTests(t, sc.db)
setInitCtxSignedInUser(sc.initCtx, tc.user) setInitCtxSignedInUser(sc.initCtx, tc.user)

View File

@ -41,7 +41,7 @@ func TestGetPluginDashboards(t *testing.T) {
s := SetupAPITestServer(t, func(hs *HTTPServer) { s := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.pluginDashboardService = pluginDashboardService hs.pluginDashboardService = pluginDashboardService
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
t.Run("Not signed in should return 404 Not Found", func(t *testing.T) { t.Run("Not signed in should return 404 Not Found", func(t *testing.T) {

View File

@ -32,7 +32,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
@ -139,9 +138,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path", func(t *testing.T) { t.Run("When matching route path", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider,
&oauthtoken.Service{}, dsService, tracer) &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -154,9 +151,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path and has dynamic url", func(t *testing.T) { t.Run("When matching route path and has dynamic url", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
proxy.matchedRoute = routes[3] proxy.matchedRoute = routes[3]
@ -168,9 +163,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path with no url", func(t *testing.T) { t.Run("When matching route path with no url", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
proxy.matchedRoute = routes[4] proxy.matchedRoute = routes[4]
@ -181,9 +174,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("When matching route path and has dynamic body", func(t *testing.T) { t.Run("When matching route path and has dynamic body", func(t *testing.T) {
ctx, req := setUp() ctx, req := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
proxy.matchedRoute = routes[5] proxy.matchedRoute = routes[5]
@ -197,9 +188,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("Validating request", func(t *testing.T) { t.Run("Validating request", func(t *testing.T) {
t.Run("plugin route with valid role", func(t *testing.T) { t.Run("plugin route with valid role", func(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
@ -208,9 +197,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("plugin route with admin role and user is editor", func(t *testing.T) { t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
@ -220,9 +207,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
t.Run("plugin route with admin role and user is admin", func(t *testing.T) { t.Run("plugin route with admin role and user is admin", func(t *testing.T) {
ctx, _ := setUp() ctx, _ := setUp()
ctx.SignedInUser.OrgRole = org.RoleAdmin ctx.SignedInUser.OrgRole = org.RoleAdmin
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
err = proxy.validateRequest() err = proxy.validateRequest()
@ -313,9 +298,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
}, },
} }
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
@ -331,9 +314,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost/asd", nil) req, err := http.NewRequest("GET", "http://localhost/asd", nil)
require.NoError(t, err) require.NoError(t, err)
client = newFakeHTTPClient(t, json2) client = newFakeHTTPClient(t, json2)
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg)
@ -350,9 +331,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
client = newFakeHTTPClient(t, []byte{}) client = newFakeHTTPClient(t, []byte{})
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
@ -376,9 +355,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -405,9 +382,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -433,9 +408,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -465,9 +438,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -492,9 +463,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -545,9 +514,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -684,9 +651,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -706,9 +671,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -724,9 +687,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -750,9 +711,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -779,9 +738,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -807,9 +764,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -835,11 +790,8 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
var err error _, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.Error(t, err) require.Error(t, err)
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`)) assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`))
} }
@ -860,10 +812,8 @@ func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService) _, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err)
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
} }
@ -906,9 +856,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer) p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
if tc.err == nil { if tc.err == nil {
require.NoError(t, err) require.NoError(t, err)
@ -936,9 +884,7 @@ func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *sett
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
@ -1055,9 +1001,7 @@ func runDatasourceAuthTest(t *testing.T, secretsService secrets.Service, secrets
tracer := tracing.InitializeTracerForTest() tracer := tracing.InitializeTracerForTest()
var routes []*plugins.Route var routes []*plugins.Route
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)
@ -1101,9 +1045,7 @@ func Test_PathCheck(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
proxy, err := NewDataSourceProxy(&datasources.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer) proxy, err := NewDataSourceProxy(&datasources.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
require.NoError(t, err) require.NoError(t, err)

View File

@ -60,7 +60,7 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
PluginAdminExternalManageEnabled: tc.pluginAdminExternalManageEnabled, PluginAdminExternalManageEnabled: tc.pluginAdminExternalManageEnabled,
} }
hs.pluginInstaller = inst hs.pluginInstaller = inst
hs.QuotaService = quotatest.New(false, nil) hs.QuotaService = quotatest.NewQuotaServiceFake()
}) })
t.Run(testName("Install", tc), func(t *testing.T) { t.Run(testName("Install", tc), func(t *testing.T) {

View File

@ -6,22 +6,10 @@ import (
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
// swagger:route GET /org/quotas getCurrentOrg getCurrentOrgQuota
//
// Fetch Organization quota.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `orgs.quotas:read` and scope `org:id:1` (orgIDScope).
//
// Responses:
// 200: getQuotaResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) GetCurrentOrgQuotas(c *models.ReqContext) response.Response { func (hs *HTTPServer) GetCurrentOrgQuotas(c *models.ReqContext) response.Response {
return hs.getOrgQuotasHelper(c, c.OrgID) return hs.getOrgQuotasHelper(c, c.OrgID)
} }
@ -41,17 +29,22 @@ func (hs *HTTPServer) GetCurrentOrgQuotas(c *models.ReqContext) response.Respons
func (hs *HTTPServer) GetOrgQuotas(c *models.ReqContext) response.Response { func (hs *HTTPServer) GetOrgQuotas(c *models.ReqContext) response.Response {
orgId, err := strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64) orgId, err := strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64)
if err != nil { if err != nil {
return response.Err(quota.ErrBadRequest.Errorf("orgId is invalid: %w", err)) return response.Error(http.StatusBadRequest, "orgId is invalid", err)
} }
return hs.getOrgQuotasHelper(c, orgId) return hs.getOrgQuotasHelper(c, orgId)
} }
func (hs *HTTPServer) getOrgQuotasHelper(c *models.ReqContext, orgID int64) response.Response { func (hs *HTTPServer) getOrgQuotasHelper(c *models.ReqContext, orgID int64) response.Response {
q, err := hs.QuotaService.GetQuotasByScope(c.Req.Context(), quota.OrgScope, orgID) if !hs.Cfg.Quota.Enabled {
if err != nil { return response.Error(404, "Quotas not enabled", nil)
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get quota", err)
} }
return response.JSON(http.StatusOK, q) query := models.GetOrgQuotasQuery{OrgId: orgID}
if err := hs.SQLStore.GetOrgQuotas(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to get org quotas", err)
}
return response.JSON(http.StatusOK, query.Result)
} }
// swagger:route PUT /orgs/{org_id}/quotas/{quota_target} orgs updateOrgQuota // swagger:route PUT /orgs/{org_id}/quotas/{quota_target} orgs updateOrgQuota
@ -70,19 +63,26 @@ func (hs *HTTPServer) getOrgQuotasHelper(c *models.ReqContext, orgID int64) resp
// 404: notFoundError // 404: notFoundError
// 500: internalServerError // 500: internalServerError
func (hs *HTTPServer) UpdateOrgQuota(c *models.ReqContext) response.Response { func (hs *HTTPServer) UpdateOrgQuota(c *models.ReqContext) response.Response {
cmd := quota.UpdateQuotaCmd{} cmd := models.UpdateOrgQuotaCmd{}
var err error var err error
if err := web.Bind(c.Req, &cmd); err != nil { if err := web.Bind(c.Req, &cmd); err != nil {
return response.Err(quota.ErrBadRequest.Errorf("bad request data: %w", err)) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
cmd.OrgID, err = strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64) if !hs.Cfg.Quota.Enabled {
return response.Error(404, "Quotas not enabled", nil)
}
cmd.OrgId, err = strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64)
if err != nil { if err != nil {
return response.Err(quota.ErrBadRequest.Errorf("orgId is invalid: %w", err)) return response.Error(http.StatusBadRequest, "orgId is invalid", err)
} }
cmd.Target = web.Params(c.Req)[":target"] cmd.Target = web.Params(c.Req)[":target"]
if err := hs.QuotaService.Update(c.Req.Context(), &cmd); err != nil { if _, ok := hs.Cfg.Quota.Org.ToMap()[cmd.Target]; !ok {
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to update org quotas", err) return response.Error(404, "Invalid quota target", nil)
}
if err := hs.SQLStore.UpdateOrgQuota(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to update org quotas", err)
} }
return response.Success("Organization quota updated") return response.Success("Organization quota updated")
} }
@ -114,17 +114,22 @@ func (hs *HTTPServer) UpdateOrgQuota(c *models.ReqContext) response.Response {
// 404: notFoundError // 404: notFoundError
// 500: internalServerError // 500: internalServerError
func (hs *HTTPServer) GetUserQuotas(c *models.ReqContext) response.Response { func (hs *HTTPServer) GetUserQuotas(c *models.ReqContext) response.Response {
if !setting.Quota.Enabled {
return response.Error(404, "Quotas not enabled", nil)
}
id, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) id, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
if err != nil { if err != nil {
return response.Err(quota.ErrBadRequest.Errorf("id is invalid: %w", err)) return response.Error(http.StatusBadRequest, "id is invalid", err)
} }
q, err := hs.QuotaService.GetQuotasByScope(c.Req.Context(), quota.UserScope, id) query := models.GetUserQuotasQuery{UserId: id}
if err != nil {
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to get org quotas", err) if err := hs.SQLStore.GetUserQuotas(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to get org quotas", err)
} }
return response.JSON(http.StatusOK, q) return response.JSON(http.StatusOK, query.Result)
} }
// swagger:route PUT /admin/users/{user_id}/quotas/{quota_target} admin_users updateUserQuota // swagger:route PUT /admin/users/{user_id}/quotas/{quota_target} admin_users updateUserQuota
@ -143,19 +148,26 @@ func (hs *HTTPServer) GetUserQuotas(c *models.ReqContext) response.Response {
// 404: notFoundError // 404: notFoundError
// 500: internalServerError // 500: internalServerError
func (hs *HTTPServer) UpdateUserQuota(c *models.ReqContext) response.Response { func (hs *HTTPServer) UpdateUserQuota(c *models.ReqContext) response.Response {
cmd := quota.UpdateQuotaCmd{} cmd := models.UpdateUserQuotaCmd{}
var err error var err error
if err := web.Bind(c.Req, &cmd); err != nil { if err := web.Bind(c.Req, &cmd); err != nil {
return response.Err(quota.ErrBadRequest.Errorf("bad request data: %w", err)) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
cmd.UserID, err = strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) if !setting.Quota.Enabled {
return response.Error(404, "Quotas not enabled", nil)
}
cmd.UserId, err = strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
if err != nil { if err != nil {
return response.Err(quota.ErrBadRequest.Errorf("id is invalid: %w", err)) return response.Error(http.StatusBadRequest, "id is invalid", err)
} }
cmd.Target = web.Params(c.Req)[":target"] cmd.Target = web.Params(c.Req)[":target"]
if err := hs.QuotaService.Update(c.Req.Context(), &cmd); err != nil { if _, ok := setting.Quota.User.ToMap()[cmd.Target]; !ok {
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to update org quotas", err) return response.Error(404, "Invalid quota target", nil)
}
if err := hs.SQLStore.UpdateUserQuota(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to update org quotas", err)
} }
return response.Success("Organization quota updated") return response.Success("Organization quota updated")
} }
@ -164,7 +176,7 @@ func (hs *HTTPServer) UpdateUserQuota(c *models.ReqContext) response.Response {
type UpdateUserQuotaParams struct { type UpdateUserQuotaParams struct {
// in:body // in:body
// required:true // required:true
Body quota.UpdateQuotaCmd `json:"body"` Body models.UpdateUserQuotaCmd `json:"body"`
// in:path // in:path
// required:true // required:true
QuotaTarget string `json:"quota_target"` QuotaTarget string `json:"quota_target"`
@ -191,7 +203,7 @@ type GetOrgQuotaParams struct {
type UpdateOrgQuotaParam struct { type UpdateOrgQuotaParam struct {
// in:body // in:body
// required:true // required:true
Body quota.UpdateQuotaCmd `json:"body"` Body models.UpdateOrgQuotaCmd `json:"body"`
// in:path // in:path
// required:true // required:true
QuotaTarget string `json:"quota_target"` QuotaTarget string `json:"quota_target"`
@ -203,5 +215,5 @@ type UpdateOrgQuotaParam struct {
// swagger:response getQuotaResponse // swagger:response getQuotaResponse
type GetQuotaResponseResponse struct { type GetQuotaResponseResponse struct {
// in:body // in:body
Body []*quota.QuotaDTO `json:"body"` Body []*models.UserQuotaDTO `json:"body"`
} }

View File

@ -32,13 +32,17 @@ var testOrgQuota = setting.OrgQuota{
func setupDBAndSettingsForAccessControlQuotaTests(t *testing.T, sc accessControlScenarioContext) { func setupDBAndSettingsForAccessControlQuotaTests(t *testing.T, sc accessControlScenarioContext) {
t.Helper() t.Helper()
sc.hs.Cfg.Quota.Enabled = true
sc.hs.Cfg.Quota.Org = &testOrgQuota
// Required while sqlstore quota.go relies on setting global variables
setting.Quota = sc.hs.Cfg.Quota
// Create two orgs with the context user // Create two orgs with the context user
setupOrgsDBForAccessControlTests(t, sc.db, sc, 2) setupOrgsDBForAccessControlTests(t, sc.db, sc, 2)
} }
func TestAPIEndpoint_GetCurrentOrgQuotas_LegacyAccessControl(t *testing.T) { func TestAPIEndpoint_GetCurrentOrgQuotas_LegacyAccessControl(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.Quota.Enabled = true
cfg.RBACEnabled = false cfg.RBACEnabled = false
sc := setupHTTPServerWithCfg(t, true, cfg) sc := setupHTTPServerWithCfg(t, true, cfg)
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
@ -58,9 +62,7 @@ func TestAPIEndpoint_GetCurrentOrgQuotas_LegacyAccessControl(t *testing.T) {
} }
func TestAPIEndpoint_GetCurrentOrgQuotas_AccessControl(t *testing.T) { func TestAPIEndpoint_GetCurrentOrgQuotas_AccessControl(t *testing.T) {
cfg := setting.NewCfg() sc := setupHTTPServer(t, true)
cfg.Quota.Enabled = true
sc := setupHTTPServerWithCfg(t, true, cfg)
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
setupDBAndSettingsForAccessControlQuotaTests(t, sc) setupDBAndSettingsForAccessControlQuotaTests(t, sc)
@ -84,7 +86,6 @@ func TestAPIEndpoint_GetCurrentOrgQuotas_AccessControl(t *testing.T) {
func TestAPIEndpoint_GetOrgQuotas_LegacyAccessControl(t *testing.T) { func TestAPIEndpoint_GetOrgQuotas_LegacyAccessControl(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.Quota.Enabled = true
cfg.RBACEnabled = false cfg.RBACEnabled = false
sc := setupHTTPServerWithCfg(t, true, cfg) sc := setupHTTPServerWithCfg(t, true, cfg)
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
@ -104,9 +105,7 @@ func TestAPIEndpoint_GetOrgQuotas_LegacyAccessControl(t *testing.T) {
} }
func TestAPIEndpoint_GetOrgQuotas_AccessControl(t *testing.T) { func TestAPIEndpoint_GetOrgQuotas_AccessControl(t *testing.T) {
cfg := setting.NewCfg() sc := setupHTTPServer(t, true)
cfg.Quota.Enabled = true
sc := setupHTTPServerWithCfg(t, true, cfg)
setupDBAndSettingsForAccessControlQuotaTests(t, sc) setupDBAndSettingsForAccessControlQuotaTests(t, sc)
t.Run("AccessControl allows viewing another org quotas with correct permissions", func(t *testing.T) { t.Run("AccessControl allows viewing another org quotas with correct permissions", func(t *testing.T) {
@ -131,7 +130,6 @@ func TestAPIEndpoint_GetOrgQuotas_AccessControl(t *testing.T) {
func TestAPIEndpoint_PutOrgQuotas_LegacyAccessControl(t *testing.T) { func TestAPIEndpoint_PutOrgQuotas_LegacyAccessControl(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.Quota.Enabled = true
cfg.RBACEnabled = false cfg.RBACEnabled = false
sc := setupHTTPServerWithCfg(t, true, cfg) sc := setupHTTPServerWithCfg(t, true, cfg)
setInitCtxSignedInViewer(sc.initCtx) setInitCtxSignedInViewer(sc.initCtx)
@ -153,20 +151,7 @@ func TestAPIEndpoint_PutOrgQuotas_LegacyAccessControl(t *testing.T) {
} }
func TestAPIEndpoint_PutOrgQuotas_AccessControl(t *testing.T) { func TestAPIEndpoint_PutOrgQuotas_AccessControl(t *testing.T) {
cfg := setting.NewCfg() sc := setupHTTPServer(t, true)
cfg.Quota = setting.QuotaSettings{
Enabled: true,
Global: setting.GlobalQuota{
Org: 5,
},
Org: setting.OrgQuota{
User: 5,
},
User: setting.UserQuota{
Org: 5,
},
}
sc := setupHTTPServerWithCfg(t, true, cfg)
setupDBAndSettingsForAccessControlQuotaTests(t, sc) setupDBAndSettingsForAccessControlQuotaTests(t, sc)
input := strings.NewReader(testUpdateOrgQuotaCmd) input := strings.NewReader(testUpdateOrgQuotaCmd)

View File

@ -20,7 +20,6 @@ import (
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/login/authinfoservice" "github.com/grafana/grafana/pkg/services/login/authinfoservice"
authinfostore "github.com/grafana/grafana/pkg/services/login/authinfoservice/database" authinfostore "github.com/grafana/grafana/pkg/services/login/authinfoservice/database"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/searchusers" "github.com/grafana/grafana/pkg/services/searchusers"
"github.com/grafana/grafana/pkg/services/searchusers/filters" "github.com/grafana/grafana/pkg/services/searchusers/filters"
"github.com/grafana/grafana/pkg/services/secrets/database" "github.com/grafana/grafana/pkg/services/secrets/database"
@ -69,8 +68,7 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
} }
user, err := sqlStore.CreateUser(context.Background(), createUserCmd) user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
require.Nil(t, err) require.Nil(t, err)
hs.userService, err = userimpl.ProvideService(sqlStore, nil, sc.cfg, nil, nil, quotatest.New(false, nil)) hs.userService = userimpl.ProvideService(sqlStore, nil, sc.cfg, nil, nil)
require.NoError(t, err)
sc.handlerFunc = hs.GetUserByID sc.handlerFunc = hs.GetUserByID

View File

@ -254,7 +254,7 @@ var wireSet = wire.NewSet(
wire.Bind(new(social.Service), new(*social.SocialService)), wire.Bind(new(social.Service), new(*social.SocialService)),
oauthtoken.ProvideService, oauthtoken.ProvideService,
auth.ProvideActiveAuthTokenService, auth.ProvideActiveAuthTokenService,
wire.Bind(new(auth.ActiveTokenService), new(*auth.ActiveAuthTokenService)), wire.Bind(new(models.ActiveTokenService), new(*auth.ActiveAuthTokenService)),
wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)),
tempo.ProvideService, tempo.ProvideService,
loki.ProvideService, loki.ProvideService,

View File

@ -14,15 +14,15 @@ func Quota(quotaService quota.Service) func(string) web.Handler {
panic("quotaService is nil") panic("quotaService is nil")
} }
//https://open.spotify.com/track/7bZSoBEAEEUsGEuLOf94Jm?si=T1Tdju5qRSmmR0zph_6RBw fuuuuunky //https://open.spotify.com/track/7bZSoBEAEEUsGEuLOf94Jm?si=T1Tdju5qRSmmR0zph_6RBw fuuuuunky
return func(targetSrv string) web.Handler { return func(target string) web.Handler {
return func(c *models.ReqContext) { return func(c *models.ReqContext) {
limitReached, err := quotaService.QuotaReached(c, quota.TargetSrv(targetSrv)) limitReached, err := quotaService.QuotaReached(c, target)
if err != nil { if err != nil {
c.JsonApiErr(500, "Failed to get quota", err) c.JsonApiErr(500, "Failed to get quota", err)
return return
} }
if limitReached { if limitReached {
c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", targetSrv), nil) c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil)
return return
} }
} }

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
@ -30,6 +30,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Global.User = 4
}) })
middlewareScenario(t, "and global session quota not reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "and global session quota not reached", func(t *testing.T, sc *scenarioContext) {
@ -39,6 +41,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code) assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Global.Session = 10
}) })
middlewareScenario(t, "and global session quota reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "and global session quota reached", func(t *testing.T, sc *scenarioContext) {
@ -48,10 +52,13 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Global.Session = 1
}) })
}) })
t.Run("with user logged in", func(t *testing.T) { t.Run("with user logged in", func(t *testing.T) {
const quotaUsed = 4
setUp := func(sc *scenarioContext) { setUp := func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
sc.userService.ExpectedSignedInUser = &user.SignedInUser{UserID: 12} sc.userService.ExpectedSignedInUser = &user.SignedInUser{UserID: 12}
@ -72,6 +79,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Global.DataSource = quotaUsed
}) })
middlewareScenario(t, "user Org quota not reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "user Org quota not reached", func(t *testing.T, sc *scenarioContext) {
@ -84,6 +93,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code) assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.User.Org = quotaUsed + 1
}) })
middlewareScenario(t, "user Org quota reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "user Org quota reached", func(t *testing.T, sc *scenarioContext) {
@ -95,6 +106,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.User.Org = quotaUsed
}) })
middlewareScenario(t, "org dashboard quota not reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org dashboard quota not reached", func(t *testing.T, sc *scenarioContext) {
@ -106,6 +119,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code) assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed + 1
}) })
middlewareScenario(t, "org dashboard quota reached", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org dashboard quota reached", func(t *testing.T, sc *scenarioContext) {
@ -117,6 +132,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed
}) })
middlewareScenario(t, "org dashboard quota reached, but quotas disabled", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org dashboard quota reached, but quotas disabled", func(t *testing.T, sc *scenarioContext) {
@ -128,6 +145,9 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code) assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed
cfg.Quota.Enabled = false
}) })
middlewareScenario(t, "org alert quota reached and unified alerting is enabled", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org alert quota reached and unified alerting is enabled", func(t *testing.T, sc *scenarioContext) {
@ -142,6 +162,7 @@ func TestMiddlewareQuota(t *testing.T) {
cfg.UnifiedAlerting.Enabled = new(bool) cfg.UnifiedAlerting.Enabled = new(bool)
*cfg.UnifiedAlerting.Enabled = true *cfg.UnifiedAlerting.Enabled = true
cfg.Quota.Org.AlertRule = quotaUsed
}) })
middlewareScenario(t, "org alert quota not reached and unified alerting is enabled", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org alert quota not reached and unified alerting is enabled", func(t *testing.T, sc *scenarioContext) {
@ -156,6 +177,7 @@ func TestMiddlewareQuota(t *testing.T) {
cfg.UnifiedAlerting.Enabled = new(bool) cfg.UnifiedAlerting.Enabled = new(bool)
*cfg.UnifiedAlerting.Enabled = true *cfg.UnifiedAlerting.Enabled = true
cfg.Quota.Org.AlertRule = quotaUsed + 1
}) })
middlewareScenario(t, "org alert quota reached but ngalert disabled", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org alert quota reached but ngalert disabled", func(t *testing.T, sc *scenarioContext) {
@ -168,6 +190,8 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 403, sc.resp.Code) assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Org.AlertRule = quotaUsed
}) })
middlewareScenario(t, "org alert quota not reached but ngalert disabled", func(t *testing.T, sc *scenarioContext) { middlewareScenario(t, "org alert quota not reached but ngalert disabled", func(t *testing.T, sc *scenarioContext) {
@ -179,15 +203,58 @@ func TestMiddlewareQuota(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code) assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) { }, func(cfg *setting.Cfg) {
configure(cfg) configure(cfg)
cfg.Quota.Org.AlertRule = quotaUsed + 1
}) })
}) })
} }
func getQuotaHandler(reached bool, target string) web.Handler { func getQuotaHandler(reached bool, target string) web.Handler {
qs := quotatest.New(reached, nil) qs := &mockQuotaService{
reached: reached,
}
return Quota(qs)(target) return Quota(qs)(target)
} }
func configure(cfg *setting.Cfg) { func configure(cfg *setting.Cfg) {
cfg.AnonymousEnabled = false cfg.AnonymousEnabled = false
cfg.Quota = setting.QuotaSettings{
Enabled: true,
Org: &setting.OrgQuota{
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
AlertRule: 5,
},
User: &setting.UserQuota{
Org: 5,
},
Global: &setting.GlobalQuota{
Org: 5,
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
Session: 5,
AlertRule: 5,
},
}
}
type mockQuotaService struct {
reached bool
err error
}
func (m *mockQuotaService) QuotaReached(c *models.ReqContext, target string) (bool, error) {
return m.reached, m.err
}
func (m *mockQuotaService) CheckQuotaReached(c context.Context, target string, params *quota.ScopeParameters) (bool, error) {
return m.reached, m.err
}
func (m *mockQuotaService) DeleteByUser(c context.Context, userID int64) error {
return m.err
} }

91
pkg/models/quotas.go Normal file
View File

@ -0,0 +1,91 @@
package models
import (
"errors"
"time"
)
var ErrInvalidQuotaTarget = errors.New("invalid quota target")
type Quota struct {
Id int64
OrgId int64
UserId int64
Target string
Limit int64
Created time.Time
Updated time.Time
}
type QuotaScope struct {
Name string
Target string
DefaultLimit int64
}
type OrgQuotaDTO struct {
OrgId int64 `json:"org_id"`
Target string `json:"target"`
Limit int64 `json:"limit"`
Used int64 `json:"used"`
}
type UserQuotaDTO struct {
UserId int64 `json:"user_id"`
Target string `json:"target"`
Limit int64 `json:"limit"`
Used int64 `json:"used"`
}
type GlobalQuotaDTO struct {
Target string `json:"target"`
Limit int64 `json:"limit"`
Used int64 `json:"used"`
}
type GetOrgQuotaByTargetQuery struct {
Target string
OrgId int64
Default int64
UnifiedAlertingEnabled bool
Result *OrgQuotaDTO
}
type GetOrgQuotasQuery struct {
OrgId int64
UnifiedAlertingEnabled bool
Result []*OrgQuotaDTO
}
type GetUserQuotaByTargetQuery struct {
Target string
UserId int64
Default int64
UnifiedAlertingEnabled bool
Result *UserQuotaDTO
}
type GetUserQuotasQuery struct {
UserId int64
UnifiedAlertingEnabled bool
Result []*UserQuotaDTO
}
type GetGlobalQuotaByTargetQuery struct {
Target string
Default int64
UnifiedAlertingEnabled bool
Result *GlobalQuotaDTO
}
type UpdateOrgQuotaCmd struct {
Target string `json:"target"`
Limit int64 `json:"limit"`
OrgId int64 `json:"-"`
}
type UpdateUserQuotaCmd struct {
Target string `json:"target"`
Limit int64 `json:"limit"`
UserId int64 `json:"-"`
}

View File

@ -76,6 +76,10 @@ type UserTokenService interface {
GetUserRevokedTokens(ctx context.Context, userId int64) ([]*UserToken, error) GetUserRevokedTokens(ctx context.Context, userId int64) ([]*UserToken, error)
} }
type ActiveTokenService interface {
ActiveTokenCount(ctx context.Context) (int64, error)
}
type UserTokenBackgroundService interface { type UserTokenBackgroundService interface {
registry.BackgroundService registry.BackgroundService
} }

View File

@ -272,7 +272,7 @@ var wireBasicSet = wire.NewSet(
wire.Bind(new(social.Service), new(*social.SocialService)), wire.Bind(new(social.Service), new(*social.SocialService)),
oauthtoken.ProvideService, oauthtoken.ProvideService,
auth.ProvideActiveAuthTokenService, auth.ProvideActiveAuthTokenService,
wire.Bind(new(auth.ActiveTokenService), new(*auth.ActiveAuthTokenService)), wire.Bind(new(models.ActiveTokenService), new(*auth.ActiveAuthTokenService)),
wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)),
tempo.ProvideService, tempo.ProvideService,
loki.ProvideService, loki.ProvideService,

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest" "github.com/grafana/grafana/pkg/services/licensing/licensingtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
@ -226,8 +225,7 @@ func setupTestEnvironment(t *testing.T, permissions []accesscontrol.Permission,
sql := db.InitTestDB(t) sql := db.InitTestDB(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
teamSvc := teamimpl.ProvideService(sql, cfg) teamSvc := teamimpl.ProvideService(sql, cfg)
userSvc, err := userimpl.ProvideService(sql, nil, cfg, teamimpl.ProvideService(sql, cfg), nil, quotatest.New(false, nil)) userSvc := userimpl.ProvideService(sql, nil, cfg, teamimpl.ProvideService(sql, cfg), nil)
require.NoError(t, err)
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
mock := accesscontrolmock.New().WithPermissions(permissions) mock := accesscontrolmock.New().WithPermissions(permissions)

View File

@ -20,7 +20,6 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
dashboardstore "github.com/grafana/grafana/pkg/services/dashboards/database" dashboardstore "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -57,9 +56,7 @@ func TestIntegrationAnnotations(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
}) })
quotaService := quotatest.New(false, nil) dashboardStore := dashboardstore.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg))
dashboardStore, err := dashboardstore.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg), quotaService)
require.NoError(t, err)
testDashboard1 := models.SaveDashboardCommand{ testDashboard1 := models.SaveDashboardCommand{
UserId: 1, UserId: 1,
@ -456,9 +453,7 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
var maximumTagsLength int64 = 60 var maximumTagsLength int64 = 60
repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql, sql.Cfg), maximumTagsLength: maximumTagsLength} repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql, sql.Cfg), maximumTagsLength: maximumTagsLength}
quotaService := quotatest.New(false, nil) dashboardStore := dashboardstore.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg))
dashboardStore, err := dashboardstore.ProvideDashboardStore(sql, sql.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql, sql.Cfg), quotaService)
require.NoError(t, err)
testDashboard1 := models.SaveDashboardCommand{ testDashboard1 := models.SaveDashboardCommand{
UserId: 1, UserId: 1,

View File

@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -14,34 +13,16 @@ type Service struct {
store store store store
} }
func ProvideService(db db.DB, cfg *setting.Cfg, quotaService quota.Service) (apikey.Service, error) { func ProvideService(db db.DB, cfg *setting.Cfg) apikey.Service {
s := &Service{}
if cfg.IsFeatureToggleEnabled(featuremgmt.FlagNewDBLibrary) { if cfg.IsFeatureToggleEnabled(featuremgmt.FlagNewDBLibrary) {
s.store = &sqlxStore{ return &Service{
sess: db.GetSqlxSession(), store: &sqlxStore{
cfg: cfg, sess: db.GetSqlxSession(),
cfg: cfg,
},
} }
} }
s.store = &sqlStore{db: db, cfg: cfg} return &Service{store: &sqlStore{db: db, cfg: cfg}}
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return s, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: apikey.QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: s.Usage,
}); err != nil {
return s, err
}
return s, nil
}
func (s *Service) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
return s.store.Count(ctx, scopeParams)
} }
func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error { func (s *Service) GetAPIKeys(ctx context.Context, query *apikey.GetApiKeysQuery) error {
@ -68,24 +49,3 @@ func (s *Service) AddAPIKey(ctx context.Context, cmd *apikey.AddCommand) error {
func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error { func (s *Service) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error {
return s.store.UpdateAPIKeyLastUsedDate(ctx, tokenID) return s.store.UpdateAPIKeyLastUsedDate(ctx, tokenID)
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.GlobalScope)
if err != nil {
return limits, err
}
orgQuotaTag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.OrgScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.ApiKey)
limits.Set(orgQuotaTag, cfg.Quota.Org.ApiKey)
return limits, nil
}

View File

@ -10,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore/session" "github.com/grafana/grafana/pkg/services/sqlstore/session"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -143,35 +142,3 @@ func (ss *sqlxStore) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64
_, err := ss.sess.Exec(ctx, `UPDATE api_key SET last_used_at=? WHERE id=?`, &now, tokenID) _, err := ss.sess.Exec(ctx, `UPDATE api_key SET last_used_at=? WHERE id=?`, &now, tokenID)
return err return err
} }
func (ss *sqlxStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
type result struct {
Count int64
}
r := result{}
if err := ss.sess.Get(ctx, &r, `SELECT COUNT(*) AS count FROM api_key`); err != nil {
return u, err
} else {
tag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.GlobalScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
if scopeParams.OrgID != 0 {
if err := ss.sess.Get(ctx, &r, `SELECT COUNT(*) AS count FROM api_key WHERE org_id = ?`, scopeParams.OrgID); err != nil {
return u, err
} else {
tag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.OrgScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
}
return u, nil
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/quota"
) )
type store interface { type store interface {
@ -16,6 +15,4 @@ type store interface {
GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error GetApiKeyByName(ctx context.Context, query *apikey.GetByNameQuery) error
GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error) GetAPIKeyByHash(ctx context.Context, hash string) (*apikey.APIKey, error)
UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64) error
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
} }

View File

@ -11,8 +11,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -176,47 +174,3 @@ func (ss *sqlStore) UpdateAPIKeyLastUsedDate(ctx context.Context, tokenID int64)
return nil return nil
}) })
} }
func (ss *sqlStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
type result struct {
Count int64
}
r := result{}
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) AS count FROM api_key"
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.GlobalScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
if scopeParams.OrgID != 0 {
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) AS count FROM api_key WHERE org_id = ?"
if _, err := sess.SQL(rawSQL, scopeParams.OrgID).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, quota.OrgScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
}
return u, nil
}

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
) )
@ -65,8 +64,3 @@ type GetByIDQuery struct {
ApiKeyId int64 ApiKeyId int64
Result *APIKey Result *APIKey
} }
const (
QuotaTargetSrv quota.TargetSrv = "api_key"
QuotaTarget quota.Target = "api_key"
)

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/serverlock"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
@ -42,38 +41,19 @@ type UserAuthTokenService struct {
log log.Logger log log.Logger
} }
type ActiveTokenService interface {
ActiveTokenCount(ctx context.Context, _ *quota.ScopeParameters) (*quota.Map, error)
}
type ActiveAuthTokenService struct { type ActiveAuthTokenService struct {
cfg *setting.Cfg cfg *setting.Cfg
sqlStore db.DB sqlStore db.DB
} }
func ProvideActiveAuthTokenService(cfg *setting.Cfg, sqlStore db.DB, quotaService quota.Service) (*ActiveAuthTokenService, error) { func ProvideActiveAuthTokenService(cfg *setting.Cfg, sqlStore db.DB) *ActiveAuthTokenService {
s := &ActiveAuthTokenService{ return &ActiveAuthTokenService{
cfg: cfg, cfg: cfg,
sqlStore: sqlStore, sqlStore: sqlStore,
} }
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return s, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: s.ActiveTokenCount,
}); err != nil {
return s, err
}
return s, nil
} }
func (a *ActiveAuthTokenService) ActiveTokenCount(ctx context.Context, _ *quota.ScopeParameters) (*quota.Map, error) { func (a *ActiveAuthTokenService) ActiveTokenCount(ctx context.Context) (int64, error) {
var count int64 var count int64
var err error var err error
err = a.sqlStore.WithDbSession(ctx, func(dbSession *db.Session) error { err = a.sqlStore.WithDbSession(ctx, func(dbSession *db.Session) error {
@ -86,14 +66,7 @@ func (a *ActiveAuthTokenService) ActiveTokenCount(ctx context.Context, _ *quota.
return err return err
}) })
tag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope) return count, err
if err != nil {
return nil, err
}
u := &quota.Map{}
u.Set(tag, count)
return u, err
} }
func (s *UserAuthTokenService) CreateToken(ctx context.Context, user *user.User, clientIP net.IP, userAgent string) (*models.UserToken, error) { func (s *UserAuthTokenService) CreateToken(ctx context.Context, user *user.User, clientIP net.IP, userAgent string) (*models.UserToken, error) {
@ -499,19 +472,3 @@ func hashToken(token string) string {
hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) hashBytes := sha256.Sum256([]byte(token + setting.SecretKey))
return hex.EncodeToString(hashBytes[:]) return hex.EncodeToString(hashBytes[:])
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.Session)
return limits, nil
}

View File

@ -14,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -41,12 +40,8 @@ func TestUserAuthToken(t *testing.T) {
userToken := createToken() userToken := createToken()
t.Run("Can count active tokens", func(t *testing.T) { t.Run("Can count active tokens", func(t *testing.T) {
m, err := ctx.activeTokenService.ActiveTokenCount(context.Background(), &quota.ScopeParameters{}) count, err := ctx.activeTokenService.ActiveTokenCount(context.Background())
require.Nil(t, err) require.Nil(t, err)
tag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope)
require.NoError(t, err)
count, ok := m.Get(tag)
require.True(t, ok)
require.Equal(t, int64(1), count) require.Equal(t, int64(1), count)
}) })
@ -213,12 +208,8 @@ func TestUserAuthToken(t *testing.T) {
require.Nil(t, notGood) require.Nil(t, notGood)
t.Run("should not find active token when expired", func(t *testing.T) { t.Run("should not find active token when expired", func(t *testing.T) {
m, err := ctx.activeTokenService.ActiveTokenCount(context.Background(), &quota.ScopeParameters{}) count, err := ctx.activeTokenService.ActiveTokenCount(context.Background())
require.Nil(t, err) require.Nil(t, err)
tag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope)
require.NoError(t, err)
count, ok := m.Get(tag)
require.True(t, ok)
require.Equal(t, int64(0), count) require.Equal(t, int64(0), count)
}) })
}) })

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
) )
type userAuthToken struct { type userAuthToken struct {
@ -72,8 +71,3 @@ func (uat *userAuthToken) toUserToken(ut *models.UserToken) error {
return nil return nil
} }
const (
QuotaTargetSrv quota.TargetSrv = "auth"
QuotaTarget quota.Target = "session"
)

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboardimport" "github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
@ -65,9 +64,9 @@ func (api *ImportDashboardAPI) ImportDashboard(c *models.ReqContext) response.Re
return response.Error(http.StatusUnprocessableEntity, "Dashboard must be set", nil) return response.Error(http.StatusUnprocessableEntity, "Dashboard must be set", nil)
} }
limitReached, err := api.quotaService.QuotaReached(c, dashboards.QuotaTargetSrv) limitReached, err := api.quotaService.QuotaReached(c, "dashboard")
if err != nil { if err != nil {
return response.Err(err) return response.Error(500, "failed to get quota", err)
} }
if limitReached { if limitReached {
@ -84,12 +83,12 @@ func (api *ImportDashboardAPI) ImportDashboard(c *models.ReqContext) response.Re
} }
type QuotaService interface { type QuotaService interface {
QuotaReached(c *models.ReqContext, target quota.TargetSrv) (bool, error) QuotaReached(c *models.ReqContext, target string) (bool, error)
} }
type quotaServiceFunc func(c *models.ReqContext, target quota.TargetSrv) (bool, error) type quotaServiceFunc func(c *models.ReqContext, target string) (bool, error)
func (fn quotaServiceFunc) QuotaReached(c *models.ReqContext, target quota.TargetSrv) (bool, error) { func (fn quotaServiceFunc) QuotaReached(c *models.ReqContext, target string) (bool, error) {
return fn(c, target) return fn(c, target)
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboardimport" "github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/web/webtest" "github.com/grafana/grafana/pkg/web/webtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -166,10 +165,10 @@ func (s *serviceMock) ImportDashboard(ctx context.Context, req *dashboardimport.
return nil, nil return nil, nil
} }
func quotaReached(c *models.ReqContext, target quota.TargetSrv) (bool, error) { func quotaReached(c *models.ReqContext, target string) (bool, error) {
return true, nil return true, nil
} }
func quotaNotReached(c *models.ReqContext, target quota.TargetSrv) (bool, error) { func quotaNotReached(c *models.ReqContext, target string) (bool, error) {
return false, nil return false, nil
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
) )
// DashboardService is a service for operating on dashboards. // DashboardService is a service for operating on dashboards.
@ -78,7 +77,6 @@ type Store interface {
ValidateDashboardBeforeSave(ctx context.Context, dashboard *models.Dashboard, overwrite bool) (bool, error) ValidateDashboardBeforeSave(ctx context.Context, dashboard *models.Dashboard, overwrite bool) (bool, error)
DeleteACLByUser(context.Context, int64) error DeleteACLByUser(context.Context, int64) error
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
// CountDashboardsInFolder returns the number of dashboards associated with // CountDashboardsInFolder returns the number of dashboards associated with
// the given parent folder ID. // the given parent folder ID.
CountDashboardsInFolder(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error) CountDashboardsInFolder(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error)

View File

@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
@ -27,10 +26,7 @@ func TestIntegrationDashboardACLDataAccess(t *testing.T) {
setup := func(t *testing.T) { setup := func(t *testing.T) {
sqlStore = db.InitTestDB(t) sqlStore = db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
var err error
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
currentUser = createUser(t, sqlStore, "viewer", "Viewer", false) currentUser = createUser(t, sqlStore, "viewer", "Viewer", false)
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp") savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
childDash = insertTestDashboard(t, dashboardStore, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp") childDash = insertTestDashboard(t, dashboardStore, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")

View File

@ -16,8 +16,6 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion" dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
@ -44,23 +42,8 @@ type DashboardTag struct {
// DashboardStore implements the Store interface // DashboardStore implements the Store interface
var _ dashboards.Store = (*DashboardStore)(nil) var _ dashboards.Store = (*DashboardStore)(nil)
func ProvideDashboardStore(sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tagService tag.Service, quotaService quota.Service) (*DashboardStore, error) { func ProvideDashboardStore(sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tagService tag.Service) *DashboardStore {
s := &DashboardStore{store: sqlStore, cfg: cfg, log: log.New("dashboard-store"), features: features, tagService: tagService} return &DashboardStore{store: sqlStore, cfg: cfg, log: log.New("dashboard-store"), features: features, tagService: tagService}
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return nil, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: dashboards.QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: s.Count,
}); err != nil {
return nil, err
}
return s, nil
} }
func (d *DashboardStore) emitEntityEvent() bool { func (d *DashboardStore) emitEntityEvent() bool {
@ -308,50 +291,6 @@ func (d *DashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context
}) })
} }
func (d *DashboardStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
type result struct {
Count int64
}
r := result{}
if err := d.store.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM dashboard WHERE is_folder=%s", d.store.GetDialect().BooleanStr(false))
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.GlobalScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
if scopeParams.OrgID != 0 {
if err := d.store.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM dashboard WHERE org_id=? AND is_folder=%s", d.store.GetDialect().BooleanStr(false))
if _, err := sess.SQL(rawSQL, scopeParams.OrgID).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.OrgScope)
if err != nil {
return nil, err
}
u.Set(tag, r.Count)
}
}
return u, nil
}
func getExistingDashboardByIdOrUidForUpdate(sess *db.Session, dash *models.Dashboard, dialect migrator.Dialect, overwrite bool) (bool, error) { func getExistingDashboardByIdOrUidForUpdate(sess *db.Session, dash *models.Dashboard, dialect migrator.Dialect, overwrite bool) (bool, error) {
dashWithIdExists := false dashWithIdExists := false
isParentFolderChanged := false isParentFolderChanged := false
@ -1079,27 +1018,6 @@ func (d *DashboardStore) GetDashboardTags(ctx context.Context, query *models.Get
}) })
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.GlobalScope)
if err != nil {
return &quota.Map{}, err
}
orgQuotaTag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.OrgScope)
if err != nil {
return &quota.Map{}, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.Dashboard)
limits.Set(orgQuotaTag, cfg.Quota.Org.Dashboard)
return limits, nil
}
// This will be updated to take CountDashboardsInFolderQuery as an argument and // This will be updated to take CountDashboardsInFolderQuery as an argument and
// lookup dashboards using the ParentFolderUID when the NestedFolder // lookup dashboards using the ParentFolderUID when the NestedFolder
// implementation is complete. // implementation is complete.

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -34,10 +33,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
setup := func() { setup := func() {
sqlStore = db.InitTestDB(t) sqlStore = db.InitTestDB(t)
sqlStore.Cfg.RBACEnabled = false sqlStore.Cfg.RBACEnabled = false
quotaService := quotatest.New(false, nil) dashboardStore = ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
var err error
dashboardStore, err = ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp") folder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod", "webapp") dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod", "webapp")
childDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, folder.Id, false, "prod", "webapp") childDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, folder.Id, false, "prod", "webapp")
@ -190,9 +186,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
setup2 := func() { setup2 := func() {
sqlStore = db.InitTestDB(t) sqlStore = db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod") folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod")
folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod") folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod")
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod") dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod")
@ -297,9 +291,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
setup3 := func() { setup3 := func() {
sqlStore = db.InitTestDB(t) sqlStore = db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod") folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod")
folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod") folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod")
insertTestDashboard(t, dashboardStore, "folder in another org", 2, 0, true, "prod") insertTestDashboard(t, dashboardStore, "folder in another org", 2, 0, true, "prod")
@ -481,9 +473,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
var sqlStore *sqlstore.SQLStore var sqlStore *sqlstore.SQLStore
var folder1, folder2 *models.Dashboard var folder1, folder2 *models.Dashboard
sqlStore = db.InitTestDB(t) sqlStore = db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder2 = insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod") folder2 = insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
_ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.Id, false, "prod") _ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.Id, false, "prod")
folder1 = insertTestDashboard(t, dashboardStore, title, orgId, 0, true, "prod") folder1 = insertTestDashboard(t, dashboardStore, title, orgId, 0, true, "prod")
@ -498,9 +488,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
t.Run("GetFolderByUID", func(t *testing.T) { t.Run("GetFolderByUID", func(t *testing.T) {
var orgId int64 = 1 var orgId int64 = 1
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod") folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.Id, false, "prod") dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.Id, false, "prod")
@ -524,9 +512,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
t.Run("GetFolderByID", func(t *testing.T) { t.Run("GetFolderByID", func(t *testing.T) {
var orgId int64 = 1 var orgId int64 = 1
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod") folder := insertTestDashboard(t, dashboardStore, "TEST", orgId, 0, true, "prod")
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.Id, false, "prod") dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.Id, false, "prod")

View File

@ -10,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
) )
@ -19,9 +18,7 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
t.Skip("skipping integration test") t.Skip("skipping integration test")
} }
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
folderCmd := models.SaveDashboardCommand{ folderCmd := models.SaveDashboardCommand{
OrgId: 1, OrgId: 1,

View File

@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/publicdashboards/database" "github.com/grafana/grafana/pkg/services/publicdashboards/database"
publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models" publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/services/star"
@ -43,10 +42,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
starService = starimpl.ProvideService(sqlStore, cfg) starService = starimpl.ProvideService(sqlStore, cfg)
quotaService := quotatest.New(false, nil) dashboardStore = ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg))
var err error
dashboardStore, err = ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp") savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp") savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.Id, false, "prod") insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.Id, false, "prod")
@ -589,9 +585,7 @@ func TestIntegrationDashboardDataAccessGivenPluginWithImportedDashboards(t *test
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.IsFeatureToggleEnabled = func(key string) bool { return false } cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
pluginId := "test-app" pluginId := "test-app"
appFolder := insertTestDashboardForPlugin(t, dashboardStore, "app-test", 1, 0, true, pluginId) appFolder := insertTestDashboardForPlugin(t, dashboardStore, "app-test", 1, 0, true, pluginId)
@ -603,7 +597,7 @@ func TestIntegrationDashboardDataAccessGivenPluginWithImportedDashboards(t *test
OrgId: 1, OrgId: 1,
} }
err = dashboardStore.GetDashboardsByPluginID(context.Background(), &query) err := dashboardStore.GetDashboardsByPluginID(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
} }
@ -615,9 +609,7 @@ func TestIntegrationDashboard_SortingOptions(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.IsFeatureToggleEnabled = func(key string) bool { return false } cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false) dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false)
dashA := insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false) dashA := insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false)
@ -668,9 +660,7 @@ func TestIntegrationDashboard_Filter(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.IsFeatureToggleEnabled = func(key string) bool { return false } cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
quotaService := quotatest.New(false, nil) dashboardStore := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false) insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false)
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false) dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false)
qNoFilter := &models.FindPersistedDashboardsQuery{ qNoFilter := &models.FindPersistedDashboardsQuery{

View File

@ -4,7 +4,6 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
) )
@ -31,11 +30,6 @@ type DashboardSearchProjection struct {
SortMeta int64 SortMeta int64
} }
const (
QuotaTargetSrv quota.TargetSrv = "dashboard"
QuotaTarget quota.Target = "dashboard"
)
type CountDashboardsInFolderQuery struct { type CountDashboardsInFolderQuery struct {
FolderUID string FolderUID string
} }

View File

@ -17,7 +17,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -43,7 +42,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
}), }),
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardNotFound, err) assert.Equal(t, dashboards.ErrDashboardNotFound, err)
}) })
@ -63,7 +62,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: false, Overwrite: false,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardNotFound, err) assert.Equal(t, dashboards.ErrDashboardNotFound, err)
}) })
@ -105,7 +104,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sqlStore) err := callSaveWithError(cmd, sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId) assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
@ -125,7 +124,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
@ -145,7 +144,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
@ -166,7 +165,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
@ -187,7 +186,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
@ -208,7 +207,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
@ -229,7 +228,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
@ -250,7 +249,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
@ -271,7 +270,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
@ -292,7 +291,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: true, Overwrite: true,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err) require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId) assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
@ -433,7 +432,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardFolderNotFound, err) assert.Equal(t, dashboards.ErrDashboardFolderNotFound, err)
}) })
@ -449,7 +448,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err) assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err)
}) })
@ -489,7 +488,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err) assert.Equal(t, dashboards.ErrDashboardVersionMismatch, err)
}) })
@ -528,7 +527,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err) assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
}) })
@ -544,7 +543,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err) assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
}) })
@ -560,7 +559,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err) assert.Equal(t, dashboards.ErrDashboardWithSameNameInFolderExists, err)
}) })
}) })
@ -648,7 +647,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardWithSameUIDExists, err) assert.Equal(t, dashboards.ErrDashboardWithSameUIDExists, err)
}) })
@ -712,7 +711,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err) assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
}) })
@ -728,7 +727,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err) assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
}) })
@ -744,7 +743,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err) assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
}) })
@ -760,7 +759,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err) assert.Equal(t, dashboards.ErrDashboardTypeMismatch, err)
}) })
@ -775,7 +774,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardWithSameNameAsFolder, err) assert.Equal(t, dashboards.ErrDashboardWithSameNameAsFolder, err)
}) })
@ -790,7 +789,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
Overwrite: shouldOverwrite, Overwrite: shouldOverwrite,
} }
err := callSaveWithError(t, cmd, sc.sqlStore) err := callSaveWithError(cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardFolderWithSameNameAsDashboard, err) assert.Equal(t, dashboards.ErrDashboardFolderWithSameNameAsDashboard, err)
}) })
}) })
@ -822,9 +821,7 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
@ -881,9 +878,7 @@ func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
@ -897,14 +892,12 @@ func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore
return res return res
} }
func callSaveWithError(t *testing.T, cmd models.SaveDashboardCommand, sqlStore db.DB) error { func callSaveWithError(cmd models.SaveDashboardCommand, sqlStore db.DB) error {
dto := toSaveDashboardDto(cmd) dto := toSaveDashboardDto(cmd)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
@ -912,7 +905,7 @@ func callSaveWithError(t *testing.T, cmd models.SaveDashboardCommand, sqlStore d
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(),
accesscontrolmock.New(), accesscontrolmock.New(),
) )
_, err = service.SaveDashboard(context.Background(), &dto, false) _, err := service.SaveDashboard(context.Background(), &dto, false)
return err return err
} }
@ -941,9 +934,7 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),
@ -981,9 +972,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *mo
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
service := ProvideDashboardService( service := ProvideDashboardService(
cfg, dashboardStore, &dummyDashAlertExtractor{}, cfg, dashboardStore, &dummyDashAlertExtractor{},
featuremgmt.WithFeatures(), featuremgmt.WithFeatures(),

View File

@ -6,7 +6,6 @@ import (
context "context" context "context"
models "github.com/grafana/grafana/pkg/models" models "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
) )
@ -474,10 +473,6 @@ type mockConstructorTestingTNewFakeDashboardStore interface {
Cleanup(func()) Cleanup(func())
} }
func (_m *FakeDashboardStore) Count(context.Context, *quota.ScopeParameters) (*quota.Map, error) {
return nil, nil
}
// NewFakeDashboardStore creates a new instance of FakeDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // NewFakeDashboardStore creates a new instance of FakeDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFakeDashboardStore(t mockConstructorTestingTNewFakeDashboardStore) *FakeDashboardStore { func NewFakeDashboardStore(t mockConstructorTestingTNewFakeDashboardStore) *FakeDashboardStore {
mock := &FakeDashboardStore{} mock := &FakeDashboardStore{}

View File

@ -4,7 +4,6 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
) )
@ -194,8 +193,3 @@ type DatasourcesPermissionFilterQuery struct {
Datasources []*DataSource Datasources []*DataSource
Result []*DataSource Result []*DataSource
} }
const (
QuotaTargetSrv quota.TargetSrv = "data_source"
QuotaTarget quota.Target = "data_source"
)

View File

@ -20,7 +20,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/kvstore" "github.com/grafana/grafana/pkg/services/secrets/kvstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -53,8 +52,7 @@ type cachedRoundTripper struct {
func ProvideService( func ProvideService(
db db.DB, secretsService secrets.Service, secretsStore kvstore.SecretsKVStore, cfg *setting.Cfg, db db.DB, secretsService secrets.Service, secretsStore kvstore.SecretsKVStore, cfg *setting.Cfg,
features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl, datasourcePermissionsService accesscontrol.DatasourcePermissionsService, features featuremgmt.FeatureToggles, ac accesscontrol.AccessControl, datasourcePermissionsService accesscontrol.DatasourcePermissionsService,
quotaService quota.Service, ) *Service {
) (*Service, error) {
dslogger := log.New("datasources") dslogger := log.New("datasources")
store := &SqlStore{db: db, logger: dslogger} store := &SqlStore{db: db, logger: dslogger}
s := &Service{ s := &Service{
@ -75,23 +73,7 @@ func ProvideService(
ac.RegisterScopeAttributeResolver(NewNameScopeResolver(store)) ac.RegisterScopeAttributeResolver(NewNameScopeResolver(store))
ac.RegisterScopeAttributeResolver(NewIDScopeResolver(store)) ac.RegisterScopeAttributeResolver(NewIDScopeResolver(store))
defaultLimits, err := readQuotaConfig(cfg) return s
if err != nil {
return nil, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: datasources.QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: s.Usage,
}); err != nil {
return nil, err
}
return s, nil
}
func (s *Service) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
return s.SQLStore.Count(ctx, scopeParams)
} }
// DataSourceRetriever interface for retrieving a datasource. // DataSourceRetriever interface for retrieving a datasource.
@ -609,24 +591,3 @@ func (s *Service) fillWithSecureJSONData(ctx context.Context, cmd *datasources.U
return nil return nil
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, quota.GlobalScope)
if err != nil {
return limits, err
}
orgQuotaTag, err := quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, quota.OrgScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.DataSource)
limits.Set(orgQuotaTag, cfg.Quota.Org.DataSource)
return limits, nil
}

View File

@ -21,7 +21,6 @@ import (
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
@ -201,9 +200,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
rt1, err := dsService.GetHTTPTransport(context.Background(), &ds, provider) rt1, err := dsService.GetHTTPTransport(context.Background(), &ds, provider)
require.NoError(t, err) require.NoError(t, err)
@ -238,9 +235,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
@ -289,9 +284,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
@ -337,9 +330,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
@ -382,9 +373,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
@ -417,9 +406,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
@ -486,9 +473,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Id: 1, Id: 1,
Url: "http://k8s:8001", Url: "http://k8s:8001",
@ -522,9 +507,7 @@ func TestService_GetHttpTransport(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
ds := datasources.DataSource{ ds := datasources.DataSource{
Type: datasources.DS_ES, Type: datasources.DS_ES,
@ -561,9 +544,7 @@ func TestService_getTimeout(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
for _, tc := range testCases { for _, tc := range testCases {
ds := &datasources.DataSource{ ds := &datasources.DataSource{
@ -584,9 +565,7 @@ func TestService_GetDecryptedValues(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
jsonData := map[string]string{ jsonData := map[string]string{
"password": "securePassword", "password": "securePassword",
@ -612,9 +591,7 @@ func TestService_GetDecryptedValues(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
quotaService := quotatest.New(false, nil) dsService := ProvideService(sqlStore, secretsService, secretsStore, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
jsonData := map[string]string{ jsonData := map[string]string{
"password": "securePassword", "password": "securePassword",

View File

@ -16,8 +16,6 @@ import (
"github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -31,8 +29,6 @@ type Store interface {
AddDataSource(context.Context, *datasources.AddDataSourceCommand) error AddDataSource(context.Context, *datasources.AddDataSourceCommand) error
UpdateDataSource(context.Context, *datasources.UpdateDataSourceCommand) error UpdateDataSource(context.Context, *datasources.UpdateDataSourceCommand) error
GetAllDataSources(ctx context.Context, query *datasources.GetAllDataSourcesQuery) error GetAllDataSources(ctx context.Context, query *datasources.GetAllDataSourcesQuery) error
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
} }
type SqlStore struct { type SqlStore struct {
@ -175,50 +171,6 @@ func (ss *SqlStore) DeleteDataSource(ctx context.Context, cmd *datasources.Delet
}) })
} }
func (ss *SqlStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
type result struct {
Count int64
}
r := result{}
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) AS count FROM data_source"
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, quota.GlobalScope)
if err != nil {
return u, err
}
u.Set(tag, r.Count)
}
if scopeParams.OrgID != 0 {
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) AS count FROM data_source WHERE org_id=?"
if _, err := sess.SQL(rawSQL, scopeParams.OrgID).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, quota.OrgScope)
if err != nil {
return u, err
}
u.Set(tag, r.Count)
}
}
return u, nil
}
func (ss *SqlStore) AddDataSource(ctx context.Context, cmd *datasources.AddDataSourceCommand) error { func (ss *SqlStore) AddDataSource(ctx context.Context, cmd *datasources.AddDataSourceCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error { return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
existing := datasources.DataSource{OrgId: cmd.OrgId, Name: cmd.Name} existing := datasources.DataSource{OrgId: cmd.OrgId, Name: cmd.Name}

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -584,8 +583,7 @@ func TestIntegrationGetChildren(t *testing.T) {
func CreateOrg(t *testing.T, db *sqlstore.SQLStore) int64 { func CreateOrg(t *testing.T, db *sqlstore.SQLStore) int64 {
t.Helper() t.Helper()
orgService, err := orgimpl.ProvideService(db, db.Cfg, quotatest.New(false, nil)) orgService := orgimpl.ProvideService(db, db.Cfg)
require.NoError(t, err)
orgID, err := orgService.GetOrCreate(context.Background(), "test-org") orgID, err := orgService.GetOrCreate(context.Background(), "test-org")
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {

View File

@ -19,7 +19,6 @@ import (
dashdb "github.com/grafana/grafana/pkg/services/dashboards/database" dashdb "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest" "github.com/grafana/grafana/pkg/services/licensing/licensingtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamimpl" "github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -592,9 +591,7 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
toSave.SetUid(uid) toSave.SetUid(uid)
// seed dashboard // seed dashboard
quotaService := quotatest.New(false, nil) dashStore := dashdb.ProvideDashboardStore(store, store.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(store, store.Cfg))
dashStore, err := dashdb.ProvideDashboardStore(store, store.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(store, store.Cfg), quotaService)
require.NoError(t, err)
dash, err := dashStore.SaveDashboard(context.Background(), models.SaveDashboardCommand{ dash, err := dashStore.SaveDashboard(context.Background(), models.SaveDashboardCommand{
Dashboard: toSave.Data, Dashboard: toSave.Data,
UserId: 1, UserId: 1,
@ -606,8 +603,7 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []acce
license := licensingtest.NewFakeLicensing() license := licensingtest.NewFakeLicensing()
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
teamSvc := teamimpl.ProvideService(store, store.Cfg) teamSvc := teamimpl.ProvideService(store, store.Cfg)
userSvc, err := userimpl.ProvideService(store, nil, store.Cfg, nil, nil, quotatest.New(false, nil)) userSvc := userimpl.ProvideService(store, nil, store.Cfg, nil, nil)
require.NoError(t, err)
folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions(
setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc, userSvc) setting.NewCfg(), routing.NewRouteRegister(), store, ac, license, &dashboards.FakeDashboardStore{}, ac, teamSvc, userSvc)

View File

@ -28,7 +28,6 @@ import (
"github.com/grafana/grafana/pkg/services/folder/folderimpl" "github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -279,9 +278,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash
cfg.RBACEnabled = false cfg.RBACEnabled = false
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
cfg.IsFeatureToggleEnabled = features.IsEnabled cfg.IsFeatureToggleEnabled = features.IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashAlertExtractor := alerting.ProvideDashAlertExtractorService(nil, nil, nil) dashAlertExtractor := alerting.ProvideDashAlertExtractorService(nil, nil, nil)
ac := acmock.New() ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
@ -307,9 +304,7 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user user.S
ac := acmock.New() ac := acmock.New()
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
d := dashboardservice.ProvideDashboardService( d := dashboardservice.ProvideDashboardService(
cfg, dashboardStore, nil, cfg, dashboardStore, nil,
@ -410,9 +405,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
orgID := int64(1) orgID := int64(1)
role := org.RoleAdmin role := org.RoleAdmin
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
ac := acmock.New().WithDisabled() ac := acmock.New().WithDisabled()
// TODO: Update tests to work with rbac // TODO: Update tests to work with rbac
@ -449,7 +442,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
Login: userInDbName, Login: userInDbName,
} }
_, err = sqlStore.CreateUser(context.Background(), cmd) _, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)
sc := scenarioContext{ sc := scenarioContext{

View File

@ -26,7 +26,6 @@ import (
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest" "github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -692,9 +691,7 @@ func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil, nil) dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil, nil)
ac := acmock.New() ac := acmock.New()
service := dashboardservice.ProvideDashboardService( service := dashboardservice.ProvideDashboardService(
@ -718,9 +715,7 @@ func createFolderWithACL(t *testing.T, sqlStore db.DB, title string, user *user.
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
d := dashboardservice.ProvideDashboardService(cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac) d := dashboardservice.ProvideDashboardService(cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac)
s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, d, dashboardStore, features, folderPermissions, nil) s := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), cfg, d, dashboardStore, features, folderPermissions, nil)
@ -813,9 +808,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
orgID := int64(1) orgID := int64(1)
role := org.RoleAdmin role := org.RoleAdmin
sqlStore, cfg := db.InitTestDBwithCfg(t) sqlStore, cfg := db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
ac := acmock.New() ac := acmock.New()
@ -854,7 +847,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
Login: userInDbName, Login: userInDbName,
} }
_, err = sqlStore.CreateUser(context.Background(), cmd) _, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)
sc := scenarioContext{ sc := scenarioContext{

View File

@ -71,17 +71,13 @@ func (ls *Implementation) UpsertUser(ctx context.Context, cmd *models.UpsertUser
return login.ErrSignupNotAllowed return login.ErrSignupNotAllowed
} }
// we may insert in both user and org_user tables limitReached, errLimit := ls.QuotaService.QuotaReached(cmd.ReqContext, "user")
// therefore we need to query check quota for both user and org services if errLimit != nil {
for _, srv := range []string{user.QuotaTargetSrv, org.QuotaTargetSrv} { cmd.ReqContext.Logger.Warn("Error getting user quota.", "error", errLimit)
limitReached, errLimit := ls.QuotaService.QuotaReached(cmd.ReqContext, quota.TargetSrv(srv)) return login.ErrGettingUserQuota
if errLimit != nil { }
cmd.ReqContext.Logger.Warn("Error getting user quota.", "error", errLimit) if limitReached {
return login.ErrGettingUserQuota return login.ErrUsersQuotaReached
}
if limitReached {
return login.ErrUsersQuotaReached
}
} }
result, errCreateUser := ls.createUser(extUser) result, errCreateUser := ls.createUser(extUser)

View File

@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/services/login/logintest" "github.com/grafana/grafana/pkg/services/login/logintest"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -26,7 +26,7 @@ func Test_syncOrgRoles_doesNotBreakWhenTryingToRemoveLastOrgAdmin(t *testing.T)
authInfoMock := &logintest.AuthInfoServiceFake{} authInfoMock := &logintest.AuthInfoServiceFake{}
login := Implementation{ login := Implementation{
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{},
AuthInfoService: authInfoMock, AuthInfoService: authInfoMock,
SQLStore: nil, SQLStore: nil,
userService: usertest.NewUserServiceFake(), userService: usertest.NewUserServiceFake(),
@ -51,7 +51,7 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
orgService.ExpectedOrgListResponse = createResponseWithOneErrLastOrgAdminItem() orgService.ExpectedOrgListResponse = createResponseWithOneErrLastOrgAdminItem()
login := Implementation{ login := Implementation{
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{},
AuthInfoService: authInfoMock, AuthInfoService: authInfoMock,
SQLStore: nil, SQLStore: nil,
userService: usertest.NewUserServiceFake(), userService: usertest.NewUserServiceFake(),
@ -66,7 +66,7 @@ func Test_syncOrgRoles_whenTryingToRemoveLastOrgLogsError(t *testing.T) {
func Test_teamSync(t *testing.T) { func Test_teamSync(t *testing.T) {
authInfoMock := &logintest.AuthInfoServiceFake{} authInfoMock := &logintest.AuthInfoServiceFake{}
login := Implementation{ login := Implementation{
QuotaService: quotatest.New(false, nil), QuotaService: &quotaimpl.Service{},
AuthInfoService: authInfoMock, AuthInfoService: authInfoMock,
} }

View File

@ -145,28 +145,3 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
alertRules: api.AlertRules, alertRules: api.AlertRules,
}), m) }), m)
} }
func (api *API) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
if orgUsage, err := api.RuleStore.Count(ctx, scopeParams.OrgID); err != nil {
return u, err
} else {
tag, err := quota.NewTag(models.QuotaTargetSrv, models.QuotaTarget, quota.OrgScope)
if err != nil {
return u, err
}
u.Set(tag, orgUsage)
}
if globalUsage, err := api.RuleStore.Count(ctx, 0); err != nil {
return u, err
} else {
tag, err := quota.NewTag(models.QuotaTargetSrv, models.QuotaTarget, quota.GlobalScope)
if err != nil {
return u, err
}
u.Set(tag, globalUsage)
}
return u, nil
}

View File

@ -393,7 +393,7 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *models.ReqContext, groupKey ngmod
} }
if len(finalChanges.New) > 0 { if len(finalChanges.New) > 0 {
limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, ngmodels.QuotaTargetSrv, &quota.ScopeParameters{ limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, "alert_rule", &quota.ScopeParameters{
OrgID: c.OrgID, OrgID: c.OrgID,
UserID: c.UserID, UserID: c.UserID,
}) // alert rule is table name }) // alert rule is table name

View File

@ -23,6 +23,4 @@ type RuleStore interface {
// IncreaseVersionForAllRulesInNamespace Increases version for all rules that have specified namespace. Returns all rules that belong to the namespace // IncreaseVersionForAllRulesInNamespace Increases version for all rules that have specified namespace. Returns all rules that belong to the namespace
IncreaseVersionForAllRulesInNamespace(ctx context.Context, orgID int64, namespaceUID string) ([]ngmodels.AlertRuleKeyWithVersion, error) IncreaseVersionForAllRulesInNamespace(ctx context.Context, orgID int64, namespaceUID string) ([]ngmodels.AlertRuleKeyWithVersion, error)
Count(ctx context.Context, orgID int64) (int64, error)
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/util/cmputil" "github.com/grafana/grafana/pkg/util/cmputil"
) )
@ -461,11 +460,6 @@ func (g RulesGroup) SortByGroupIndex() {
}) })
} }
const (
QuotaTargetSrv quota.TargetSrv = "ngalert"
QuotaTarget quota.Target = "alert_rule"
)
type ruleKeyContextKey struct{} type ruleKeyContextKey struct{}
func WithRuleKey(ctx context.Context, ruleKey AlertRuleKey) context.Context { func WithRuleKey(ctx context.Context, ruleKey AlertRuleKey) context.Context {

View File

@ -239,19 +239,6 @@ func (ng *AlertNG) init() error {
} }
api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics()) api.RegisterAPIEndpoints(ng.Metrics.GetAPIMetrics())
defaultLimits, err := readQuotaConfig(ng.Cfg)
if err != nil {
return err
}
if err := ng.QuotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: models.QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: api.Usage,
}); err != nil {
return err
}
log.RegisterContextualLogProvider(func(ctx context.Context) ([]interface{}, bool) { log.RegisterContextualLogProvider(func(ctx context.Context) ([]interface{}, bool) {
key, ok := models.RuleKeyFromContext(ctx) key, ok := models.RuleKeyFromContext(ctx)
if !ok { if !ok {
@ -321,32 +308,3 @@ func (ng *AlertNG) IsDisabled() bool {
} }
return !ng.Cfg.UnifiedAlerting.IsEnabled() return !ng.Cfg.UnifiedAlerting.IsEnabled()
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
var alertOrgQuota int64
var alertGlobalQuota int64
if cfg.UnifiedAlerting.IsEnabled() {
alertOrgQuota = cfg.Quota.Org.AlertRule
alertGlobalQuota = cfg.Quota.Global.AlertRule
}
globalQuotaTag, err := quota.NewTag(models.QuotaTargetSrv, models.QuotaTarget, quota.GlobalScope)
if err != nil {
return limits, err
}
orgQuotaTag, err := quota.NewTag(models.QuotaTargetSrv, models.QuotaTarget, quota.OrgScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, alertGlobalQuota)
limits.Set(orgQuotaTag, alertOrgQuota)
return limits, nil
}

View File

@ -48,7 +48,7 @@ type RuleStore interface {
// //
//go:generate mockery --name QuotaChecker --structname MockQuotaChecker --inpackage --filename quota_checker_mock.go --with-expecter //go:generate mockery --name QuotaChecker --structname MockQuotaChecker --inpackage --filename quota_checker_mock.go --with-expecter
type QuotaChecker interface { type QuotaChecker interface {
CheckQuotaReached(ctx context.Context, target quota.TargetSrv, scopeParams *quota.ScopeParameters) (bool, error) CheckQuotaReached(ctx context.Context, target string, scopeParams *quota.ScopeParameters) (bool, error)
} }
// PersistConfig validates to config before eventually persisting it if no error occurs // PersistConfig validates to config before eventually persisting it if no error occurs

View File

@ -1,4 +1,4 @@
// Code generated by mockery v2.14.0. DO NOT EDIT. // Code generated by mockery v2.12.0. DO NOT EDIT.
package provisioning package provisioning
@ -7,6 +7,8 @@ import (
quota "github.com/grafana/grafana/pkg/services/quota" quota "github.com/grafana/grafana/pkg/services/quota"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
testing "testing"
) )
// MockQuotaChecker is an autogenerated mock type for the QuotaChecker type // MockQuotaChecker is an autogenerated mock type for the QuotaChecker type
@ -23,18 +25,18 @@ func (_m *MockQuotaChecker) EXPECT() *MockQuotaChecker_Expecter {
} }
// CheckQuotaReached provides a mock function with given fields: ctx, target, scopeParams // CheckQuotaReached provides a mock function with given fields: ctx, target, scopeParams
func (_m *MockQuotaChecker) CheckQuotaReached(ctx context.Context, target quota.TargetSrv, scopeParams *quota.ScopeParameters) (bool, error) { func (_m *MockQuotaChecker) CheckQuotaReached(ctx context.Context, target string, scopeParams *quota.ScopeParameters) (bool, error) {
ret := _m.Called(ctx, target, scopeParams) ret := _m.Called(ctx, target, scopeParams)
var r0 bool var r0 bool
if rf, ok := ret.Get(0).(func(context.Context, quota.TargetSrv, *quota.ScopeParameters) bool); ok { if rf, ok := ret.Get(0).(func(context.Context, string, *quota.ScopeParameters) bool); ok {
r0 = rf(ctx, target, scopeParams) r0 = rf(ctx, target, scopeParams)
} else { } else {
r0 = ret.Get(0).(bool) r0 = ret.Get(0).(bool)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, quota.TargetSrv, *quota.ScopeParameters) error); ok { if rf, ok := ret.Get(1).(func(context.Context, string, *quota.ScopeParameters) error); ok {
r1 = rf(ctx, target, scopeParams) r1 = rf(ctx, target, scopeParams)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
@ -49,16 +51,16 @@ type MockQuotaChecker_CheckQuotaReached_Call struct {
} }
// CheckQuotaReached is a helper method to define mock.On call // CheckQuotaReached is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - target quota.TargetSrv // - target string
// - scopeParams *quota.ScopeParameters // - scopeParams *quota.ScopeParameters
func (_e *MockQuotaChecker_Expecter) CheckQuotaReached(ctx interface{}, target interface{}, scopeParams interface{}) *MockQuotaChecker_CheckQuotaReached_Call { func (_e *MockQuotaChecker_Expecter) CheckQuotaReached(ctx interface{}, target interface{}, scopeParams interface{}) *MockQuotaChecker_CheckQuotaReached_Call {
return &MockQuotaChecker_CheckQuotaReached_Call{Call: _e.mock.On("CheckQuotaReached", ctx, target, scopeParams)} return &MockQuotaChecker_CheckQuotaReached_Call{Call: _e.mock.On("CheckQuotaReached", ctx, target, scopeParams)}
} }
func (_c *MockQuotaChecker_CheckQuotaReached_Call) Run(run func(ctx context.Context, target quota.TargetSrv, scopeParams *quota.ScopeParameters)) *MockQuotaChecker_CheckQuotaReached_Call { func (_c *MockQuotaChecker_CheckQuotaReached_Call) Run(run func(ctx context.Context, target string, scopeParams *quota.ScopeParameters)) *MockQuotaChecker_CheckQuotaReached_Call {
_c.Call.Run(func(args mock.Arguments) { _c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(quota.TargetSrv), args[2].(*quota.ScopeParameters)) run(args[0].(context.Context), args[1].(string), args[2].(*quota.ScopeParameters))
}) })
return _c return _c
} }
@ -68,13 +70,8 @@ func (_c *MockQuotaChecker_CheckQuotaReached_Call) Return(_a0 bool, _a1 error) *
return _c return _c
} }
type mockConstructorTestingTNewMockQuotaChecker interface { // NewMockQuotaChecker creates a new instance of MockQuotaChecker. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
mock.TestingT func NewMockQuotaChecker(t testing.TB) *MockQuotaChecker {
Cleanup(func())
}
// NewMockQuotaChecker creates a new instance of MockQuotaChecker. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewMockQuotaChecker(t mockConstructorTestingTNewMockQuotaChecker) *MockQuotaChecker {
mock := &MockQuotaChecker{} mock := &MockQuotaChecker{}
mock.Mock.Test(t) mock.Mock.Test(t)

View File

@ -10,7 +10,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
@ -269,29 +268,6 @@ func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertR
}) })
} }
// Count returns either the number of the alert rules under a specific org (if orgID is not zero)
// or the number of all the alert rules
func (st DBstore) Count(ctx context.Context, orgID int64) (int64, error) {
type result struct {
Count int64
}
r := result{}
err := st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) as count from alert_rule"
args := make([]interface{}, 0)
if orgID != 0 {
rawSQL += " WHERE org_id=?"
args = append(args, orgID)
}
if _, err := sess.SQL(rawSQL, args...).Get(&r); err != nil {
return err
}
return nil
})
return r.Count, err
}
func (st DBstore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) (int64, error) { func (st DBstore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) (int64, error) {
var interval int64 = 0 var interval int64 = 0
return interval, st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { return interval, st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error {

View File

@ -339,7 +339,3 @@ func (f *RuleStore) IncreaseVersionForAllRulesInNamespace(_ context.Context, org
} }
return result, nil return result, nil
} }
func (f *RuleStore) Count(ctx context.Context, orgID int64) (int64, error) {
return 0, nil
}

View File

@ -31,7 +31,6 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets/database" "github.com/grafana/grafana/pkg/services/secrets/database"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
@ -76,9 +75,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
m := metrics.NewNGAlert(prometheus.NewRegistry()) m := metrics.NewNGAlert(prometheus.NewRegistry())
sqlStore := db.InitTestDB(tb) sqlStore := db.InitTestDB(tb)
secretsService := secretsManager.SetupTestService(tb, database.ProvideSecretsStore(sqlStore)) secretsService := secretsManager.SetupTestService(tb, database.ProvideSecretsStore(sqlStore))
quotaService := quotatest.New(false, nil) dashboardStore := databasestore.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := databasestore.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(tb, err)
ac := acmock.New() ac := acmock.New()
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
@ -95,7 +92,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardService, dashboardStore, features, folderPermissions, nil) folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardService, dashboardStore, features, folderPermissions, nil)
ng, err := ngalert.ProvideService( ng, err := ngalert.ProvideService(
cfg, &FakeFeatures{}, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, quotatest.New(false, nil), cfg, &FakeFeatures{}, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, nil,
secretsService, nil, m, folderService, ac, &dashboards.FakeDashboardService{}, nil, bus, ac, annotationstest.NewFakeAnnotationsRepo(), secretsService, nil, m, folderService, ac, &dashboards.FakeDashboardService{}, nil, bus, ac, annotationstest.NewFakeAnnotationsRepo(),
) )
require.NoError(tb, err) require.NoError(tb, err)

View File

@ -204,9 +204,3 @@ func (o ByOrgName) Less(i, j int) bool {
return o[i].Name < o[j].Name return o[i].Name < o[j].Name
} }
const (
QuotaTargetSrv string = "org"
OrgQuotaTarget string = "org"
OrgUserQuotaTarget string = "org_user"
)

View File

@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -19,9 +18,9 @@ type Service struct {
log log.Logger log log.Logger
} }
func ProvideService(db db.DB, cfg *setting.Cfg, quotaService quota.Service) (org.Service, error) { func ProvideService(db db.DB, cfg *setting.Cfg) org.Service {
log := log.New("org service") log := log.New("org service")
s := &Service{ return &Service{
store: &sqlStore{ store: &sqlStore{
db: db, db: db,
dialect: db.GetDialect(), dialect: db.GetDialect(),
@ -31,24 +30,6 @@ func ProvideService(db db.DB, cfg *setting.Cfg, quotaService quota.Service) (org
cfg: cfg, cfg: cfg,
log: log, log: log,
} }
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return s, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: quota.TargetSrv(org.QuotaTargetSrv),
DefaultLimits: defaultLimits,
Reporter: s.Usage,
}); err != nil {
return s, nil
}
return s, nil
}
func (s *Service) Usage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
return s.store.Count(ctx, scopeParams)
} }
func (s *Service) GetIDForNewUser(ctx context.Context, cmd org.GetOrgIDForNewUserCommand) (int64, error) { func (s *Service) GetIDForNewUser(ctx context.Context, cmd org.GetOrgIDForNewUserCommand) (int64, error) {
@ -198,31 +179,3 @@ func (s *Service) GetOrgUsers(ctx context.Context, query *org.GetOrgUsersQuery)
func (s *Service) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUsersQuery) (*org.SearchOrgUsersQueryResult, error) { func (s *Service) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUsersQuery) (*org.SearchOrgUsersQueryResult, error) {
return s.store.SearchOrgUsers(ctx, query) return s.store.SearchOrgUsers(ctx, query)
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgQuotaTarget), quota.GlobalScope)
if err != nil {
return limits, err
}
orgQuotaTag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope)
if err != nil {
return limits, err
}
userTag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.Org)
// users per org
limits.Set(orgQuotaTag, cfg.Quota.Org.User)
// orgs per user
limits.Set(userTag, cfg.Quota.User.Org)
return limits, nil
}

View File

@ -5,7 +5,6 @@ import (
"testing" "testing"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -136,7 +135,3 @@ func (f *FakeOrgStore) SearchOrgUsers(ctx context.Context, query *org.SearchOrgU
func (f *FakeOrgStore) RemoveOrgUser(ctx context.Context, cmd *org.RemoveOrgUserCommand) error { func (f *FakeOrgStore) RemoveOrgUser(ctx context.Context, cmd *org.RemoveOrgUserCommand) error {
return f.ExpectedError return f.ExpectedError
} }
func (f *FakeOrgStore) Count(ctx context.Context, _ *quota.ScopeParameters) (*quota.Map, error) {
return nil, nil
}

View File

@ -14,8 +14,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -44,8 +42,6 @@ type store interface {
GetByName(context.Context, *org.GetOrgByNameQuery) (*org.Org, error) GetByName(context.Context, *org.GetOrgByNameQuery) (*org.Org, error)
SearchOrgUsers(context.Context, *org.SearchOrgUsersQuery) (*org.SearchOrgUsersQueryResult, error) SearchOrgUsers(context.Context, *org.SearchOrgUsersQuery) (*org.SearchOrgUsersQueryResult, error)
RemoveOrgUser(context.Context, *org.RemoveOrgUserCommand) error RemoveOrgUser(context.Context, *org.RemoveOrgUserCommand) error
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
} }
type sqlStore struct { type sqlStore struct {
@ -399,72 +395,6 @@ func (ss *sqlStore) AddOrgUser(ctx context.Context, cmd *org.AddOrgUserCommand)
}) })
} }
func (ss *sqlStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
type result struct {
Count int64
}
r := result{}
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := "SELECT COUNT(*) as count from org"
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgQuotaTarget), quota.GlobalScope)
if err != nil {
return u, err
}
u.Set(tag, r.Count)
}
if scopeParams.OrgID != 0 {
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM (SELECT user_id FROM org_user WHERE org_id=? AND user_id IN (SELECT id AS user_id FROM %s WHERE is_service_account=%s)) as subq",
ss.db.GetDialect().Quote("user"),
ss.db.GetDialect().BooleanStr(false),
)
if _, err := sess.SQL(rawSQL, scopeParams.OrgID).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope)
if err != nil {
return u, err
}
u.Set(tag, r.Count)
}
}
if scopeParams.UserID != 0 {
if err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
// should we exclude service accounts?
rawSQL := "SELECT COUNT(*) AS count FROM org_user WHERE user_id=?"
if _, err := sess.SQL(rawSQL, scopeParams.UserID).Get(&r); err != nil {
return err
}
return nil
}); err != nil {
return u, err
} else {
tag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope)
if err != nil {
return u, err
}
u.Set(tag, r.Count)
}
}
return u, nil
}
func setUsingOrgInTransaction(sess *db.Session, userID int64, orgID int64) error { func setUsingOrgInTransaction(sess *db.Session, userID int64, orgID int64) error {
user := user.User{ user := user.User{
ID: userID, ID: userID,

View File

@ -28,7 +28,6 @@ import (
publicdashboardsStore "github.com/grafana/grafana/pkg/services/publicdashboards/database" publicdashboardsStore "github.com/grafana/grafana/pkg/services/publicdashboards/database"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models"
publicdashboardsService "github.com/grafana/grafana/pkg/services/publicdashboards/service" publicdashboardsService "github.com/grafana/grafana/pkg/services/publicdashboards/service"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -301,8 +300,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
} }
// create dashboard // create dashboard
dashboardStoreService, err := dashboardStore.ProvideDashboardStore(db, db.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db, db.Cfg), quotatest.New(false, nil)) dashboardStoreService := dashboardStore.ProvideDashboardStore(db, db.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db, db.Cfg))
require.NoError(t, err)
dashboard, err := dashboardStoreService.SaveDashboard(context.Background(), saveDashboardCmd) dashboard, err := dashboardStoreService.SaveDashboard(context.Background(), saveDashboardCmd)
require.NoError(t, err) require.NoError(t, err)

View File

@ -13,7 +13,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens" "github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
@ -36,9 +35,7 @@ func TestIntegrationListPublicDashboard(t *testing.T) {
t.Skip("skipping integration test") t.Skip("skipping integration test")
} }
sqlStore, cfg := db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}}) sqlStore, cfg := db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := ProvideStore(sqlStore) publicdashboardStore := ProvideStore(sqlStore)
var orgId int64 = 1 var orgId int64 = 1
@ -81,10 +78,7 @@ func TestIntegrationFindDashboard(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -111,10 +105,7 @@ func TestIntegrationExistsEnabledByAccessToken(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -184,10 +175,7 @@ func TestIntegrationExistsEnabledByDashboardUid(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -249,10 +237,7 @@ func TestIntegrationFindByDashboardUid(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -314,12 +299,10 @@ func TestIntegrationFindByAccessToken(t *testing.T) {
var dashboardStore *dashboardsDB.DashboardStore var dashboardStore *dashboardsDB.DashboardStore
var publicdashboardStore *PublicDashboardStoreImpl var publicdashboardStore *PublicDashboardStoreImpl
var savedDashboard *models.Dashboard var savedDashboard *models.Dashboard
var err error
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil)) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -386,10 +369,7 @@ func TestIntegrationCreatePublicDashboard(t *testing.T) {
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}}) sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true) savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true)
@ -456,13 +436,10 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
var publicdashboardStore *PublicDashboardStoreImpl var publicdashboardStore *PublicDashboardStoreImpl
var savedDashboard *models.Dashboard var savedDashboard *models.Dashboard
var anotherSavedDashboard *models.Dashboard var anotherSavedDashboard *models.Dashboard
var err error
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}}) sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
anotherSavedDashboard = insertTestDashboard(t, dashboardStore, "test another Dashie", 1, 0, true) anotherSavedDashboard = insertTestDashboard(t, dashboardStore, "test another Dashie", 1, 0, true)
@ -552,13 +529,10 @@ func TestIntegrationGetOrgIdByAccessToken(t *testing.T) {
var dashboardStore *dashboardsDB.DashboardStore var dashboardStore *dashboardsDB.DashboardStore
var publicdashboardStore *PublicDashboardStoreImpl var publicdashboardStore *PublicDashboardStoreImpl
var savedDashboard *models.Dashboard var savedDashboard *models.Dashboard
var err error
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
} }
@ -625,12 +599,10 @@ func TestIntegrationDelete(t *testing.T) {
var publicdashboardStore *PublicDashboardStoreImpl var publicdashboardStore *PublicDashboardStoreImpl
var savedDashboard *models.Dashboard var savedDashboard *models.Dashboard
var savedPublicDashboard *PublicDashboard var savedPublicDashboard *PublicDashboard
var err error
setup := func() { setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t) sqlStore, cfg = db.InitTestDBwithCfg(t)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil)) dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore) publicdashboardStore = ProvideStore(sqlStore)
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
savedPublicDashboard = insertPublicDashboard(t, publicdashboardStore, savedDashboard.Uid, savedDashboard.OrgId, true) savedPublicDashboard = insertPublicDashboard(t, publicdashboardStore, savedDashboard.Uid, savedDashboard.OrgId, true)

View File

@ -20,7 +20,6 @@ import (
"github.com/grafana/grafana/pkg/services/publicdashboards/database" "github.com/grafana/grafana/pkg/services/publicdashboards/database"
"github.com/grafana/grafana/pkg/services/publicdashboards/internal" "github.com/grafana/grafana/pkg/services/publicdashboards/internal"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -356,8 +355,7 @@ const (
func TestGetQueryDataResponse(t *testing.T) { func TestGetQueryDataResponse(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t) sqlStore := sqlstore.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil)) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
service := &PublicDashboardServiceImpl{ service := &PublicDashboardServiceImpl{
@ -740,8 +738,7 @@ func TestGetAnnotations(t *testing.T) {
func TestGetMetricRequest(t *testing.T) { func TestGetMetricRequest(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil)) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
publicDashboard := &PublicDashboard{ publicDashboard := &PublicDashboard{
@ -814,8 +811,7 @@ func TestGetUniqueDashboardDatasourceUids(t *testing.T) {
func TestBuildMetricRequest(t *testing.T) { func TestBuildMetricRequest(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil)) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@ -1026,8 +1022,7 @@ func TestBuildMetricRequest(t *testing.T) {
func TestBuildAnonymousUser(t *testing.T) { func TestBuildAnonymousUser(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil)) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
require.NoError(t, err)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
//publicdashboardStore := database.ProvideStore(sqlStore) //publicdashboardStore := database.ProvideStore(sqlStore)
//service := &PublicDashboardServiceImpl{ //service := &PublicDashboardServiceImpl{

View File

@ -21,7 +21,6 @@ import (
"github.com/grafana/grafana/pkg/services/publicdashboards/database" "github.com/grafana/grafana/pkg/services/publicdashboards/database"
"github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens" "github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -126,9 +125,7 @@ func TestGetPublicDashboard(t *testing.T) {
func TestCreatePublicDashboard(t *testing.T) { func TestCreatePublicDashboard(t *testing.T) {
t.Run("Create public dashboard", func(t *testing.T) { t.Run("Create public dashboard", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@ -150,7 +147,7 @@ func TestCreatePublicDashboard(t *testing.T) {
}, },
} }
_, err = service.Create(context.Background(), SignedInUser, dto) _, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err) require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgId, dashboard.Uid) pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgId, dashboard.Uid)
@ -174,9 +171,7 @@ func TestCreatePublicDashboard(t *testing.T) {
t.Run("Validate pubdash has default time setting value", func(t *testing.T) { t.Run("Validate pubdash has default time setting value", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@ -196,7 +191,7 @@ func TestCreatePublicDashboard(t *testing.T) {
}, },
} }
_, err = service.Create(context.Background(), SignedInUser, dto) _, err := service.Create(context.Background(), SignedInUser, dto)
require.NoError(t, err) require.NoError(t, err)
pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgId, dashboard.Uid) pubdash, err := service.FindByDashboardUid(context.Background(), dashboard.OrgId, dashboard.Uid)
@ -206,9 +201,7 @@ func TestCreatePublicDashboard(t *testing.T) {
t.Run("Validate pubdash whose dashboard has template variables returns error", func(t *testing.T) { t.Run("Validate pubdash whose dashboard has template variables returns error", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
templateVars := make([]map[string]interface{}, 1) templateVars := make([]map[string]interface{}, 1)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars, nil)
@ -229,7 +222,7 @@ func TestCreatePublicDashboard(t *testing.T) {
}, },
} }
_, err = service.Create(context.Background(), SignedInUser, dto) _, err := service.Create(context.Background(), SignedInUser, dto)
require.Error(t, err) require.Error(t, err)
}) })
@ -272,8 +265,7 @@ func TestCreatePublicDashboard(t *testing.T) {
t.Run("Returns error if public dashboard exists", func(t *testing.T) { t.Run("Returns error if public dashboard exists", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil)) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@ -324,9 +316,7 @@ func TestCreatePublicDashboard(t *testing.T) {
func TestUpdatePublicDashboard(t *testing.T) { func TestUpdatePublicDashboard(t *testing.T) {
t.Run("Updating public dashboard", func(t *testing.T) { t.Run("Updating public dashboard", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@ -388,9 +378,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
t.Run("Updating set empty time settings", func(t *testing.T) { t.Run("Updating set empty time settings", func(t *testing.T) {
sqlStore := db.InitTestDB(t) sqlStore := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg))
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil) dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)

View File

@ -23,7 +23,6 @@ import (
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes" fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
dsSvc "github.com/grafana/grafana/pkg/services/datasources/service" dsSvc "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager" secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -390,9 +389,7 @@ func setup(t *testing.T) *testContext {
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
ss := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) ss := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
ssvc := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) ssvc := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
quotaService := quotatest.New(false, nil) ds := dsSvc.ProvideService(nil, ssvc, ss, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
ds, err := dsSvc.ProvideService(nil, ssvc, ss, nil, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
fakeDatasourceService := &fakeDatasources.FakeDataSourceService{ fakeDatasourceService := &fakeDatasources.FakeDataSourceService{
DataSources: nil, DataSources: nil,
SimulatePluginFailure: false, SimulatePluginFailure: false,

View File

@ -1,42 +0,0 @@
package quota
import (
"context"
"sync"
)
type Context struct {
context.Context
TargetToSrv *TargetToSrv
}
func FromContext(ctx context.Context, targetToSrv *TargetToSrv) Context {
if targetToSrv == nil {
targetToSrv = NewTargetToSrv()
}
return Context{Context: ctx, TargetToSrv: targetToSrv}
}
type TargetToSrv struct {
mutex sync.RWMutex
m map[Target]TargetSrv
}
func NewTargetToSrv() *TargetToSrv {
return &TargetToSrv{m: make(map[Target]TargetSrv)}
}
func (m *TargetToSrv) Get(target Target) (TargetSrv, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()
srv, ok := m.m[target]
return srv, ok
}
func (m *TargetToSrv) Set(target Target, srv TargetSrv) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.m[target] = srv
}

View File

@ -1,216 +1,10 @@
package quota package quota
import ( import "errors"
"strings"
"sync"
"time"
"github.com/grafana/grafana/pkg/util/errutil" var ErrInvalidQuotaTarget = errors.New("invalid quota target")
)
var ErrBadRequest = errutil.NewBase(errutil.StatusBadRequest, "quota.bad-request")
var ErrInvalidTargetSrv = errutil.NewBase(errutil.StatusBadRequest, "quota.invalid-target")
var ErrInvalidScope = errutil.NewBase(errutil.StatusBadRequest, "quota.invalid-scope")
var ErrInvalidTarget = errutil.NewBase(errutil.StatusInternal, "quota.invalid-target-table")
var ErrTargetSrvConflict = errutil.NewBase(errutil.StatusBadRequest, "quota.target-srv-conflict")
var ErrDisabled = errutil.NewBase(errutil.StatusForbidden, "quota.disabled", errutil.WithPublicMessage("Quotas not enabled"))
var ErrInvalidTagFormat = errutil.NewBase(errutil.StatusInternal, "quota.invalid-invalid-tag-format")
type ScopeParameters struct { type ScopeParameters struct {
OrgID int64 OrgID int64
UserID int64 UserID int64
} }
type Scope string
const (
GlobalScope Scope = "global"
OrgScope Scope = "org"
UserScope Scope = "user"
)
func (s Scope) Validate() error {
switch s {
case GlobalScope, OrgScope, UserScope:
return nil
default:
return ErrInvalidScope.Errorf("bad scope: %s", s)
}
}
type TargetSrv string
type Target string
const delimiter = ":"
// Tag is a string with the format <srv>:<target>:<scope>
type Tag string
func NewTag(srv TargetSrv, t Target, scope Scope) (Tag, error) {
if err := scope.Validate(); err != nil {
return "", err
}
tag := Tag(strings.Join([]string{string(srv), string(t), string(scope)}, delimiter))
return tag, nil
}
func (t Tag) split() ([]string, error) {
parts := strings.SplitN(string(t), delimiter, -1)
if len(parts) != 3 {
return nil, ErrInvalidTagFormat.Errorf("tag format should be ^(?<srv>\\w):(?<target>\\w):(?<scope>\\w)$")
}
return parts, nil
}
func (t Tag) GetSrv() (TargetSrv, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return TargetSrv(parts[0]), nil
}
func (t Tag) GetTarget() (Target, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return Target(parts[1]), nil
}
func (t Tag) GetScope() (Scope, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return Scope(parts[2]), nil
}
type Item struct {
Tag Tag
Value int64
}
type Map struct {
mutex sync.RWMutex
m map[Tag]int64
}
func (m *Map) Set(tag Tag, limit int64) {
m.mutex.Lock()
defer m.mutex.Unlock()
if len(m.m) == 0 {
m.m = make(map[Tag]int64, 0)
}
m.m[tag] = limit
}
func (m *Map) Get(tag Tag) (int64, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()
limit, ok := m.m[tag]
return limit, ok
}
func (m *Map) Merge(l2 *Map) {
l2.mutex.RLock()
defer l2.mutex.RUnlock()
for k, v := range l2.m {
// TODO check for conflicts?
m.Set(k, v)
}
}
func (m *Map) Iter() <-chan Item {
m.mutex.RLock()
defer m.mutex.RUnlock()
ch := make(chan Item)
go func() {
defer close(ch)
for t, v := range m.m {
ch <- Item{Tag: t, Value: v}
}
}()
return ch
}
func (m *Map) Scopes() (map[Scope]struct{}, error) {
res := make(map[Scope]struct{})
for item := range m.Iter() {
scope, err := item.Tag.GetScope()
if err != nil {
return nil, err
}
res[scope] = struct{}{}
}
return res, nil
}
func (m *Map) Services() (map[TargetSrv]struct{}, error) {
res := make(map[TargetSrv]struct{})
for item := range m.Iter() {
srv, err := item.Tag.GetSrv()
if err != nil {
return nil, err
}
res[srv] = struct{}{}
}
return res, nil
}
func (m *Map) Targets() (map[Target]struct{}, error) {
res := make(map[Target]struct{})
for item := range m.Iter() {
target, err := item.Tag.GetTarget()
if err != nil {
return nil, err
}
res[target] = struct{}{}
}
return res, nil
}
type Quota struct {
Id int64
OrgId int64
UserId int64
Target string
Limit int64
Created time.Time
Updated time.Time
}
type QuotaDTO struct {
OrgId int64 `json:"org_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Target string `json:"target"`
Limit int64 `json:"limit"`
Used int64 `json:"used"`
Service string `json:"-"`
Scope string `json:"-"`
}
func (dto QuotaDTO) Tag() (Tag, error) {
return NewTag(TargetSrv(dto.Service), Target(dto.Target), Scope(dto.Scope))
}
type UpdateQuotaCmd struct {
Target string `json:"target"`
Limit int64 `json:"limit"`
OrgID int64 `json:"-"`
UserID int64 `json:"-"`
}
type NewUsageReporter struct {
TargetSrv TargetSrv
DefaultLimits *Map
Reporter UsageReporterFunc
}

View File

@ -7,24 +7,7 @@ import (
) )
type Service interface { type Service interface {
// GetQuotasByScope returns the quota for the specific scope (global, organization, user) QuotaReached(c *models.ReqContext, target string) (bool, error)
// If the scope is organization, the ID is expected to be the organisation ID. CheckQuotaReached(ctx context.Context, target string, scopeParams *ScopeParameters) (bool, error)
// If the scope is user, the id is expected to be the user ID. DeleteByUser(context.Context, int64) error
GetQuotasByScope(ctx context.Context, scope Scope, ID int64) ([]QuotaDTO, error)
// Update overrides the quota for a specific scope (global, organization, user).
// If the cmd.OrgID is set, then the organization quota are updated.
// If the cmd.UseID is set, then the user quota are updated.
Update(ctx context.Context, cmd *UpdateQuotaCmd) error
// QuotaReached is called by the quota middleware for applying quota enforcement to API handlers
QuotaReached(c *models.ReqContext, targetSrv TargetSrv) (bool, error)
// CheckQuotaReached checks if the quota limitations have been reached for a specific service
CheckQuotaReached(ctx context.Context, targetSrv TargetSrv, scopeParams *ScopeParameters) (bool, error)
// DeleteQuotaForUser deletes custom quota limitations for the user
DeleteQuotaForUser(ctx context.Context, userID int64) error
// DeleteByOrg(ctx context.Context, orgID int64) error
// RegisterQuotaReporter registers a service UsageReporterFunc, targets and their default limits
RegisterQuotaReporter(e *NewUsageReporter) error
} }
type UsageReporterFunc func(ctx context.Context, scopeParams *ScopeParameters) (*Map, error)

View File

@ -2,81 +2,38 @@ package quotaimpl
import ( import (
"context" "context"
"fmt"
"sync"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"golang.org/x/sync/errgroup"
) )
type serviceDisabled struct { type Service struct {
store store
authTokenService models.ActiveTokenService
Cfg *setting.Cfg
SQLStore sqlstore.Store
Logger log.Logger
} }
func (s *serviceDisabled) QuotaReached(c *models.ReqContext, targetSrv quota.TargetSrv) (bool, error) { func ProvideService(db db.DB, cfg *setting.Cfg, tokenService models.ActiveTokenService, ss *sqlstore.SQLStore) quota.Service {
return false, nil return &Service{
} store: &sqlStore{db: db},
Cfg: cfg,
func (s *serviceDisabled) GetQuotasByScope(ctx context.Context, scope quota.Scope, id int64) ([]quota.QuotaDTO, error) { authTokenService: tokenService,
return nil, quota.ErrDisabled SQLStore: ss,
} Logger: log.New("quota_service"),
func (s *serviceDisabled) Update(ctx context.Context, cmd *quota.UpdateQuotaCmd) error {
return quota.ErrDisabled
}
func (s *serviceDisabled) CheckQuotaReached(ctx context.Context, targetSrv quota.TargetSrv, scopeParams *quota.ScopeParameters) (bool, error) {
return false, nil
}
func (s *serviceDisabled) DeleteQuotaForUser(ctx context.Context, userID int64) error {
return quota.ErrDisabled
}
func (s *serviceDisabled) RegisterQuotaReporter(e *quota.NewUsageReporter) error {
return nil
}
type service struct {
store store
Cfg *setting.Cfg
Logger log.Logger
mutex sync.RWMutex
reporters map[quota.TargetSrv]quota.UsageReporterFunc
defaultLimits *quota.Map
targetToSrv *quota.TargetToSrv
}
func ProvideService(db db.DB, cfg *setting.Cfg) quota.Service {
logger := log.New("quota_service")
s := service{
store: &sqlStore{db: db, logger: logger},
Cfg: cfg,
Logger: logger,
reporters: make(map[quota.TargetSrv]quota.UsageReporterFunc),
defaultLimits: &quota.Map{},
targetToSrv: quota.NewTargetToSrv(),
} }
if s.IsDisabled() {
return &serviceDisabled{}
}
return &s
}
func (s *service) IsDisabled() bool {
return !s.Cfg.Quota.Enabled
} }
// QuotaReached checks that quota is reached for a target. Runs CheckQuotaReached and take context and scope parameters from the request context // QuotaReached checks that quota is reached for a target. Runs CheckQuotaReached and take context and scope parameters from the request context
func (s *service) QuotaReached(c *models.ReqContext, targetSrv quota.TargetSrv) (bool, error) { func (s *Service) QuotaReached(c *models.ReqContext, target string) (bool, error) {
if !s.Cfg.Quota.Enabled {
return false, nil
}
// No request context means this is a background service, like LDAP Background Sync // No request context means this is a background service, like LDAP Background Sync
if c == nil { if c == nil {
return false, nil return false, nil
@ -89,129 +46,91 @@ func (s *service) QuotaReached(c *models.ReqContext, targetSrv quota.TargetSrv)
UserID: c.UserID, UserID: c.UserID,
} }
} }
return s.CheckQuotaReached(c.Req.Context(), targetSrv, params) return s.CheckQuotaReached(c.Req.Context(), target, params)
}
func (s *service) GetQuotasByScope(ctx context.Context, scope quota.Scope, id int64) ([]quota.QuotaDTO, error) {
if err := scope.Validate(); err != nil {
return nil, err
}
q := make([]quota.QuotaDTO, 0)
scopeParams := quota.ScopeParameters{}
if scope == quota.OrgScope {
scopeParams.OrgID = id
} else if scope == quota.UserScope {
scopeParams.UserID = id
}
c, err := s.getContext(ctx)
if err != nil {
return nil, err
}
customLimits, err := s.store.Get(c, &scopeParams)
if err != nil {
return nil, err
}
u, err := s.getUsage(ctx, &scopeParams)
if err != nil {
return nil, err
}
for item := range s.defaultLimits.Iter() {
limit := item.Value
scp, err := item.Tag.GetScope()
if err != nil {
return nil, err
}
if scp != scope {
continue
}
if targetCustomLimit, ok := customLimits.Get(item.Tag); ok {
limit = targetCustomLimit
}
target, err := item.Tag.GetTarget()
if err != nil {
return nil, err
}
srv, err := item.Tag.GetSrv()
if err != nil {
return nil, err
}
used, _ := u.Get(item.Tag)
q = append(q, quota.QuotaDTO{
Target: string(target),
Limit: limit,
OrgId: scopeParams.OrgID,
UserId: scopeParams.UserID,
Used: used,
Service: string(srv),
Scope: string(scope),
})
}
return q, nil
}
func (s *service) Update(ctx context.Context, cmd *quota.UpdateQuotaCmd) error {
targetFound := false
knownTargets, err := s.defaultLimits.Targets()
if err != nil {
return err
}
for t := range knownTargets {
if t == quota.Target(cmd.Target) {
targetFound = true
}
}
if !targetFound {
return quota.ErrInvalidTarget.Errorf("unknown quota target: %s", cmd.Target)
}
c, err := s.getContext(ctx)
if err != nil {
return err
}
return s.store.Update(c, cmd)
} }
// CheckQuotaReached check that quota is reached for a target. If ScopeParameters are not defined, only global scope is checked // CheckQuotaReached check that quota is reached for a target. If ScopeParameters are not defined, only global scope is checked
func (s *service) CheckQuotaReached(ctx context.Context, targetSrv quota.TargetSrv, scopeParams *quota.ScopeParameters) (bool, error) { func (s *Service) CheckQuotaReached(ctx context.Context, target string, scopeParams *quota.ScopeParameters) (bool, error) {
targetSrvLimits, err := s.getOverridenLimits(ctx, targetSrv, scopeParams) if !s.Cfg.Quota.Enabled {
return false, nil
}
// get the list of scopes that this target is valid for. Org, User, Global
scopes, err := s.getQuotaScopes(target)
if err != nil { if err != nil {
return false, err return false, err
} }
for _, scope := range scopes {
s.Logger.Debug("Checking quota", "target", target, "scope", scope)
usageReporterFunc, ok := s.getReporter(targetSrv) switch scope.Name {
if !ok { case "global":
return false, quota.ErrInvalidTargetSrv if scope.DefaultLimit < 0 {
} continue
targetUsage, err := usageReporterFunc(ctx, scopeParams)
if err != nil {
return false, err
}
for t, limit := range targetSrvLimits {
switch {
case limit < 0:
continue
case limit == 0:
return true, nil
default:
u, ok := targetUsage.Get(t)
if !ok {
return false, fmt.Errorf("no usage for target:%s", t)
} }
if u >= limit { if scope.DefaultLimit == 0 {
return true, nil
}
if target == "session" {
usedSessions, err := s.authTokenService.ActiveTokenCount(ctx)
if err != nil {
return false, err
}
if usedSessions > scope.DefaultLimit {
s.Logger.Debug("Sessions limit reached", "active", usedSessions, "limit", scope.DefaultLimit)
return true, nil
}
continue
}
query := models.GetGlobalQuotaByTargetQuery{Target: scope.Target, UnifiedAlertingEnabled: s.Cfg.UnifiedAlerting.IsEnabled()}
// TODO : move GetGlobalQuotaByTarget to a global quota service
if err := s.SQLStore.GetGlobalQuotaByTarget(ctx, &query); err != nil {
return true, err
}
if query.Result.Used >= scope.DefaultLimit {
return true, nil
}
case "org":
if scopeParams == nil {
continue
}
query := models.GetOrgQuotaByTargetQuery{
OrgId: scopeParams.OrgID,
Target: scope.Target,
Default: scope.DefaultLimit,
UnifiedAlertingEnabled: s.Cfg.UnifiedAlerting.IsEnabled(),
}
// TODO: move GetOrgQuotaByTarget from sqlstore to quota store
if err := s.SQLStore.GetOrgQuotaByTarget(ctx, &query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil
}
case "user":
if scopeParams == nil || scopeParams.UserID == 0 {
continue
}
query := models.GetUserQuotaByTargetQuery{UserId: scopeParams.UserID, Target: scope.Target, Default: scope.DefaultLimit, UnifiedAlertingEnabled: s.Cfg.UnifiedAlerting.IsEnabled()}
// TODO: move GetUserQuotaByTarget from sqlstore to quota store
if err := s.SQLStore.GetUserQuotaByTarget(ctx, &query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil return true, nil
} }
} }
@ -219,127 +138,68 @@ func (s *service) CheckQuotaReached(ctx context.Context, targetSrv quota.TargetS
return false, nil return false, nil
} }
func (s *service) DeleteQuotaForUser(ctx context.Context, userID int64) error { func (s *Service) getQuotaScopes(target string) ([]models.QuotaScope, error) {
c, err := s.getContext(ctx) scopes := make([]models.QuotaScope, 0)
if err != nil { switch target {
return err case "user":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.User},
models.QuotaScope{Name: "org", Target: "org_user", DefaultLimit: s.Cfg.Quota.Org.User},
)
return scopes, nil
case "org":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.Org},
models.QuotaScope{Name: "user", Target: "org_user", DefaultLimit: s.Cfg.Quota.User.Org},
)
return scopes, nil
case "dashboard":
scopes = append(scopes,
models.QuotaScope{
Name: "global",
Target: target,
DefaultLimit: s.Cfg.Quota.Global.Dashboard,
},
models.QuotaScope{
Name: "org",
Target: target,
DefaultLimit: s.Cfg.Quota.Org.Dashboard,
},
)
return scopes, nil
case "data_source":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.DataSource},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: s.Cfg.Quota.Org.DataSource},
)
return scopes, nil
case "api_key":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.ApiKey},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: s.Cfg.Quota.Org.ApiKey},
)
return scopes, nil
case "session":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.Session},
)
return scopes, nil
case "alert_rule": // target need to match the respective database name
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.AlertRule},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: s.Cfg.Quota.Org.AlertRule},
)
return scopes, nil
case "file":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.File},
)
return scopes, nil
default:
return scopes, quota.ErrInvalidQuotaTarget
} }
return s.store.DeleteByUser(c, userID)
} }
func (s *service) RegisterQuotaReporter(e *quota.NewUsageReporter) error { func (s *Service) DeleteByUser(ctx context.Context, userID int64) error {
s.mutex.Lock() return s.store.DeleteByUser(ctx, userID)
defer s.mutex.Unlock()
_, ok := s.reporters[e.TargetSrv]
if ok {
return quota.ErrTargetSrvConflict.Errorf("target service: %s already exists", e.TargetSrv)
}
s.reporters[e.TargetSrv] = e.Reporter
for item := range e.DefaultLimits.Iter() {
target, err := item.Tag.GetTarget()
if err != nil {
return err
}
srv, err := item.Tag.GetSrv()
if err != nil {
return err
}
s.targetToSrv.Set(target, srv)
s.defaultLimits.Set(item.Tag, item.Value)
}
return nil
}
func (s *service) getReporter(target quota.TargetSrv) (quota.UsageReporterFunc, bool) {
s.mutex.RLock()
defer s.mutex.RUnlock()
r, ok := s.reporters[target]
return r, ok
}
type reporter struct {
target quota.TargetSrv
reporterFunc quota.UsageReporterFunc
}
func (s *service) getReporters() <-chan reporter {
ch := make(chan reporter)
go func() {
s.mutex.RLock()
defer func() {
s.mutex.RUnlock()
close(ch)
}()
for t, r := range s.reporters {
ch <- reporter{target: t, reporterFunc: r}
}
}()
return ch
}
func (s *service) getOverridenLimits(ctx context.Context, targetSrv quota.TargetSrv, scopeParams *quota.ScopeParameters) (map[quota.Tag]int64, error) {
targetSrvLimits := make(map[quota.Tag]int64)
c, err := s.getContext(ctx)
if err != nil {
return nil, err
}
customLimits, err := s.store.Get(c, scopeParams)
if err != nil {
return targetSrvLimits, err
}
for item := range s.defaultLimits.Iter() {
srv, err := item.Tag.GetSrv()
if err != nil {
return nil, err
}
if srv != targetSrv {
continue
}
defaultLimit := item.Value
if customLimit, ok := customLimits.Get(item.Tag); ok {
targetSrvLimits[item.Tag] = customLimit
} else {
targetSrvLimits[item.Tag] = defaultLimit
}
}
return targetSrvLimits, nil
}
func (s *service) getUsage(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
usage := &quota.Map{}
g, ctx := errgroup.WithContext(ctx)
for r := range s.getReporters() {
r := r
g.Go(func() error {
u, err := r.reporterFunc(ctx, scopeParams)
if err != nil {
return err
}
usage.Merge(u)
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return usage, nil
}
func (s *service) getContext(ctx context.Context) (quota.Context, error) {
return quota.FromContext(ctx, s.targetToSrv), nil
} }

View File

@ -3,481 +3,26 @@ package quotaimpl
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/dashboards"
dashboardStore "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/datasources"
dsservice "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/ngalert"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
ngalertmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
ngalerttests "github.com/grafana/grafana/pkg/services/ngalert/tests"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/services/sqlstore"
storesrv "github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/xorcare/pointer"
) )
func TestQuotaService(t *testing.T) { func TestQuotaService(t *testing.T) {
quotaStore := &quotatest.FakeQuotaStore{} quotaStore := &FakeQuotaStore{}
quotaService := service{ quotaService := Service{
store: quotaStore, store: quotaStore,
} }
t.Run("delete quota", func(t *testing.T) { t.Run("delete quota", func(t *testing.T) {
err := quotaService.DeleteQuotaForUser(context.Background(), 1) err := quotaService.DeleteByUser(context.Background(), 1)
require.NoError(t, err) require.NoError(t, err)
}) })
} }
func TestIntegrationQuotaCommandsAndQueries(t *testing.T) { type FakeQuotaStore struct {
if testing.Short() { ExpectedError error
t.Skip("skipping integration test")
}
sqlStore := sqlstore.InitTestDB(t)
sqlStore.Cfg.Quota = setting.QuotaSettings{
Enabled: true,
Org: setting.OrgQuota{
User: 2,
Dashboard: 3,
DataSource: 4,
ApiKey: 5,
AlertRule: 6,
},
User: setting.UserQuota{
Org: 7,
},
Global: setting.GlobalQuota{
Org: 8,
User: 9,
Dashboard: 10,
DataSource: 11,
ApiKey: 12,
Session: 13,
AlertRule: 14,
File: 15,
},
}
b := bus.ProvideBus(tracing.InitializeTracerForTest())
quotaService := ProvideService(sqlStore, sqlStore.Cfg)
orgService, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotaService)
require.NoError(t, err)
userService, err := userimpl.ProvideService(sqlStore, orgService, sqlStore.Cfg, nil, nil, quotaService)
require.NoError(t, err)
setupEnv(t, sqlStore, b, quotaService)
u, err := userService.Create(context.Background(), &user.CreateUserCommand{
Name: "TestUser",
SkipOrgSetup: true,
})
require.NoError(t, err)
o, err := orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{
Name: "TestOrg",
UserID: u.ID,
})
require.NoError(t, err)
// fetch global default limit/usage
defaultGlobalLimits := make(map[quota.Tag]int64)
existingGlobalUsage := make(map[quota.Tag]int64)
scope := quota.GlobalScope
result, err := quotaService.GetQuotasByScope(context.Background(), scope, 0)
require.NoError(t, err)
for _, r := range result {
tag, err := r.Tag()
require.NoError(t, err)
defaultGlobalLimits[tag] = r.Limit
existingGlobalUsage[tag] = r.Used
}
tag, err := quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgQuotaTarget), scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.Org, defaultGlobalLimits[tag])
tag, err = quota.NewTag(quota.TargetSrv(user.QuotaTargetSrv), quota.Target(user.QuotaTarget), scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.User, defaultGlobalLimits[tag])
tag, err = quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.Dashboard, defaultGlobalLimits[tag])
tag, err = quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.DataSource, defaultGlobalLimits[tag])
tag, err = quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.ApiKey, defaultGlobalLimits[tag])
tag, err = quota.NewTag(auth.QuotaTargetSrv, auth.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.Session, defaultGlobalLimits[tag])
tag, err = quota.NewTag(ngalertmodels.QuotaTargetSrv, ngalertmodels.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.AlertRule, defaultGlobalLimits[tag])
tag, err = quota.NewTag(storesrv.QuotaTargetSrv, storesrv.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Global.File, defaultGlobalLimits[tag])
// fetch default limit/usage for org
defaultOrgLimits := make(map[quota.Tag]int64)
existingOrgUsage := make(map[quota.Tag]int64)
scope = quota.OrgScope
result, err = quotaService.GetQuotasByScope(context.Background(), scope, o.ID)
require.NoError(t, err)
for _, r := range result {
tag, err := r.Tag()
require.NoError(t, err)
defaultOrgLimits[tag] = r.Limit
existingOrgUsage[tag] = r.Used
}
tag, err = quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Org.User, defaultOrgLimits[tag])
tag, err = quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Org.Dashboard, defaultOrgLimits[tag])
tag, err = quota.NewTag(datasources.QuotaTargetSrv, datasources.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Org.DataSource, defaultOrgLimits[tag])
tag, err = quota.NewTag(apikey.QuotaTargetSrv, apikey.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Org.ApiKey, defaultOrgLimits[tag])
tag, err = quota.NewTag(ngalertmodels.QuotaTargetSrv, ngalertmodels.QuotaTarget, scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.Org.AlertRule, defaultOrgLimits[tag])
// fetch default limit/usage for user
defaultUserLimits := make(map[quota.Tag]int64)
existingUserUsage := make(map[quota.Tag]int64)
scope = quota.UserScope
result, err = quotaService.GetQuotasByScope(context.Background(), scope, u.ID)
require.NoError(t, err)
for _, r := range result {
tag, err := r.Tag()
require.NoError(t, err)
defaultUserLimits[tag] = r.Limit
existingUserUsage[tag] = r.Used
}
tag, err = quota.NewTag(quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), scope)
require.NoError(t, err)
require.Equal(t, sqlStore.Cfg.Quota.User.Org, defaultUserLimits[tag])
t.Run("Given saved org quota for users", func(t *testing.T) {
// update quota for the created org and limit users to 1
var customOrgUserLimit int64 = 1
orgCmd := quota.UpdateQuotaCmd{
OrgID: o.ID,
Target: org.OrgUserQuotaTarget,
Limit: customOrgUserLimit,
}
err := quotaService.Update(context.Background(), &orgCmd)
require.NoError(t, err)
t.Run("Should be able to get saved limit/usage for org users", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope, &quota.ScopeParameters{OrgID: o.ID})
require.NoError(t, err)
require.Equal(t, customOrgUserLimit, q.Limit)
require.Equal(t, int64(1), q.Used)
})
t.Run("Should be able to get default org users limit/usage for unknown org", func(t *testing.T) {
unknownOrgID := -1
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope, &quota.ScopeParameters{OrgID: int64(unknownOrgID)})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultOrgLimits[tag], q.Limit)
require.Equal(t, int64(0), q.Used)
})
t.Run("Should be able to get zero used org alert quota when table does not exist (ngalert is not enabled - default case)", func(t *testing.T) {
// disable Grafana Alerting
cfg := *sqlStore.Cfg
cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{Enabled: pointer.Bool(false)}
quotaSrv := ProvideService(sqlStore, &cfg)
q, err := getQuotaBySrvTargetScope(t, quotaSrv, ngalertmodels.QuotaTargetSrv, ngalertmodels.QuotaTarget, quota.OrgScope, &quota.ScopeParameters{OrgID: o.ID})
require.NoError(t, err)
require.Equal(t, int64(0), q.Limit)
})
t.Run("Should be able to quota list for org", func(t *testing.T) {
result, err := quotaService.GetQuotasByScope(context.Background(), quota.OrgScope, o.ID)
require.NoError(t, err)
require.Len(t, result, 5)
require.NoError(t, err)
for _, res := range result {
tag, err := res.Tag()
require.NoError(t, err)
limit := defaultOrgLimits[tag]
used := existingOrgUsage[tag]
if res.Target == org.OrgUserQuotaTarget {
limit = customOrgUserLimit
used = 1 // one user in the created org
}
require.Equal(t, limit, res.Limit)
require.Equal(t, used, res.Used)
}
})
})
t.Run("Given saved org quota for dashboards", func(t *testing.T) {
// update quota for the created org and limit dashboards to 1
var customOrgDashboardLimit int64 = 1
orgCmd := quota.UpdateQuotaCmd{
OrgID: o.ID,
Target: string(dashboards.QuotaTarget),
Limit: customOrgDashboardLimit,
}
err := quotaService.Update(context.Background(), &orgCmd)
require.NoError(t, err)
t.Run("Should be able to get saved quota by org id and target", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.OrgScope, &quota.ScopeParameters{OrgID: o.ID})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, customOrgDashboardLimit, q.Limit)
require.Equal(t, existingOrgUsage[tag], q.Used)
})
})
t.Run("Given saved user quota for org", func(t *testing.T) {
// update quota for the created user and limit orgs to 1
var customUserOrgsLimit int64 = 1
userQuotaCmd := quota.UpdateQuotaCmd{
UserID: u.ID,
Target: org.OrgUserQuotaTarget,
Limit: customUserOrgsLimit,
}
err := quotaService.Update(context.Background(), &userQuotaCmd)
require.NoError(t, err)
t.Run("Should be able to get saved limit/usage for user orgs", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope, &quota.ScopeParameters{UserID: u.ID})
require.NoError(t, err)
require.Equal(t, customUserOrgsLimit, q.Limit)
require.Equal(t, int64(1), q.Used)
})
t.Run("Should be able to get default user orgs limit/usage for unknown user", func(t *testing.T) {
var unknownUserID int64 = -1
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope, &quota.ScopeParameters{UserID: unknownUserID})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultUserLimits[tag], q.Limit)
require.Equal(t, int64(0), q.Used)
})
t.Run("Should be able to quota list for user", func(t *testing.T) {
result, err = quotaService.GetQuotasByScope(context.Background(), quota.UserScope, u.ID)
require.NoError(t, err)
require.Len(t, result, 1)
for _, res := range result {
tag, err := res.Tag()
require.NoError(t, err)
limit := defaultUserLimits[tag]
used := existingUserUsage[tag]
if res.Target == org.OrgUserQuotaTarget {
limit = customUserOrgsLimit // customized quota limit.
used = 1 // one user in the created org
}
require.Equal(t, limit, res.Limit)
require.Equal(t, used, res.Used)
}
})
})
t.Run("Should be able to global user quota", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(user.QuotaTargetSrv), quota.Target(user.QuotaTarget), quota.GlobalScope, &quota.ScopeParameters{})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultGlobalLimits[tag], q.Limit)
require.Equal(t, int64(1), q.Used)
})
t.Run("Should be able to global org quota", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgQuotaTarget), quota.GlobalScope, &quota.ScopeParameters{})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultGlobalLimits[tag], q.Limit)
require.Equal(t, int64(1), q.Used)
})
t.Run("Should be able to get zero used global alert quota when table does not exist (ngalert is not enabled - default case)", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, ngalertmodels.QuotaTargetSrv, ngalertmodels.QuotaTarget, quota.GlobalScope, &quota.ScopeParameters{})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultGlobalLimits[tag], q.Limit)
require.Equal(t, int64(0), q.Used)
})
t.Run("Should be able to global dashboard quota", func(t *testing.T) {
q, err := getQuotaBySrvTargetScope(t, quotaService, dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.GlobalScope, &quota.ScopeParameters{})
require.NoError(t, err)
tag, err := q.Tag()
require.NoError(t, err)
require.Equal(t, defaultGlobalLimits[tag], q.Limit)
require.Equal(t, int64(0), q.Used)
})
// related: https://github.com/grafana/grafana/issues/14342
t.Run("Should org quota updating is successful even if it called multiple time", func(t *testing.T) {
// update quota for the created org and limit users to 1
var customOrgUserLimit int64 = 1
orgCmd := quota.UpdateQuotaCmd{
OrgID: o.ID,
Target: org.OrgUserQuotaTarget,
Limit: customOrgUserLimit,
}
err := quotaService.Update(context.Background(), &orgCmd)
require.NoError(t, err)
query, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope, &quota.ScopeParameters{OrgID: o.ID})
require.NoError(t, err)
require.Equal(t, customOrgUserLimit, query.Limit)
// XXX: resolution of `Updated` column is 1sec, so this makes delay
time.Sleep(1 * time.Second)
customOrgUserLimit = 2
orgCmd = quota.UpdateQuotaCmd{
OrgID: o.ID,
Target: org.OrgUserQuotaTarget,
Limit: customOrgUserLimit,
}
err = quotaService.Update(context.Background(), &orgCmd)
require.NoError(t, err)
query, err = getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.OrgScope, &quota.ScopeParameters{OrgID: o.ID})
require.NoError(t, err)
require.Equal(t, customOrgUserLimit, query.Limit)
})
// related: https://github.com/grafana/grafana/issues/14342
t.Run("Should user quota updating is successful even if it called multiple time", func(t *testing.T) {
// update quota for the created org and limit users to 1
var customUserOrgLimit int64 = 1
userQuotaCmd := quota.UpdateQuotaCmd{
UserID: u.ID,
Target: org.OrgUserQuotaTarget,
Limit: customUserOrgLimit,
}
err := quotaService.Update(context.Background(), &userQuotaCmd)
require.NoError(t, err)
query, err := getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope, &quota.ScopeParameters{UserID: u.ID})
require.NoError(t, err)
require.Equal(t, customUserOrgLimit, query.Limit)
// XXX: resolution of `Updated` column is 1sec, so this makes delay
time.Sleep(1 * time.Second)
customUserOrgLimit = 10
userQuotaCmd = quota.UpdateQuotaCmd{
UserID: u.ID,
Target: org.OrgUserQuotaTarget,
Limit: customUserOrgLimit,
}
err = quotaService.Update(context.Background(), &userQuotaCmd)
require.NoError(t, err)
query, err = getQuotaBySrvTargetScope(t, quotaService, quota.TargetSrv(org.QuotaTargetSrv), quota.Target(org.OrgUserQuotaTarget), quota.UserScope, &quota.ScopeParameters{UserID: u.ID})
require.NoError(t, err)
require.Equal(t, customUserOrgLimit, query.Limit)
})
// TODO data_source, file
} }
func getQuotaBySrvTargetScope(t *testing.T, quotaService quota.Service, srv quota.TargetSrv, target quota.Target, scope quota.Scope, scopeParams *quota.ScopeParameters) (quota.QuotaDTO, error) { func (f *FakeQuotaStore) DeleteByUser(ctx context.Context, userID int64) error {
t.Helper() return f.ExpectedError
var id int64 = 0
switch {
case scope == quota.OrgScope:
id = scopeParams.OrgID
case scope == quota.UserScope:
id = scopeParams.UserID
}
result, err := quotaService.GetQuotasByScope(context.Background(), scope, id)
require.NoError(t, err)
for _, r := range result {
if r.Target != string(target) {
continue
}
if r.Service != string(srv) {
continue
}
if r.Scope != string(scope) {
continue
}
require.Equal(t, r.OrgId, scopeParams.OrgID)
require.Equal(t, r.UserId, scopeParams.UserID)
return r, nil
}
return quota.QuotaDTO{}, err
}
func setupEnv(t *testing.T, sqlStore *sqlstore.SQLStore, b bus.Bus, quotaService quota.Service) {
_, err := apikeyimpl.ProvideService(sqlStore, sqlStore.Cfg, quotaService)
require.NoError(t, err)
_, err = auth.ProvideActiveAuthTokenService(sqlStore.Cfg, sqlStore, quotaService)
require.NoError(t, err)
_, err = dashboardStore.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
_, err = dsservice.ProvideService(sqlStore, secretsService, secretsStore, sqlStore.Cfg, featuremgmt.WithFeatures(), acmock.New().WithDisabled(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
m := metrics.NewNGAlert(prometheus.NewRegistry())
_, err = ngalert.ProvideService(
sqlStore.Cfg, &ngalerttests.FakeFeatures{}, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, quotaService,
secretsService, nil, m, &foldertest.FakeService{}, &acmock.Mock{}, &dashboards.FakeDashboardService{}, nil, b, &acmock.Mock{}, annotationstest.NewFakeAnnotationsRepo(),
)
require.NoError(t, err)
_, err = storesrv.ProvideService(sqlStore, featuremgmt.WithFeatures(), sqlStore.Cfg, quotaService)
require.NoError(t, err)
} }

View File

@ -1,130 +1,23 @@
package quotaimpl package quotaimpl
import ( import (
"time" "context"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
) )
type store interface { type store interface {
Get(ctx quota.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) DeleteByUser(context.Context, int64) error
Update(ctx quota.Context, cmd *quota.UpdateQuotaCmd) error
DeleteByUser(quota.Context, int64) error
} }
type sqlStore struct { type sqlStore struct {
db db.DB db db.DB
logger log.Logger
} }
func (ss *sqlStore) DeleteByUser(ctx quota.Context, userID int64) error { func (ss *sqlStore) DeleteByUser(ctx context.Context, userID int64) error {
return ss.db.WithDbSession(ctx, func(sess *db.Session) error { return ss.db.WithDbSession(ctx, func(sess *db.Session) error {
var rawSQL = "DELETE FROM quota WHERE user_id = ?" var rawSQL = "DELETE FROM quota WHERE user_id = ?"
_, err := sess.Exec(rawSQL, userID) _, err := sess.Exec(rawSQL, userID)
return err return err
}) })
} }
func (ss *sqlStore) Get(ctx quota.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
limits := quota.Map{}
if scopeParams.OrgID != 0 {
orgLimits, err := ss.getOrgScopeQuota(ctx, scopeParams.OrgID)
if err != nil {
return nil, err
}
limits.Merge(orgLimits)
}
if scopeParams.UserID != 0 {
userLimits, err := ss.getUserScopeQuota(ctx, scopeParams.UserID)
if err != nil {
return nil, err
}
limits.Merge(userLimits)
}
return &limits, nil
}
func (ss *sqlStore) Update(ctx quota.Context, cmd *quota.UpdateQuotaCmd) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
// Check if quota is already defined in the DB
quota := quota.Quota{
Target: cmd.Target,
UserId: cmd.UserID,
OrgId: cmd.OrgID,
}
has, err := sess.Get(&quota)
if err != nil {
return err
}
quota.Updated = time.Now()
quota.Limit = cmd.Limit
if !has {
quota.Created = time.Now()
// No quota in the DB for this target, so create a new one.
if _, err := sess.Insert(&quota); err != nil {
return err
}
} else {
// update existing quota entry in the DB.
_, err := sess.ID(quota.Id).Update(&quota)
if err != nil {
return err
}
}
return nil
})
}
func (ss *sqlStore) getUserScopeQuota(ctx quota.Context, userID int64) (*quota.Map, error) {
r := quota.Map{}
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
quotas := make([]*quota.Quota, 0)
if err := sess.Table("quota").Where("user_id=? AND org_id=0", userID).Find(&quotas); err != nil {
return err
}
for _, q := range quotas {
srv, ok := ctx.TargetToSrv.Get(quota.Target(q.Target))
if !ok {
ss.logger.Info("failed to get service for target", "target", q.Target)
}
tag, err := quota.NewTag(srv, quota.Target(q.Target), quota.UserScope)
if err != nil {
return err
}
r.Set(tag, q.Limit)
}
return nil
})
return &r, err
}
func (ss *sqlStore) getOrgScopeQuota(ctx quota.Context, OrgID int64) (*quota.Map, error) {
r := quota.Map{}
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
quotas := make([]*quota.Quota, 0)
if err := sess.Table("quota").Where("user_id=0 AND org_id=?", OrgID).Find(&quotas); err != nil {
return err
}
for _, q := range quotas {
srv, ok := ctx.TargetToSrv.Get(quota.Target(q.Target))
if !ok {
ss.logger.Info("failed to get service for target", "target", q.Target)
}
tag, err := quota.NewTag(srv, quota.Target(q.Target), quota.OrgScope)
if err != nil {
return err
}
r.Set(tag, q.Limit)
}
return nil
})
return &r, err
}

View File

@ -7,7 +7,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/quota"
) )
func TestIntegrationQuotaDataAccess(t *testing.T) { func TestIntegrationQuotaDataAccess(t *testing.T) {
@ -21,8 +20,7 @@ func TestIntegrationQuotaDataAccess(t *testing.T) {
} }
t.Run("quota deleted", func(t *testing.T) { t.Run("quota deleted", func(t *testing.T) {
ctx := quota.FromContext(context.Background(), &quota.TargetToSrv{}) err := quotaStore.DeleteByUser(context.Background(), 1)
err := quotaStore.DeleteByUser(ctx, 1)
require.NoError(t, err) require.NoError(t, err)
}) })
} }

View File

@ -12,46 +12,18 @@ type FakeQuotaService struct {
err error err error
} }
func New(reached bool, err error) *FakeQuotaService { func NewQuotaServiceFake() *FakeQuotaService {
return &FakeQuotaService{reached, err} return &FakeQuotaService{}
} }
func (f *FakeQuotaService) GetQuotasByScope(ctx context.Context, scope quota.Scope, id int64) ([]quota.QuotaDTO, error) { func (f *FakeQuotaService) QuotaReached(c *models.ReqContext, target string) (bool, error) {
return []quota.QuotaDTO{}, nil
}
func (f *FakeQuotaService) Update(ctx context.Context, cmd *quota.UpdateQuotaCmd) error {
return nil
}
func (f *FakeQuotaService) QuotaReached(c *models.ReqContext, target quota.TargetSrv) (bool, error) {
return f.reached, f.err return f.reached, f.err
} }
func (f *FakeQuotaService) CheckQuotaReached(c context.Context, target quota.TargetSrv, params *quota.ScopeParameters) (bool, error) { func (f *FakeQuotaService) CheckQuotaReached(c context.Context, target string, params *quota.ScopeParameters) (bool, error) {
return f.reached, f.err return f.reached, f.err
} }
func (f *FakeQuotaService) DeleteQuotaForUser(c context.Context, userID int64) error { func (f *FakeQuotaService) DeleteByUser(c context.Context, userID int64) error {
return f.err return f.err
} }
func (f *FakeQuotaService) RegisterQuotaReporter(e *quota.NewUsageReporter) error {
return f.err
}
type FakeQuotaStore struct {
ExpectedError error
}
func (f *FakeQuotaStore) DeleteByUser(ctx quota.Context, userID int64) error {
return f.ExpectedError
}
func (f *FakeQuotaStore) Get(ctx quota.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
return nil, f.ExpectedError
}
func (f *FakeQuotaStore) Update(ctx quota.Context, cmd *quota.UpdateQuotaCmd) error {
return f.ExpectedError
}

View File

@ -5,7 +5,6 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/kvstore"
@ -14,7 +13,6 @@ import (
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
dsservice "github.com/grafana/grafana/pkg/services/datasources/service" dsservice "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager" secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -29,9 +27,7 @@ func SetupTestDataSourceSecretMigrationService(t *testing.T, sqlStore db.DB, kvS
features = featuremgmt.WithFeatures(featuremgmt.FlagDisableSecretsCompatibility, true) features = featuremgmt.WithFeatures(featuremgmt.FlagDisableSecretsCompatibility, true)
} }
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
quotaService := quotatest.New(false, nil) dsService := dsservice.ProvideService(sqlStore, secretsService, secretsStore, cfg, features, acmock.New().WithDisabled(), acmock.NewMockedPermissionsService())
dsService, err := dsservice.ProvideService(sqlStore, secretsService, secretsStore, cfg, features, acmock.New().WithDisabled(), acmock.NewMockedPermissionsService(), quotaService)
require.NoError(t, err)
migService := ProvideDataSourceMigrationService(dsService, kvStore, features) migService := ProvideDataSourceMigrationService(dsService, kvStore, features)
return migService return migService
} }

View File

@ -27,7 +27,6 @@ import (
"github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/database" "github.com/grafana/grafana/pkg/services/serviceaccounts/database"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
@ -45,12 +44,9 @@ var (
func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) { func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
orgService, err := orgimpl.ProvideService(store, setting.NewCfg(), quotaService) orgService := orgimpl.ProvideService(store, setting.NewCfg())
require.NoError(t, err)
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, orgService) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, orgService)
svcmock := tests.ServiceAccountMock{} svcmock := tests.ServiceAccountMock{}
@ -61,7 +57,7 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
}() }()
orgCmd := &models.CreateOrgCommand{Name: "Some Test Org"} orgCmd := &models.CreateOrgCommand{Name: "Some Test Org"}
err = store.CreateOrg(context.Background(), orgCmd) err := store.CreateOrg(context.Background(), orgCmd)
require.Nil(t, err) require.Nil(t, err)
type testCreateSATestCase struct { type testCreateSATestCase struct {
@ -216,9 +212,7 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) { func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil)
svcmock := tests.ServiceAccountMock{} svcmock := tests.ServiceAccountMock{}
@ -290,9 +284,7 @@ func setupTestServer(t *testing.T, svc *tests.ServiceAccountMock,
sqlStore db.DB, saStore serviceaccounts.Store) (*web.Mux, *ServiceAccountsAPI) { sqlStore db.DB, saStore serviceaccounts.Store) (*web.Mux, *ServiceAccountsAPI) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
teamSvc := teamimpl.ProvideService(sqlStore, cfg) teamSvc := teamimpl.ProvideService(sqlStore, cfg)
userSvc := userimpl.ProvideService(sqlStore, nil, cfg, teamimpl.ProvideService(sqlStore, cfg), nil)
userSvc, err := userimpl.ProvideService(sqlStore, nil, cfg, teamimpl.ProvideService(sqlStore, cfg), nil, quotatest.New(false, nil))
require.NoError(t, err)
saPermissionService, err := ossaccesscontrol.ProvideServiceAccountPermissions( saPermissionService, err := ossaccesscontrol.ProvideServiceAccountPermissions(
cfg, routing.NewRouteRegister(), sqlStore, acmock, &licensing.OSSLicensingService{}, saStore, acmock, teamSvc, userSvc) cfg, routing.NewRouteRegister(), sqlStore, acmock, &licensing.OSSLicensingService{}, saStore, acmock, teamSvc, userSvc)
require.NoError(t, err) require.NoError(t, err)
@ -324,9 +316,7 @@ func setupTestServer(t *testing.T, svc *tests.ServiceAccountMock,
func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) { func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil)
svcmock := tests.ServiceAccountMock{} svcmock := tests.ServiceAccountMock{}
@ -418,9 +408,7 @@ func newString(s string) *string {
func TestServiceAccountsAPI_UpdateServiceAccount(t *testing.T) { func TestServiceAccountsAPI_UpdateServiceAccount(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil)
svcmock := tests.ServiceAccountMock{} svcmock := tests.ServiceAccountMock{}

View File

@ -23,7 +23,6 @@ import (
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/database" "github.com/grafana/grafana/pkg/services/serviceaccounts/database"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
@ -55,9 +54,7 @@ func createTokenforSA(t *testing.T, store serviceaccounts.Store, keyName string,
func TestServiceAccountsAPI_CreateToken(t *testing.T) { func TestServiceAccountsAPI_CreateToken(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil)
svcmock := tests.ServiceAccountMock{} svcmock := tests.ServiceAccountMock{}
@ -174,9 +171,7 @@ func TestServiceAccountsAPI_CreateToken(t *testing.T) {
func TestServiceAccountsAPI_DeleteToken(t *testing.T) { func TestServiceAccountsAPI_DeleteToken(t *testing.T) {
store := db.InitTestDB(t) store := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(store, store.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(store, store.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(store) kvStore := kvstore.ProvideService(store)
svcMock := &tests.ServiceAccountMock{} svcMock := &tests.ServiceAccountMock{}
saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil) saStore := database.ProvideServiceAccountsStore(store, apiKeyService, kvStore, nil)

View File

@ -14,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
@ -113,12 +112,9 @@ func TestStore_DeleteServiceAccount(t *testing.T) {
func setupTestDatabase(t *testing.T) (*sqlstore.SQLStore, *ServiceAccountsStoreImpl) { func setupTestDatabase(t *testing.T) (*sqlstore.SQLStore, *ServiceAccountsStoreImpl) {
t.Helper() t.Helper()
db := db.InitTestDB(t) db := db.InitTestDB(t)
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(db, db.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(db, db.Cfg, quotaService)
require.NoError(t, err)
kvStore := kvstore.ProvideService(db) kvStore := kvstore.ProvideService(db)
orgService, err := orgimpl.ProvideService(db, setting.NewCfg(), quotaService) orgService := orgimpl.ProvideService(db, setting.NewCfg())
require.NoError(t, err)
return db, ProvideServiceAccountsStore(db, apiKeyService, kvStore, orgService) return db, ProvideServiceAccountsStore(db, apiKeyService, kvStore, orgService)
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -71,10 +70,8 @@ func SetupApiKey(t *testing.T, sqlStore *sqlstore.SQLStore, testKey TestApiKey)
addKeyCmd.Key = "secret" addKeyCmd.Key = "secret"
} }
quotaService := quotatest.New(false, nil) apiKeyService := apikeyimpl.ProvideService(sqlStore, sqlStore.Cfg)
apiKeyService, err := apikeyimpl.ProvideService(sqlStore, sqlStore.Cfg, quotaService) err := apiKeyService.AddAPIKey(context.Background(), addKeyCmd)
require.NoError(t, err)
err = apiKeyService.AddAPIKey(context.Background(), addKeyCmd)
require.NoError(t, err) require.NoError(t, err)
if testKey.IsExpired { if testKey.IsExpired {

View File

@ -98,6 +98,34 @@ func (m *SQLStoreMock) WithNewDbSession(ctx context.Context, callback sqlstore.D
return m.ExpectedError return m.ExpectedError
} }
func (m *SQLStoreMock) GetOrgQuotaByTarget(ctx context.Context, query *models.GetOrgQuotaByTargetQuery) error {
return m.ExpectedError
}
func (m *SQLStoreMock) GetOrgQuotas(ctx context.Context, query *models.GetOrgQuotasQuery) error {
return m.ExpectedError
}
func (m *SQLStoreMock) UpdateOrgQuota(ctx context.Context, cmd *models.UpdateOrgQuotaCmd) error {
return m.ExpectedError
}
func (m *SQLStoreMock) GetUserQuotaByTarget(ctx context.Context, query *models.GetUserQuotaByTargetQuery) error {
return m.ExpectedError
}
func (m *SQLStoreMock) GetUserQuotas(ctx context.Context, query *models.GetUserQuotasQuery) error {
return m.ExpectedError
}
func (m *SQLStoreMock) UpdateUserQuota(ctx context.Context, cmd *models.UpdateUserQuotaCmd) error {
return m.ExpectedError
}
func (m *SQLStoreMock) GetGlobalQuotaByTarget(ctx context.Context, query *models.GetGlobalQuotaByTargetQuery) error {
return m.ExpectedError
}
func (m *SQLStoreMock) WithTransactionalDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error { func (m *SQLStoreMock) WithTransactionalDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error {
return m.ExpectedError return m.ExpectedError
} }

View File

@ -0,0 +1,315 @@
package sqlstore
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
const (
alertRuleTarget = "alert_rule"
dashboardTarget = "dashboard"
filesTarget = "file"
)
type targetCount struct {
Count int64
}
func (ss *SQLStore) GetOrgQuotaByTarget(ctx context.Context, query *models.GetOrgQuotaByTargetQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
quota := models.Quota{
Target: query.Target,
OrgId: query.OrgId,
}
has, err := sess.Get(&quota)
if err != nil {
return err
} else if !has {
quota.Limit = query.Default
}
var used int64
if query.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
// get quota used.
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM %s WHERE org_id=?",
dialect.Quote(query.Target))
if query.Target == dashboardTarget {
rawSQL += fmt.Sprintf(" AND is_folder=%s", dialect.BooleanStr(false))
}
// need to account for removing service accounts from the user table
if query.Target == "org_user" {
rawSQL = fmt.Sprintf("SELECT COUNT(*) as count from (select user_id from %s where org_id=? AND user_id IN (SELECT id as user_id FROM %s WHERE is_service_account=%s)) as subq",
dialect.Quote(query.Target),
dialect.Quote("user"),
dialect.BooleanStr(false),
)
}
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL, query.OrgId).Find(&resp); err != nil {
return err
}
used = resp[0].Count
}
query.Result = &models.OrgQuotaDTO{
Target: query.Target,
Limit: quota.Limit,
OrgId: query.OrgId,
Used: used,
}
return nil
})
}
func (ss *SQLStore) GetOrgQuotas(ctx context.Context, query *models.GetOrgQuotasQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
quotas := make([]*models.Quota, 0)
if err := sess.Table("quota").Where("org_id=? AND user_id=0", query.OrgId).Find(&quotas); err != nil {
return err
}
defaultQuotas := setting.Quota.Org.ToMap()
seenTargets := make(map[string]bool)
for _, q := range quotas {
seenTargets[q.Target] = true
}
for t, v := range defaultQuotas {
if _, ok := seenTargets[t]; !ok {
quotas = append(quotas, &models.Quota{
OrgId: query.OrgId,
Target: t,
Limit: v,
})
}
}
result := make([]*models.OrgQuotaDTO, len(quotas))
for i, q := range quotas {
var used int64
var rawSQL string
if q.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
// get quota used.
rawSQL = fmt.Sprintf("SELECT COUNT(*) as count from %s where org_id=?", dialect.Quote(q.Target))
// need to account for removing service accounts from the user table
if q.Target == "org_user" {
rawSQL = fmt.Sprintf("SELECT COUNT(*) as count from (select user_id from %s where org_id=? AND user_id IN (SELECT id as user_id FROM %s WHERE is_service_account=%s)) as subq",
dialect.Quote(q.Target),
dialect.Quote("user"),
dialect.BooleanStr(false),
)
}
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL, q.OrgId).Find(&resp); err != nil {
return err
}
used = resp[0].Count
}
result[i] = &models.OrgQuotaDTO{
Target: q.Target,
Limit: q.Limit,
OrgId: q.OrgId,
Used: used,
}
}
query.Result = result
return nil
})
}
func (ss *SQLStore) UpdateOrgQuota(ctx context.Context, cmd *models.UpdateOrgQuotaCmd) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
// Check if quota is already defined in the DB
quota := models.Quota{
Target: cmd.Target,
OrgId: cmd.OrgId,
}
has, err := sess.Get(&quota)
if err != nil {
return err
}
quota.Updated = time.Now()
quota.Limit = cmd.Limit
if !has {
quota.Created = time.Now()
// No quota in the DB for this target, so create a new one.
if _, err := sess.Insert(&quota); err != nil {
return err
}
} else {
// update existing quota entry in the DB.
_, err := sess.ID(quota.Id).Update(&quota)
if err != nil {
return err
}
}
return nil
})
}
func (ss *SQLStore) GetUserQuotaByTarget(ctx context.Context, query *models.GetUserQuotaByTargetQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
quota := models.Quota{
Target: query.Target,
UserId: query.UserId,
}
has, err := sess.Get(&quota)
if err != nil {
return err
} else if !has {
quota.Limit = query.Default
}
var used int64
if query.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
// get quota used.
rawSQL := fmt.Sprintf("SELECT COUNT(*) as count from %s where user_id=?", dialect.Quote(query.Target))
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL, query.UserId).Find(&resp); err != nil {
return err
}
used = resp[0].Count
}
query.Result = &models.UserQuotaDTO{
Target: query.Target,
Limit: quota.Limit,
UserId: query.UserId,
Used: used,
}
return nil
})
}
func (ss *SQLStore) GetUserQuotas(ctx context.Context, query *models.GetUserQuotasQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
quotas := make([]*models.Quota, 0)
if err := sess.Table("quota").Where("user_id=? AND org_id=0", query.UserId).Find(&quotas); err != nil {
return err
}
defaultQuotas := setting.Quota.User.ToMap()
seenTargets := make(map[string]bool)
for _, q := range quotas {
seenTargets[q.Target] = true
}
for t, v := range defaultQuotas {
if _, ok := seenTargets[t]; !ok {
quotas = append(quotas, &models.Quota{
UserId: query.UserId,
Target: t,
Limit: v,
})
}
}
result := make([]*models.UserQuotaDTO, len(quotas))
for i, q := range quotas {
var used int64
if q.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
// get quota used.
rawSQL := fmt.Sprintf("SELECT COUNT(*) as count from %s where user_id=?", dialect.Quote(q.Target))
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL, q.UserId).Find(&resp); err != nil {
return err
}
used = resp[0].Count
}
result[i] = &models.UserQuotaDTO{
Target: q.Target,
Limit: q.Limit,
UserId: q.UserId,
Used: used,
}
}
query.Result = result
return nil
})
}
func (ss *SQLStore) UpdateUserQuota(ctx context.Context, cmd *models.UpdateUserQuotaCmd) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
// Check if quota is already defined in the DB
quota := models.Quota{
Target: cmd.Target,
UserId: cmd.UserId,
}
has, err := sess.Get(&quota)
if err != nil {
return err
}
quota.Updated = time.Now()
quota.Limit = cmd.Limit
if !has {
quota.Created = time.Now()
// No quota in the DB for this target, so create a new one.
if _, err := sess.Insert(&quota); err != nil {
return err
}
} else {
// update existing quota entry in the DB.
_, err := sess.ID(quota.Id).Update(&quota)
if err != nil {
return err
}
}
return nil
})
}
func (ss *SQLStore) GetGlobalQuotaByTarget(ctx context.Context, query *models.GetGlobalQuotaByTargetQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
var used int64
if query.Target == filesTarget {
// get quota used.
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM %s",
dialect.Quote("file"))
notFolderCondition := fmt.Sprintf(" WHERE path NOT LIKE '%s'", "%/")
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL + notFolderCondition).Find(&resp); err != nil {
return err
}
used = resp[0].Count
} else if query.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
// get quota used.
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM %s",
dialect.Quote(query.Target))
if query.Target == dashboardTarget {
rawSQL += fmt.Sprintf(" WHERE is_folder=%s", dialect.BooleanStr(false))
}
// removing service accounts from count
if query.Target == dialect.Quote("user") {
rawSQL += fmt.Sprintf(" WHERE is_service_account=%s", dialect.BooleanStr(false))
}
resp := make([]*targetCount, 0)
if err := sess.SQL(rawSQL).Find(&resp); err != nil {
return err
}
used = resp[0].Count
}
query.Result = &models.GlobalQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: used,
}
return nil
})
}

View File

@ -0,0 +1,301 @@
package sqlstore
import (
"context"
"testing"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
func TestIntegrationQuotaCommandsAndQueries(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
sqlStore := InitTestDB(t)
userId := int64(1)
orgId := int64(0)
setting.Quota = setting.QuotaSettings{
Enabled: true,
Org: &setting.OrgQuota{
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
AlertRule: 5,
},
User: &setting.UserQuota{
Org: 5,
},
Global: &setting.GlobalQuota{
Org: 5,
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
Session: 5,
AlertRule: 5,
},
}
createUserCmd := user.CreateUserCommand{
Name: "TestUser",
OrgID: orgId,
SkipOrgSetup: true,
}
user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
require.NoError(t, err)
// create a new org and add user_id 1 as admin.
// we will then have an org with 1 user. and a user
// with 1 org.
userCmd := models.CreateOrgCommand{
Name: "TestOrg",
UserId: user.ID,
}
err = sqlStore.CreateOrg(context.Background(), &userCmd)
require.NoError(t, err)
orgId = userCmd.Result.Id
t.Run("Given saved org quota for users", func(t *testing.T) {
orgCmd := models.UpdateOrgQuotaCmd{
OrgId: orgId,
Target: "org_user",
Limit: 10,
}
err := sqlStore.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
t.Run("Should be able to get saved quota by org id and target", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(10), query.Result.Limit)
})
t.Run("Should be able to get default quota by org id and target", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: 123, Target: "org_user", Default: 11}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(11), query.Result.Limit)
})
t.Run("Should be able to get used org quota when rows exist", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 11}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(1), query.Result.Used)
})
t.Run("Should be able to get used org quota when no rows exist", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: 2, Target: "org_user", Default: 11}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(0), query.Result.Used)
})
t.Run("Should be able to get zero used org alert quota when table does not exist (ngalert is not enabled - default case)", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: 2, Target: "alert", Default: 11}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(0), query.Result.Used)
})
t.Run("Should be able to quota list for org", func(t *testing.T) {
query := models.GetOrgQuotasQuery{OrgId: orgId}
err = sqlStore.GetOrgQuotas(context.Background(), &query)
require.NoError(t, err)
require.Len(t, query.Result, 5)
for _, res := range query.Result {
limit := int64(5) // default quota limit
used := int64(0)
if res.Target == "org_user" {
limit = 10 // customized quota limit.
used = 1
}
require.Equal(t, limit, res.Limit)
require.Equal(t, used, res.Used)
}
})
})
t.Run("Given saved org quota for dashboards", func(t *testing.T) {
orgCmd := models.UpdateOrgQuotaCmd{
OrgId: orgId,
Target: dashboardTarget,
Limit: 10,
}
err := sqlStore.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
t.Run("Should be able to get saved quota by org id and target", func(t *testing.T) {
query := models.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: dashboardTarget, Default: 1}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(10), query.Result.Limit)
require.Equal(t, int64(0), query.Result.Used)
})
})
t.Run("Given saved user quota for org", func(t *testing.T) {
userQuotaCmd := models.UpdateUserQuotaCmd{
UserId: userId,
Target: "org_user",
Limit: 10,
}
err := sqlStore.UpdateUserQuota(context.Background(), &userQuotaCmd)
require.NoError(t, err)
t.Run("Should be able to get saved quota by user id and target", func(t *testing.T) {
query := models.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(10), query.Result.Limit)
})
t.Run("Should be able to get default quota by user id and target", func(t *testing.T) {
query := models.GetUserQuotaByTargetQuery{UserId: 9, Target: "org_user", Default: 11}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(11), query.Result.Limit)
})
t.Run("Should be able to get used user quota when rows exist", func(t *testing.T) {
query := models.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 11}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(1), query.Result.Used)
})
t.Run("Should be able to get used user quota when no rows exist", func(t *testing.T) {
query := models.GetUserQuotaByTargetQuery{UserId: 2, Target: "org_user", Default: 11}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(0), query.Result.Used)
})
t.Run("Should be able to quota list for user", func(t *testing.T) {
query := models.GetUserQuotasQuery{UserId: userId}
err = sqlStore.GetUserQuotas(context.Background(), &query)
require.NoError(t, err)
require.Len(t, query.Result, 1)
require.Equal(t, int64(10), query.Result[0].Limit)
require.Equal(t, int64(1), query.Result[0].Used)
})
})
t.Run("Should be able to global user quota", func(t *testing.T) {
query := models.GetGlobalQuotaByTargetQuery{Target: "user", Default: 5}
err = sqlStore.GetGlobalQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
require.Equal(t, int64(1), query.Result.Used)
})
t.Run("Should be able to global org quota", func(t *testing.T) {
query := models.GetGlobalQuotaByTargetQuery{Target: "org", Default: 5}
err = sqlStore.GetGlobalQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
require.Equal(t, int64(1), query.Result.Used)
})
t.Run("Should be able to get zero used global alert quota when table does not exist (ngalert is not enabled - default case)", func(t *testing.T) {
query := models.GetGlobalQuotaByTargetQuery{Target: "alert_rule", Default: 5}
err = sqlStore.GetGlobalQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
require.Equal(t, int64(0), query.Result.Used)
})
t.Run("Should be able to global dashboard quota", func(t *testing.T) {
query := models.GetGlobalQuotaByTargetQuery{Target: dashboardTarget, Default: 5}
err = sqlStore.GetGlobalQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
require.Equal(t, int64(0), query.Result.Used)
})
// related: https://github.com/grafana/grafana/issues/14342
t.Run("Should org quota updating is successful even if it called multiple time", func(t *testing.T) {
orgCmd := models.UpdateOrgQuotaCmd{
OrgId: orgId,
Target: "org_user",
Limit: 5,
}
err := sqlStore.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
query := models.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
// XXX: resolution of `Updated` column is 1sec, so this makes delay
time.Sleep(1 * time.Second)
orgCmd = models.UpdateOrgQuotaCmd{
OrgId: orgId,
Target: "org_user",
Limit: 10,
}
err = sqlStore.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
query = models.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
err = sqlStore.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(10), query.Result.Limit)
})
// related: https://github.com/grafana/grafana/issues/14342
t.Run("Should user quota updating is successful even if it called multiple time", func(t *testing.T) {
userQuotaCmd := models.UpdateUserQuotaCmd{
UserId: userId,
Target: "org_user",
Limit: 5,
}
err := sqlStore.UpdateUserQuota(context.Background(), &userQuotaCmd)
require.NoError(t, err)
query := models.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(5), query.Result.Limit)
// XXX: resolution of `Updated` column is 1sec, so this makes delay
time.Sleep(1 * time.Second)
userQuotaCmd = models.UpdateUserQuotaCmd{
UserId: userId,
Target: "org_user",
Limit: 10,
}
err = sqlStore.UpdateUserQuota(context.Background(), &userQuotaCmd)
require.NoError(t, err)
query = models.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
err = sqlStore.GetUserQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, int64(10), query.Result.Limit)
})
}

View File

@ -23,6 +23,13 @@ type Store interface {
GetSignedInUser(ctx context.Context, query *models.GetSignedInUserQuery) error GetSignedInUser(ctx context.Context, query *models.GetSignedInUserQuery) error
WithDbSession(ctx context.Context, callback DBTransactionFunc) error WithDbSession(ctx context.Context, callback DBTransactionFunc) error
WithNewDbSession(ctx context.Context, callback DBTransactionFunc) error WithNewDbSession(ctx context.Context, callback DBTransactionFunc) error
GetOrgQuotaByTarget(ctx context.Context, query *models.GetOrgQuotaByTargetQuery) error
GetOrgQuotas(ctx context.Context, query *models.GetOrgQuotasQuery) error
UpdateOrgQuota(ctx context.Context, cmd *models.UpdateOrgQuotaCmd) error
GetUserQuotaByTarget(ctx context.Context, query *models.GetUserQuotaByTargetQuery) error
GetUserQuotas(ctx context.Context, query *models.GetUserQuotasQuery) error
UpdateUserQuota(ctx context.Context, cmd *models.UpdateUserQuotaCmd) error
GetGlobalQuotaByTarget(ctx context.Context, query *models.GetGlobalQuotaByTargetQuery) error
WithTransactionalDbSession(ctx context.Context, callback DBTransactionFunc) error WithTransactionalDbSession(ctx context.Context, callback DBTransactionFunc) error
InTransaction(ctx context.Context, fn func(ctx context.Context) error) error InTransaction(ctx context.Context, fn func(ctx context.Context) error) error
Migrate(bool) error Migrate(bool) error

View File

@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -59,11 +58,6 @@ type CreateFolderCmd struct {
Path string `json:"path"` Path string `json:"path"`
} }
const (
QuotaTargetSrv quota.TargetSrv = "store"
QuotaTarget quota.Target = "file"
)
type StorageService interface { type StorageService interface {
registry.BackgroundService registry.BackgroundService
@ -103,7 +97,7 @@ func ProvideService(
features featuremgmt.FeatureToggles, features featuremgmt.FeatureToggles,
cfg *setting.Cfg, cfg *setting.Cfg,
quotaService quota.Service, quotaService quota.Service,
) (StorageService, error) { ) StorageService {
settings, err := LoadStorageConfig(cfg, features) settings, err := LoadStorageConfig(cfg, features)
if err != nil { if err != nil {
grafanaStorageLogger.Warn("error loading storage config", "error", err) grafanaStorageLogger.Warn("error loading storage config", "error", err)
@ -265,37 +259,7 @@ func ProvideService(
s := newStandardStorageService(sql, globalRoots, initializeOrgStorages, authService, cfg) s := newStandardStorageService(sql, globalRoots, initializeOrgStorages, authService, cfg)
s.quotaService = quotaService s.quotaService = quotaService
s.cfg = settings s.cfg = settings
return s
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return nil, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: QuotaTargetSrv,
DefaultLimits: defaultLimits,
Reporter: s.Usage,
}); err != nil {
return nil, err
}
return s, nil
}
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.File)
return limits, nil
} }
func createSystemBrandingPathFilter() filestorage.PathFilter { func createSystemBrandingPathFilter() filestorage.PathFilter {
@ -365,32 +329,6 @@ func (s *standardStorageService) Read(ctx context.Context, user *user.SignedInUs
return s.tree.GetFile(ctx, getOrgId(user), path) return s.tree.GetFile(ctx, getOrgId(user), path)
} }
func (s *standardStorageService) Usage(ctx context.Context, ScopeParameters *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
err := s.sql.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
type result struct {
Count int64
}
r := result{}
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM file WHERE path NOT LIKE '%s'", "%/")
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
tag, err := quota.NewTag(QuotaTargetSrv, QuotaTarget, quota.GlobalScope)
if err != nil {
return err
}
u.Set(tag, r.Count)
return nil
})
return u, err
}
type UploadRequest struct { type UploadRequest struct {
Contents []byte Contents []byte
Path string Path string
@ -457,7 +395,7 @@ func (s *standardStorageService) Upload(ctx context.Context, user *user.SignedIn
func (s *standardStorageService) checkFileQuota(ctx context.Context, path string) error { func (s *standardStorageService) checkFileQuota(ctx context.Context, path string) error {
// assumes we are only uploading to the SQL database - TODO: refactor once we introduce object stores // assumes we are only uploading to the SQL database - TODO: refactor once we introduce object stores
quotaReached, err := s.quotaService.CheckQuotaReached(ctx, QuotaTargetSrv, nil) quotaReached, err := s.quotaService.CheckQuotaReached(ctx, "file", nil)
if err != nil { if err != nil {
grafanaStorageLogger.Error("failed while checking upload quota", "path", path, "error", err) grafanaStorageLogger.Error("failed while checking upload quota", "path", path, "error", err)
return ErrUploadInternalError return ErrUploadInternalError

View File

@ -118,7 +118,7 @@ func setupUploadStore(t *testing.T, authService storageAuthService) (StorageServ
store.cfg = &GlobalStorageConfig{ store.cfg = &GlobalStorageConfig{
AllowUnsanitizedSvgUpload: true, AllowUnsanitizedSvgUpload: true,
} }
store.quotaService = quotatest.New(false, nil) store.quotaService = quotatest.NewQuotaServiceFake()
return store, mockStorage, storageName return store, mockStorage, storageName
} }
@ -297,7 +297,7 @@ func TestContentRootWithNestedStorage(t *testing.T) {
store.cfg = &GlobalStorageConfig{ store.cfg = &GlobalStorageConfig{
AllowUnsanitizedSvgUpload: true, AllowUnsanitizedSvgUpload: true,
} }
store.quotaService = quotatest.New(false, nil) store.quotaService = quotatest.NewQuotaServiceFake()
fileName := "file.jpg" fileName := "file.jpg"
tests := []struct { tests := []struct {

View File

@ -357,8 +357,3 @@ type SearchUserFilter interface {
} }
type FilterHandler func(params []string) (Filter, error) type FilterHandler func(params []string) (Filter, error)
const (
QuotaTargetSrv string = "user"
QuotaTarget string = "user"
)

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -38,8 +37,6 @@ type store interface {
BatchDisableUsers(context.Context, *user.BatchDisableUsersCommand) error BatchDisableUsers(context.Context, *user.BatchDisableUsersCommand) error
Disable(context.Context, *user.DisableUserCommand) error Disable(context.Context, *user.DisableUserCommand) error
Search(context.Context, *user.SearchUsersQuery) (*user.SearchUserQueryResult, error) Search(context.Context, *user.SearchUsersQuery) (*user.SearchUserQueryResult, error)
Count(ctx context.Context) (int64, error)
} }
type sqlStore struct { type sqlStore struct {
@ -464,22 +461,6 @@ func (ss *sqlStore) UpdatePermissions(ctx context.Context, userID int64, isAdmin
}) })
} }
func (ss *sqlStore) Count(ctx context.Context) (int64, error) {
type result struct {
Count int64
}
r := result{}
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
rawSQL := fmt.Sprintf("SELECT COUNT(*) as count from %s WHERE is_service_account=%s", ss.db.GetDialect().Quote("user"), ss.db.GetDialect().BooleanStr(false))
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
return err
}
return nil
})
return r.Count, err
}
// validateOneAdminLeft validate that there is an admin user left // validateOneAdminLeft validate that there is an admin user left
func validateOneAdminLeft(ctx context.Context, sess *db.Session) error { func validateOneAdminLeft(ctx context.Context, sess *db.Session) error {
count, err := sess.Where("is_admin=?", true).Count(&user.User{}) count, err := sess.Where("is_admin=?", true).Count(&user.User{})

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/models/roletype" "github.com/grafana/grafana/pkg/models/roletype"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/team" "github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -33,44 +32,15 @@ func ProvideService(
cfg *setting.Cfg, cfg *setting.Cfg,
teamService team.Service, teamService team.Service,
cacheService *localcache.CacheService, cacheService *localcache.CacheService,
quotaService quota.Service, ) user.Service {
) (user.Service, error) {
store := ProvideStore(db, cfg) store := ProvideStore(db, cfg)
s := &Service{ return &Service{
store: &store, store: &store,
orgService: orgService, orgService: orgService,
cfg: cfg, cfg: cfg,
teamService: teamService, teamService: teamService,
cacheService: cacheService, cacheService: cacheService,
} }
defaultLimits, err := readQuotaConfig(cfg)
if err != nil {
return s, err
}
if err := quotaService.RegisterQuotaReporter(&quota.NewUsageReporter{
TargetSrv: quota.TargetSrv(user.QuotaTargetSrv),
DefaultLimits: defaultLimits,
Reporter: s.Usage,
}); err != nil {
return s, err
}
return s, nil
}
func (s *Service) Usage(ctx context.Context, _ *quota.ScopeParameters) (*quota.Map, error) {
u := &quota.Map{}
if used, err := s.store.Count(ctx); err != nil {
return u, err
} else {
tag, err := quota.NewTag(quota.TargetSrv(user.QuotaTargetSrv), quota.Target(user.QuotaTarget), quota.GlobalScope)
if err != nil {
return u, err
}
u.Set(tag, used)
}
return u, nil
} }
func (s *Service) Create(ctx context.Context, cmd *user.CreateUserCommand) (*user.User, error) { func (s *Service) Create(ctx context.Context, cmd *user.CreateUserCommand) (*user.User, error) {
@ -334,19 +304,3 @@ func (s *Service) GetProfile(ctx context.Context, query *user.GetUserProfileQuer
result, err := s.store.GetProfile(ctx, query) result, err := s.store.GetProfile(ctx, query)
return result, err return result, err
} }
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
limits := &quota.Map{}
if cfg == nil {
return limits, nil
}
globalQuotaTag, err := quota.NewTag(quota.TargetSrv(user.QuotaTargetSrv), quota.Target(user.QuotaTarget), quota.GlobalScope)
if err != nil {
return limits, err
}
limits.Set(globalQuotaTag, cfg.Quota.Global.User)
return limits, nil
}

View File

@ -252,7 +252,3 @@ func (f *FakeUserStore) Disable(ctx context.Context, cmd *user.DisableUserComman
func (f *FakeUserStore) Search(ctx context.Context, query *user.SearchUsersQuery) (*user.SearchUserQueryResult, error) { func (f *FakeUserStore) Search(ctx context.Context, query *user.SearchUsersQuery) (*user.SearchUserQueryResult, error) {
return f.ExpectedSearchUserQueryResult, f.ExpectedError return f.ExpectedSearchUserQueryResult, f.ExpectedError
} }
func (f *FakeUserStore) Count(ctx context.Context) (int64, error) {
return 0, nil
}

View File

@ -153,6 +153,9 @@ var (
LDAPAllowSignup bool LDAPAllowSignup bool
LDAPActiveSyncEnabled bool LDAPActiveSyncEnabled bool
// Quota
Quota QuotaSettings
// Alerting // Alerting
AlertingEnabled *bool AlertingEnabled *bool
ExecuteAlerts bool ExecuteAlerts bool
@ -419,12 +422,12 @@ type Cfg struct {
LDAPSkipOrgRoleSync bool LDAPSkipOrgRoleSync bool
LDAPAllowSignup bool LDAPAllowSignup bool
Quota QuotaSettings
DefaultTheme string DefaultTheme string
DefaultLocale string DefaultLocale string
HomePage string HomePage string
Quota QuotaSettings
AutoAssignOrg bool AutoAssignOrg bool
AutoAssignOrgId int AutoAssignOrgId int
AutoAssignOrgRole string AutoAssignOrgRole string
@ -1050,12 +1053,11 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
cfg.readAzureSettings() cfg.readAzureSettings()
cfg.readSessionConfig() cfg.readSessionConfig()
cfg.readSmtpSettings() cfg.readSmtpSettings()
cfg.readQuotaSettings()
if err := cfg.readAnnotationSettings(); err != nil { if err := cfg.readAnnotationSettings(); err != nil {
return err return err
} }
cfg.readQuotaSettings()
cfg.readExpressionsSettings() cfg.readExpressionsSettings()
if err := cfg.readGrafanaEnvironmentMetrics(); err != nil { if err := cfg.readGrafanaEnvironmentMetrics(); err != nil {
return err return err

View File

@ -1,5 +1,9 @@
package setting package setting
import (
"reflect"
)
type OrgQuota struct { type OrgQuota struct {
User int64 `target:"org_user"` User int64 `target:"org_user"`
DataSource int64 `target:"data_source"` DataSource int64 `target:"data_source"`
@ -23,17 +27,45 @@ type GlobalQuota struct {
File int64 `target:"file"` File int64 `target:"file"`
} }
func (q *OrgQuota) ToMap() map[string]int64 {
return quotaToMap(*q)
}
func (q *UserQuota) ToMap() map[string]int64 {
return quotaToMap(*q)
}
func quotaToMap(q interface{}) map[string]int64 {
qMap := make(map[string]int64)
typ := reflect.TypeOf(q)
val := reflect.ValueOf(q)
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
name := field.Tag.Get("target")
if name == "" {
name = field.Name
}
if name == "-" {
continue
}
value := val.Field(i)
qMap[name] = value.Int()
}
return qMap
}
type QuotaSettings struct { type QuotaSettings struct {
Enabled bool Enabled bool
Org OrgQuota Org *OrgQuota
User UserQuota User *UserQuota
Global GlobalQuota Global *GlobalQuota
} }
func (cfg *Cfg) readQuotaSettings() { func (cfg *Cfg) readQuotaSettings() {
// set global defaults. // set global defaults.
quota := cfg.Raw.Section("quota") quota := cfg.Raw.Section("quota")
cfg.Quota.Enabled = quota.Key("enabled").MustBool(false) Quota.Enabled = quota.Key("enabled").MustBool(false)
var alertOrgQuota int64 var alertOrgQuota int64
var alertGlobalQuota int64 var alertGlobalQuota int64
@ -42,7 +74,7 @@ func (cfg *Cfg) readQuotaSettings() {
alertGlobalQuota = quota.Key("global_alert_rule").MustInt64(-1) alertGlobalQuota = quota.Key("global_alert_rule").MustInt64(-1)
} }
// per ORG Limits // per ORG Limits
cfg.Quota.Org = OrgQuota{ Quota.Org = &OrgQuota{
User: quota.Key("org_user").MustInt64(10), User: quota.Key("org_user").MustInt64(10),
DataSource: quota.Key("org_data_source").MustInt64(10), DataSource: quota.Key("org_data_source").MustInt64(10),
Dashboard: quota.Key("org_dashboard").MustInt64(10), Dashboard: quota.Key("org_dashboard").MustInt64(10),
@ -51,12 +83,12 @@ func (cfg *Cfg) readQuotaSettings() {
} }
// per User limits // per User limits
cfg.Quota.User = UserQuota{ Quota.User = &UserQuota{
Org: quota.Key("user_org").MustInt64(10), Org: quota.Key("user_org").MustInt64(10),
} }
// Global Limits // Global Limits
cfg.Quota.Global = GlobalQuota{ Quota.Global = &GlobalQuota{
User: quota.Key("global_user").MustInt64(-1), User: quota.Key("global_user").MustInt64(-1),
Org: quota.Key("global_org").MustInt64(-1), Org: quota.Key("global_org").MustInt64(-1),
DataSource: quota.Key("global_data_source").MustInt64(-1), DataSource: quota.Key("global_data_source").MustInt64(-1),
@ -66,4 +98,6 @@ func (cfg *Cfg) readQuotaSettings() {
File: quota.Key("global_file").MustInt64(-1), File: quota.Key("global_file").MustInt64(-1),
AlertRule: alertGlobalQuota, AlertRule: alertGlobalQuota,
} }
cfg.Quota = Quota
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store" ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
@ -1877,8 +1878,6 @@ func TestQuota(t *testing.T) {
// Create a user to make authenticated requests // Create a user to make authenticated requests
createUser(t, store, user.CreateUserCommand{ createUser(t, store, user.CreateUserCommand{
// needs permission to update org quota
IsAdmin: true,
DefaultOrgRole: string(org.RoleEditor), DefaultOrgRole: string(org.RoleEditor),
Password: "password", Password: "password",
Login: "grafana", Login: "grafana",
@ -1919,10 +1918,30 @@ func TestQuota(t *testing.T) {
// check quota limits // check quota limits
t.Run("when quota limit exceed creating new rule should fail", func(t *testing.T) { t.Run("when quota limit exceed creating new rule should fail", func(t *testing.T) {
// get existing org quota // get existing org quota
limit, used := apiClient.GetOrgQuotaLimits(t, 1) query := models.GetOrgQuotaByTargetQuery{OrgId: 1, Target: "alert_rule"}
apiClient.UpdateAlertRuleOrgQuota(t, 1, used) err = store.GetOrgQuotaByTarget(context.Background(), &query)
require.NoError(t, err)
used := query.Result.Used
limit := query.Result.Limit
// set org quota limit to equal used
orgCmd := models.UpdateOrgQuotaCmd{
OrgId: 1,
Target: "alert_rule",
Limit: used,
}
err := store.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
apiClient.UpdateAlertRuleOrgQuota(t, 1, limit) // reset org quota to original value
orgCmd := models.UpdateOrgQuotaCmd{
OrgId: 1,
Target: "alert_rule",
Limit: limit,
}
err := store.UpdateOrgQuota(context.Background(), &orgCmd)
require.NoError(t, err)
}) })
// try to create an alert rule // try to create an alert rule

View File

@ -16,7 +16,6 @@ import (
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -203,60 +202,6 @@ func (a apiClient) CreateFolder(t *testing.T, uID string, title string) {
a.ReloadCachedPermissions(t) a.ReloadCachedPermissions(t)
} }
func (a apiClient) GetOrgQuotaLimits(t *testing.T, orgID int64) (int64, int64) {
t.Helper()
u := fmt.Sprintf("%s/api/orgs/%d/quotas", a.url, orgID)
// nolint:gosec
resp, err := http.Get(u)
require.NoError(t, err)
defer func() {
_ = resp.Body.Close()
}()
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
results := []quota.QuotaDTO{}
require.NoError(t, json.Unmarshal(b, &results))
var limit int64 = 0
var used int64 = 0
for _, q := range results {
if q.Target != string(ngmodels.QuotaTargetSrv) {
continue
}
limit = q.Limit
used = q.Used
}
return limit, used
}
func (a apiClient) UpdateAlertRuleOrgQuota(t *testing.T, orgID int64, limit int64) {
t.Helper()
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
err := enc.Encode(&quota.UpdateQuotaCmd{
Target: "alert_rule",
Limit: limit,
OrgID: orgID,
})
require.NoError(t, err)
u := fmt.Sprintf("%s/api/orgs/%d/quotas/alert_rule", a.url, orgID)
// nolint:gosec
client := &http.Client{}
req, err := http.NewRequest(http.MethodPut, u, &buf)
require.NoError(t, err)
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
require.NoError(t, err)
defer func() {
_ = resp.Body.Close()
}()
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) (int, string) { func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) (int, string) {
t.Helper() t.Helper()
buf := bytes.Buffer{} buf := bytes.Buffer{}

View File

@ -16,14 +16,16 @@ import (
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service" datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/oauthtoken" "github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/services/secrets/fakes"
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore" secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager" secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/legacydata" "github.com/grafana/grafana/pkg/tsdb/legacydata"
) )
func TestHandleRequest(t *testing.T) { func TestHandleRequest(t *testing.T) {
cfg := &setting.Cfg{}
t.Run("Should invoke plugin manager QueryData when handling request for query", func(t *testing.T) { t.Run("Should invoke plugin manager QueryData when handling request for query", func(t *testing.T) {
origOAuthIsOAuthPassThruEnabledFunc := oAuthIsOAuthPassThruEnabledFunc origOAuthIsOAuthPassThruEnabledFunc := oAuthIsOAuthPassThruEnabledFunc
oAuthIsOAuthPassThruEnabledFunc = func(oAuthTokenService oauthtoken.OAuthTokenService, ds *datasources.DataSource) bool { oAuthIsOAuthPassThruEnabledFunc = func(oAuthTokenService oauthtoken.OAuthTokenService, ds *datasources.DataSource) bool {
@ -44,10 +46,7 @@ func TestHandleRequest(t *testing.T) {
secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore()) secretsService := secretsmng.SetupTestService(t, fakes.NewFakeSecretsStore())
secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger")) secretsStore := secretskvs.NewSQLSecretsKVStore(sqlStore, secretsService, log.New("test.logger"))
datasourcePermissions := acmock.NewMockedPermissionsService() datasourcePermissions := acmock.NewMockedPermissionsService()
quotaService := quotatest.New(false, nil) dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), datasourcePermissions)
dsService, err := datasourceservice.ProvideService(nil, secretsService, secretsStore, sqlStore.Cfg, featuremgmt.WithFeatures(), acmock.New(), datasourcePermissions, quotaService)
require.NoError(t, err)
s := ProvideService(client, nil, dsService) s := ProvideService(client, nil, dsService)
ds := &datasources.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()} ds := &datasources.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}