diff --git a/pkg/infra/metrics/metrics.go b/pkg/infra/metrics/metrics.go index 1feca5c9ff0..b900ca7a168 100644 --- a/pkg/infra/metrics/metrics.go +++ b/pkg/infra/metrics/metrics.go @@ -12,7 +12,6 @@ import ( const ExporterName = "grafana" var ( - // MInstanceStart is a metric counter for started instances MInstanceStart prometheus.Counter @@ -191,6 +190,9 @@ var ( // StatsTotalLibraryVariables is a metric of total number of library variables stored in Grafana. StatsTotalLibraryVariables prometheus.Gauge + + // StatsTotalDataKeys is a metric of total number of data keys stored in Grafana. + StatsTotalDataKeys prometheus.Gauge ) func init() { @@ -565,6 +567,12 @@ func init() { Help: "total amount of library variables in the database", Namespace: ExporterName, }) + + StatsTotalDataKeys = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "stat_totals_data_keys", + Help: "total amount of data keys in the database", + Namespace: ExporterName, + }) } // SetBuildInformation sets the build information for this binary @@ -660,6 +668,7 @@ func initMetricVars() { MAccessEvaluationCount, StatsTotalLibraryPanels, StatsTotalLibraryVariables, + StatsTotalDataKeys, ) } diff --git a/pkg/infra/usagestats/statscollector/service.go b/pkg/infra/usagestats/statscollector/service.go index 1f9a7a752d7..c21ab107ee8 100644 --- a/pkg/infra/usagestats/statscollector/service.go +++ b/pkg/infra/usagestats/statscollector/service.go @@ -130,6 +130,7 @@ func (s *Service) collect(ctx context.Context) (map[string]interface{}, error) { m["stats.folders_viewers_can_edit.count"] = statsQuery.Result.FoldersViewersCanEdit m["stats.folders_viewers_can_admin.count"] = statsQuery.Result.FoldersViewersCanAdmin m["stats.api_keys.count"] = statsQuery.Result.APIKeys + m["stats.data_keys.count"] = statsQuery.Result.DataKeys ossEditionCount := 1 enterpriseEditionCount := 0 @@ -311,6 +312,7 @@ func (s *Service) updateTotalStats(ctx context.Context) bool { metrics.StatsTotalAlertRules.Set(float64(statsQuery.Result.AlertRules)) metrics.StatsTotalLibraryPanels.Set(float64(statsQuery.Result.LibraryPanels)) metrics.StatsTotalLibraryVariables.Set(float64(statsQuery.Result.LibraryVariables)) + metrics.StatsTotalDataKeys.Set(float64(statsQuery.Result.DataKeys)) dsStats := models.GetDataSourceStatsQuery{} if err := s.sqlstore.GetDataSourceStats(ctx, &dsStats); err != nil { diff --git a/pkg/infra/usagestats/statscollector/service_test.go b/pkg/infra/usagestats/statscollector/service_test.go index 91419ac5cbc..6e8c157d174 100644 --- a/pkg/infra/usagestats/statscollector/service_test.go +++ b/pkg/infra/usagestats/statscollector/service_test.go @@ -9,12 +9,11 @@ import ( sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" - "github.com/grafana/grafana/pkg/infra/httpclient" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/models" @@ -252,6 +251,8 @@ func TestCollectingUsageStats(t *testing.T) { assert.EqualValues(t, 1, metrics["stats.packaging.deb.count"]) assert.EqualValues(t, 1, metrics["stats.distributor.hosted-grafana.count"]) + assert.EqualValues(t, 11, metrics["stats.data_keys.count"]) + assert.InDelta(t, int64(65), metrics["stats.uptime"], 6) } @@ -294,6 +295,7 @@ func mockSystemStats(sqlStore *mockstore.SQLStoreMock) { FoldersViewersCanAdmin: 1, FoldersViewersCanEdit: 5, APIKeys: 2, + DataKeys: 11, } } diff --git a/pkg/models/stats.go b/pkg/models/stats.go index cd992d7f870..0636edba62a 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -39,6 +39,7 @@ type SystemStats struct { DailyActiveEditors int64 DailyActiveViewers int64 DailyActiveSessions int64 + DataKeys int64 } type DataSourceStats struct { diff --git a/pkg/services/secrets/manager/cache.go b/pkg/services/secrets/manager/cache.go index 2e7ba7a4a02..e6fe94a7768 100644 --- a/pkg/services/secrets/manager/cache.go +++ b/pkg/services/secrets/manager/cache.go @@ -1,8 +1,11 @@ package manager import ( + "strconv" "sync" "time" + + "github.com/prometheus/client_golang/prometheus" ) var ( @@ -36,6 +39,11 @@ func (c *dataKeyCache) get(id string) ([]byte, bool) { defer c.RUnlock() entry, exists := c.entries[id] + + cacheReadsCounter.With(prometheus.Labels{ + "hit": strconv.FormatBool(exists), + }).Inc() + if !exists || entry.expired() { return nil, false } diff --git a/pkg/services/secrets/manager/manager.go b/pkg/services/secrets/manager/manager.go index 2967c23ee53..2c1f23b03db 100644 --- a/pkg/services/secrets/manager/manager.go +++ b/pkg/services/secrets/manager/manager.go @@ -7,6 +7,7 @@ import ( "encoding/base64" "errors" "fmt" + "strconv" "time" "github.com/grafana/grafana/pkg/infra/log" @@ -16,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/services/kmsproviders" "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/setting" + "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" "xorm.io/xorm" ) @@ -130,11 +132,20 @@ func (s *SecretsService) EncryptWithDBSession(ctx context.Context, payload []byt return s.enc.Encrypt(ctx, payload, setting.SecretKey) } + var err error + defer func() { + opsCounter.With(prometheus.Labels{ + "success": strconv.FormatBool(err == nil), + "operation": OpEncrypt, + }).Inc() + }() + // If encryption featuremgmt.FlagEnvelopeEncryption toggle is on, use envelope encryption scope := opt() keyName := s.keyName(scope) - dataKey, err := s.dataKey(ctx, keyName) + var dataKey []byte + dataKey, err = s.dataKey(ctx, keyName) if err != nil { if errors.Is(err, secrets.ErrDataKeyNotFound) { dataKey, err = s.newDataKey(ctx, keyName, scope, sess) @@ -146,7 +157,8 @@ func (s *SecretsService) EncryptWithDBSession(ctx context.Context, payload []byt } } - encrypted, err := s.enc.Encrypt(ctx, payload, string(dataKey)) + var encrypted []byte + encrypted, err = s.enc.Encrypt(ctx, payload, string(dataKey)) if err != nil { return nil, err } @@ -174,8 +186,17 @@ func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, e } // If encryption featuremgmt.FlagEnvelopeEncryption toggle is on, use envelope encryption + var err error + defer func() { + opsCounter.With(prometheus.Labels{ + "success": strconv.FormatBool(err == nil), + "operation": OpDecrypt, + }).Inc() + }() + if len(payload) == 0 { - return nil, fmt.Errorf("unable to decrypt empty payload") + err = fmt.Errorf("unable to decrypt empty payload") + return nil, err } var dataKey []byte @@ -187,12 +208,13 @@ func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, e payload = payload[1:] endOfKey := bytes.Index(payload, []byte{'#'}) if endOfKey == -1 { - return nil, fmt.Errorf("could not find valid key in encrypted payload") + err = fmt.Errorf("could not find valid key in encrypted payload") + return nil, err } b64Key := payload[:endOfKey] payload = payload[endOfKey+1:] key := make([]byte, b64.DecodedLen(len(b64Key))) - _, err := b64.Decode(key, b64Key) + _, err = b64.Decode(key, b64Key) if err != nil { return nil, err } @@ -204,7 +226,10 @@ func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, e } } - return s.enc.Decrypt(ctx, payload, string(dataKey)) + var decrypted []byte + decrypted, err = s.enc.Decrypt(ctx, payload, string(dataKey)) + + return decrypted, err } func (s *SecretsService) EncryptJsonData(ctx context.Context, kv map[string]string, opt secrets.EncryptionOptions) (map[string][]byte, error) { diff --git a/pkg/services/secrets/manager/metrics.go b/pkg/services/secrets/manager/metrics.go new file mode 100644 index 00000000000..2b1e83fec6d --- /dev/null +++ b/pkg/services/secrets/manager/metrics.go @@ -0,0 +1,37 @@ +package manager + +import ( + "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + OpEncrypt = "encrypt" + OpDecrypt = "decrypt" +) + +var ( + opsCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metrics.ExporterName, + Name: "encryption_ops_total", + Help: "A counter for encryption operations", + }, + []string{"success", "operation"}, + ) + cacheReadsCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metrics.ExporterName, + Name: "encryption_cache_reads_total", + Help: "A counter for encryption cache reads", + }, + []string{"hit"}, + ) +) + +func init() { + prometheus.MustRegister( + opsCounter, + cacheReadsCounter, + ) +} diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index e1e1500994e..6befd745b1a 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -102,6 +102,7 @@ func (ss *SQLStore) GetSystemStats(ctx context.Context, query *models.GetSystemS sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("api_key") + `WHERE service_account_id IS NULL) AS api_keys,`) sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_panels,`, models.PanelElement) sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_variables,`, models.VariableElement) + sb.Write(`(SELECT COUNT(*) FROM ` + dialect.Quote("data_keys") + `) AS data_keys,`) sb.Write(ss.roleCounterSQL(ctx))