Files
grafana/pkg/storage/secret/metadata/keeper_store_test.go
BrunoandMatheus Macabu 0d67442f1a Secrets manager: create secure value using the active keeper (#114039)
* Secrets manager: create secure value using the active keeper

* SecureValueService.Update: fetch secure value from db to get the keeper

* fix keeper_store_test.go

* SecureValueService: fix bug in update where the current version keeper wasn't being passed to the createNewVersion method

* make gofmt

* remove outdated test

* update TestModel

* undo enterprise_imports changes

* use xkube.Namespace

* migrator: set secret_secure_value.keeper to 'system' when the column is null

* indent cue

* fix tests

* fix enterprise imports

* properly fix enterprise imports

* make update-workspace

* go mod tidy

---------

Co-authored-by: Matheus Macabu <macabu.matheus@gmail.com>
2025-11-21 11:20:16 -03:00

377 lines
12 KiB
Go

package metadata_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/storage/secret/database"
"github.com/grafana/grafana/pkg/storage/secret/metadata"
"github.com/grafana/grafana/pkg/storage/secret/migrator"
)
func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
t.Parallel()
defaultKeeperName := "kp-test"
defaultKeeperNS := "default"
testKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
testKeeper.Name = defaultKeeperName
testKeeper.Namespace = defaultKeeperNS
t.Run("get the system keeper config", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
// get system keeper config
keeperConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, defaultKeeperNS, contracts.SystemKeeperName, contracts.ReadOpts{})
require.NoError(t, err)
require.IsType(t, &secretv1beta1.SystemKeeperConfig{}, keeperConfig)
})
t.Run("get test keeper config", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
//
_, err := keeperMetadataStorage.Create(ctx, testKeeper, "testuser")
require.NoError(t, err)
keeperConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, defaultKeeperNS, defaultKeeperName, contracts.ReadOpts{})
require.NoError(t, err)
require.NotNil(t, keeperConfig)
require.NotEmpty(t, keeperConfig.Type())
})
t.Run("get test keeper config when listing", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
//
_, err := keeperMetadataStorage.Create(ctx, testKeeper, "testuser")
require.NoError(t, err)
keeperList, err := keeperMetadataStorage.List(ctx, xkube.Namespace(defaultKeeperNS))
require.NoError(t, err)
require.NotEmpty(t, keeperList)
require.Len(t, keeperList, 1)
keeper := keeperList[0]
require.Equal(t, "kp-test", keeper.Name)
require.Equal(t, "default", keeper.Namespace)
require.Equal(t, "description", keeper.Spec.Description)
})
t.Run("create a keeper and then delete it", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
keeperTest := "kp-test2"
keeperNamespaceTest := "ns"
testKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "another description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
testKeeper.Name = keeperTest
testKeeper.Namespace = keeperNamespaceTest
// create the keeper
_, err := keeperMetadataStorage.Create(ctx, testKeeper, "testuser")
require.NoError(t, err)
// we are able to get it
keeperConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, keeperNamespaceTest, keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.NotNil(t, keeperConfig)
require.NotEmpty(t, keeperConfig.Type())
// now we delete it
delErr := keeperMetadataStorage.Delete(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest)
require.NoError(t, delErr)
// and we shouldn't be able to get it again
_, getErr := keeperMetadataStorage.GetKeeperConfig(ctx, keeperNamespaceTest, keeperTest, contracts.ReadOpts{})
require.Errorf(t, getErr, "keeper not found")
})
t.Run("create, update and validate keeper", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
keeperTest := "kp-test3"
keeperNamespaceTest := "ns"
// Create initial keeper
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
initialKeeper.Name = keeperTest
initialKeeper.Namespace = keeperNamespaceTest
// Create the keeper
_, err := keeperMetadataStorage.Create(ctx, initialKeeper, "testuser")
require.NoError(t, err)
// Validate that the description was set
keeper, err := keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "initial description", keeper.Spec.Description)
// Update the keeper with new values
updatedKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "updated description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
updatedKeeper.Name = keeperTest
updatedKeeper.Namespace = keeperNamespaceTest
// Perform the update
_, err = keeperMetadataStorage.Update(ctx, updatedKeeper, "testuser")
require.NoError(t, err)
// Validate updated values
updatedConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, keeperNamespaceTest, keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.NotNil(t, updatedConfig)
require.NotEmpty(t, updatedConfig.Type())
// Validate that the description was updated
updatedKeeper, err = keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "updated description", updatedKeeper.Spec.Description)
})
t.Run("update keeper with different AWS configuration", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
keeperTest := "kp-test4"
keeperNamespaceTest := "ns"
// Create initial keeper with first AWS config
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_1",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_1",
},
KmsKeyID: ptr.To("kms-key-id-1"),
},
},
}
initialKeeper.Name = keeperTest
initialKeeper.Namespace = keeperNamespaceTest
// Create the keeper
_, err := keeperMetadataStorage.Create(ctx, initialKeeper, "testuser")
require.NoError(t, err)
// Verify initial AWS config
keeper, err := keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "AWS_ACCESS_KEY_ID_1", keeper.Spec.Aws.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_1", keeper.Spec.Aws.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-1", *keeper.Spec.Aws.KmsKeyID)
// Update with new AWS config
updatedKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "updated description",
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_2",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_2",
},
KmsKeyID: ptr.To("kms-key-id-2"),
},
},
}
updatedKeeper.Name = keeperTest
updatedKeeper.Namespace = keeperNamespaceTest
// Perform the update
_, err = keeperMetadataStorage.Update(ctx, updatedKeeper, "testuser")
require.NoError(t, err)
// Verify updated AWS config
updatedKeeper, err = keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "AWS_ACCESS_KEY_ID_2", updatedKeeper.Spec.Aws.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_2", updatedKeeper.Spec.Aws.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-2", *updatedKeeper.Spec.Aws.KmsKeyID)
})
t.Run("list keepers in empty namespace", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
keeperList, err := keeperMetadataStorage.List(ctx, "")
require.NoError(t, err)
require.Empty(t, keeperList)
})
t.Run("read non-existent keeper", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
_, err := keeperMetadataStorage.Read(ctx, "ns", "non-existent", contracts.ReadOpts{})
require.Error(t, err)
require.ErrorIs(t, err, contracts.ErrKeeperNotFound)
})
t.Run("update keeper with different namespace", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
keeperTest := "kp-test5"
keeperNamespaceTest := "ns1"
// Create initial keeper
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY",
},
},
},
}
initialKeeper.Name = keeperTest
initialKeeper.Namespace = keeperNamespaceTest
// Create the keeper
_, err := keeperMetadataStorage.Create(ctx, initialKeeper, "testuser")
require.NoError(t, err)
// Try to update with different namespace
updatedKeeper := initialKeeper.DeepCopy()
updatedKeeper.Namespace = "ns2"
updatedKeeper.Spec.Description = "updated description"
_, err = keeperMetadataStorage.Update(ctx, updatedKeeper, "testuser")
// should this return contracts.ErrKeeperNotFound directly?
// require.Equal(t, contracts.ErrKeeperNotFound, err)
require.Error(t, err, "db failure: keeper not found")
// Verify original keeper is unchanged
keeper, err := keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "initial description", keeper.Spec.Description)
})
t.Run("update non-existent keeper", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
nonExistentKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "some description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
nonExistentKeeper.Name = "non-existent"
nonExistentKeeper.Namespace = "ns"
_, err := keeperMetadataStorage.Update(ctx, nonExistentKeeper, "testuser")
require.Error(t, err, "db failure: keeper not found")
})
}
func Test_KeeperMetadataStorage_SetAsActive(t *testing.T) {
t.Parallel()
keeperMetadataStorage := initStorage(t)
k1, err := keeperMetadataStorage.Create(t.Context(), &secretv1beta1.Keeper{
ObjectMeta: v1.ObjectMeta{Namespace: "ns1", Name: "k1"},
Spec: secretv1beta1.KeeperSpec{
Description: "description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}, "actor-uid")
require.NoError(t, err)
k2, err := keeperMetadataStorage.Create(t.Context(), &secretv1beta1.Keeper{
ObjectMeta: v1.ObjectMeta{Namespace: "ns1", Name: "k2"},
Spec: secretv1beta1.KeeperSpec{
Description: "description",
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}, "actor-uid")
require.NoError(t, err)
require.NoError(t, keeperMetadataStorage.SetAsActive(t.Context(), xkube.Namespace(k1.Namespace), k1.Name))
keeperName, _, err := keeperMetadataStorage.GetActiveKeeperConfig(t.Context(), k1.Namespace)
require.NoError(t, err)
require.Equal(t, k1.Name, keeperName)
require.NoError(t, keeperMetadataStorage.SetAsActive(t.Context(), xkube.Namespace(k2.Namespace), k2.Name))
keeperName, _, err = keeperMetadataStorage.GetActiveKeeperConfig(t.Context(), k2.Namespace)
require.NoError(t, err)
require.Equal(t, k2.Name, keeperName)
}
func initStorage(t *testing.T) contracts.KeeperMetadataStorage {
testDB := sqlstore.NewTestStore(t, sqlstore.WithMigrator(migrator.New()))
tracer := noop.NewTracerProvider().Tracer("test")
db := database.ProvideDatabase(testDB, tracer)
// Initialize the keeper storage
keeperMetadataStorage, err := metadata.ProvideKeeperMetadataStorage(db, tracer, nil)
require.NoError(t, err)
return keeperMetadataStorage
}