Provisioning: Add inline secure values to repository schema (#109594)
This commit is contained in:
@@ -17,9 +17,23 @@ type Repository struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec RepositorySpec `json:"spec,omitempty"`
|
||||
Secure SecureValues `json:"secure,omitzero,omitempty"`
|
||||
Status RepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NOT YET USED FOR REAL -- testing secure value workflow
|
||||
type SecureValues struct {
|
||||
// Token used to connect the configured repository
|
||||
Token common.InlineSecureValue `json:"token,omitzero,omitempty"`
|
||||
|
||||
// Some webhooks (github) require a secret key value
|
||||
WebhookSecret common.InlineSecureValue `json:"webhookSecret,omitzero,omitempty"`
|
||||
}
|
||||
|
||||
func (v SecureValues) IsZero() bool {
|
||||
return v.Token.IsZero() && v.WebhookSecret.IsZero()
|
||||
}
|
||||
|
||||
type LocalRepositoryConfig struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
@@ -665,6 +665,7 @@ func (in *Repository) DeepCopyInto(out *Repository) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Secure = in.Secure
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@@ -1085,6 +1086,24 @@ func (in *ResourceWrapper) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SecureValues) DeepCopyInto(out *SecureValues) {
|
||||
*out = *in
|
||||
out.Token = in.Token
|
||||
out.WebhookSecret = in.WebhookSecret
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecureValues.
|
||||
func (in *SecureValues) DeepCopy() *SecureValues {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SecureValues)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SyncJobOptions) DeepCopyInto(out *SyncJobOptions) {
|
||||
*out = *in
|
||||
|
||||
@@ -57,6 +57,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceStats": schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceType": schema_pkg_apis_provisioning_v0alpha1_ResourceType(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceWrapper": schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SecureValues": schema_pkg_apis_provisioning_v0alpha1_SecureValues(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncJobOptions": schema_pkg_apis_provisioning_v0alpha1_SyncJobOptions(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncOptions": schema_pkg_apis_provisioning_v0alpha1_SyncOptions(ref),
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncStatus": schema_pkg_apis_provisioning_v0alpha1_SyncStatus(ref),
|
||||
@@ -1435,6 +1436,12 @@ func schema_pkg_apis_provisioning_v0alpha1_Repository(ref common.ReferenceCallba
|
||||
Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec"),
|
||||
},
|
||||
},
|
||||
"secure": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SecureValues"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
@@ -1445,7 +1452,7 @@ func schema_pkg_apis_provisioning_v0alpha1_Repository(ref common.ReferenceCallba
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryStatus", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SecureValues", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2359,6 +2366,35 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref common.ReferenceC
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_SecureValues(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "NOT YET USED FOR REAL -- testing secure value workflow",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"token": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Token used to connect the configured repository",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue"),
|
||||
},
|
||||
},
|
||||
"webhookSecret": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Some webhooks (github) require a secret key value",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_SyncJobOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -16,6 +16,7 @@ type RepositoryApplyConfiguration struct {
|
||||
v1.TypeMetaApplyConfiguration `json:",inline"`
|
||||
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
|
||||
Spec *RepositorySpecApplyConfiguration `json:"spec,omitempty"`
|
||||
Secure *SecureValuesApplyConfiguration `json:"secure,omitempty"`
|
||||
Status *RepositoryStatusApplyConfiguration `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
@@ -196,6 +197,14 @@ func (b *RepositoryApplyConfiguration) WithSpec(value *RepositorySpecApplyConfig
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSecure sets the Secure field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Secure field is set to the value of the last call.
|
||||
func (b *RepositoryApplyConfiguration) WithSecure(value *SecureValuesApplyConfiguration) *RepositoryApplyConfiguration {
|
||||
b.Secure = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithStatus sets the Status field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Status field is set to the value of the last call.
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
commonv0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
// SecureValuesApplyConfiguration represents a declarative configuration of the SecureValues type for use
|
||||
// with apply.
|
||||
type SecureValuesApplyConfiguration struct {
|
||||
Token *commonv0alpha1.InlineSecureValue `json:"token,omitempty"`
|
||||
WebhookSecret *commonv0alpha1.InlineSecureValue `json:"webhookSecret,omitempty"`
|
||||
}
|
||||
|
||||
// SecureValuesApplyConfiguration constructs a declarative configuration of the SecureValues type for use with
|
||||
// apply.
|
||||
func SecureValues() *SecureValuesApplyConfiguration {
|
||||
return &SecureValuesApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithToken sets the Token field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Token field is set to the value of the last call.
|
||||
func (b *SecureValuesApplyConfiguration) WithToken(value commonv0alpha1.InlineSecureValue) *SecureValuesApplyConfiguration {
|
||||
b.Token = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithWebhookSecret sets the WebhookSecret field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the WebhookSecret field is set to the value of the last call.
|
||||
func (b *SecureValuesApplyConfiguration) WithWebhookSecret(value commonv0alpha1.InlineSecureValue) *SecureValuesApplyConfiguration {
|
||||
b.WebhookSecret = &value
|
||||
return b
|
||||
}
|
||||
@@ -62,6 +62,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
|
||||
return &provisioningv0alpha1.ResourceCountApplyConfiguration{}
|
||||
case v0alpha1.SchemeGroupVersion.WithKind("ResourceRef"):
|
||||
return &provisioningv0alpha1.ResourceRefApplyConfiguration{}
|
||||
case v0alpha1.SchemeGroupVersion.WithKind("SecureValues"):
|
||||
return &provisioningv0alpha1.SecureValuesApplyConfiguration{}
|
||||
case v0alpha1.SchemeGroupVersion.WithKind("SyncJobOptions"):
|
||||
return &provisioningv0alpha1.SyncJobOptionsApplyConfiguration{}
|
||||
case v0alpha1.SchemeGroupVersion.WithKind("SyncOptions"):
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
_ "github.com/robfig/cron/v3"
|
||||
_ "github.com/russellhaering/goxmldsig"
|
||||
_ "github.com/spf13/cobra" // used by the standalone apiserver cli
|
||||
_ "github.com/spyzhov/ajson"
|
||||
_ "github.com/stretchr/testify/require"
|
||||
_ "gocloud.dev/secrets/awskms"
|
||||
_ "gocloud.dev/secrets/azurekeyvault"
|
||||
@@ -52,5 +53,4 @@ import (
|
||||
_ "github.com/grafana/e2e"
|
||||
_ "github.com/grafana/gofpdf"
|
||||
_ "github.com/grafana/gomemcache/memcache"
|
||||
_ "github.com/spyzhov/ajson"
|
||||
)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/github"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
@@ -14,7 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func ProvideTestEnv(
|
||||
@@ -34,7 +36,8 @@ func ProvideTestEnv(
|
||||
resourceClient resource.ResourceClient,
|
||||
idService auth.IDService,
|
||||
githubFactory *github.Factory,
|
||||
repositorySecrets secrets.RepositorySecrets,
|
||||
decryptService secret.DecryptService,
|
||||
repositorySecrets secrets.RepositorySecrets, // TODO... remove
|
||||
) (*TestEnv, error) {
|
||||
return &TestEnv{
|
||||
TestingT: testingT,
|
||||
@@ -50,7 +53,8 @@ func ProvideTestEnv(
|
||||
ResourceClient: resourceClient,
|
||||
IDService: idService,
|
||||
GitHubFactory: githubFactory,
|
||||
RepositorySecrets: repositorySecrets,
|
||||
DecryptService: decryptService,
|
||||
RepositorySecrets: repositorySecrets, // TODO, remove
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -72,5 +76,6 @@ type TestEnv struct {
|
||||
ResourceClient resource.ResourceClient
|
||||
IDService auth.IDService
|
||||
GitHubFactory *github.Factory
|
||||
RepositorySecrets secrets.RepositorySecrets
|
||||
DecryptService secret.DecryptService
|
||||
RepositorySecrets secrets.RepositorySecrets // NOTE, this will be removed soon
|
||||
}
|
||||
|
||||
@@ -1430,7 +1430,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
testEnv, err := ProvideTestEnv(testingT, server, sqlStore, cfg, notificationServiceMock, grpcserverProvider, inMemory, httpclientProvider, oauthtokentestService, featureToggles, resourceClient, idimplService, factory, repositorySecrets)
|
||||
testEnv, err := ProvideTestEnv(testingT, server, sqlStore, cfg, notificationServiceMock, grpcserverProvider, inMemory, httpclientProvider, oauthtokentestService, featureToggles, resourceClient, idimplService, factory, v3, repositorySecrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2375,6 +2375,9 @@
|
||||
"metadata": {
|
||||
"default": {}
|
||||
},
|
||||
"secure": {
|
||||
"default": {}
|
||||
},
|
||||
"spec": {
|
||||
"default": {}
|
||||
},
|
||||
@@ -3464,6 +3467,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"secure": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SecureValues"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
@@ -4193,6 +4204,30 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SecureValues": {
|
||||
"description": "NOT YET USED FOR REAL -- testing secure value workflow",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"description": "Token used to connect the configured repository",
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.InlineSecureValue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"webhookSecret": {
|
||||
"description": "Some webhooks (github) require a secret key value",
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.InlineSecureValue"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncJobOptions": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -4406,6 +4441,45 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.InlineSecureValue": {
|
||||
"description": "Allow access to a secure value inside",
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"create"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"remove"
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"create": {
|
||||
"description": "Create a secure value -- this is only used for POST/PUT",
|
||||
"type": "string",
|
||||
"maxLength": 24576,
|
||||
"minLength": 1
|
||||
},
|
||||
"name": {
|
||||
"description": "Name in the secret service (reference)",
|
||||
"type": "string",
|
||||
"maxLength": 253,
|
||||
"minLength": 1
|
||||
},
|
||||
"remove": {
|
||||
"description": "Remove this value from the secure value map Values owned by this resource will be deleted if necessary",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
@@ -38,18 +36,9 @@ func TestIntegrationOpenAPIs(t *testing.T) {
|
||||
|
||||
t.Run("check valid version response", func(t *testing.T) {
|
||||
disco := h.NewDiscoveryClient()
|
||||
req := disco.RESTClient().Get().
|
||||
Prefix("version").
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
result := req.Do(context.Background())
|
||||
require.NoError(t, result.Error())
|
||||
|
||||
raw, err := result.Raw()
|
||||
require.NoError(t, err)
|
||||
info := apimachineryversion.Info{}
|
||||
err = json.Unmarshal(raw, &info)
|
||||
info, err := disco.ServerVersion()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, runtime.Version(), info.GoVersion)
|
||||
|
||||
// Make sure the gitVersion is parsable
|
||||
v, err := version.Parse(info.GitVersion)
|
||||
@@ -57,10 +46,15 @@ func TestIntegrationOpenAPIs(t *testing.T) {
|
||||
require.Equal(t, info.Major, fmt.Sprintf("%d", v.Major()))
|
||||
require.Equal(t, info.Minor, fmt.Sprintf("%d", v.Minor()))
|
||||
|
||||
// Check that OpenAPI v2 (used by kubectl) returns properly
|
||||
v2, err := disco.OpenAPISchema()
|
||||
require.NoError(t, err, "requesting OpenAPI v2")
|
||||
require.Equal(t, "Grafana API Server", v2.Info.Title)
|
||||
// Check the v3 path resolves properly
|
||||
// NOTE: fetching the v2 schema sometimes returns a 503 in our test infrastructure
|
||||
// Removing the explicit `OneOf` properties from InlineSecureValue in:
|
||||
// https://github.com/grafana/grafana/blob/main/pkg/apimachinery/apis/common/v0alpha1/secure_values.go#L78
|
||||
// will consistently support V2, however kubectl and everything else continues to work
|
||||
paths, err := disco.OpenAPIV3().Paths()
|
||||
|
||||
require.NoError(t, err, "requesting OpenAPI v3")
|
||||
require.NotEmpty(t, paths, "has registered paths")
|
||||
})
|
||||
|
||||
dir := "openapi_snapshots"
|
||||
|
||||
@@ -9,14 +9,105 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_InlineSecrets(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
helper := runGrafana(t, useAppPlatformSecrets)
|
||||
createOptions := metav1.CreateOptions{FieldValidation: "Strict"}
|
||||
ctx := context.Background()
|
||||
|
||||
decryptService := helper.GetEnv().DecryptService
|
||||
require.NotNil(t, decryptService, "decrypt service wired properly")
|
||||
|
||||
type expectedField struct {
|
||||
Path []string
|
||||
DecryptedValue string // only try decrypting if not empty
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
values map[string]any
|
||||
inputFile string
|
||||
expectedFields []expectedField
|
||||
}{
|
||||
{
|
||||
name: "inline github token encrypted",
|
||||
values: map[string]any{
|
||||
"SecureTokenCreate": "some-token",
|
||||
"SecureWebhookSecretCreate": "some-secret",
|
||||
},
|
||||
inputFile: "testdata/github-with-inline-secrets.json.tmpl",
|
||||
expectedFields: []expectedField{
|
||||
{
|
||||
Path: []string{"secure", "token", "name"},
|
||||
DecryptedValue: "some-token",
|
||||
},
|
||||
{
|
||||
Path: []string{"secure", "webhookSecret", "name"},
|
||||
DecryptedValue: "some-secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
input := helper.RenderObject(t, test.inputFile, test.values)
|
||||
obj, err := helper.Repositories.Resource.Create(ctx, input, createOptions)
|
||||
require.NoError(t, err, "failed to create resource")
|
||||
require.True(t, strings.HasPrefix(obj.GetName(), "test-"), "created a unique name")
|
||||
var created []string
|
||||
|
||||
// Move encrypted token mutation
|
||||
for _, expectedField := range test.expectedFields {
|
||||
name, found, err := unstructured.NestedString(obj.Object, expectedField.Path...)
|
||||
require.NoError(t, err, "error getting expected path")
|
||||
require.True(t, found, expectedField.Path)
|
||||
require.NotEmpty(t, name, expectedField.Path)
|
||||
created = append(created, name)
|
||||
|
||||
if expectedField.DecryptedValue != "" {
|
||||
decrypted, err := decryptService.Decrypt(ctx, "provisioning.grafana.app", obj.GetNamespace(), name)
|
||||
require.NoError(t, err, "decryption error")
|
||||
require.Len(t, decrypted, 1)
|
||||
|
||||
val := decrypted[name].Value()
|
||||
require.NotNil(t, val)
|
||||
require.Equal(t, expectedField.DecryptedValue, val.DangerouslyExposeAndConsumeValue())
|
||||
}
|
||||
}
|
||||
|
||||
err = helper.Repositories.Resource.Delete(ctx, obj.GetName(), metav1.DeleteOptions{})
|
||||
require.NoError(t, err, "failed to delete repository")
|
||||
|
||||
// Finalizers will be running async... so we need to wait until it is actually removed
|
||||
require.Eventually(t, func() bool {
|
||||
_, err := helper.Repositories.Resource.Get(ctx, obj.GetName(), metav1.GetOptions{})
|
||||
return apierrors.IsNotFound(err)
|
||||
}, time.Second*15, time.Millisecond*300, "should be removed")
|
||||
|
||||
// now check that we can no longer decrypt the requested values
|
||||
results, err := decryptService.Decrypt(ctx, "provisioning.grafana.app", obj.GetNamespace(), created...)
|
||||
require.NoError(t, err, "failed to execute decrypt with removed secrets")
|
||||
for k, v := range results {
|
||||
require.ErrorContains(t, v.Error(), "not found", "expecting not found error for all secrets: %s", k)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationProvisioning_LegacySecrets(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"apiVersion": "provisioning.grafana.app/v0alpha1",
|
||||
"kind": "Repository",
|
||||
"metadata": {
|
||||
"generateName": "test-"
|
||||
},
|
||||
"spec": {
|
||||
"title": "title",
|
||||
"description": "something",
|
||||
"type": "github",
|
||||
"github": {
|
||||
"url": "{{ or .URL "https://github.com/grafana/grafana-git-sync-demo" }}",
|
||||
"branch": "{{ or .Branch "integration-test" }}",
|
||||
"generateDashboardPreviews": {{ if .GenerateDashboardPreviews }} true {{ else }} false {{ end }},
|
||||
"token": "{{ or .Token "" }}",
|
||||
"path": "{{ or .Path "grafana/" }}"
|
||||
}
|
||||
},
|
||||
"secure": {
|
||||
"token": { "create": "{{ or .SecureTokenCreate "" }}" },
|
||||
"webhookSecret": { "create": "{{ or .SecureWebhookSecretCreate "" }}" }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user