mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Stats: use dashboard stats rather than list (#99130)
This commit is contained in:
parent
5a930e0ec6
commit
e019e34eb5
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
"github.com/grafana/grafana/pkg/services/stats/statsimpl"
|
||||
@ -28,7 +29,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestConcurrentUsersMetrics(t *testing.T) {
|
||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore, &dashboards.FakeDashboardService{}, &foldertest.FakeService{}, &orgtest.FakeOrgService{})
|
||||
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore, &dashboards.FakeDashboardService{}, &foldertest.FakeService{}, &orgtest.FakeOrgService{}, featuremgmt.WithFeatures())
|
||||
s := createService(t, cfg, sqlStore, statsService)
|
||||
|
||||
createConcurrentTokens(t, sqlStore)
|
||||
@ -46,7 +47,7 @@ func TestConcurrentUsersMetrics(t *testing.T) {
|
||||
|
||||
func TestConcurrentUsersStats(t *testing.T) {
|
||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore, &dashboards.FakeDashboardService{}, &foldertest.FakeService{}, &orgtest.FakeOrgService{})
|
||||
statsService := statsimpl.ProvideService(&setting.Cfg{}, sqlStore, &dashboards.FakeDashboardService{}, &foldertest.FakeService{}, &orgtest.FakeOrgService{}, featuremgmt.WithFeatures())
|
||||
s := createService(t, cfg, sqlStore, statsService)
|
||||
|
||||
createConcurrentTokens(t, sqlStore)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
@ -340,5 +341,35 @@ func (a *dashboardSqlAccess) CountRepositoryObjects(context.Context, *resource.C
|
||||
|
||||
// GetStats implements ResourceServer.
|
||||
func (a *dashboardSqlAccess) GetStats(ctx context.Context, req *resource.ResourceStatsRequest) (*resource.ResourceStatsResponse, error) {
|
||||
return nil, fmt.Errorf("not yet (GetStats)")
|
||||
info, err := claims.ParseNamespace(req.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read namespace")
|
||||
}
|
||||
if info.OrgID == 0 {
|
||||
return nil, fmt.Errorf("invalid OrgID found in namespace")
|
||||
}
|
||||
|
||||
if len(req.Kinds) != 1 {
|
||||
return nil, fmt.Errorf("only can query for dashboard kind in legacy fallback")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(req.Kinds[0], "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid kind")
|
||||
}
|
||||
|
||||
count, err := a.dashStore.CountInOrg(ctx, info.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Group: parts[0],
|
||||
Resource: parts[1],
|
||||
Count: count,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ type DashboardService interface {
|
||||
RestoreDashboard(ctx context.Context, dashboard *Dashboard, user identity.Requester, optionalFolderUID string) error
|
||||
CleanUpDeletedDashboards(ctx context.Context) (int64, error)
|
||||
GetSoftDeletedDashboard(ctx context.Context, orgID int64, uid string) (*Dashboard, error)
|
||||
CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error)
|
||||
}
|
||||
|
||||
// PluginService is a service for operating on plugin dashboards.
|
||||
@ -82,6 +83,7 @@ type Store interface {
|
||||
ValidateDashboardBeforeSave(ctx context.Context, dashboard *Dashboard, overwrite bool) (bool, error)
|
||||
|
||||
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
|
||||
CountInOrg(ctx context.Context, orgID int64) (int64, error)
|
||||
// CountDashboardsInFolder returns the number of dashboards associated with
|
||||
// the given parent folder ID.
|
||||
CountDashboardsInFolders(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error)
|
||||
|
@ -168,6 +168,36 @@ func (_m *FakeDashboardService) FindDashboards(ctx context.Context, query *FindP
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountDashboardsInOrg provides a mock function with given fields: ctx, orgID
|
||||
func (_m *FakeDashboardService) CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||
ret := _m.Called(ctx, orgID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CountDashboardsInOrg")
|
||||
}
|
||||
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok {
|
||||
return rf(ctx, orgID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) int64); ok {
|
||||
r0 = rf(ctx, orgID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, orgID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetAllDashboards provides a mock function with given fields: ctx
|
||||
func (_m *FakeDashboardService) GetAllDashboards(ctx context.Context) ([]*Dashboard, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
@ -266,6 +266,24 @@ func (d *dashboardStore) Count(ctx context.Context, scopeParams *quota.ScopePara
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (d *dashboardStore) CountInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||
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 org_id=? AND is_folder=%s", d.store.GetDialect().BooleanStr(false))
|
||||
if _, err := sess.SQL(rawSQL, orgID).Get(&r); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return r.Count, nil
|
||||
}
|
||||
|
||||
func getExistingDashboardByIDOrUIDForUpdate(sess *db.Session, dash *dashboards.Dashboard, overwrite bool) (bool, error) {
|
||||
dashWithIdExists := false
|
||||
isParentFolderChanged := false
|
||||
|
@ -61,6 +61,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
||||
savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.ID, savedFolder.UID, false, "prod")
|
||||
savedDash2 = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod")
|
||||
insertTestDashboard(t, dashboardStore, "test dash org2", 2, 0, "", false, "")
|
||||
insertTestRule(t, sqlStore, savedFolder.OrgID, savedFolder.UID)
|
||||
}
|
||||
|
||||
@ -81,6 +82,17 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
||||
require.Positive(t, len(savedFolder.UID))
|
||||
})
|
||||
|
||||
t.Run("Should be able to get dashboard counts per org", func(t *testing.T) {
|
||||
setup()
|
||||
count, err := dashboardStore.CountInOrg(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(3), count)
|
||||
|
||||
count, err = dashboardStore.CountInOrg(context.Background(), 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), count)
|
||||
})
|
||||
|
||||
t.Run("Should be able to get dashboard by id", func(t *testing.T) {
|
||||
setup()
|
||||
query := dashboards.GetDashboardQuery{
|
||||
|
@ -180,11 +180,10 @@ func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.Sc
|
||||
total := int64(0)
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(org.ID))
|
||||
dashs, err := dr.listDashboardsThroughK8s(ctx, org.ID)
|
||||
orgDashboards, err := dr.CountDashboardsInOrg(ctx, org.ID)
|
||||
if err != nil {
|
||||
return u, err
|
||||
return nil, err
|
||||
}
|
||||
orgDashboards := int64(len(dashs))
|
||||
total += orgDashboards
|
||||
|
||||
tag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.OrgScope)
|
||||
@ -206,6 +205,28 @@ func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.Sc
|
||||
return dr.dashboardStore.Count(ctx, scopeParams)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesCliDashboards) {
|
||||
resp, err := dr.k8sclient.getSearcher().GetStats(ctx, &resource.ResourceStatsRequest{
|
||||
Namespace: dr.k8sclient.getNamespace(orgID),
|
||||
Kinds: []string{
|
||||
v0alpha1.GROUP + "/" + v0alpha1.DashboardResourceInfo.GetName(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(resp.Stats) != 1 {
|
||||
return 0, fmt.Errorf("expected 1 stat, got %d", len(resp.Stats))
|
||||
}
|
||||
|
||||
return resp.Stats[0].Count, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.CountInOrg(ctx, orgID)
|
||||
}
|
||||
|
||||
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
||||
limits := "a.Map{}
|
||||
|
||||
|
@ -271,6 +271,11 @@ func (m *mockResourceIndexClient) Search(ctx context.Context, req *resource.Reso
|
||||
return args.Get(0).(*resource.ResourceSearchResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockResourceIndexClient) GetStats(ctx context.Context, in *resource.ResourceStatsRequest, opts ...grpc.CallOption) (*resource.ResourceStatsResponse, error) {
|
||||
args := m.Called(in)
|
||||
return args.Get(0).(*resource.ResourceStatsResponse), args.Error(1)
|
||||
}
|
||||
|
||||
type mockResourceInterface struct {
|
||||
mock.Mock
|
||||
dynamic.ResourceInterface
|
||||
@ -1779,44 +1784,17 @@ func TestQuotaCount(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
dashboardUnstructuredOrg1 := unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{{
|
||||
Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"name": "uid",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"test": "test",
|
||||
"version": int64(1),
|
||||
"title": "testing slugify",
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
dashboardUnstructuredOrg2 := unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{{
|
||||
Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"name": "uid",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"test": "test",
|
||||
"version": int64(1),
|
||||
"title": "testing slugify",
|
||||
},
|
||||
countOrg1 := resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Count: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
countOrg2 := resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"name": "uid2",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"test": "test2",
|
||||
"version": int64(1),
|
||||
"title": "testing slugify2",
|
||||
},
|
||||
},
|
||||
Count: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1833,13 +1811,11 @@ func TestQuotaCount(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sClientMock, k8sResourceMock := setupK8sDashboardTests(service)
|
||||
ctx, k8sClientMock, _ := setupK8sDashboardTests(service)
|
||||
orgSvc := orgtest.FakeOrgService{ExpectedOrgs: orgs}
|
||||
service.orgService = &orgSvc
|
||||
k8sClientMock.On("getClient", mock.Anything, int64(1)).Return(k8sResourceMock, true).Once()
|
||||
k8sClientMock.On("getClient", mock.Anything, int64(2)).Return(k8sResourceMock, true).Once()
|
||||
k8sResourceMock.On("List", mock.Anything, mock.Anything).Return(&dashboardUnstructuredOrg2, nil).Once()
|
||||
k8sResourceMock.On("List", mock.Anything, mock.Anything).Return(&dashboardUnstructuredOrg1, nil).Once()
|
||||
k8sClientMock.searcher.On("GetStats", mock.Anything).Return(&countOrg2, nil).Once()
|
||||
k8sClientMock.searcher.On("GetStats", mock.Anything).Return(&countOrg1, nil).Once()
|
||||
|
||||
result, err := service.Count(ctx, query)
|
||||
require.NoError(t, err)
|
||||
@ -1858,6 +1834,38 @@ func TestQuotaCount(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCountDashboardsInOrg(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
count := resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Count: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("CountInOrg", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
_, err := service.CountDashboardsInOrg(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sClientMock, _ := setupK8sDashboardTests(service)
|
||||
k8sClientMock.searcher.On("GetStats", mock.Anything).Return(&count, nil).Once()
|
||||
result, err := service.CountDashboardsInOrg(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, result, int64(3))
|
||||
})
|
||||
}
|
||||
|
||||
func TestLegacySaveCommandToUnstructured(t *testing.T) {
|
||||
namespace := "test-namespace"
|
||||
t.Run("successfully converts save command to unstructured", func(t *testing.T) {
|
||||
|
@ -48,6 +48,37 @@ func (_m *FakeDashboardStore) Count(_a0 context.Context, _a1 *quota.ScopeParamet
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountInOrg provides a mock function with given fields: _a0, _a1
|
||||
func (_m *FakeDashboardStore) CountInOrg(_a0 context.Context, _a1 int64) (int64, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Count")
|
||||
}
|
||||
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) int64); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
|
||||
// CountDashboardsInFolders provides a mock function with given fields: ctx, request
|
||||
func (_m *FakeDashboardStore) CountDashboardsInFolders(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error) {
|
||||
ret := _m.Called(ctx, request)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@ -21,13 +22,14 @@ import (
|
||||
const activeUserTimeLimit = time.Hour * 24 * 30
|
||||
const dailyActiveUserTimeLimit = time.Hour * 24
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, db db.DB, dashSvc dashboards.DashboardService, folderSvc folder.Service, orgSvc org.Service) stats.Service {
|
||||
func ProvideService(cfg *setting.Cfg, db db.DB, dashSvc dashboards.DashboardService, folderSvc folder.Service, orgSvc org.Service, features featuremgmt.FeatureToggles) stats.Service {
|
||||
return &sqlStatsService{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
folderSvc: folderSvc,
|
||||
dashSvc: dashSvc,
|
||||
orgSvc: orgSvc,
|
||||
features: features,
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,48 +37,23 @@ type sqlStatsService struct {
|
||||
db db.DB
|
||||
cfg *setting.Cfg
|
||||
dashSvc dashboards.DashboardService
|
||||
features featuremgmt.FeatureToggles
|
||||
folderSvc folder.Service
|
||||
orgSvc org.Service
|
||||
}
|
||||
|
||||
type dashboardStats struct {
|
||||
count int
|
||||
bytesTotal int
|
||||
bytesMax int
|
||||
}
|
||||
|
||||
func (ss *sqlStatsService) collectDashboardStats(ctx context.Context, orgs []*org.OrgDTO, calculateByteSize bool) (dashboardStats, error) {
|
||||
stats := dashboardStats{
|
||||
count: 0,
|
||||
bytesTotal: 0,
|
||||
bytesMax: 0,
|
||||
}
|
||||
|
||||
func (ss *sqlStatsService) getDashboardCount(ctx context.Context, orgs []*org.OrgDTO) (int64, error) {
|
||||
count := int64(0)
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getStatsRequester(org.ID))
|
||||
dashs, err := ss.dashSvc.GetAllDashboardsByOrgId(ctx, org.ID)
|
||||
dashsCount, err := ss.dashSvc.CountDashboardsInOrg(ctx, org.ID)
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
stats.count += len(dashs)
|
||||
|
||||
// only calculate bytes if needed
|
||||
if calculateByteSize {
|
||||
for _, dash := range dashs {
|
||||
b, err := dash.Data.ToDB()
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
stats.bytesTotal += len(b)
|
||||
|
||||
if len(b) > stats.bytesMax {
|
||||
stats.bytesMax = len(b)
|
||||
}
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
count += dashsCount
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (ss *sqlStatsService) getTagCount(ctx context.Context, orgs []*org.OrgDTO) (int64, error) {
|
||||
@ -172,7 +149,6 @@ func (ss *sqlStatsService) GetSystemStats(ctx context.Context, query *stats.GetS
|
||||
monthlyActiveUserDeadlineDate := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
sb.Write(`(SELECT COUNT(*) FROM `+dialect.Quote("user")+` WHERE `+
|
||||
notServiceAccount(dialect)+` AND last_seen_at > ?) AS monthly_active_users,`, monthlyActiveUserDeadlineDate)
|
||||
|
||||
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_provisioning") + `) AS provisioned_dashboards,`)
|
||||
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_snapshot") + `) AS snapshots,`)
|
||||
sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_version") + `) AS dashboard_versions,`)
|
||||
@ -190,6 +166,11 @@ func (ss *sqlStatsService) GetSystemStats(ctx context.Context, query *stats.GetS
|
||||
if ss.IsUnifiedAlertingEnabled() {
|
||||
sb.Write(`(SELECT COUNT(DISTINCT (` + dialect.Quote("rule_group") + `)) FROM ` + dialect.Quote("alert_rule") + `) AS rule_groups,`)
|
||||
}
|
||||
// currently not supported when dashboards are in unified storage
|
||||
if !ss.features.IsEnabledGlobally(featuremgmt.FlagKubernetesCliDashboards) {
|
||||
sb.Write(`(SELECT SUM(LENGTH(data)) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboard_bytes_total,`, dialect.BooleanStr(false))
|
||||
sb.Write(`(SELECT MAX(LENGTH(data)) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboard_bytes_max,`, dialect.BooleanStr(false))
|
||||
}
|
||||
|
||||
sb.Write(ss.roleCounterSQL(ctx))
|
||||
|
||||
@ -216,13 +197,11 @@ func (ss *sqlStatsService) GetSystemStats(ctx context.Context, query *stats.GetS
|
||||
result.Orgs = int64(len(orgs))
|
||||
|
||||
// for services in unified storage, get the stats through the service rather than the db directly
|
||||
dashStats, err := ss.collectDashboardStats(ctx, orgs, true)
|
||||
dashCount, err := ss.getDashboardCount(ctx, orgs)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.DashboardBytesMax = int64(dashStats.bytesMax)
|
||||
result.DashboardBytesTotal = int64(dashStats.bytesTotal)
|
||||
result.Dashboards = int64(dashStats.count)
|
||||
result.Dashboards = dashCount
|
||||
|
||||
folderCount, err := ss.getFolderCount(ctx, orgs)
|
||||
if err != nil {
|
||||
@ -329,11 +308,11 @@ func (ss *sqlStatsService) GetAdminStats(ctx context.Context, query *stats.GetAd
|
||||
result.Orgs = int64(len(orgs))
|
||||
|
||||
// for services in unified storage, get the stats through the service rather than the db directly
|
||||
dashStats, err := ss.collectDashboardStats(ctx, orgs, false)
|
||||
dashCount, err := ss.getDashboardCount(ctx, orgs)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Dashboards = int64(dashStats.count)
|
||||
result.Dashboards = dashCount
|
||||
|
||||
tagCount, err := ss.getTagCount(ctx, orgs)
|
||||
if err != nil {
|
||||
|
@ -10,12 +10,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/correlations"
|
||||
"github.com/grafana/grafana/pkg/services/correlations/correlationstest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@ -40,15 +40,9 @@ func TestIntegrationStatsDataAccess(t *testing.T) {
|
||||
db, cfg := db.InitTestDBWithCfg(t)
|
||||
orgSvc := populateDB(t, db, cfg)
|
||||
dashSvc := &dashboards.FakeDashboardService{}
|
||||
emptyJson := simplejson.New()
|
||||
emptyJsonBytes, err := emptyJson.ToDB()
|
||||
require.NoError(t, err)
|
||||
largerJson := simplejson.NewFromAny(map[string]string{"key": "value"})
|
||||
largerJsonBytes, err := largerJson.ToDB()
|
||||
require.NoError(t, err)
|
||||
dashSvc.On("GetAllDashboardsByOrgId", mock.Anything, int64(1)).Return([]*dashboards.Dashboard{{Data: largerJson}, {Data: emptyJson}}, nil)
|
||||
dashSvc.On("GetAllDashboardsByOrgId", mock.Anything, int64(2)).Return([]*dashboards.Dashboard{}, nil)
|
||||
dashSvc.On("GetAllDashboardsByOrgId", mock.Anything, int64(3)).Return([]*dashboards.Dashboard{}, nil)
|
||||
dashSvc.On("CountDashboardsInOrg", mock.Anything, int64(1)).Return(int64(2), nil)
|
||||
dashSvc.On("CountDashboardsInOrg", mock.Anything, int64(2)).Return(int64(1), nil)
|
||||
dashSvc.On("CountDashboardsInOrg", mock.Anything, int64(3)).Return(int64(0), nil)
|
||||
dashSvc.On("GetDashboardTags", mock.Anything, &dashboards.GetDashboardTagsQuery{OrgID: 1}).Return([]*dashboards.DashboardTagCloudItem{{Term: "test"}}, nil)
|
||||
dashSvc.On("GetDashboardTags", mock.Anything, &dashboards.GetDashboardTagsQuery{OrgID: 2}).Return([]*dashboards.DashboardTagCloudItem{}, nil)
|
||||
dashSvc.On("GetDashboardTags", mock.Anything, &dashboards.GetDashboardTagsQuery{OrgID: 3}).Return([]*dashboards.DashboardTagCloudItem{}, nil)
|
||||
@ -61,6 +55,7 @@ func TestIntegrationStatsDataAccess(t *testing.T) {
|
||||
dashSvc: dashSvc,
|
||||
orgSvc: orgSvc,
|
||||
folderSvc: folderService,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
t.Run("Get system stats should not results in error", func(t *testing.T) {
|
||||
@ -76,10 +71,8 @@ func TestIntegrationStatsDataAccess(t *testing.T) {
|
||||
assert.Equal(t, int64(0), result.APIKeys)
|
||||
assert.Equal(t, int64(2), result.Correlations)
|
||||
assert.Equal(t, int64(3), result.Orgs)
|
||||
assert.Equal(t, int64(2), result.Dashboards)
|
||||
assert.Equal(t, int64(3), result.Dashboards)
|
||||
assert.Equal(t, int64(9), result.Folders) // will return 3 folders for each org
|
||||
assert.Equal(t, int64(len(largerJsonBytes)+len(emptyJsonBytes)), result.DashboardBytesTotal)
|
||||
assert.Equal(t, int64(len(largerJsonBytes)), result.DashboardBytesMax)
|
||||
assert.NotNil(t, result.DatabaseCreatedTime)
|
||||
assert.Equal(t, db.GetDialect().DriverName(), result.DatabaseDriver)
|
||||
})
|
||||
@ -113,7 +106,7 @@ func TestIntegrationStatsDataAccess(t *testing.T) {
|
||||
stats, err := statsService.GetAdminStats(context.Background(), &query)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), stats.Tags)
|
||||
assert.Equal(t, int64(2), stats.Dashboards)
|
||||
assert.Equal(t, int64(3), stats.Dashboards)
|
||||
assert.Equal(t, int64(3), stats.Orgs)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user