Service Accounts: Implement basic usage stats (#46619)

* Stats: do not count SAs as users

* Stats: implement basic service account metrics

* Stats: do not count service account tokens as api keys

* Stats: fix metric names

* Stats: add SA stats test

* rename user to sa
This commit is contained in:
Jguer
2022-03-16 15:54:34 +00:00
committed by GitHub
parent 943a8508a6
commit d5883c1b27
7 changed files with 106 additions and 10 deletions

View File

@@ -0,0 +1,37 @@
package database
import (
"context"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
func (s *ServiceAccountsStoreImpl) GetUsageMetrics(ctx context.Context) (map[string]interface{}, error) {
stats := map[string]interface{}{"stats.serviceaccounts.enabled.count": int64(1)}
sb := &sqlstore.SQLBuilder{}
dialect := s.sqlStore.Dialect
sb.Write("SELECT ")
sb.Write(`(SELECT COUNT(*) FROM ` + dialect.Quote("user") +
` WHERE is_service_account = ` + dialect.BooleanStr(true) + `) AS serviceaccounts,`)
sb.Write(`(SELECT COUNT(*) FROM ` + dialect.Quote("api_key") +
` WHERE service_account_id IS NOT NULL ) AS serviceaccount_tokens`)
type saStats struct {
ServiceAccounts int64 `xorm:"serviceaccounts"`
Tokens int64 `xorm:"serviceaccount_tokens"`
}
var sqlStats saStats
if err := s.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
_, err := sess.SQL(sb.GetSQLString(), sb.GetParams()...).Get(&sqlStats)
return err
}); err != nil {
return nil, err
}
stats["stats.serviceaccounts.count"] = sqlStats.ServiceAccounts
stats["stats.serviceaccounts.tokens.count"] = sqlStats.Tokens
return stats, nil
}

View File

@@ -0,0 +1,41 @@
package database
import (
"context"
"testing"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStore_UsageStats(t *testing.T) {
saToCreate := tests.TestUser{Login: "servicetestwithTeam@admin", IsServiceAccount: true}
db, store := setupTestDatabase(t)
sa := tests.SetupUserServiceAccount(t, db, saToCreate)
keyName := t.Name()
key, err := apikeygen.New(sa.OrgId, keyName)
require.NoError(t, err)
cmd := models.AddApiKeyCommand{
Name: keyName,
Role: "Viewer",
OrgId: sa.OrgId,
Key: key.HashedKey,
SecondsToLive: 0,
Result: &models.ApiKey{},
}
err = store.AddServiceAccountToken(context.Background(), sa.Id, &cmd)
require.NoError(t, err)
stats, err := store.GetUsageMetrics(context.Background())
require.NoError(t, err)
assert.Equal(t, int64(1), stats["stats.serviceaccounts.count"].(int64))
assert.Equal(t, int64(1), stats["stats.serviceaccounts.tokens.count"].(int64))
assert.Equal(t, int64(1), stats["stats.serviceaccounts.enabled.count"].(int64))
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
@@ -30,6 +31,7 @@ func ProvideServiceAccountsService(
store *sqlstore.SQLStore,
ac accesscontrol.AccessControl,
routeRegister routing.RouteRegister,
usageStats usagestats.Service,
) (*ServiceAccountsService, error) {
s := &ServiceAccountsService{
features: features,
@@ -41,6 +43,8 @@ func ProvideServiceAccountsService(
if err := RegisterRoles(ac); err != nil {
s.log.Error("Failed to register roles", "error", err)
}
usageStats.RegisterMetricsFunc(s.store.GetUsageMetrics)
}
serviceaccountsAPI := api.NewServiceAccountsAPI(cfg, s, ac, routeRegister, s.store)

View File

@@ -25,4 +25,5 @@ type Store interface {
ListTokens(ctx context.Context, orgID int64, serviceAccount int64) ([]*models.ApiKey, error)
DeleteServiceAccountToken(ctx context.Context, orgID, serviceAccountID, tokenID int64) error
AddServiceAccountToken(ctx context.Context, serviceAccountID int64, cmd *models.AddApiKeyCommand) error
GetUsageMetrics(ctx context.Context) (map[string]interface{}, error)
}

View File

@@ -138,3 +138,7 @@ func (s *ServiceAccountsStoreMock) AddServiceAccountToken(ctx context.Context, s
s.Calls.AddServiceAccountToken = append(s.Calls.AddServiceAccountToken, []interface{}{ctx, cmd})
return nil
}
func (s *ServiceAccountsStoreMock) GetUsageMetrics(ctx context.Context) (map[string]interface{}, error) {
return map[string]interface{}{}, nil
}