Browse Source

add allowEmptyResponse to vaultdynamicsecrets (#4271)

Signed-off-by: Kyaak <kyaak.dev@gmail.com>
Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Martin Schwamberger 1 year ago
parent
commit
e7c89f68bb

+ 5 - 0
apis/generators/v1alpha1/types_vault.go

@@ -50,6 +50,11 @@ type VaultDynamicSecretSpec struct {
 
 	// Vault path to obtain the dynamic secret from
 	Path string `json:"path"`
+
+	// Do not fail if no secrets are found. Useful for requests where no data is expected.
+	// +optional
+	// +kubebuilder:default=false
+	AllowEmptyResponse bool `json:"allowEmptyResponse,omitempty"`
 }
 
 // +kubebuilder:validation:Enum=Data;Auth

+ 5 - 0
config/crds/bases/generators.external-secrets.io_clustergenerators.yaml

@@ -735,6 +735,11 @@ spec:
                     type: object
                   vaultDynamicSecretSpec:
                     properties:
+                      allowEmptyResponse:
+                        default: false
+                        description: Do not fail if no secrets are found. Useful for
+                          requests where no data is expected.
+                        type: boolean
                       controller:
                         description: |-
                           Used to select the correct ESO controller (think: ingress.ingressClassName)

+ 5 - 0
config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml

@@ -41,6 +41,11 @@ spec:
             type: object
           spec:
             properties:
+              allowEmptyResponse:
+                default: false
+                description: Do not fail if no secrets are found. Useful for requests
+                  where no data is expected.
+                type: boolean
               controller:
                 description: |-
                   Used to select the correct ESO controller (think: ingress.ingressClassName)

+ 8 - 0
deploy/crds/bundle.yaml

@@ -14541,6 +14541,10 @@ spec:
                       type: object
                     vaultDynamicSecretSpec:
                       properties:
+                        allowEmptyResponse:
+                          default: false
+                          description: Do not fail if no secrets are found. Useful for requests where no data is expected.
+                          type: boolean
                         controller:
                           description: |-
                             Used to select the correct ESO controller (think: ingress.ingressClassName)
@@ -16444,6 +16448,10 @@ spec:
               type: object
             spec:
               properties:
+                allowEmptyResponse:
+                  default: false
+                  description: Do not fail if no secrets are found. Useful for requests where no data is expected.
+                  type: boolean
                 controller:
                   description: |-
                     Used to select the correct ESO controller (think: ingress.ingressClassName)

+ 5 - 0
pkg/generator/vault/vault.go

@@ -81,6 +81,11 @@ func (g *Generator) generate(ctx context.Context, c *provider.Provider, jsonSpec
 	if err != nil {
 		return nil, err
 	}
+
+	if result == nil && res.Spec.AllowEmptyResponse {
+		return nil, nil
+	}
+
 	if result == nil {
 		return nil, fmt.Errorf(errGetSecret, errors.New("empty response from Vault"))
 	}

+ 78 - 2
pkg/generator/vault/vault_test.go

@@ -161,6 +161,80 @@ spec:
 				err: errors.New("unable to get dynamic secret: empty response from Vault"),
 			},
 		},
+		"AllowEmptyVaultPOST": {
+			reason: "Allow empty response from Vault POST.",
+			args: args{
+				corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: VaultDynamicSecret
+spec:
+  provider:
+    auth:
+      kubernetes:
+        role: test
+        serviceAccountRef:
+          name: "testing"
+  method: POST
+  parameters:
+    foo: "bar"
+  path: "github/token/example"
+  allowEmptyResponse: true`),
+				},
+				kube: clientfake.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "testing",
+						Namespace: "testing",
+					},
+					Secrets: []corev1.ObjectReference{
+						{
+							Name: "test",
+						},
+					},
+				}).Build(),
+			},
+			want: want{
+				err: nil,
+				val: nil,
+			},
+		},
+		"AllowEmptyVaultGET": {
+			reason: "Allow empty response from Vault GET.",
+			args: args{
+				corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
+				jsonSpec: &apiextensions.JSON{
+					Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: VaultDynamicSecret
+spec:
+  provider:
+    auth:
+      kubernetes:
+        role: test
+        serviceAccountRef:
+          name: "testing"
+  method: GET
+  parameters:
+    foo: "bar"
+  path: "github/token/example"
+  allowEmptyResponse: true`),
+				},
+				kube: clientfake.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "testing",
+						Namespace: "testing",
+					},
+					Secrets: []corev1.ObjectReference{
+						{
+							Name: "test",
+						},
+					},
+				}).Build(),
+			},
+			want: want{
+				err: nil,
+				val: nil,
+			},
+		},
 	}
 
 	for name, tc := range cases {
@@ -168,8 +242,10 @@ spec:
 			c := &provider.Provider{NewVaultClient: fake.ClientWithLoginMock}
 			gen := &Generator{}
 			val, err := gen.generate(context.Background(), c, tc.args.jsonSpec, tc.args.kube, tc.args.corev1, "testing")
-			if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
-				t.Errorf("\n%s\nvault.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
+			if err != nil || tc.want.err != nil {
+				if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
+					t.Errorf("\n%s\nvault.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
+				}
 			}
 			if diff := cmp.Diff(tc.want.val, val); diff != "" {
 				t.Errorf("\n%s\nvault.GetSecret(...): -want val, +got val:\n%s", tc.reason, diff)