SecretsManager: Introduce metrics and logs (#107582)

Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com>
This commit is contained in:
Dana Axinte
2025-07-03 17:32:18 +01:00
committed by GitHub
co-authored by Michael Mandrus
parent 66d9a33cc9
commit a59ec345c2
19 changed files with 685 additions and 35 deletions
@@ -77,7 +77,7 @@ func TestEncryptionService_DataKeys(t *testing.T) {
testDB := sqlstore.NewTestStore(t, sqlstore.WithMigrator(migrator.New()))
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
tracer := noop.NewTracerProvider().Tracer("test")
store, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features)
store, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features, nil)
require.NoError(t, err)
ctx := context.Background()
@@ -183,7 +183,7 @@ func TestEncryptionService_UseCurrentProvider(t *testing.T) {
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
testDB := sqlstore.NewTestStore(t, sqlstore.WithMigrator(migrator.New()))
tracer := noop.NewTracerProvider().Tracer("test")
encryptionStore, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features)
encryptionStore, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features, nil)
require.NoError(t, err)
encMgr, err := ProvideEncryptionManager(
@@ -374,7 +374,7 @@ func TestIntegration_SecretsService(t *testing.T) {
EncryptionProvider: "secretKey.v1",
},
}
store, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features)
store, err := encryptionstorage.ProvideDataKeyStorage(database.ProvideDatabase(testDB, tracer), tracer, features, nil)
require.NoError(t, err)
usageStats := &usagestats.UsageStatsMock{T: t}
@@ -31,7 +31,7 @@ func setupTestService(tb testing.TB) *EncryptionManager {
EncryptionProvider: "secretKey.v1",
},
}
store, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features)
store, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features, nil)
require.NoError(tb, err)
usageStats := &usagestats.UsageStatsMock{T: tb}
@@ -0,0 +1,71 @@
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
)
const (
namespace = "grafana_secrets_manager"
subsystem = "keeper"
)
// KeeperMetrics is a struct that contains all the metrics for an implementation of all keepers.
type KeeperMetrics struct {
StoreDuration *prometheus.HistogramVec
UpdateDuration *prometheus.HistogramVec
ExposeDuration *prometheus.HistogramVec
DeleteDuration *prometheus.HistogramVec
}
func newKeeperMetrics() *KeeperMetrics {
return &KeeperMetrics{
StoreDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "store_duration_seconds",
Help: "Duration of keeper store operations",
Buckets: prometheus.DefBuckets,
}, []string{"keeper_type"}),
UpdateDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "update_duration_seconds",
Help: "Duration of keeper update operations",
Buckets: prometheus.DefBuckets,
}, []string{"keeper_type"}),
ExposeDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "expose_duration_seconds",
Help: "Duration of keeper expose operations",
Buckets: prometheus.DefBuckets,
}, []string{"keeper_type"}),
DeleteDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "delete_duration_seconds",
Help: "Duration of keeper delete operations",
Buckets: prometheus.DefBuckets,
}, []string{"keeper_type"}),
}
}
// NewKeeperMetrics creates a new KeeperMetrics struct containing registered metrics
func NewKeeperMetrics(reg prometheus.Registerer) *KeeperMetrics {
m := newKeeperMetrics()
if reg != nil {
reg.MustRegister(
m.StoreDuration,
m.UpdateDuration,
m.ExposeDuration,
m.DeleteDuration,
)
}
return m
}
func NewTestMetrics() *KeeperMetrics {
return newKeeperMetrics()
}
@@ -6,6 +6,7 @@ import (
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/sqlkeeper"
"github.com/prometheus/client_golang/prometheus"
)
// OSSKeeperService is the OSS implementation of the Service interface.
@@ -19,10 +20,11 @@ func ProvideService(
tracer trace.Tracer,
store contracts.EncryptedValueStorage,
encryptionManager contracts.EncryptionManager,
reg prometheus.Registerer,
) (*OSSKeeperService, error) {
return &OSSKeeperService{
// TODO: rename to system keeper or something like that
systemKeeper: sqlkeeper.NewSQLKeeper(tracer, encryptionManager, store),
systemKeeper: sqlkeeper.NewSQLKeeper(tracer, encryptionManager, store, reg),
}, nil
}
@@ -49,7 +49,7 @@ func setupTestService(t *testing.T, cfg *setting.Cfg) (*OSSKeeperService, error)
database := database.ProvideDatabase(testDB, tracer)
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
dataKeyStore, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features)
dataKeyStore, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features, nil)
require.NoError(t, err)
encValueStore, err := encryptionstorage.ProvideEncryptedValueStorage(database, tracer, features)
@@ -59,7 +59,7 @@ func setupTestService(t *testing.T, cfg *setting.Cfg) (*OSSKeeperService, error)
require.NoError(t, err)
// Initialize the keeper service
keeperService, err := ProvideService(tracer, encValueStore, encryptionManager)
keeperService, err := ProvideService(tracer, encValueStore, encryptionManager, nil)
return keeperService, err
}
@@ -3,9 +3,12 @@ package sqlkeeper
import (
"context"
"fmt"
"time"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/metrics"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
@@ -14,6 +17,7 @@ type SQLKeeper struct {
tracer trace.Tracer
encryptionManager contracts.EncryptionManager
store contracts.EncryptedValueStorage
metrics *metrics.KeeperMetrics
}
var _ contracts.Keeper = (*SQLKeeper)(nil)
@@ -22,19 +26,21 @@ func NewSQLKeeper(
tracer trace.Tracer,
encryptionManager contracts.EncryptionManager,
store contracts.EncryptedValueStorage,
reg prometheus.Registerer,
) *SQLKeeper {
return &SQLKeeper{
tracer: tracer,
encryptionManager: encryptionManager,
store: store,
metrics: metrics.NewKeeperMetrics(reg),
}
}
// TODO: parameter cfg is not being used
func (s *SQLKeeper) Store(ctx context.Context, _ secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
ctx, span := s.tracer.Start(ctx, "SQLKeeper.Store", trace.WithAttributes(attribute.String("namespace", namespace)))
defer span.End()
start := time.Now()
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef))
if err != nil {
return "", fmt.Errorf("unable to encrypt value: %w", err)
@@ -45,8 +51,8 @@ func (s *SQLKeeper) Store(ctx context.Context, _ secretv0alpha1.KeeperConfig, na
return "", fmt.Errorf("unable to store encrypted value: %w", err)
}
s.metrics.StoreDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds())
externalID := contracts.ExternalID(encryptedVal.UID)
span.SetAttributes(attribute.String("externalID", externalID.String()))
return externalID, nil
@@ -59,6 +65,7 @@ func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
))
defer span.End()
start := time.Now()
encryptedValue, err := s.store.Get(ctx, namespace, externalID.String())
if err != nil {
return "", fmt.Errorf("unable to get encrypted value: %w", err)
@@ -70,6 +77,8 @@ func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
}
exposedValue := secretv0alpha1.NewExposedSecureValue(string(exposedBytes))
s.metrics.ExposeDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds())
return exposedValue, nil
}
@@ -80,10 +89,14 @@ func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
))
defer span.End()
start := time.Now()
err := s.store.Delete(ctx, namespace, externalID.String())
if err != nil {
return fmt.Errorf("failed to delete encrypted value: %w", err)
}
s.metrics.DeleteDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds())
return nil
}
@@ -94,6 +107,7 @@ func (s *SQLKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
))
defer span.End()
start := time.Now()
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef))
if err != nil {
return fmt.Errorf("unable to encrypt value: %w", err)
@@ -103,5 +117,8 @@ func (s *SQLKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
if err != nil {
return fmt.Errorf("failed to update encrypted value: %w", err)
}
s.metrics.UpdateDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds())
return nil
}
@@ -159,7 +159,7 @@ func setupTestService(t *testing.T, cfg *setting.Cfg) (*SQLKeeper, error) {
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
// Initialize the encryption manager
dataKeyStore, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features)
dataKeyStore, err := encryptionstorage.ProvideDataKeyStorage(database, tracer, features, nil)
require.NoError(t, err)
usageStats := &usagestats.UsageStatsMock{T: t}
@@ -178,7 +178,7 @@ func setupTestService(t *testing.T, cfg *setting.Cfg) (*SQLKeeper, error) {
require.NoError(t, err)
// Initialize the SQLKeeper
sqlKeeper := NewSQLKeeper(tracer, encMgr, encValueStore)
sqlKeeper := NewSQLKeeper(tracer, encMgr, encValueStore, nil)
return sqlKeeper, nil
}