Secrets: Move decrypt types to contracts and export public at root pkg (#108376)

* Secrets: Move decrypt types to contracts and export public at root pkg

* Provisioning: Replace decrypt pkg imports

* Merge wire changes
This commit is contained in:
Matheus Macabu
2025-07-23 12:14:28 +02:00
committed by GitHub
parent f657044afb
commit 0e7b041b27
14 changed files with 115 additions and 107 deletions
@@ -5,7 +5,7 @@ package mocks
import (
context "context"
service "github.com/grafana/grafana/pkg/registry/apis/secret/service"
contracts "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
mock "github.com/stretchr/testify/mock"
)
@@ -23,7 +23,7 @@ func (_m *MockDecryptService) EXPECT() *MockDecryptService_Expecter {
}
// Decrypt provides a mock function with given fields: ctx, namespace, names
func (_m *MockDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]service.DecryptResult, error) {
func (_m *MockDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]contracts.DecryptResult, error) {
_va := make([]interface{}, len(names))
for _i := range names {
_va[_i] = names[_i]
@@ -37,16 +37,16 @@ func (_m *MockDecryptService) Decrypt(ctx context.Context, namespace string, nam
panic("no return value specified for Decrypt")
}
var r0 map[string]service.DecryptResult
var r0 map[string]contracts.DecryptResult
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, ...string) (map[string]service.DecryptResult, error)); ok {
if rf, ok := ret.Get(0).(func(context.Context, string, ...string) (map[string]contracts.DecryptResult, error)); ok {
return rf(ctx, namespace, names...)
}
if rf, ok := ret.Get(0).(func(context.Context, string, ...string) map[string]service.DecryptResult); ok {
if rf, ok := ret.Get(0).(func(context.Context, string, ...string) map[string]contracts.DecryptResult); ok {
r0 = rf(ctx, namespace, names...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]service.DecryptResult)
r0 = ret.Get(0).(map[string]contracts.DecryptResult)
}
}
@@ -86,12 +86,12 @@ func (_c *MockDecryptService_Decrypt_Call) Run(run func(ctx context.Context, nam
return _c
}
func (_c *MockDecryptService_Decrypt_Call) Return(_a0 map[string]service.DecryptResult, _a1 error) *MockDecryptService_Decrypt_Call {
func (_c *MockDecryptService_Decrypt_Call) Return(_a0 map[string]contracts.DecryptResult, _a1 error) *MockDecryptService_Decrypt_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDecryptService_Decrypt_Call) RunAndReturn(run func(context.Context, string, ...string) (map[string]service.DecryptResult, error)) *MockDecryptService_Decrypt_Call {
func (_c *MockDecryptService_Decrypt_Call) RunAndReturn(run func(context.Context, string, ...string) (map[string]contracts.DecryptResult, error)) *MockDecryptService_Decrypt_Call {
_c.Call.Return(run)
return _c
}
@@ -7,17 +7,17 @@ import (
"github.com/grafana/grafana-app-sdk/logging"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
grafanasecrets "github.com/grafana/grafana/pkg/services/secrets"
legacysecrets "github.com/grafana/grafana/pkg/services/secrets"
)
func ProvideRepositorySecrets(
features featuremgmt.FeatureToggles,
legacySecretsSvc grafanasecrets.Service,
legacySecretsSvc legacysecrets.Service,
secretsSvc contracts.SecureValueClient,
decryptSvc service.DecryptService,
decryptSvc secret.DecryptService,
) RepositorySecrets {
return NewRepositorySecrets(features, NewSecretsService(secretsSvc, decryptSvc), NewSingleTenant(legacySecretsSvc))
}
@@ -6,20 +6,17 @@ import (
"github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/registry/apis/secret"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
grafanasecrets "github.com/grafana/grafana/pkg/registry/apis/secret/service"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
)
const svcName = "provisioning"
//go:generate mockery --name SecureValueClient --structname MockSecureValueClient --inpackage --filename secure_value_client_mock.go --with-expecter
type SecureValueClient interface {
Client(ctx context.Context, namespace string) (dynamic.ResourceInterface, error)
}
type SecureValueClient = secret.SecureValueClient
//go:generate mockery --name Service --structname MockService --inpackage --filename secret_mock.go --with-expecter
type Service interface {
@@ -30,13 +27,13 @@ type Service interface {
var _ Service = (*secretsService)(nil)
//go:generate mockery --name DecryptService --structname MockDecryptService --srcpkg=github.com/grafana/grafana/pkg/registry/apis/secret/service --filename decrypt_service_mock.go --with-expecter
//go:generate mockery --name DecryptService --structname MockDecryptService --srcpkg=github.com/grafana/grafana/pkg/registry/apis/secret --filename decrypt_service_mock.go --with-expecter
type secretsService struct {
secureValues SecureValueClient
decryptSvc grafanasecrets.DecryptService
decryptSvc secret.DecryptService
}
func NewSecretsService(secretsSvc SecureValueClient, decryptSvc grafanasecrets.DecryptService) Service {
func NewSecretsService(secretsSvc SecureValueClient, decryptSvc secret.DecryptService) Service {
return &secretsService{
secureValues: secretsSvc,
decryptSvc: decryptSvc,
@@ -7,8 +7,8 @@ import (
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets/mocks"
"github.com/grafana/grafana/pkg/registry/apis/secret"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -258,7 +258,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
secretName: "test-secret",
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
exposedValue := secretv1beta1.NewExposedSecureValue("decrypted-data")
mockResult := service.NewDecryptResultValue(&exposedValue)
mockResult := secret.NewDecryptResultValue(&exposedValue)
mockDecryptSvc.EXPECT().Decrypt(
mock.MatchedBy(func(ctx context.Context) bool {
@@ -267,7 +267,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
}),
"test-namespace",
"test-secret",
).Return(map[string]service.DecryptResult{
).Return(map[string]secret.DecryptResult{
"test-secret": mockResult,
}, nil)
},
@@ -299,16 +299,16 @@ func TestSecretsService_Decrypt(t *testing.T) {
}),
"test-namespace",
"test-secret",
).Return(map[string]service.DecryptResult{}, nil)
).Return(map[string]secret.DecryptResult{}, nil)
},
expectedError: contracts.ErrDecryptNotFound.Error(),
expectedError: secret.ErrDecryptNotFound.Error(),
},
{
name: "decrypt result has error",
namespace: "test-namespace",
secretName: "test-secret",
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
mockResult := service.NewDecryptResultErr(errors.New("decryption failed"))
mockResult := secret.NewDecryptResultErr(errors.New("decryption failed"))
mockDecryptSvc.EXPECT().Decrypt(
mock.MatchedBy(func(ctx context.Context) bool {
@@ -316,7 +316,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
}),
"test-namespace",
"test-secret",
).Return(map[string]service.DecryptResult{
).Return(map[string]secret.DecryptResult{
"test-secret": mockResult,
}, nil)
},
@@ -354,7 +354,7 @@ func TestSecretsService_Decrypt_ServiceIdentityContext(t *testing.T) {
mockDecryptSvc := &mocks.MockDecryptService{}
exposedValue := secretv1beta1.NewExposedSecureValue("test-data")
mockResult := service.NewDecryptResultValue(&exposedValue)
mockResult := secret.NewDecryptResultValue(&exposedValue)
// Create a more detailed context matcher to verify the service identity context is created correctly
mockDecryptSvc.EXPECT().Decrypt(
@@ -364,7 +364,7 @@ func TestSecretsService_Decrypt_ServiceIdentityContext(t *testing.T) {
}),
"test-namespace",
"test-secret",
).Return(map[string]service.DecryptResult{
).Return(map[string]secret.DecryptResult{
"test-secret": mockResult,
}, nil)
@@ -23,3 +23,32 @@ type DecryptStorage interface {
type DecryptAuthorizer interface {
Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (identity string, allowed bool)
}
// DecryptService is the inferface for the decrypt service.
type DecryptService interface {
Decrypt(ctx context.Context, namespace string, names ...string) (map[string]DecryptResult, error)
}
// DecryptResult is the (union) result of a decryption operation.
// It contains the decrypted `value` when the decryption succeeds, and the `err` when it fails.
// It is not possible to construct a `DecryptResult` where both `value` and `err` are set from another package.
type DecryptResult struct {
value *secretv1beta1.ExposedSecureValue
err error
}
func (d DecryptResult) Error() error {
return d.err
}
func (d DecryptResult) Value() *secretv1beta1.ExposedSecureValue {
return d.value
}
func NewDecryptResultErr(err error) DecryptResult {
return DecryptResult{err: err}
}
func NewDecryptResultValue(value *secretv1beta1.ExposedSecureValue) DecryptResult {
return DecryptResult{value: value}
}
+5 -8
View File
@@ -4,7 +4,6 @@ import (
"context"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
@@ -12,23 +11,21 @@ type OSSDecryptService struct {
decryptStore contracts.DecryptStorage
}
var _ service.DecryptService = &OSSDecryptService{}
func ProvideDecryptService(decryptStore contracts.DecryptStorage) service.DecryptService {
func ProvideDecryptService(decryptStore contracts.DecryptStorage) contracts.DecryptService {
return &OSSDecryptService{
decryptStore: decryptStore,
}
}
func (d *OSSDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]service.DecryptResult, error) {
results := make(map[string]service.DecryptResult, len(names))
func (d *OSSDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]contracts.DecryptResult, error) {
results := make(map[string]contracts.DecryptResult, len(names))
for _, name := range names {
exposedSecureValue, err := d.decryptStore.Decrypt(ctx, xkube.Namespace(namespace), name)
if err != nil {
results[name] = service.NewDecryptResultErr(err)
results[name] = contracts.NewDecryptResultErr(err)
} else {
results[name] = service.NewDecryptResultValue(&exposedSecureValue)
results[name] = contracts.NewDecryptResultValue(&exposedSecureValue)
}
}
@@ -6,7 +6,7 @@ import (
"testing"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@@ -23,8 +23,8 @@ func TestDecryptService(t *testing.T) {
mockErr := errors.New("mock error")
mockStorage := &MockDecryptStorage{}
mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv1beta1.ExposedSecureValue(""), mockErr)
decryptedValuesResp := map[string]service.DecryptResult{
"secure-value-1": service.NewDecryptResultErr(mockErr),
decryptedValuesResp := map[string]contracts.DecryptResult{
"secure-value-1": contracts.NewDecryptResultErr(mockErr),
}
decryptService := &OSSDecryptService{
@@ -49,9 +49,9 @@ func TestDecryptService(t *testing.T) {
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
Return(exposedSecureValue2, nil)
decryptedValuesResp := map[string]service.DecryptResult{
"secure-value-1": service.NewDecryptResultValue(&exposedSecureValue1),
"secure-value-2": service.NewDecryptResultValue(&exposedSecureValue2),
decryptedValuesResp := map[string]contracts.DecryptResult{
"secure-value-1": contracts.NewDecryptResultValue(&exposedSecureValue1),
"secure-value-2": contracts.NewDecryptResultValue(&exposedSecureValue2),
}
decryptService := &OSSDecryptService{
@@ -75,9 +75,9 @@ func TestDecryptService(t *testing.T) {
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
Return(secretv1beta1.ExposedSecureValue(""), mockErr)
decryptedValuesResp := map[string]service.DecryptResult{
"secure-value-1": service.NewDecryptResultValue(&exposedSecureValue),
"secure-value-2": service.NewDecryptResultErr(mockErr),
decryptedValuesResp := map[string]contracts.DecryptResult{
"secure-value-1": contracts.NewDecryptResultValue(&exposedSecureValue),
"secure-value-2": contracts.NewDecryptResultErr(mockErr),
}
decryptService := &OSSDecryptService{
@@ -0,0 +1,25 @@
package secret
import (
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
// DecryptService is a decrypt client for secure value secrets.
type DecryptService = contracts.DecryptService
var (
ErrDecryptNotFound = contracts.ErrDecryptNotFound
ErrDecryptNotAuthorized = contracts.ErrDecryptNotAuthorized
ErrDecryptFailed = contracts.ErrDecryptFailed
)
type DecryptResult = contracts.DecryptResult
func NewDecryptResultErr(err error) DecryptResult {
return contracts.NewDecryptResultErr(err)
}
func NewDecryptResultValue(value *secretv1beta1.ExposedSecureValue) DecryptResult {
return contracts.NewDecryptResultValue(value)
}
-8
View File
@@ -1,8 +0,0 @@
package secret
import "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
var (
ErrSecureValueNotFound = contracts.ErrSecureValueNotFound
ErrSecureValueAlreadyExists = contracts.ErrSecureValueAlreadyExists
)
@@ -23,6 +23,11 @@ import (
authsvc "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
)
var (
ErrSecureValueNotFound = contracts.ErrSecureValueNotFound
ErrSecureValueAlreadyExists = contracts.ErrSecureValueAlreadyExists
)
// SecureValueClient is a CRUD client for the secure value API.
type SecureValueClient = contracts.SecureValueClient
@@ -1,36 +0,0 @@
package service
import (
"context"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
)
// DecryptResult is the (union) result of a decryption operation.
// It contains the decrypted `value` when the decryption succeeds, and the `err` when it fails.
// It is not possible to construct a `DecryptResult` where both `value` and `err` are set from another package.
type DecryptResult struct {
value *secretv1beta1.ExposedSecureValue
err error
}
func (d DecryptResult) Error() error {
return d.err
}
func (d DecryptResult) Value() *secretv1beta1.ExposedSecureValue {
return d.value
}
func NewDecryptResultErr(err error) DecryptResult {
return DecryptResult{err: err}
}
func NewDecryptResultValue(value *secretv1beta1.ExposedSecureValue) DecryptResult {
return DecryptResult{value: value}
}
// DecryptService is the inferface for the decrypt service.
type DecryptService interface {
Decrypt(ctx context.Context, namespace string, names ...string) (map[string]DecryptResult, error)
}
@@ -140,7 +140,7 @@ type Sut struct {
SecureValueService contracts.SecureValueService
SecureValueMetadataStorage contracts.SecureValueMetadataStorage
DecryptStorage contracts.DecryptStorage
DecryptService service.DecryptService
DecryptService contracts.DecryptService
EncryptedValueStorage contracts.EncryptedValueStorage
SQLKeeper *sqlkeeper.SQLKeeper
Database *database.Database
+8 -8
View File
@@ -788,11 +788,11 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
if err != nil {
return nil, err
}
decryptService := decrypt.ProvideDecryptService(decryptStorage)
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, decryptService)
v3 := decrypt.ProvideDecryptService(decryptStorage)
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, v3)
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, repositorySecrets, factory, renderingService, resourceClient, eventualRestConfigProvider)
v3 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v3)
v4 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v4)
if err != nil {
return nil, err
}
@@ -1346,11 +1346,11 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface {
if err != nil {
return nil, err
}
decryptService := decrypt.ProvideDecryptService(decryptStorage)
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, decryptService)
v3 := decrypt.ProvideDecryptService(decryptStorage)
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, v3)
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, repositorySecrets, factory, renderingService, resourceClient, eventualRestConfigProvider)
v3 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v3)
v4 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v4)
if err != nil {
return nil, err
}
@@ -7,7 +7,6 @@ import (
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/service"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/mitchellh/copystructure"
@@ -111,24 +110,24 @@ func (m *model) list(namespace string) (*secretv1beta1.SecureValueList, error) {
return &secretv1beta1.SecureValueList{Items: out}, nil
}
func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.DecryptResult, error) {
func (m *model) decrypt(decrypter, namespace, name string) (map[string]contracts.DecryptResult, error) {
for _, v := range m.secureValues {
if v.Namespace == namespace &&
v.Name == name &&
v.active {
if slices.ContainsFunc(v.Spec.Decrypters, func(d string) bool { return d == decrypter }) {
return map[string]service.DecryptResult{
name: service.NewDecryptResultValue(deepCopy(v).Spec.Value),
return map[string]contracts.DecryptResult{
name: contracts.NewDecryptResultValue(deepCopy(v).Spec.Value),
}, nil
}
return map[string]service.DecryptResult{
name: service.NewDecryptResultErr(contracts.ErrDecryptNotAuthorized),
return map[string]contracts.DecryptResult{
name: contracts.NewDecryptResultErr(contracts.ErrDecryptNotAuthorized),
}, nil
}
}
return map[string]service.DecryptResult{
name: service.NewDecryptResultErr(contracts.ErrDecryptNotFound),
return map[string]contracts.DecryptResult{
name: contracts.NewDecryptResultErr(contracts.ErrDecryptNotFound),
}, nil
}