From db9f80a7a3f86e8b737de658efad786ff451b042 Mon Sep 17 00:00:00 2001 From: Jguer Date: Thu, 7 Jul 2022 14:03:16 +0000 Subject: [PATCH] ServiceAccounts: Add Prometheus metrics service (#51831) * ServiceAccounts: Add Prometheus metrics service * add missing init in tests --- .../backgroundsvcs/background_services.go | 3 + .../serviceaccounts/database/stats.go | 62 +++++++++++++++++++ .../serviceaccounts/database/stats_test.go | 1 + .../serviceaccounts/manager/service.go | 6 ++ .../serviceaccounts/serviceaccounts.go | 1 + pkg/services/serviceaccounts/tests/common.go | 1 + 6 files changed, 74 insertions(+) diff --git a/pkg/server/backgroundsvcs/background_services.go b/pkg/server/backgroundsvcs/background_services.go index 9fa9558f050..a46b4ab94ce 100644 --- a/pkg/server/backgroundsvcs/background_services.go +++ b/pkg/server/backgroundsvcs/background_services.go @@ -24,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/services/searchV2" secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/serviceaccounts" + samanager "github.com/grafana/grafana/pkg/services/serviceaccounts/manager" "github.com/grafana/grafana/pkg/services/store" "github.com/grafana/grafana/pkg/services/store/sanitizer" "github.com/grafana/grafana/pkg/services/thumbs" @@ -39,6 +40,7 @@ func ProvideBackgroundServiceRegistry( pluginsUpdateChecker *updatechecker.PluginsService, metrics *metrics.InternalMetricsService, secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache, thumbnailsService thumbs.Service, StorageService store.StorageService, searchService searchV2.SearchService, entityEventsService store.EntityEventsService, + saService *samanager.ServiceAccountsService, // Need to make sure these are initialized, is there a better place to put them? _ dashboardsnapshots.Service, _ *alerting.AlertNotificationService, _ serviceaccounts.Service, _ *guardian.Provider, @@ -68,6 +70,7 @@ func ProvideBackgroundServiceRegistry( thumbnailsService, searchService, entityEventsService, + saService, ) } diff --git a/pkg/services/serviceaccounts/database/stats.go b/pkg/services/serviceaccounts/database/stats.go index edce5f15b44..2d47333c9c5 100644 --- a/pkg/services/serviceaccounts/database/stats.go +++ b/pkg/services/serviceaccounts/database/stats.go @@ -2,10 +2,69 @@ package database import ( "context" + "sync" + "time" "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/prometheus/client_golang/prometheus" ) +const ( + ExporterName = "grafana" + metricsCollectionInterval = time.Minute * 30 +) + +var ( + // MStatTotalServiceAccounts is a metric gauge for total number of service accounts + MStatTotalServiceAccounts prometheus.Gauge + + // MStatTotalServiceAccountTokens is a metric gauge for total number of service account tokens + MStatTotalServiceAccountTokens prometheus.Gauge + + once sync.Once + Initialised bool = false +) + +func InitMetrics() { + once.Do(func() { + MStatTotalServiceAccounts = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "stat_total_service_accounts", + Help: "total amount of service accounts", + Namespace: ExporterName, + }) + + MStatTotalServiceAccountTokens = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "stat_total_service_account_tokens", + Help: "total amount of service account tokens", + Namespace: ExporterName, + }) + + prometheus.MustRegister( + MStatTotalServiceAccounts, + MStatTotalServiceAccountTokens, + ) + }) +} + +func (s *ServiceAccountsStoreImpl) RunMetricsCollection(ctx context.Context) error { + if _, err := s.GetUsageMetrics(ctx); err != nil { + s.log.Warn("Failed to get usage metrics", "error", err.Error()) + } + updateStatsTicker := time.NewTicker(metricsCollectionInterval) + defer updateStatsTicker.Stop() + + for { + select { + case <-updateStatsTicker.C: + if _, err := s.GetUsageMetrics(ctx); err != nil { + s.log.Warn("Failed to get usage metrics", "error", err.Error()) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} + func (s *ServiceAccountsStoreImpl) GetUsageMetrics(ctx context.Context) (map[string]interface{}, error) { stats := map[string]interface{}{} @@ -39,5 +98,8 @@ func (s *ServiceAccountsStoreImpl) GetUsageMetrics(ctx context.Context) (map[str stats["stats.serviceaccounts.tokens.count"] = sqlStats.Tokens stats["stats.serviceaccounts.in_teams.count"] = sqlStats.InTeams + MStatTotalServiceAccountTokens.Set(float64(sqlStats.Tokens)) + MStatTotalServiceAccounts.Set(float64(sqlStats.ServiceAccounts)) + return stats, nil } diff --git a/pkg/services/serviceaccounts/database/stats_test.go b/pkg/services/serviceaccounts/database/stats_test.go index d9aa95755ff..467c5e534d2 100644 --- a/pkg/services/serviceaccounts/database/stats_test.go +++ b/pkg/services/serviceaccounts/database/stats_test.go @@ -16,6 +16,7 @@ func TestStore_UsageStats(t *testing.T) { saToCreate := tests.TestUser{Login: "servicetestwithTeam@admin", IsServiceAccount: true} db, store := setupTestDatabase(t) sa := tests.SetupUserServiceAccount(t, db, saToCreate) + InitMetrics() keyName := t.Name() key, err := apikeygen.New(sa.OrgID, keyName) diff --git a/pkg/services/serviceaccounts/manager/service.go b/pkg/services/serviceaccounts/manager/service.go index 5049a8c2b28..cab12c094b8 100644 --- a/pkg/services/serviceaccounts/manager/service.go +++ b/pkg/services/serviceaccounts/manager/service.go @@ -32,6 +32,7 @@ func ProvideServiceAccountsService( routeRegister routing.RouteRegister, usageStats usagestats.Service, ) (*ServiceAccountsService, error) { + database.InitMetrics() s := &ServiceAccountsService{ store: database.NewServiceAccountsStore(store, kvStore), log: log.New("serviceaccounts"), @@ -49,6 +50,11 @@ func ProvideServiceAccountsService( return s, nil } +func (sa *ServiceAccountsService) Run(ctx context.Context) error { + sa.log.Debug("Started Service Account Metrics collection service") + return sa.store.RunMetricsCollection(ctx) +} + func (sa *ServiceAccountsService) CreateServiceAccount(ctx context.Context, orgID int64, name string) (*serviceaccounts.ServiceAccountDTO, error) { return sa.store.CreateServiceAccount(ctx, orgID, name) } diff --git a/pkg/services/serviceaccounts/serviceaccounts.go b/pkg/services/serviceaccounts/serviceaccounts.go index 71490383fb6..68bf94bd560 100644 --- a/pkg/services/serviceaccounts/serviceaccounts.go +++ b/pkg/services/serviceaccounts/serviceaccounts.go @@ -31,4 +31,5 @@ type Store interface { DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *AddServiceAccountTokenCommand) error GetUsageMetrics(ctx context.Context) (map[string]interface{}, error) + RunMetricsCollection(ctx context.Context) error } diff --git a/pkg/services/serviceaccounts/tests/common.go b/pkg/services/serviceaccounts/tests/common.go index a697d64fbe5..f695b2f6349 100644 --- a/pkg/services/serviceaccounts/tests/common.go +++ b/pkg/services/serviceaccounts/tests/common.go @@ -133,6 +133,7 @@ type Calls struct { } type ServiceAccountsStoreMock struct { + serviceaccounts.Store Calls Calls }