mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 23:53:10 +08:00
Provisioning: use new secure value client (#108227)
* Use provider in Webhooks registration * First stab at using the new client * Simplify mock * Able to generate graph in OSS * Remove line already ensured by provider * Handle the K8s not found error as well * Commit regenerated wire file * Add the hack also for deletion * Fix secrets test util * Format file
This commit is contained in:

committed by
GitHub

parent
58eb0ec954
commit
810868c156
@ -16,7 +16,7 @@ import (
|
||||
func ProvideRepositorySecrets(
|
||||
features featuremgmt.FeatureToggles,
|
||||
legacySecretsSvc grafanasecrets.Service,
|
||||
secretsSvc *service.SecureValueService,
|
||||
secretsSvc contracts.SecureValueClient,
|
||||
decryptSvc service.DecryptService,
|
||||
) RepositorySecrets {
|
||||
return NewRepositorySecrets(features, NewSecretsService(secretsSvc, decryptSvc), NewSingleTenant(legacySecretsSvc))
|
||||
|
@ -5,22 +5,20 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
grafanasecrets "github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
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 SecureValueService --structname MockSecureValueService --inpackage --filename secure_value_mock.go --with-expecter
|
||||
type SecureValueService interface {
|
||||
Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error)
|
||||
Update(ctx context.Context, newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error)
|
||||
Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error)
|
||||
Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error)
|
||||
//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)
|
||||
}
|
||||
|
||||
//go:generate mockery --name Service --structname MockService --inpackage --filename secret_mock.go --with-expecter
|
||||
@ -34,52 +32,70 @@ 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
|
||||
type secretsService struct {
|
||||
secretsSvc SecureValueService
|
||||
decryptSvc grafanasecrets.DecryptService
|
||||
secureValues SecureValueClient
|
||||
decryptSvc grafanasecrets.DecryptService
|
||||
}
|
||||
|
||||
func NewSecretsService(secretsSvc SecureValueService, decryptSvc grafanasecrets.DecryptService) Service {
|
||||
func NewSecretsService(secretsSvc SecureValueClient, decryptSvc grafanasecrets.DecryptService) Service {
|
||||
return &secretsService{
|
||||
secretsSvc: secretsSvc,
|
||||
decryptSvc: decryptSvc,
|
||||
secureValues: secretsSvc,
|
||||
decryptSvc: decryptSvc,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *secretsService) Encrypt(ctx context.Context, namespace, name string, data string) (string, error) {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
client, err := s.secureValues.Client(ctx, namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
val := secretv1beta1.NewExposedSecureValue(data)
|
||||
secret := &secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "provisioning: " + name,
|
||||
Value: &val,
|
||||
Decrypters: []string{svcName},
|
||||
},
|
||||
// Try to get existing secret
|
||||
existingUnstructured, err := client.Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// If secret doesn't exist (not found error), we'll create it
|
||||
// For other errors, return the error
|
||||
if !errors.Is(err, contracts.ErrSecureValueNotFound) {
|
||||
// Check if it's a k8s not found error
|
||||
if !isNotFoundError(err) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
existing, err := s.secretsSvc.Read(ctx, xkube.Namespace(namespace), name)
|
||||
if err != nil && !errors.Is(err, contracts.ErrSecureValueNotFound) {
|
||||
return "", err
|
||||
}
|
||||
if existingUnstructured != nil {
|
||||
// Update the value directly in the unstructured object
|
||||
if err := unstructured.SetNestedField(existingUnstructured.Object, data, "spec", "value"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if existing != nil {
|
||||
existing.Spec.Value = &val
|
||||
existing, _, err = s.secretsSvc.Update(ctx, existing, user.GetUID())
|
||||
// Update using dynamic client
|
||||
result, err := client.Update(ctx, existingUnstructured, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return existing.GetName(), nil
|
||||
return result.GetName(), nil
|
||||
}
|
||||
|
||||
finalSecret, err := s.secretsSvc.Create(ctx, secret, user.GetUID())
|
||||
// Create the secret directly as unstructured
|
||||
secret := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "secret.grafana.app/v1beta1",
|
||||
"kind": "SecureValue",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"description": "provisioning: " + name,
|
||||
"value": data,
|
||||
"decrypters": []string{svcName},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create new secret
|
||||
finalSecret, err := client.Create(ctx, secret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -115,11 +131,43 @@ func (s *secretsService) Delete(ctx context.Context, namespace string, name stri
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = identity.WithServiceIdentityContext(ctx, ns.OrgID, identity.WithServiceIdentityName(svcName))
|
||||
|
||||
if _, err := s.secretsSvc.Delete(ctx, xkube.Namespace(namespace), name); err != nil {
|
||||
ctx = identity.WithServiceIdentityContext(ctx, ns.OrgID, identity.WithServiceIdentityName(svcName))
|
||||
client, err := s.secureValues.Client(ctx, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.Delete(ctx, name, metav1.DeleteOptions{}); err != nil {
|
||||
// FIXME: This is a temporary workaround until the client abstraction properly handles
|
||||
// k8s not found errors. The client should normalize these errors to return contracts.ErrSecureValueNotFound
|
||||
if isNotFoundError(err) {
|
||||
return contracts.ErrSecureValueNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to check if error is a not found error
|
||||
// FIXME: This is a temporary workaround until the client abstraction properly handles
|
||||
// k8s not found errors. The client should normalize these errors to return contracts.ErrSecureValueNotFound
|
||||
func isNotFoundError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for Grafana's secure value not found error
|
||||
if errors.Is(err, contracts.ErrSecureValueNotFound) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for k8s not found error
|
||||
if apierrors.IsNotFound(err) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Fallback for generic not found error messages
|
||||
return err.Error() == "not found"
|
||||
}
|
||||
|
@ -6,18 +6,48 @@ import (
|
||||
"testing"
|
||||
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets/mocks"
|
||||
"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"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
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/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// mockDynamicInterface implements a simplified version of the dynamic.ResourceInterface
|
||||
type mockDynamicInterface struct {
|
||||
dynamic.ResourceInterface
|
||||
getResult *unstructured.Unstructured
|
||||
getErr error
|
||||
createResult *unstructured.Unstructured
|
||||
createErr error
|
||||
updateResult *unstructured.Unstructured
|
||||
updateErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (m *mockDynamicInterface) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return m.getResult, m.getErr
|
||||
}
|
||||
|
||||
func (m *mockDynamicInterface) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return m.createResult, m.createErr
|
||||
}
|
||||
|
||||
func (m *mockDynamicInterface) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return m.updateResult, m.updateErr
|
||||
}
|
||||
|
||||
func (m *mockDynamicInterface) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
|
||||
return m.deleteErr
|
||||
}
|
||||
|
||||
func TestNewSecretsService(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
@ -33,7 +63,7 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace string
|
||||
secretName string
|
||||
data string
|
||||
setupMocks func(*MockSecureValueService, *mocks.MockDecryptService)
|
||||
setupMocks func(*MockSecureValueClient, *mocks.MockDecryptService, *mockDynamicInterface)
|
||||
expectedName string
|
||||
expectedError string
|
||||
}{
|
||||
@ -42,49 +72,24 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
data: "secret-data",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
// Assert Read call with correct parameters
|
||||
mockSecretsSvc.EXPECT().Read(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
xkube.Namespace("test-namespace"),
|
||||
"test-secret",
|
||||
).Return(nil, contracts.ErrSecureValueNotFound)
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Assert Create call with detailed validation
|
||||
mockSecretsSvc.EXPECT().Create(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
mock.MatchedBy(func(sv *secretv1beta1.SecureValue) bool {
|
||||
if sv.Namespace != "test-namespace" || sv.Name != "test-secret" {
|
||||
return false
|
||||
}
|
||||
if sv.Spec.Description != "provisioning: test-secret" {
|
||||
return false
|
||||
}
|
||||
if sv.Spec.Value == nil {
|
||||
return false
|
||||
}
|
||||
if len(sv.Spec.Decrypters) != 1 || sv.Spec.Decrypters[0] != svcName {
|
||||
return false
|
||||
}
|
||||
// Verify the actual secret value
|
||||
if sv.Spec.Value.DangerouslyExposeAndConsumeValue() != "secret-data" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}),
|
||||
":test-uid",
|
||||
).Return(&secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-secret",
|
||||
Namespace: "test-namespace",
|
||||
// Mock Get call to return not found error (secret doesn't exist)
|
||||
mockResourceInterface.getResult = nil
|
||||
mockResourceInterface.getErr = contracts.ErrSecureValueNotFound
|
||||
|
||||
// Mock Create call
|
||||
mockResourceInterface.createResult = &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-secret",
|
||||
"namespace": "test-namespace",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
}
|
||||
mockResourceInterface.createErr = nil
|
||||
},
|
||||
expectedName: "test-secret",
|
||||
},
|
||||
@ -93,54 +98,37 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace: "test-namespace",
|
||||
secretName: "existing-secret",
|
||||
data: "new-secret-data",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
existingSecret := &secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "existing-secret",
|
||||
Namespace: "test-namespace",
|
||||
},
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "provisioning: existing-secret",
|
||||
Decrypters: []string{svcName},
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
existingSecret := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "existing-secret",
|
||||
"namespace": "test-namespace",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"description": "provisioning: existing-secret",
|
||||
"decrypters": []string{svcName},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Assert Read call with context validation
|
||||
mockSecretsSvc.EXPECT().Read(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
xkube.Namespace("test-namespace"),
|
||||
"existing-secret",
|
||||
).Return(existingSecret, nil)
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Assert Update call with detailed validation
|
||||
mockSecretsSvc.EXPECT().Update(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
mock.MatchedBy(func(sv *secretv1beta1.SecureValue) bool {
|
||||
if sv.Namespace != "test-namespace" || sv.Name != "existing-secret" {
|
||||
return false
|
||||
}
|
||||
if sv.Spec.Value == nil {
|
||||
return false
|
||||
}
|
||||
// Verify the updated secret value
|
||||
if sv.Spec.Value.DangerouslyExposeAndConsumeValue() != "new-secret-data" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}),
|
||||
":test-uid",
|
||||
).Return(&secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "existing-secret",
|
||||
Namespace: "test-namespace",
|
||||
// Mock Get call to return existing secret
|
||||
mockResourceInterface.getResult = existingSecret
|
||||
mockResourceInterface.getErr = nil
|
||||
|
||||
// Mock Update call
|
||||
mockResourceInterface.updateResult = &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "existing-secret",
|
||||
"namespace": "test-namespace",
|
||||
},
|
||||
},
|
||||
}, true, nil)
|
||||
}
|
||||
mockResourceInterface.updateErr = nil
|
||||
},
|
||||
expectedName: "existing-secret",
|
||||
},
|
||||
@ -149,15 +137,13 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
data: "secret-data",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockSecretsSvc.EXPECT().Read(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
xkube.Namespace("test-namespace"),
|
||||
"test-secret",
|
||||
).Return(nil, errors.New("database error"))
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Get call to return error
|
||||
mockResourceInterface.getResult = nil
|
||||
mockResourceInterface.getErr = errors.New("database error")
|
||||
},
|
||||
expectedError: "database error",
|
||||
},
|
||||
@ -166,29 +152,17 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
data: "secret-data",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockSecretsSvc.EXPECT().Read(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
xkube.Namespace("test-namespace"),
|
||||
"test-secret",
|
||||
).Return(nil, contracts.ErrSecureValueNotFound)
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
mockSecretsSvc.EXPECT().Create(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
mock.MatchedBy(func(sv *secretv1beta1.SecureValue) bool {
|
||||
return sv.Namespace == "test-namespace" &&
|
||||
sv.Name == "test-secret" &&
|
||||
sv.Spec.Value != nil &&
|
||||
sv.Spec.Value.DangerouslyExposeAndConsumeValue() == "secret-data"
|
||||
}),
|
||||
":test-uid",
|
||||
).Return(nil, errors.New("creation failed"))
|
||||
// Mock Get call to return not found error
|
||||
mockResourceInterface.getResult = nil
|
||||
mockResourceInterface.getErr = contracts.ErrSecureValueNotFound
|
||||
|
||||
// Mock Create call to return error
|
||||
mockResourceInterface.createResult = nil
|
||||
mockResourceInterface.createErr = errors.New("creation failed")
|
||||
},
|
||||
expectedError: "creation failed",
|
||||
},
|
||||
@ -197,40 +171,30 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
namespace: "test-namespace",
|
||||
secretName: "existing-secret",
|
||||
data: "new-secret-data",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
existingSecret := &secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "existing-secret",
|
||||
Namespace: "test-namespace",
|
||||
},
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "provisioning: existing-secret",
|
||||
Decrypters: []string{svcName},
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
existingSecret := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "existing-secret",
|
||||
"namespace": "test-namespace",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"description": "provisioning: existing-secret",
|
||||
"decrypters": []string{svcName},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockSecretsSvc.EXPECT().Read(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
xkube.Namespace("test-namespace"),
|
||||
"existing-secret",
|
||||
).Return(existingSecret, nil)
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
mockSecretsSvc.EXPECT().Update(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
return err == nil && requester != nil && requester.GetUID() == ":test-uid"
|
||||
}),
|
||||
mock.MatchedBy(func(sv *secretv1beta1.SecureValue) bool {
|
||||
return sv.Namespace == "test-namespace" &&
|
||||
sv.Name == "existing-secret" &&
|
||||
sv.Spec.Value != nil &&
|
||||
sv.Spec.Value.DangerouslyExposeAndConsumeValue() == "new-secret-data"
|
||||
}),
|
||||
":test-uid",
|
||||
).Return(nil, false, errors.New("update failed"))
|
||||
// Mock Get call to return existing secret
|
||||
mockResourceInterface.getResult = existingSecret
|
||||
mockResourceInterface.getErr = nil
|
||||
|
||||
// Mock Update call to return error
|
||||
mockResourceInterface.updateResult = nil
|
||||
mockResourceInterface.updateErr = errors.New("update failed")
|
||||
},
|
||||
expectedError: "update failed",
|
||||
},
|
||||
@ -238,17 +202,15 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
mockResourceInterface := &mockDynamicInterface{}
|
||||
|
||||
tt.setupMocks(mockSecretsSvc, mockDecryptSvc)
|
||||
tt.setupMocks(mockSecretsSvc, mockDecryptSvc, mockResourceInterface)
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = identity.WithRequester(ctx, &identity.StaticRequester{
|
||||
UserUID: "test-uid",
|
||||
})
|
||||
|
||||
result, err := svc.Encrypt(ctx, tt.namespace, tt.secretName, tt.data)
|
||||
|
||||
@ -263,10 +225,13 @@ func TestSecretsService_Encrypt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretsService_Encrypt_NoIdentity(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
func TestSecretsService_Encrypt_ClientError(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
|
||||
// Setup client to return error
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(nil, errors.New("client error"))
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
|
||||
ctx := context.Background()
|
||||
@ -274,6 +239,7 @@ func TestSecretsService_Encrypt_NoIdentity(t *testing.T) {
|
||||
result, err := svc.Encrypt(ctx, "test-namespace", "test-secret", "secret-data")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "client error")
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
@ -282,7 +248,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
name string
|
||||
namespace string
|
||||
secretName string
|
||||
setupMocks func(*MockSecureValueService, *mocks.MockDecryptService)
|
||||
setupMocks func(*MockSecureValueClient, *mocks.MockDecryptService)
|
||||
expectedResult []byte
|
||||
expectedError string
|
||||
}{
|
||||
@ -290,7 +256,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
name: "successfully decrypt secret",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
exposedValue := secretv1beta1.NewExposedSecureValue("decrypted-data")
|
||||
mockResult := service.NewDecryptResultValue(&exposedValue)
|
||||
|
||||
@ -311,7 +277,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
name: "decrypt service error",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockDecryptSvc.EXPECT().Decrypt(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
return ctx != nil
|
||||
@ -326,7 +292,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
name: "secret not found in results",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockDecryptSvc.EXPECT().Decrypt(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
return ctx != nil
|
||||
@ -341,7 +307,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
name: "decrypt result has error",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockResult := service.NewDecryptResultErr(errors.New("decryption failed"))
|
||||
|
||||
mockDecryptSvc.EXPECT().Decrypt(
|
||||
@ -360,7 +326,7 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
|
||||
tt.setupMocks(mockSecretsSvc, mockDecryptSvc)
|
||||
@ -382,15 +348,15 @@ func TestSecretsService_Decrypt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test to verify that the Decrypt method creates the correct StaticRequester
|
||||
func TestSecretsService_Decrypt_StaticRequesterCreation(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
// Test to verify that the Decrypt method creates the correct service identity context
|
||||
func TestSecretsService_Decrypt_ServiceIdentityContext(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
|
||||
exposedValue := secretv1beta1.NewExposedSecureValue("test-data")
|
||||
mockResult := service.NewDecryptResultValue(&exposedValue)
|
||||
|
||||
// Create a more detailed context matcher to verify the StaticRequester is created correctly
|
||||
// Create a more detailed context matcher to verify the service identity context is created correctly
|
||||
mockDecryptSvc.EXPECT().Decrypt(
|
||||
mock.MatchedBy(func(ctx context.Context) bool {
|
||||
// At minimum, verify the context is not nil and is different from the original
|
||||
@ -416,38 +382,53 @@ func TestSecretsService_Delete(t *testing.T) {
|
||||
name string
|
||||
namespace string
|
||||
secretName string
|
||||
setupMocks func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService)
|
||||
setupMocks func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "delete success",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockSecretsSvc.EXPECT().
|
||||
Delete(mock.Anything, xkube.Namespace("test-namespace"), "test-secret").
|
||||
Return(&secretv1beta1.SecureValue{}, nil)
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Delete call
|
||||
mockResourceInterface.deleteErr = nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete returns error",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueService, mockDecryptSvc *mocks.MockDecryptService) {
|
||||
mockSecretsSvc.EXPECT().
|
||||
Delete(mock.Anything, xkube.Namespace("test-namespace"), "test-secret").
|
||||
Return(nil, errors.New("delete failed"))
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Delete call to return error
|
||||
mockResourceInterface.deleteErr = errors.New("delete failed")
|
||||
},
|
||||
expectedError: "delete failed",
|
||||
},
|
||||
{
|
||||
name: "client error",
|
||||
namespace: "test-namespace",
|
||||
secretName: "test-secret",
|
||||
setupMocks: func(mockSecretsSvc *MockSecureValueClient, mockDecryptSvc *mocks.MockDecryptService, mockResourceInterface *mockDynamicInterface) {
|
||||
// Setup client to return error
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(nil, errors.New("client error"))
|
||||
},
|
||||
expectedError: "client error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueService(t)
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
mockResourceInterface := &mockDynamicInterface{}
|
||||
|
||||
tt.setupMocks(mockSecretsSvc, mockDecryptSvc)
|
||||
tt.setupMocks(mockSecretsSvc, mockDecryptSvc, mockResourceInterface)
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
ctx := context.Background()
|
||||
@ -463,3 +444,125 @@ func TestSecretsService_Delete(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNotFoundError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "nil error",
|
||||
err: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "grafana secure value not found error",
|
||||
err: contracts.ErrSecureValueNotFound,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "k8s not found error",
|
||||
err: apierrors.NewNotFound(schema.GroupResource{Group: "secret.grafana.app", Resource: "securevalues"}, "test-secret"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "generic not found error message",
|
||||
err: errors.New("not found"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "other error",
|
||||
err: errors.New("internal server error"),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "wrapped grafana error",
|
||||
err: errors.New("wrapped: " + contracts.ErrSecureValueNotFound.Error()),
|
||||
expected: false, // wrapped errors won't match errors.Is unless properly wrapped
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isNotFoundError(tt.err)
|
||||
assert.Equal(t, tt.expected, result, "isNotFoundError(%v) = %v, want %v", tt.err, result, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretsService_Encrypt_WithK8sNotFoundError(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
mockResourceInterface := &mockDynamicInterface{}
|
||||
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Get call to return k8s not found error
|
||||
k8sNotFoundErr := apierrors.NewNotFound(schema.GroupResource{Group: "secret.grafana.app", Resource: "securevalues"}, "test-secret")
|
||||
mockResourceInterface.getResult = nil
|
||||
mockResourceInterface.getErr = k8sNotFoundErr
|
||||
|
||||
// Mock Create call to succeed
|
||||
mockResourceInterface.createResult = &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-secret",
|
||||
"namespace": "test-namespace",
|
||||
},
|
||||
},
|
||||
}
|
||||
mockResourceInterface.createErr = nil
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
ctx := context.Background()
|
||||
|
||||
result, err := svc.Encrypt(ctx, "test-namespace", "test-secret", "secret-data")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test-secret", result)
|
||||
}
|
||||
|
||||
func TestSecretsService_Delete_WithK8sNotFoundError(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
mockResourceInterface := &mockDynamicInterface{}
|
||||
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Delete call to return k8s not found error
|
||||
k8sNotFoundErr := apierrors.NewNotFound(schema.GroupResource{Group: "secret.grafana.app", Resource: "securevalues"}, "test-secret")
|
||||
mockResourceInterface.deleteErr = k8sNotFoundErr
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
ctx := context.Background()
|
||||
|
||||
err := svc.Delete(ctx, "test-namespace", "test-secret")
|
||||
|
||||
// Should return contracts.ErrSecureValueNotFound instead of k8s error
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, contracts.ErrSecureValueNotFound)
|
||||
}
|
||||
|
||||
func TestSecretsService_Delete_WithGrafanaNotFoundError(t *testing.T) {
|
||||
mockSecretsSvc := NewMockSecureValueClient(t)
|
||||
mockDecryptSvc := &mocks.MockDecryptService{}
|
||||
mockResourceInterface := &mockDynamicInterface{}
|
||||
|
||||
// Setup client to return the mock resource interface
|
||||
mockSecretsSvc.EXPECT().Client(mock.Anything, "test-namespace").Return(mockResourceInterface, nil)
|
||||
|
||||
// Mock Delete call to return Grafana not found error
|
||||
mockResourceInterface.deleteErr = contracts.ErrSecureValueNotFound
|
||||
|
||||
svc := NewSecretsService(mockSecretsSvc, mockDecryptSvc)
|
||||
ctx := context.Background()
|
||||
|
||||
err := svc.Delete(ctx, "test-namespace", "test-secret")
|
||||
|
||||
// Should return contracts.ErrSecureValueNotFound
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, contracts.ErrSecureValueNotFound)
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||
|
||||
package secrets
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
dynamic "k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// MockSecureValueClient is an autogenerated mock type for the SecureValueClient type
|
||||
type MockSecureValueClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockSecureValueClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockSecureValueClient) EXPECT() *MockSecureValueClient_Expecter {
|
||||
return &MockSecureValueClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Client provides a mock function with given fields: ctx, namespace
|
||||
func (_m *MockSecureValueClient) Client(ctx context.Context, namespace string) (dynamic.ResourceInterface, error) {
|
||||
ret := _m.Called(ctx, namespace)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Client")
|
||||
}
|
||||
|
||||
var r0 dynamic.ResourceInterface
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (dynamic.ResourceInterface, error)); ok {
|
||||
return rf(ctx, namespace)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) dynamic.ResourceInterface); ok {
|
||||
r0 = rf(ctx, namespace)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(dynamic.ResourceInterface)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, namespace)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockSecureValueClient_Client_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Client'
|
||||
type MockSecureValueClient_Client_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Client is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - namespace string
|
||||
func (_e *MockSecureValueClient_Expecter) Client(ctx interface{}, namespace interface{}) *MockSecureValueClient_Client_Call {
|
||||
return &MockSecureValueClient_Client_Call{Call: _e.mock.On("Client", ctx, namespace)}
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueClient_Client_Call) Run(run func(ctx context.Context, namespace string)) *MockSecureValueClient_Client_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueClient_Client_Call) Return(_a0 dynamic.ResourceInterface, _a1 error) *MockSecureValueClient_Client_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueClient_Client_Call) RunAndReturn(run func(context.Context, string) (dynamic.ResourceInterface, error)) *MockSecureValueClient_Client_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockSecureValueClient creates a new instance of MockSecureValueClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockSecureValueClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockSecureValueClient {
|
||||
mock := &MockSecureValueClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||
|
||||
package secrets
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
v1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
xkube "github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
)
|
||||
|
||||
// MockSecureValueService is an autogenerated mock type for the SecureValueService type
|
||||
type MockSecureValueService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockSecureValueService_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockSecureValueService) EXPECT() *MockSecureValueService_Expecter {
|
||||
return &MockSecureValueService_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, sv, actorUID
|
||||
func (_m *MockSecureValueService) Create(ctx context.Context, sv *v1beta1.SecureValue, actorUID string) (*v1beta1.SecureValue, error) {
|
||||
ret := _m.Called(ctx, sv, actorUID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Create")
|
||||
}
|
||||
|
||||
var r0 *v1beta1.SecureValue
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v1beta1.SecureValue, string) (*v1beta1.SecureValue, error)); ok {
|
||||
return rf(ctx, sv, actorUID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v1beta1.SecureValue, string) *v1beta1.SecureValue); ok {
|
||||
r0 = rf(ctx, sv, actorUID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1beta1.SecureValue)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v1beta1.SecureValue, string) error); ok {
|
||||
r1 = rf(ctx, sv, actorUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockSecureValueService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
|
||||
type MockSecureValueService_Create_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Create is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - sv *v1beta1.SecureValue
|
||||
// - actorUID string
|
||||
func (_e *MockSecureValueService_Expecter) Create(ctx interface{}, sv interface{}, actorUID interface{}) *MockSecureValueService_Create_Call {
|
||||
return &MockSecureValueService_Create_Call{Call: _e.mock.On("Create", ctx, sv, actorUID)}
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Create_Call) Run(run func(ctx context.Context, sv *v1beta1.SecureValue, actorUID string)) *MockSecureValueService_Create_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(*v1beta1.SecureValue), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Create_Call) Return(_a0 *v1beta1.SecureValue, _a1 error) *MockSecureValueService_Create_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Create_Call) RunAndReturn(run func(context.Context, *v1beta1.SecureValue, string) (*v1beta1.SecureValue, error)) *MockSecureValueService_Create_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, namespace, name
|
||||
func (_m *MockSecureValueService) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*v1beta1.SecureValue, error) {
|
||||
ret := _m.Called(ctx, namespace, name)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Delete")
|
||||
}
|
||||
|
||||
var r0 *v1beta1.SecureValue
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, xkube.Namespace, string) (*v1beta1.SecureValue, error)); ok {
|
||||
return rf(ctx, namespace, name)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, xkube.Namespace, string) *v1beta1.SecureValue); ok {
|
||||
r0 = rf(ctx, namespace, name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1beta1.SecureValue)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, xkube.Namespace, string) error); ok {
|
||||
r1 = rf(ctx, namespace, name)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockSecureValueService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
|
||||
type MockSecureValueService_Delete_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Delete is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - namespace xkube.Namespace
|
||||
// - name string
|
||||
func (_e *MockSecureValueService_Expecter) Delete(ctx interface{}, namespace interface{}, name interface{}) *MockSecureValueService_Delete_Call {
|
||||
return &MockSecureValueService_Delete_Call{Call: _e.mock.On("Delete", ctx, namespace, name)}
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Delete_Call) Run(run func(ctx context.Context, namespace xkube.Namespace, name string)) *MockSecureValueService_Delete_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(xkube.Namespace), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Delete_Call) Return(_a0 *v1beta1.SecureValue, _a1 error) *MockSecureValueService_Delete_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Delete_Call) RunAndReturn(run func(context.Context, xkube.Namespace, string) (*v1beta1.SecureValue, error)) *MockSecureValueService_Delete_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Read provides a mock function with given fields: ctx, namespace, name
|
||||
func (_m *MockSecureValueService) Read(ctx context.Context, namespace xkube.Namespace, name string) (*v1beta1.SecureValue, error) {
|
||||
ret := _m.Called(ctx, namespace, name)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Read")
|
||||
}
|
||||
|
||||
var r0 *v1beta1.SecureValue
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, xkube.Namespace, string) (*v1beta1.SecureValue, error)); ok {
|
||||
return rf(ctx, namespace, name)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, xkube.Namespace, string) *v1beta1.SecureValue); ok {
|
||||
r0 = rf(ctx, namespace, name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1beta1.SecureValue)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, xkube.Namespace, string) error); ok {
|
||||
r1 = rf(ctx, namespace, name)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockSecureValueService_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read'
|
||||
type MockSecureValueService_Read_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Read is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - namespace xkube.Namespace
|
||||
// - name string
|
||||
func (_e *MockSecureValueService_Expecter) Read(ctx interface{}, namespace interface{}, name interface{}) *MockSecureValueService_Read_Call {
|
||||
return &MockSecureValueService_Read_Call{Call: _e.mock.On("Read", ctx, namespace, name)}
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Read_Call) Run(run func(ctx context.Context, namespace xkube.Namespace, name string)) *MockSecureValueService_Read_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(xkube.Namespace), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Read_Call) Return(_a0 *v1beta1.SecureValue, _a1 error) *MockSecureValueService_Read_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Read_Call) RunAndReturn(run func(context.Context, xkube.Namespace, string) (*v1beta1.SecureValue, error)) *MockSecureValueService_Read_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, newSecureValue, actorUID
|
||||
func (_m *MockSecureValueService) Update(ctx context.Context, newSecureValue *v1beta1.SecureValue, actorUID string) (*v1beta1.SecureValue, bool, error) {
|
||||
ret := _m.Called(ctx, newSecureValue, actorUID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Update")
|
||||
}
|
||||
|
||||
var r0 *v1beta1.SecureValue
|
||||
var r1 bool
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v1beta1.SecureValue, string) (*v1beta1.SecureValue, bool, error)); ok {
|
||||
return rf(ctx, newSecureValue, actorUID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v1beta1.SecureValue, string) *v1beta1.SecureValue); ok {
|
||||
r0 = rf(ctx, newSecureValue, actorUID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1beta1.SecureValue)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v1beta1.SecureValue, string) bool); ok {
|
||||
r1 = rf(ctx, newSecureValue, actorUID)
|
||||
} else {
|
||||
r1 = ret.Get(1).(bool)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, *v1beta1.SecureValue, string) error); ok {
|
||||
r2 = rf(ctx, newSecureValue, actorUID)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// MockSecureValueService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update'
|
||||
type MockSecureValueService_Update_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Update is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - newSecureValue *v1beta1.SecureValue
|
||||
// - actorUID string
|
||||
func (_e *MockSecureValueService_Expecter) Update(ctx interface{}, newSecureValue interface{}, actorUID interface{}) *MockSecureValueService_Update_Call {
|
||||
return &MockSecureValueService_Update_Call{Call: _e.mock.On("Update", ctx, newSecureValue, actorUID)}
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Update_Call) Run(run func(ctx context.Context, newSecureValue *v1beta1.SecureValue, actorUID string)) *MockSecureValueService_Update_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(*v1beta1.SecureValue), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Update_Call) Return(_a0 *v1beta1.SecureValue, _a1 bool, _a2 error) *MockSecureValueService_Update_Call {
|
||||
_c.Call.Return(_a0, _a1, _a2)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockSecureValueService_Update_Call) RunAndReturn(run func(context.Context, *v1beta1.SecureValue, string) (*v1beta1.SecureValue, bool, error)) *MockSecureValueService_Update_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockSecureValueService creates a new instance of MockSecureValueService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockSecureValueService(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockSecureValueService {
|
||||
mock := &MockSecureValueService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
@ -17,11 +17,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks/pullrequest"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
grafanasecrets "github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
@ -39,9 +37,7 @@ type WebhookExtraBuilder struct {
|
||||
func ProvideWebhooks(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
legacySecretsSvc grafanasecrets.Service,
|
||||
secretsSvc *service.SecureValueService,
|
||||
decryptSvc service.DecryptService,
|
||||
repositorySecrets secrets.RepositorySecrets,
|
||||
ghFactory *github.Factory,
|
||||
renderer rendering.Service,
|
||||
blobstore resource.ResourceClient,
|
||||
@ -68,7 +64,6 @@ func ProvideWebhooks(
|
||||
evaluator := pullrequest.NewEvaluator(screenshotRenderer, parsers, urlProvider)
|
||||
commenter := pullrequest.NewCommenter()
|
||||
pullRequestWorker := pullrequest.NewPullRequestWorker(evaluator, commenter)
|
||||
repositorySecrets := secrets.NewRepositorySecrets(features, secrets.NewSecretsService(secretsSvc, decryptSvc), secrets.NewSingleTenant(legacySecretsSvc))
|
||||
|
||||
return NewWebhookExtra(
|
||||
render,
|
||||
|
@ -24,8 +24,6 @@ type SecureValueService struct {
|
||||
keeperService contracts.KeeperService
|
||||
}
|
||||
|
||||
var _ contracts.SecureValueService = &SecureValueService{}
|
||||
|
||||
func ProvideSecureValueService(
|
||||
tracer trace.Tracer,
|
||||
accessClient claims.AccessClient,
|
||||
@ -33,7 +31,7 @@ func ProvideSecureValueService(
|
||||
secureValueMetadataStorage contracts.SecureValueMetadataStorage,
|
||||
keeperMetadataStorage contracts.KeeperMetadataStorage,
|
||||
keeperService contracts.KeeperService,
|
||||
) *SecureValueService {
|
||||
) contracts.SecureValueService {
|
||||
return &SecureValueService{
|
||||
tracer: tracer,
|
||||
accessClient: accessClient,
|
||||
|
@ -136,7 +136,7 @@ func Setup(t *testing.T, opts ...func(*SetupConfig)) Sut {
|
||||
}
|
||||
|
||||
type Sut struct {
|
||||
SecureValueService *service.SecureValueService
|
||||
SecureValueService contracts.SecureValueService
|
||||
SecureValueMetadataStorage contracts.SecureValueMetadataStorage
|
||||
DecryptStorage contracts.DecryptStorage
|
||||
DecryptService service.DecryptService
|
||||
|
@ -772,14 +772,16 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
|
||||
return nil, err
|
||||
}
|
||||
secureValueService := service12.ProvideSecureValueService(tracer, accessClient, databaseDatabase, secureValueMetadataStorage, keeperMetadataStorage, ossKeeperService)
|
||||
secureValueValidator := validator3.ProvideSecureValueValidator()
|
||||
secureValueClient := secret.ProvideSecureValueClient(secureValueService, secureValueValidator)
|
||||
decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer)
|
||||
decryptStorage, err := metadata.ProvideDecryptStorage(tracer, ossKeeperService, keeperMetadataStorage, secureValueMetadataStorage, decryptAuthorizer, registerer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decryptService := decrypt.ProvideDecryptService(decryptStorage)
|
||||
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueService, decryptService)
|
||||
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, secretsService, secureValueService, decryptService, factory, renderingService, resourceClient, eventualRestConfigProvider)
|
||||
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, decryptService)
|
||||
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, repositorySecrets, factory, renderingService, resourceClient, eventualRestConfigProvider)
|
||||
v2 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
|
||||
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v2)
|
||||
if err != nil {
|
||||
@ -1321,14 +1323,16 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface {
|
||||
return nil, err
|
||||
}
|
||||
secureValueService := service12.ProvideSecureValueService(tracer, accessClient, databaseDatabase, secureValueMetadataStorage, keeperMetadataStorage, ossKeeperService)
|
||||
secureValueValidator := validator3.ProvideSecureValueValidator()
|
||||
secureValueClient := secret.ProvideSecureValueClient(secureValueService, secureValueValidator)
|
||||
decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer)
|
||||
decryptStorage, err := metadata.ProvideDecryptStorage(tracer, ossKeeperService, keeperMetadataStorage, secureValueMetadataStorage, decryptAuthorizer, registerer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decryptService := decrypt.ProvideDecryptService(decryptStorage)
|
||||
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueService, decryptService)
|
||||
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, secretsService, secureValueService, decryptService, factory, renderingService, resourceClient, eventualRestConfigProvider)
|
||||
repositorySecrets := secrets.ProvideRepositorySecrets(featureToggles, secretsService, secureValueClient, decryptService)
|
||||
webhookExtraBuilder := webhooks.ProvideWebhooks(cfg, featureToggles, repositorySecrets, factory, renderingService, resourceClient, eventualRestConfigProvider)
|
||||
v2 := extras.ProvideProvisioningOSSExtras(webhookExtraBuilder)
|
||||
apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, repositorySecrets, tracingService, v2)
|
||||
if err != nil {
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
apisregistry "github.com/grafana/grafana/pkg/registry/apis"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/extras"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
gsmKMSProviders "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/kmsproviders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper"
|
||||
@ -139,6 +140,7 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
aggregatorrunner.ProvideNoopAggregatorConfigurator,
|
||||
apisregistry.WireSetExts,
|
||||
gsmKMSProviders.ProvideOSSKMSProviders,
|
||||
secret.ProvideSecureValueClient,
|
||||
provisioningExtras,
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user