Browse Source

feat: add path to cloud.ru provider (#5952)

Co-authored-by: aoboyarskiy <aoboyarskiy@cloud.ru>
heavyandrew 3 weeks ago
parent
commit
395e4e3a10

+ 80 - 0
docs/provider/cloudru.md

@@ -191,3 +191,83 @@ curl --location 'https://secretmanager.api.cloud.ru/v1/secrets' \
             name:
             name:
               regexp: "my.*secret"
               regexp: "my.*secret"
     ```
     ```
+  * Search the secrets by path:
+    ```yaml
+    apiVersion: external-secrets.io/v1
+    kind: ExternalSecret
+    metadata:
+      name: csm-ext-secret
+    spec:
+      refreshInterval: 10s
+      secretStoreRef:
+        name: csm
+        kind: SecretStore
+      target:
+        name: my-awesome-secret
+        creationPolicy: Owner
+      dataFrom:
+        - find: # Get all secrets from the specified path
+            path: "oss/snmp-auths"
+    ```
+  * Search the secrets by path with name filter:
+    ```yaml
+    apiVersion: external-secrets.io/v1
+    kind: ExternalSecret
+    metadata:
+      name: csm-ext-secret
+    spec:
+      refreshInterval: 10s
+      secretStoreRef:
+        name: csm
+        kind: SecretStore
+      target:
+        name: my-awesome-secret
+        creationPolicy: Owner
+      dataFrom:
+        - find: # Get secrets from path matching the name pattern
+            path: "oss/snmp-auths"
+            name:
+              regexp: ".*"
+    ```
+  * Search the secrets by path with tags:
+    ```yaml
+    apiVersion: external-secrets.io/v1
+    kind: ExternalSecret
+    metadata:
+      name: csm-ext-secret
+    spec:
+      refreshInterval: 10s
+      secretStoreRef:
+        name: csm
+        kind: SecretStore
+      target:
+        name: my-awesome-secret
+        creationPolicy: Owner
+      dataFrom:
+        - find: # Get secrets from path with specific tags
+            path: "oss/snmp-auths"
+            tags:
+              env: production
+    ```
+  * Search the secrets by path with name and tags:
+    ```yaml
+    apiVersion: external-secrets.io/v1
+    kind: ExternalSecret
+    metadata:
+      name: csm-ext-secret
+    spec:
+      refreshInterval: 10s
+      secretStoreRef:
+        name: csm
+        kind: SecretStore
+      target:
+        name: my-awesome-secret
+        creationPolicy: Owner
+      dataFrom:
+        - find: # Get secrets from path matching name pattern and tags
+            path: "oss/snmp-auths"
+            name:
+              regexp: "auth.*"
+            tags:
+              env: production
+    ```

+ 4 - 0
providers/v1/cloudru/secretmanager/adapter/csm_client.go

@@ -60,6 +60,7 @@ type ListSecretsRequest struct {
 	Labels    map[string]string
 	Labels    map[string]string
 	NameExact string
 	NameExact string
 	NameRegex string
 	NameRegex string
+	Path      string
 }
 }
 
 
 // Credentials holds the keyID and secret for the CSM client.
 // Credentials holds the keyID and secret for the CSM client.
@@ -99,6 +100,9 @@ func (c *APIClient) ListSecrets(ctx context.Context, req *ListSecretsRequest) ([
 	case req.NameRegex != "":
 	case req.NameRegex != "":
 		searchReq.Name = &smsV2.SearchSecretRequest_Regex{Regex: req.NameRegex}
 		searchReq.Name = &smsV2.SearchSecretRequest_Regex{Regex: req.NameRegex}
 	}
 	}
+	if req.Path != "" {
+		searchReq.Path = req.Path
+	}
 
 
 	var err error
 	var err error
 	ctx, err = c.authCtx(ctx)
 	ctx, err = c.authCtx(ctx)

+ 6 - 3
providers/v1/cloudru/secretmanager/client.go

@@ -107,10 +107,10 @@ func (c *Client) GetSecretMap(ctx context.Context, ref esv1.ExternalSecretDataRe
 	return out, nil
 	return out, nil
 }
 }
 
 
-// GetAllSecrets gets all secrets by the remote reference.
+// GetAllSecrets returns all secrets matching the find criteria (path, name, tags).
 func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
 func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind) (map[string][]byte, error) {
-	if len(ref.Tags) == 0 && ref.Name == nil {
-		return nil, fmt.Errorf("at least one of the following fields must be set: tags, name")
+	if len(ref.Tags) == 0 && ref.Name == nil && ref.Path == nil {
+		return nil, fmt.Errorf("at least one of the following fields must be set: tags, name, path")
 	}
 	}
 
 
 	var nameFilter string
 	var nameFilter string
@@ -123,6 +123,9 @@ func (c *Client) GetAllSecrets(ctx context.Context, ref esv1.ExternalSecretFind)
 		Labels:    ref.Tags,
 		Labels:    ref.Tags,
 		NameRegex: nameFilter,
 		NameRegex: nameFilter,
 	}
 	}
+	if ref.Path != nil {
+		searchReq.Path = *ref.Path
+	}
 	secrets, err := c.apiClient.ListSecrets(ctx, searchReq)
 	secrets, err := c.apiClient.ListSecrets(ctx, searchReq)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("failed to list secrets: %w", err)
 		return nil, fmt.Errorf("failed to list secrets: %w", err)

+ 20 - 1
providers/v1/cloudru/secretmanager/client_test.go

@@ -298,10 +298,29 @@ func TestClientGetAllSecrets(t *testing.T) {
 			wantErr: nil,
 			wantErr: nil,
 		},
 		},
 		{
 		{
+			name: "success_path_only",
+			ref: esv1.ExternalSecretFind{
+				Path: func() *string {
+					s := "oss/snmp-auths"
+					return &s
+				}(),
+			},
+			setup: func(mock *fake.MockSecretProvider) {
+				mock.MockListSecrets([]*smsV2.Secret{
+					{Id: keyID, Name: "secret1", Path: "oss/snmp-auths/secret1"},
+				}, nil)
+				mock.MockAccessSecretVersion([]byte(`value1`), nil)
+			},
+			wantPayload: map[string][]byte{
+				"oss/snmp-auths/secret1": []byte(`value1`),
+			},
+			wantErr: nil,
+		},
+		{
 			name:        "error_no_filters",
 			name:        "error_no_filters",
 			ref:         esv1.ExternalSecretFind{},
 			ref:         esv1.ExternalSecretFind{},
 			wantPayload: nil,
 			wantPayload: nil,
-			wantErr:     errors.New("at least one of the following fields must be set: tags, name"),
+			wantErr:     errors.New("at least one of the following fields must be set: tags, name, path"),
 		},
 		},
 		{
 		{
 			name: "error_list_secrets",
 			name: "error_list_secrets",