Просмотр исходного кода

test: lock down kubernetes v2 push transport semantics

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
Moritz Johner 2 месяцев назад
Родитель
Сommit
7b98789ca0

+ 56 - 0
providers/v2/adapter/store/client_test.go

@@ -21,6 +21,7 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	pb "github.com/external-secrets/external-secrets/proto/provider"
@@ -43,6 +44,7 @@ type fakeV2Provider struct {
 
 	pushSecretErr         error
 	pushSecretData        map[string][]byte
+	pushSecretSecret      *corev1.Secret
 	pushSecretPayload     *pb.PushSecretData
 	pushSecretProviderRef *pb.ProviderReference
 	pushSecretNamespace   string
@@ -89,6 +91,9 @@ func (f *fakeV2Provider) GetAllSecrets(_ context.Context, find esv1.ExternalSecr
 
 func (f *fakeV2Provider) PushSecret(_ context.Context, secretData map[string][]byte, pushSecretData *pb.PushSecretData, providerRef *pb.ProviderReference, sourceNamespace string) error {
 	f.pushSecretData = secretData
+	f.pushSecretSecret = &corev1.Secret{
+		Data: secretData,
+	}
 	f.pushSecretPayload = pushSecretData
 	f.pushSecretProviderRef = providerRef
 	f.pushSecretNamespace = sourceNamespace
@@ -297,6 +302,57 @@ func TestClientPushSecretConvertsPayloadAndMetadata(t *testing.T) {
 	}
 }
 
+func TestClientPushSecretForwardsKubernetesSecretMetadata(t *testing.T) {
+	providerRef := &pb.ProviderReference{Name: "provider", Namespace: "config-ns"}
+	provider := &fakeV2Provider{}
+	client := NewClient(provider, providerRef, "tenant-a")
+
+	metadata := []byte(`{"owner":"eso"}`)
+	secret := &corev1.Secret{
+		Type: corev1.SecretTypeDockerConfigJson,
+		ObjectMeta: metav1.ObjectMeta{
+			Labels:      map[string]string{"team": "platform"},
+			Annotations: map[string]string{"owner": "eso"},
+		},
+		Data: map[string][]byte{
+			".dockerconfigjson": []byte("payload"),
+		},
+	}
+	pushData := fakePushSecretData{
+		property:  "property",
+		secretKey: ".dockerconfigjson",
+		remoteKey: "remote/path",
+		metadata:  &apiextensionsv1.JSON{Raw: metadata},
+	}
+
+	err := client.PushSecret(context.Background(), secret, pushData)
+	if err != nil {
+		t.Fatalf("PushSecret() error = %v", err)
+	}
+
+	if provider.pushSecretSecret == nil {
+		t.Fatal("expected pushed secret to be recorded")
+	}
+	if provider.pushSecretSecret.Type != corev1.SecretTypeDockerConfigJson {
+		t.Errorf("expected secret type %q, got %q", corev1.SecretTypeDockerConfigJson, provider.pushSecretSecret.Type)
+	}
+	if got, want := provider.pushSecretSecret.Labels["team"], "platform"; got != want {
+		t.Errorf("expected secret label team=%q, got %q", want, got)
+	}
+	if got, want := provider.pushSecretSecret.Annotations["owner"], "eso"; got != want {
+		t.Errorf("expected secret annotation owner=%q, got %q", want, got)
+	}
+	if got, want := string(provider.pushSecretSecret.Data[".dockerconfigjson"]), "payload"; got != want {
+		t.Errorf("expected secret payload %q, got %q", want, got)
+	}
+	if provider.pushSecretPayload == nil {
+		t.Fatal("expected push payload to be recorded")
+	}
+	if string(provider.pushSecretPayload.Metadata) != string(metadata) {
+		t.Fatalf("unexpected metadata: %q", string(provider.pushSecretPayload.Metadata))
+	}
+}
+
 func TestClientDeleteSecretConvertsRemoteRef(t *testing.T) {
 	providerRef := &pb.ProviderReference{Name: "provider", Namespace: "config-ns"}
 	provider := &fakeV2Provider{}

+ 100 - 11
providers/v2/adapter/store/server_test.go

@@ -19,6 +19,7 @@ import (
 	"errors"
 	"testing"
 
+	"google.golang.org/protobuf/reflect/protoreflect"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -58,17 +59,17 @@ type fakeSecretsClient struct {
 	getAllSecretsErr      error
 	getAllSecretsFind     esv1.ExternalSecretFind
 
-	pushSecretErr     error
-	pushSecretSecret  *corev1.Secret
-	pushSecretData    esv1.PushSecretData
-	deleteSecretErr   error
-	deleteSecretRef   esv1.PushSecretRemoteRef
-	secretExistsResp  bool
-	secretExistsErr   error
-	secretExistsRef   esv1.PushSecretRemoteRef
-	validateResult    esv1.ValidationResult
-	validateErr       error
-	closeCalled       bool
+	pushSecretErr    error
+	pushSecretSecret *corev1.Secret
+	pushSecretData   esv1.PushSecretData
+	deleteSecretErr  error
+	deleteSecretRef  esv1.PushSecretRemoteRef
+	secretExistsResp bool
+	secretExistsErr  error
+	secretExistsRef  esv1.PushSecretRemoteRef
+	validateResult   esv1.ValidationResult
+	validateErr      error
+	closeCalled      bool
 }
 
 func (f *fakeSecretsClient) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
@@ -384,6 +385,70 @@ func TestServerPushDeleteAndExistsMapWriteRequests(t *testing.T) {
 	}
 }
 
+func TestServerPushSecretForwardsKubernetesSecretMetadata(t *testing.T) {
+	mapper := &specMapperRecorder{
+		spec: &esv1.SecretStoreSpec{Provider: &esv1.SecretStoreProvider{Fake: &esv1.FakeProvider{}}},
+	}
+	fakeClient := &fakeSecretsClient{}
+
+	server := NewServer(nil, ProviderMapping{
+		schema.GroupVersionKind{Group: "provider.external-secrets.io", Version: "v2alpha1", Kind: "Fake"}: &fakeProviderInterface{
+			caps: esv1.SecretStoreReadWrite,
+			newClient: func(context.Context, esv1.GenericStore, client.Client, string) (esv1.SecretsClient, error) {
+				return fakeClient, nil
+			},
+		},
+	}, mapper.mapRef)
+
+	req := &pb.PushSecretRequest{
+		ProviderRef: &pb.ProviderReference{
+			ApiVersion: "provider.external-secrets.io/v2alpha1",
+			Kind:       "Fake",
+			Name:       "backend",
+		},
+		SourceNamespace: "tenant-a",
+		SecretData: map[string][]byte{
+			".dockerconfigjson": []byte("payload"),
+		},
+		PushSecretData: &pb.PushSecretData{
+			RemoteKey: "remote/path",
+			SecretKey: ".dockerconfigjson",
+			Property:  "property",
+			Metadata:  []byte(`{"owner":"eso"}`),
+		},
+	}
+	if !setPushSecretRequestStringField(req, "secret_type", string(corev1.SecretTypeDockerConfigJson)) {
+		t.Errorf("push request is missing secret_type field")
+	}
+	if !setPushSecretRequestStringMapField(req, "secret_labels", map[string]string{"team": "platform"}) {
+		t.Errorf("push request is missing secret_labels field")
+	}
+	if !setPushSecretRequestStringMapField(req, "secret_annotations", map[string]string{"owner": "eso"}) {
+		t.Errorf("push request is missing secret_annotations field")
+	}
+
+	_, err := server.PushSecret(context.Background(), req)
+	if err != nil {
+		t.Fatalf("PushSecret() error = %v", err)
+	}
+
+	if fakeClient.pushSecretSecret == nil {
+		t.Fatal("expected pushed secret to be recorded")
+	}
+	if got, want := string(fakeClient.pushSecretSecret.Data[".dockerconfigjson"]), "payload"; got != want {
+		t.Errorf("expected payload %q, got %q", want, got)
+	}
+	if got, want := fakeClient.pushSecretSecret.Type, corev1.SecretTypeDockerConfigJson; got != want {
+		t.Errorf("expected secret type %q, got %q", want, got)
+	}
+	if got, want := fakeClient.pushSecretSecret.Labels["team"], "platform"; got != want {
+		t.Errorf("expected secret label team=%q, got %q", want, got)
+	}
+	if got, want := fakeClient.pushSecretSecret.Annotations["owner"], "eso"; got != want {
+		t.Errorf("expected secret annotation owner=%q, got %q", want, got)
+	}
+}
+
 func TestServerValidateMapsReadyUnknownAndErrorResults(t *testing.T) {
 	t.Run("ready", func(t *testing.T) {
 		resp := runValidateTest(t, esv1.ValidationResultReady, nil)
@@ -548,3 +613,27 @@ func runValidateTest(t *testing.T, result esv1.ValidationResult, validateErr err
 	}
 	return resp
 }
+
+func setPushSecretRequestStringField(req *pb.PushSecretRequest, fieldName protoreflect.Name, value string) bool {
+	msg := req.ProtoReflect()
+	field := msg.Descriptor().Fields().ByName(fieldName)
+	if field == nil {
+		return false
+	}
+	msg.Set(field, protoreflect.ValueOfString(value))
+	return true
+}
+
+func setPushSecretRequestStringMapField(req *pb.PushSecretRequest, fieldName protoreflect.Name, values map[string]string) bool {
+	msg := req.ProtoReflect()
+	field := msg.Descriptor().Fields().ByName(fieldName)
+	if field == nil {
+		return false
+	}
+
+	fieldMap := msg.Mutable(field).Map()
+	for key, value := range values {
+		fieldMap.Set(protoreflect.ValueOfString(key).MapKey(), protoreflect.ValueOfString(value))
+	}
+	return true
+}

+ 77 - 0
providers/v2/common/grpc/client_test.go

@@ -22,6 +22,7 @@ import (
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/test/bufconn"
+	"google.golang.org/protobuf/reflect/protoreflect"
 
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	pb "github.com/external-secrets/external-secrets/proto/provider"
@@ -319,6 +320,58 @@ func TestClientPushDeleteExistsAndCapabilitiesSendProviderReferenceAndNamespace(
 	assertProviderRefEqual(t, mock.capabilitiesRequest.ProviderRef, providerRef)
 }
 
+func TestClientPushSecretSendsExpandedKubernetesSecretFields(t *testing.T) {
+	mock := &mockServer{}
+	conn, cleanup := setupTestServer(t, mock)
+	defer cleanup()
+
+	client := NewClientWithConn(conn)
+	providerRef := &pb.ProviderReference{Name: "provider", Namespace: "config-ns"}
+
+	err := client.PushSecret(context.Background(), map[string][]byte{
+		".dockerconfigjson": []byte("payload"),
+	}, &pb.PushSecretData{
+		RemoteKey: "remote/path",
+		SecretKey: ".dockerconfigjson",
+		Property:  "property",
+		Metadata:  []byte(`{"owner":"eso"}`),
+	}, providerRef, "tenant-a")
+	if err != nil {
+		t.Fatalf("PushSecret failed: %v", err)
+	}
+	if mock.pushSecretRequest == nil {
+		t.Fatal("expected push secret request to be recorded")
+	}
+	if got, want := string(mock.pushSecretRequest.SecretData[".dockerconfigjson"]), "payload"; got != want {
+		t.Errorf("expected request secret data %q, got %q", want, got)
+	}
+	assertProviderRefEqual(t, mock.pushSecretRequest.ProviderRef, providerRef)
+	if got, want := mock.pushSecretRequest.SourceNamespace, "tenant-a"; got != want {
+		t.Errorf("expected source namespace %q, got %q", want, got)
+	}
+
+	secretType, ok := getPushSecretRequestStringField(mock.pushSecretRequest, "secret_type")
+	if !ok {
+		t.Errorf("push request is missing secret_type field")
+	} else if want := "kubernetes.io/dockerconfigjson"; secretType != want {
+		t.Errorf("expected secret_type=%q, got %q", want, secretType)
+	}
+
+	labels, ok := getPushSecretRequestStringMapField(mock.pushSecretRequest, "secret_labels")
+	if !ok {
+		t.Errorf("push request is missing secret_labels field")
+	} else if got, want := labels["team"], "platform"; got != want {
+		t.Errorf("expected secret_labels.team=%q, got %q", want, got)
+	}
+
+	annotations, ok := getPushSecretRequestStringMapField(mock.pushSecretRequest, "secret_annotations")
+	if !ok {
+		t.Errorf("push request is missing secret_annotations field")
+	} else if got, want := annotations["owner"], "eso"; got != want {
+		t.Errorf("expected secret_annotations.owner=%q, got %q", want, got)
+	}
+}
+
 func TestClientValidate(t *testing.T) {
 	t.Run("success", func(t *testing.T) {
 		mock := &mockServer{}
@@ -392,3 +445,27 @@ func assertProviderRefEqual(t *testing.T, got, want *pb.ProviderReference) {
 		t.Fatalf("unexpected provider ref: got=%#v want=%#v", got, want)
 	}
 }
+
+func getPushSecretRequestStringField(req *pb.PushSecretRequest, fieldName protoreflect.Name) (string, bool) {
+	msg := req.ProtoReflect()
+	field := msg.Descriptor().Fields().ByName(fieldName)
+	if field == nil {
+		return "", false
+	}
+	return msg.Get(field).String(), true
+}
+
+func getPushSecretRequestStringMapField(req *pb.PushSecretRequest, fieldName protoreflect.Name) (map[string]string, bool) {
+	msg := req.ProtoReflect()
+	field := msg.Descriptor().Fields().ByName(fieldName)
+	if field == nil {
+		return nil, false
+	}
+
+	values := map[string]string{}
+	msg.Get(field).Map().Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
+		values[key.String()] = value.String()
+		return true
+	})
+	return values, true
+}