Secrets: Add single tenant SecureValueClient (#108099)

* Secrets: Add single tenant SecureValueClient

* SecureValueClient: Rename file

* SecureValueClient: Move original type to contracts package and export it by aliasing
pull/108216/head
Matheus Macabu 4 days ago committed by GitHub
parent a0873736aa
commit 6c69ae244e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      pkg/registry/apis/secret/contracts/secure_value.go
  2. 231
      pkg/registry/apis/secret/secure_value_client.go
  3. 109
      pkg/registry/apis/secret/secure_value_client_test.go
  4. 19
      pkg/registry/apis/secret/validator/keeper_test.go
  5. 7
      pkg/registry/apis/secret/validator/secure_value.go
  6. 37
      pkg/registry/apis/secret/validator/secure_value_test.go

@ -4,6 +4,8 @@ import (
"context" "context"
"errors" "errors"
"k8s.io/client-go/dynamic"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
) )
@ -45,3 +47,7 @@ type SecureValueService interface {
Update(ctx context.Context, newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) Update(ctx context.Context, newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error)
Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error)
} }
type SecureValueClient interface {
Client(ctx context.Context, namespace string) (dynamic.ResourceInterface, error)
}

@ -0,0 +1,231 @@
package secret
import (
"context"
"fmt"
claims "github.com/grafana/authlib/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/admission"
"k8s.io/client-go/dynamic"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
// SecureValueClient is a CRUD client for the secure value API.
type SecureValueClient = contracts.SecureValueClient
type secureValueClient struct {
namespace string
service contracts.SecureValueService
validator contracts.SecureValueValidator
}
var _ SecureValueClient = &secureValueClient{}
func ProvideSecureValueClient(service contracts.SecureValueService, validator contracts.SecureValueValidator) SecureValueClient {
return &secureValueClient{
service: service,
validator: validator,
}
}
// Client returns a resource interface that is scoped to a specific namespace.
func (c *secureValueClient) Client(ctx context.Context, namespace string) (dynamic.ResourceInterface, error) {
return c.Namespace(namespace), nil
}
// Namespace returns a resource interface that is scoped to a specific namespace.
func (c *secureValueClient) Namespace(ns string) dynamic.ResourceInterface {
info, err := claims.ParseNamespace(ns)
if err != nil {
panic(err)
}
if len(info.Value) == 0 {
panic("namespace is required")
}
ret := *c
ret.namespace = ns
return &ret
}
// Create a new secure value. Options and subresources are not supported and ignored.
func (c *secureValueClient) Create(ctx context.Context, obj *unstructured.Unstructured, _ metav1.CreateOptions, _ ...string) (*unstructured.Unstructured, error) {
if len(c.namespace) == 0 {
return nil, fmt.Errorf("namespace is required")
}
sv, err := fromUnstructured(obj)
if err != nil {
return nil, err
}
if sv.Namespace != c.namespace {
return nil, fmt.Errorf("namespace mismatch")
}
if errs := c.validator.Validate(sv, nil, admission.Create); len(errs) > 0 {
return nil, fmt.Errorf("invalid secure value: %w", errs.ToAggregate())
}
user, ok := claims.AuthInfoFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing auth info in context")
}
createdSv, err := c.service.Create(ctx, sv, user.GetUID())
if err != nil {
return nil, err
}
return toUnstructured(createdSv)
}
// Get a secure value by name. Options and subresources are not supported and ignored.
func (c *secureValueClient) Get(ctx context.Context, name string, _ metav1.GetOptions, _ ...string) (*unstructured.Unstructured, error) {
if len(c.namespace) == 0 {
return nil, fmt.Errorf("namespace is required")
}
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
sv, err := c.service.Read(ctx, xkube.Namespace(c.namespace), name)
if err != nil {
return nil, err
}
return toUnstructured(sv)
}
// Update a secure value. Options and subresources are not supported and ignored.
func (c *secureValueClient) Update(ctx context.Context, obj *unstructured.Unstructured, _ metav1.UpdateOptions, _ ...string) (*unstructured.Unstructured, error) {
if len(c.namespace) == 0 {
return nil, fmt.Errorf("namespace is required")
}
oldUnstructured, err := c.Get(ctx, obj.GetName(), metav1.GetOptions{})
if err != nil {
return nil, err
}
oldSv, err := fromUnstructured(oldUnstructured)
if err != nil {
return nil, err
}
sv, err := fromUnstructured(obj)
if err != nil {
return nil, err
}
if sv.Namespace != c.namespace {
return nil, fmt.Errorf("namespace mismatch")
}
if errs := c.validator.Validate(sv, oldSv, admission.Update); len(errs) > 0 {
return nil, fmt.Errorf("invalid secure value: %w", errs.ToAggregate())
}
user, ok := claims.AuthInfoFrom(ctx)
if !ok {
return nil, fmt.Errorf("missing auth info in context")
}
updatedSv, _, err := c.service.Update(ctx, sv, user.GetUID())
if err != nil {
return nil, err
}
return toUnstructured(updatedSv)
}
// Delete a secure value by name. Options and subresources are not supported and ignored.
func (c *secureValueClient) Delete(ctx context.Context, name string, _ metav1.DeleteOptions, _ ...string) error {
if len(c.namespace) == 0 {
return fmt.Errorf("namespace is required")
}
if len(name) == 0 {
return fmt.Errorf("name is required")
}
_, err := c.service.Delete(ctx, xkube.Namespace(c.namespace), name)
return err
}
// List all secure values in the namespace. Options and subresources are not supported and ignored.
func (c *secureValueClient) List(ctx context.Context, _ metav1.ListOptions) (*unstructured.UnstructuredList, error) {
if len(c.namespace) == 0 {
return nil, fmt.Errorf("namespace is required")
}
list, err := c.service.List(ctx, xkube.Namespace(c.namespace))
if err != nil {
return nil, err
}
items := make([]unstructured.Unstructured, 0, len(list.Items))
for _, sv := range list.Items {
u, err := toUnstructured(&sv)
if err != nil {
return nil, err
}
items = append(items, *u)
}
return &unstructured.UnstructuredList{
Items: items,
}, nil
}
// DeleteCollection is not supported and returns an error.
func (c *secureValueClient) DeleteCollection(_ context.Context, _ metav1.DeleteOptions, _ metav1.ListOptions) error {
return fmt.Errorf("deleteCollection is not supported")
}
// Watch is not supported and returns an error.
func (c *secureValueClient) Watch(_ context.Context, _ metav1.ListOptions) (watch.Interface, error) {
return nil, fmt.Errorf("watch is not supported")
}
// Patch is not supported and returns an error.
func (c *secureValueClient) Patch(_ context.Context, _ string, _ types.PatchType, _ []byte, _ metav1.PatchOptions, _ ...string) (*unstructured.Unstructured, error) {
return nil, fmt.Errorf("patch is not supported")
}
// Apply is not supported and returns an error.
func (c *secureValueClient) Apply(_ context.Context, _ string, _ *unstructured.Unstructured, _ metav1.ApplyOptions, _ ...string) (*unstructured.Unstructured, error) {
return nil, fmt.Errorf("apply is not supported")
}
// UpdateStatus is not supported and returns an error.
func (c *secureValueClient) UpdateStatus(_ context.Context, _ *unstructured.Unstructured, _ metav1.UpdateOptions) (*unstructured.Unstructured, error) {
return nil, fmt.Errorf("updateStatus is not supported")
}
// ApplyStatus is not supported and returns an error.
func (c *secureValueClient) ApplyStatus(_ context.Context, _ string, _ *unstructured.Unstructured, _ metav1.ApplyOptions) (*unstructured.Unstructured, error) {
return nil, fmt.Errorf("applyStatus is not supported")
}
func toUnstructured(sv *secretv1beta1.SecureValue) (*unstructured.Unstructured, error) {
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(sv)
if err != nil {
return nil, err
}
return &unstructured.Unstructured{Object: unstructuredObj}, nil
}
func fromUnstructured(u *unstructured.Unstructured) (*secretv1beta1.SecureValue, error) {
sv := new(secretv1beta1.SecureValue)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, sv); err != nil {
return nil, err
}
return sv, nil
}

@ -0,0 +1,109 @@
package secret
import (
"testing"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/registry/apis/secret/validator"
)
func TestIntegration_SecureValueClient_CRUD(t *testing.T) {
setup := testutils.Setup(t)
validator := validator.ProvideSecureValueValidator()
client := ProvideSecureValueClient(
setup.SecureValueService,
validator,
)
ns := "stacks-1234"
ctx := testutils.CreateUserAuthContext(t.Context(), ns, map[string][]string{
"securevalues:read": {"securevalues:uid:*"},
})
nsClient, err := client.Client(ctx, ns)
require.NoError(t, err)
require.NotNil(t, nsClient)
sv := &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: "test-sv",
Namespace: ns,
},
Spec: secretv1beta1.SecureValueSpec{
Description: "test-description",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value")),
},
}
unstructured, err := toUnstructured(sv)
require.NoError(t, err)
// Create
created, err := nsClient.Create(ctx, unstructured, metav1.CreateOptions{})
require.NoError(t, err)
createdSv, err := fromUnstructured(created)
require.NoError(t, err)
require.NotEmpty(t, createdSv.UID)
require.Nil(t, createdSv.Spec.Value)
require.Equal(t, sv.Name, createdSv.Name)
require.Equal(t, sv.Namespace, createdSv.Namespace)
// Read
read, err := nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
require.NoError(t, err)
readSv, err := fromUnstructured(read)
require.NoError(t, err)
require.EqualValues(t, createdSv, readSv)
// Update
updatedSv := &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: createdSv.Name,
Namespace: createdSv.Namespace,
},
Spec: secretv1beta1.SecureValueSpec{
Description: "test-description-updated",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value-updated")),
},
}
unstructured, err = toUnstructured(updatedSv)
require.NoError(t, err)
_, err = nsClient.Update(ctx, unstructured, metav1.UpdateOptions{})
require.NoError(t, err)
read, err = nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
require.NoError(t, err)
readSv, err = fromUnstructured(read)
require.NoError(t, err)
require.Equal(t, updatedSv.Spec.Description, readSv.Spec.Description)
require.Equal(t, updatedSv.Name, readSv.Name)
require.Equal(t, updatedSv.Namespace, readSv.Namespace)
// List
list, err := nsClient.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, list.Items, 1)
require.Equal(t, createdSv.Name, list.Items[0].GetName())
require.Equal(t, createdSv.Namespace, list.Items[0].GetNamespace())
// Delete
err = nsClient.Delete(ctx, createdSv.Name, metav1.DeleteOptions{})
require.NoError(t, err)
read, err = nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
require.ErrorIs(t, err, contracts.ErrSecureValueNotFound)
require.Nil(t, read)
}

@ -11,6 +11,8 @@ import (
) )
func TestValidateKeeper(t *testing.T) { func TestValidateKeeper(t *testing.T) {
validator := ProvideKeeperValidator()
t.Run("when creating a new keeper", func(t *testing.T) { t.Run("when creating a new keeper", func(t *testing.T) {
t.Run("the `description` must be present", func(t *testing.T) { t.Run("the `description` must be present", func(t *testing.T) {
keeper := &secretv1beta1.Keeper{ keeper := &secretv1beta1.Keeper{
@ -23,7 +25,6 @@ func TestValidateKeeper(t *testing.T) {
}, },
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.description", errs[0].Field) require.Equal(t, "spec.description", errs[0].Field)
@ -41,7 +42,6 @@ func TestValidateKeeper(t *testing.T) {
}, },
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -54,7 +54,6 @@ func TestValidateKeeper(t *testing.T) {
}, },
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -81,7 +80,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAWS.DeepCopy() keeper := validKeeperAWS.DeepCopy()
keeper.Spec.Aws.AccessKeyID = secretv1beta1.KeeperCredentialValue{} keeper.Spec.Aws.AccessKeyID = secretv1beta1.KeeperCredentialValue{}
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.aws.accessKeyID", errs[0].Field) require.Equal(t, "spec.aws.accessKeyID", errs[0].Field)
@ -95,7 +93,6 @@ func TestValidateKeeper(t *testing.T) {
ValueFromConfig: "c", ValueFromConfig: "c",
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.aws.accessKeyID", errs[0].Field) require.Equal(t, "spec.aws.accessKeyID", errs[0].Field)
@ -107,7 +104,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAWS.DeepCopy() keeper := validKeeperAWS.DeepCopy()
keeper.Spec.Aws.SecretAccessKey = secretv1beta1.KeeperCredentialValue{} keeper.Spec.Aws.SecretAccessKey = secretv1beta1.KeeperCredentialValue{}
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field) require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field)
@ -121,7 +117,6 @@ func TestValidateKeeper(t *testing.T) {
ValueFromConfig: "c", ValueFromConfig: "c",
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field) require.Equal(t, "spec.aws.secretAccessKey", errs[0].Field)
@ -148,7 +143,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAzure.DeepCopy() keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.KeyVaultName = "" keeper.Spec.Azure.KeyVaultName = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.azure.keyVaultName", errs[0].Field) require.Equal(t, "spec.azure.keyVaultName", errs[0].Field)
@ -158,7 +152,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAzure.DeepCopy() keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.TenantID = "" keeper.Spec.Azure.TenantID = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.azure.tenantID", errs[0].Field) require.Equal(t, "spec.azure.tenantID", errs[0].Field)
@ -168,7 +161,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAzure.DeepCopy() keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.ClientID = "" keeper.Spec.Azure.ClientID = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientID", errs[0].Field) require.Equal(t, "spec.azure.clientID", errs[0].Field)
@ -179,7 +171,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperAzure.DeepCopy() keeper := validKeeperAzure.DeepCopy()
keeper.Spec.Azure.ClientSecret = secretv1beta1.KeeperCredentialValue{} keeper.Spec.Azure.ClientSecret = secretv1beta1.KeeperCredentialValue{}
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientSecret", errs[0].Field) require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
@ -193,7 +184,6 @@ func TestValidateKeeper(t *testing.T) {
ValueFromConfig: "c", ValueFromConfig: "c",
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.azure.clientSecret", errs[0].Field) require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
@ -216,7 +206,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperGCP.DeepCopy() keeper := validKeeperGCP.DeepCopy()
keeper.Spec.Gcp.ProjectID = "" keeper.Spec.Gcp.ProjectID = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.gcp.projectID", errs[0].Field) require.Equal(t, "spec.gcp.projectID", errs[0].Field)
@ -226,7 +215,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperGCP.DeepCopy() keeper := validKeeperGCP.DeepCopy()
keeper.Spec.Gcp.CredentialsFile = "" keeper.Spec.Gcp.CredentialsFile = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.gcp.credentialsFile", errs[0].Field) require.Equal(t, "spec.gcp.credentialsFile", errs[0].Field)
@ -250,7 +238,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperHashiCorp.DeepCopy() keeper := validKeeperHashiCorp.DeepCopy()
keeper.Spec.HashiCorpVault.Address = "" keeper.Spec.HashiCorpVault.Address = ""
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.hashiCorpVault.address", errs[0].Field) require.Equal(t, "spec.hashiCorpVault.address", errs[0].Field)
@ -261,7 +248,6 @@ func TestValidateKeeper(t *testing.T) {
keeper := validKeeperHashiCorp.DeepCopy() keeper := validKeeperHashiCorp.DeepCopy()
keeper.Spec.HashiCorpVault.Token = secretv1beta1.KeeperCredentialValue{} keeper.Spec.HashiCorpVault.Token = secretv1beta1.KeeperCredentialValue{}
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field) require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field)
@ -275,7 +261,6 @@ func TestValidateKeeper(t *testing.T) {
ValueFromConfig: "c", ValueFromConfig: "c",
} }
validator := ProvideKeeperValidator()
errs := validator.Validate(keeper, nil, admission.Create) errs := validator.Validate(keeper, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field) require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field)

@ -38,6 +38,13 @@ func (v *secureValueValidator) Validate(sv, oldSv *secretv1beta1.SecureValue, op
} }
// General validations. // General validations.
if sv.Name == "" {
errs = append(errs, field.Required(field.NewPath("metadata", "name"), "a `name` is required"))
}
if sv.Namespace == "" {
errs = append(errs, field.Required(field.NewPath("metadata", "namespace"), "a `namespace` is required"))
}
if sv.Spec.Value != nil && len(*sv.Spec.Value) > contracts.SECURE_VALUE_RAW_INPUT_MAX_SIZE_BYTES { if sv.Spec.Value != nil && len(*sv.Spec.Value) > contracts.SECURE_VALUE_RAW_INPUT_MAX_SIZE_BYTES {
errs = append( errs = append(
errs, errs,

@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/utils/ptr" "k8s.io/utils/ptr"
@ -14,9 +15,13 @@ import (
) )
func TestValidateSecureValue(t *testing.T) { func TestValidateSecureValue(t *testing.T) {
objectMeta := metav1.ObjectMeta{Name: "test", Namespace: "test"}
validator := ProvideSecureValueValidator()
t.Run("when creating a new securevalue", func(t *testing.T) { t.Run("when creating a new securevalue", func(t *testing.T) {
keeper := "keeper" keeper := "keeper"
validSecureValue := &secretv1beta1.SecureValue{ validSecureValue := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "description", Description: "description",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
@ -29,7 +34,6 @@ func TestValidateSecureValue(t *testing.T) {
sv := validSecureValue.DeepCopy() sv := validSecureValue.DeepCopy()
sv.Spec.Description = "" sv.Spec.Description = ""
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.description", errs[0].Field) require.Equal(t, "spec.description", errs[0].Field)
@ -41,7 +45,6 @@ func TestValidateSecureValue(t *testing.T) {
sv.Spec.Value = nil sv.Spec.Value = nil
sv.Spec.Ref = nil sv.Spec.Ref = nil
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -50,7 +53,6 @@ func TestValidateSecureValue(t *testing.T) {
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("")) sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue(""))
sv.Spec.Ref = nil sv.Spec.Ref = nil
validator = ProvideSecureValueValidator()
errs = validator.Validate(sv, nil, admission.Create) errs = validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -60,7 +62,6 @@ func TestValidateSecureValue(t *testing.T) {
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("value")) sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("value"))
sv.Spec.Ref = &ref sv.Spec.Ref = &ref
validator = ProvideSecureValueValidator()
errs = validator.Validate(sv, nil, admission.Create) errs = validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -71,7 +72,6 @@ func TestValidateSecureValue(t *testing.T) {
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue(strings.Repeat("a", contracts.SECURE_VALUE_RAW_INPUT_MAX_SIZE_BYTES+1))) sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue(strings.Repeat("a", contracts.SECURE_VALUE_RAW_INPUT_MAX_SIZE_BYTES+1)))
sv.Spec.Ref = nil sv.Spec.Ref = nil
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.value", errs[0].Field) require.Equal(t, "spec.value", errs[0].Field)
@ -81,6 +81,7 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("when updating a securevalue", func(t *testing.T) { t.Run("when updating a securevalue", func(t *testing.T) {
t.Run("when trying to switch from a `value` (old) to a `ref` (new), it returns an error", func(t *testing.T) { t.Run("when trying to switch from a `value` (old) to a `ref` (new), it returns an error", func(t *testing.T) {
oldSv := &secretv1beta1.SecureValue{ oldSv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Ref: nil, // empty `ref` means a `value` was present. Ref: nil, // empty `ref` means a `value` was present.
}, },
@ -88,6 +89,7 @@ func TestValidateSecureValue(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Ref: &ref, Ref: &ref,
}, },
@ -102,18 +104,19 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("when trying to switch from a `ref` (old) to a `value` (new), it returns an error", func(t *testing.T) { t.Run("when trying to switch from a `ref` (old) to a `value` (new), it returns an error", func(t *testing.T) {
ref := "non-empty" ref := "non-empty"
oldSv := &secretv1beta1.SecureValue{ oldSv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Ref: &ref, Ref: &ref,
}, },
} }
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, oldSv, admission.Update) errs := validator.Validate(sv, oldSv, admission.Update)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -122,6 +125,7 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("when both `value` and `ref` are set, it returns an error", func(t *testing.T) { t.Run("when both `value` and `ref` are set, it returns an error", func(t *testing.T) {
refNonEmpty := "non-empty" refNonEmpty := "non-empty"
oldSv := &secretv1beta1.SecureValue{ oldSv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Ref: &refNonEmpty, Ref: &refNonEmpty,
}, },
@ -129,24 +133,24 @@ func TestValidateSecureValue(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
Ref: &ref, Ref: &ref,
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, oldSv, admission.Update) errs := validator.Validate(sv, oldSv, admission.Update)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
oldSv = &secretv1beta1.SecureValue{ oldSv = &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Value: ptr.To(secretv1beta1.NewExposedSecureValue("non-empty")), Value: ptr.To(secretv1beta1.NewExposedSecureValue("non-empty")),
}, },
} }
validator = ProvideSecureValueValidator()
errs = validator.Validate(sv, oldSv, admission.Update) errs = validator.Validate(sv, oldSv, admission.Update)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -154,26 +158,26 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("when no changes are made, it returns no errors", func(t *testing.T) { t.Run("when no changes are made, it returns no errors", func(t *testing.T) {
oldSv := &secretv1beta1.SecureValue{ oldSv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "old-description", Description: "old-description",
}, },
} }
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "new-description", Description: "new-description",
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, oldSv, admission.Update) errs := validator.Validate(sv, oldSv, admission.Update)
require.Empty(t, errs) require.Empty(t, errs)
}) })
t.Run("when the old object is `nil` it returns an error", func(t *testing.T) { t.Run("when the old object is `nil` it returns an error", func(t *testing.T) {
sv := &secretv1beta1.SecureValue{} sv := &secretv1beta1.SecureValue{ObjectMeta: objectMeta}
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Update) errs := validator.Validate(sv, nil, admission.Update)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -183,18 +187,19 @@ func TestValidateSecureValue(t *testing.T) {
keeperA := "a-keeper" keeperA := "a-keeper"
keeperAnother := "another-keeper" keeperAnother := "another-keeper"
oldSv := &secretv1beta1.SecureValue{ oldSv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Keeper: &keeperA, Keeper: &keeperA,
}, },
} }
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Keeper: &keeperAnother, Keeper: &keeperAnother,
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, oldSv, admission.Update) errs := validator.Validate(sv, oldSv, admission.Update)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec", errs[0].Field) require.Equal(t, "spec", errs[0].Field)
@ -204,6 +209,7 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("`decrypters` must have unique items", func(t *testing.T) { t.Run("`decrypters` must have unique items", func(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "description", Ref: &ref, Description: "description", Ref: &ref,
@ -214,7 +220,6 @@ func TestValidateSecureValue(t *testing.T) {
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.decrypters.[1]", errs[0].Field) require.Equal(t, "spec.decrypters.[1]", errs[0].Field)
@ -223,6 +228,7 @@ func TestValidateSecureValue(t *testing.T) {
t.Run("`decrypters` list can be empty", func(t *testing.T) { t.Run("`decrypters` list can be empty", func(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "description", Ref: &ref, Description: "description", Ref: &ref,
@ -230,7 +236,6 @@ func TestValidateSecureValue(t *testing.T) {
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Empty(t, errs) require.Empty(t, errs)
}) })
@ -251,6 +256,7 @@ func TestValidateSecureValue(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "description", Ref: &ref, Description: "description", Ref: &ref,
@ -258,7 +264,6 @@ func TestValidateSecureValue(t *testing.T) {
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 3) require.Len(t, errs, 3)
}) })
@ -271,6 +276,7 @@ func TestValidateSecureValue(t *testing.T) {
ref := "ref" ref := "ref"
sv := &secretv1beta1.SecureValue{ sv := &secretv1beta1.SecureValue{
ObjectMeta: objectMeta,
Spec: secretv1beta1.SecureValueSpec{ Spec: secretv1beta1.SecureValueSpec{
Description: "description", Ref: &ref, Description: "description", Ref: &ref,
@ -278,7 +284,6 @@ func TestValidateSecureValue(t *testing.T) {
}, },
} }
validator := ProvideSecureValueValidator()
errs := validator.Validate(sv, nil, admission.Create) errs := validator.Validate(sv, nil, admission.Create)
require.Len(t, errs, 1) require.Len(t, errs, 1)
require.Equal(t, "spec.decrypters", errs[0].Field) require.Equal(t, "spec.decrypters", errs[0].Field)

Loading…
Cancel
Save