Provisioning: Begin using secrets store (#108044)

- Provisioning: Begin using secrets store
- Refactor integration with secrets store
- Add back the legacy service
- Separate concerns for encrypt and decrypt
- Handle update within Encrypt function
- Add interface for secure value service
- Add feature flag for using secrets service
- Add the dual service for temporary solution.

* Add first integration tests for encrypted tokens
* Add integration test for app platform secrets
* Validate it has the name or not
* Create wire provider
* Always save to the secret if provided secret

---------

Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com>
Co-authored-by: Roberto Jiménez Sánchez <jszroberto@gmail.com>
This commit is contained in:
Stephanie Hingtgen
2025-07-15 15:43:17 +02:00
committed by GitHub
co-authored by Roberto Jimenez Sanchez Roberto Jiménez Sánchez
parent 68b9a5f57c
commit d39a47a89b
30 changed files with 2591 additions and 194 deletions
@@ -0,0 +1,177 @@
// Code generated by mockery v2.52.4. DO NOT EDIT.
package featuremgmt
import (
context "context"
mock "github.com/stretchr/testify/mock"
)
// MockFeatureToggles is an autogenerated mock type for the FeatureToggles type
type MockFeatureToggles struct {
mock.Mock
}
type MockFeatureToggles_Expecter struct {
mock *mock.Mock
}
func (_m *MockFeatureToggles) EXPECT() *MockFeatureToggles_Expecter {
return &MockFeatureToggles_Expecter{mock: &_m.Mock}
}
// GetEnabled provides a mock function with given fields: ctx
func (_m *MockFeatureToggles) GetEnabled(ctx context.Context) map[string]bool {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for GetEnabled")
}
var r0 map[string]bool
if rf, ok := ret.Get(0).(func(context.Context) map[string]bool); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]bool)
}
}
return r0
}
// MockFeatureToggles_GetEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEnabled'
type MockFeatureToggles_GetEnabled_Call struct {
*mock.Call
}
// GetEnabled is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockFeatureToggles_Expecter) GetEnabled(ctx interface{}) *MockFeatureToggles_GetEnabled_Call {
return &MockFeatureToggles_GetEnabled_Call{Call: _e.mock.On("GetEnabled", ctx)}
}
func (_c *MockFeatureToggles_GetEnabled_Call) Run(run func(ctx context.Context)) *MockFeatureToggles_GetEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *MockFeatureToggles_GetEnabled_Call) Return(_a0 map[string]bool) *MockFeatureToggles_GetEnabled_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockFeatureToggles_GetEnabled_Call) RunAndReturn(run func(context.Context) map[string]bool) *MockFeatureToggles_GetEnabled_Call {
_c.Call.Return(run)
return _c
}
// IsEnabled provides a mock function with given fields: ctx, flag
func (_m *MockFeatureToggles) IsEnabled(ctx context.Context, flag string) bool {
ret := _m.Called(ctx, flag)
if len(ret) == 0 {
panic("no return value specified for IsEnabled")
}
var r0 bool
if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok {
r0 = rf(ctx, flag)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// MockFeatureToggles_IsEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnabled'
type MockFeatureToggles_IsEnabled_Call struct {
*mock.Call
}
// IsEnabled is a helper method to define mock.On call
// - ctx context.Context
// - flag string
func (_e *MockFeatureToggles_Expecter) IsEnabled(ctx interface{}, flag interface{}) *MockFeatureToggles_IsEnabled_Call {
return &MockFeatureToggles_IsEnabled_Call{Call: _e.mock.On("IsEnabled", ctx, flag)}
}
func (_c *MockFeatureToggles_IsEnabled_Call) Run(run func(ctx context.Context, flag string)) *MockFeatureToggles_IsEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *MockFeatureToggles_IsEnabled_Call) Return(_a0 bool) *MockFeatureToggles_IsEnabled_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockFeatureToggles_IsEnabled_Call) RunAndReturn(run func(context.Context, string) bool) *MockFeatureToggles_IsEnabled_Call {
_c.Call.Return(run)
return _c
}
// IsEnabledGlobally provides a mock function with given fields: flag
func (_m *MockFeatureToggles) IsEnabledGlobally(flag string) bool {
ret := _m.Called(flag)
if len(ret) == 0 {
panic("no return value specified for IsEnabledGlobally")
}
var r0 bool
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(flag)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// MockFeatureToggles_IsEnabledGlobally_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnabledGlobally'
type MockFeatureToggles_IsEnabledGlobally_Call struct {
*mock.Call
}
// IsEnabledGlobally is a helper method to define mock.On call
// - flag string
func (_e *MockFeatureToggles_Expecter) IsEnabledGlobally(flag interface{}) *MockFeatureToggles_IsEnabledGlobally_Call {
return &MockFeatureToggles_IsEnabledGlobally_Call{Call: _e.mock.On("IsEnabledGlobally", flag)}
}
func (_c *MockFeatureToggles_IsEnabledGlobally_Call) Run(run func(flag string)) *MockFeatureToggles_IsEnabledGlobally_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockFeatureToggles_IsEnabledGlobally_Call) Return(_a0 bool) *MockFeatureToggles_IsEnabledGlobally_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockFeatureToggles_IsEnabledGlobally_Call) RunAndReturn(run func(string) bool) *MockFeatureToggles_IsEnabledGlobally_Call {
_c.Call.Return(run)
return _c
}
// NewMockFeatureToggles creates a new instance of MockFeatureToggles. 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 NewMockFeatureToggles(t interface {
mock.TestingT
Cleanup(func())
}) *MockFeatureToggles {
mock := &MockFeatureToggles{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+1
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
)
//go:generate mockery --name FeatureToggles --structname MockFeatureToggles --inpackage --filename feature_toggles_mock.go --with-expecter
type FeatureToggles interface {
// IsEnabled checks if a feature is enabled for a given context.
// The settings may be per user, tenant, or globally set in the cloud
+7
View File
@@ -337,6 +337,13 @@ var (
RequiresRestart: true,
Owner: grafanaAppPlatformSquad,
},
{
Name: "provisioningSecretsService",
Description: "Experimental feature to use the secrets service for provisioning instead of the legacy secrets",
Stage: FeatureStageExperimental,
RequiresRestart: true,
Owner: grafanaAppPlatformSquad,
},
{
Name: "grafanaAPIServerEnsureKubectlAccess",
Description: "Start an additional https handler and write kubectl options",
+1
View File
@@ -43,6 +43,7 @@ mlExpressions,experimental,@grafana/alerting-squad,false,false,false
datasourceAPIServers,experimental,@grafana/grafana-app-platform-squad,false,true,false
grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false
provisioning,experimental,@grafana/grafana-app-platform-squad,false,true,false
provisioningSecretsService,experimental,@grafana/grafana-app-platform-squad,false,true,false
grafanaAPIServerEnsureKubectlAccess,experimental,@grafana/grafana-app-platform-squad,true,true,false
featureToggleAdminPage,experimental,@grafana/grafana-operator-experience-squad,false,true,false
awsAsyncQueryCaching,GA,@grafana/aws-datasources,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
43 datasourceAPIServers experimental @grafana/grafana-app-platform-squad false true false
44 grafanaAPIServerWithExperimentalAPIs experimental @grafana/grafana-app-platform-squad true true false
45 provisioning experimental @grafana/grafana-app-platform-squad false true false
46 provisioningSecretsService experimental @grafana/grafana-app-platform-squad false true false
47 grafanaAPIServerEnsureKubectlAccess experimental @grafana/grafana-app-platform-squad true true false
48 featureToggleAdminPage experimental @grafana/grafana-operator-experience-squad false true false
49 awsAsyncQueryCaching GA @grafana/aws-datasources false false false
+4
View File
@@ -183,6 +183,10 @@ const (
// Next generation provisioning... and git
FlagProvisioning = "provisioning"
// FlagProvisioningSecretsService
// Experimental feature to use the secrets service for provisioning instead of the legacy secrets
FlagProvisioningSecretsService = "provisioningSecretsService"
// FlagGrafanaAPIServerEnsureKubectlAccess
// Start an additional https handler and write kubectl options
FlagGrafanaAPIServerEnsureKubectlAccess = "grafanaAPIServerEnsureKubectlAccess"
+16
View File
@@ -2506,6 +2506,22 @@
"requiresRestart": true
}
},
{
"metadata": {
"name": "provisioningSecretsService",
"resourceVersion": "1752501981893",
"creationTimestamp": "2025-07-14T14:05:50Z",
"annotations": {
"grafana.app/updatedTimestamp": "2025-07-14 14:06:21.89322 +0000 UTC"
}
},
"spec": {
"description": "Experimental feature to use the secrets service for provisioning instead of the legacy secrets",
"stage": "experimental",
"codeowner": "@grafana/grafana-app-platform-squad",
"requiresRestart": true
}
},
{
"metadata": {
"name": "publicDashboardsEmailSharing",