1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Support MetadataPolicy=Fetch for GCP Secrets Manager (#2111)

* Support MetadataPolicy=Fetch for GCP Secrets Manager

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>

* Use '.' instead of '/' to split metadata

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>

* Support annotations/labels

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>

---------

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>
This commit is contained in:
Shuhei Kitagawa 2023-03-21 06:27:59 +09:00 committed by GitHub
parent ee13e61645
commit 07f237e071
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 280 additions and 0 deletions

View file

@ -336,6 +336,10 @@ func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
return nil, fmt.Errorf(errUninitalizedGCPProvider)
}
if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
return c.getSecretMetadata(ctx, ref)
}
version := ref.Version
if version == "" {
version = defaultVersion
@ -378,6 +382,80 @@ func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
return []byte(val.String()), nil
}
func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
secret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
Name: fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, ref.Key),
})
err = parseError(err)
if err != nil {
return nil, fmt.Errorf(errClientGetSecretAccess, err)
}
const (
annotations = "annotations"
labels = "labels"
)
extractMetadataKey := func(s string, p string) string {
prefix := p + "."
if !strings.HasPrefix(s, prefix) {
return ""
}
return strings.TrimPrefix(s, prefix)
}
if annotation := extractMetadataKey(ref.Property, annotations); annotation != "" {
v, ok := secret.GetAnnotations()[annotation]
if !ok {
return nil, fmt.Errorf("annotation with key %s does not exist in secret %s", annotation, ref.Key)
}
return []byte(v), nil
}
if label := extractMetadataKey(ref.Property, labels); label != "" {
v, ok := secret.GetLabels()[label]
if !ok {
return nil, fmt.Errorf("label with key %s does not exist in secret %s", label, ref.Key)
}
return []byte(v), nil
}
if ref.Property == annotations {
j, err := json.Marshal(secret.GetAnnotations())
if err != nil {
return nil, fmt.Errorf("faild marshaling annotations into json: %w", err)
}
return j, nil
}
if ref.Property == labels {
j, err := json.Marshal(secret.GetLabels())
if err != nil {
return nil, fmt.Errorf("faild marshaling labels into json: %w", err)
}
return j, nil
}
if ref.Property != "" {
return nil, fmt.Errorf("invalid property %s: metadata property should start with either %s or %s", ref.Property, annotations, labels)
}
j, err := json.Marshal(map[string]map[string]string{
"annotations": secret.GetAnnotations(),
"labels": secret.GetLabels(),
})
if err != nil {
return nil, fmt.Errorf("faild marshaling metadata map into json: %w", err)
}
return j, nil
}
// GetSecretMap returns multiple k/v pairs from the provider.
func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if c.smClient == nil || c.store.ProjectID == "" {

View file

@ -193,6 +193,207 @@ func TestSecretManagerGetSecret(t *testing.T) {
}
}
func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
tests := []struct {
name string
ref esv1beta1.ExternalSecretDataRemoteRef
getSecretMockReturn fakesm.GetSecretMockReturn
expectedSecret string
expectedErr string
}{
{
name: "annotation is specified",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "annotations.managed-by",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Annotations: map[string]string{
"managed-by": "external-secrets",
},
},
Err: nil,
},
expectedSecret: "external-secrets",
},
{
name: "label is specified",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "labels.managed-by",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Labels: map[string]string{
"managed-by": "external-secrets",
},
},
Err: nil,
},
expectedSecret: "external-secrets",
},
{
name: "annotations is specified",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "annotations",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Annotations: map[string]string{
"annotationKey1": "annotationValue1",
"annotationKey2": "annotationValue2",
},
Labels: map[string]string{
"labelKey1": "labelValue1",
"labelKey2": "labelValue2",
},
},
Err: nil,
},
expectedSecret: `{"annotationKey1":"annotationValue1","annotationKey2":"annotationValue2"}`,
},
{
name: "labels is specified",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "labels",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Annotations: map[string]string{
"annotationKey1": "annotationValue1",
"annotationKey2": "annotationValue2",
},
Labels: map[string]string{
"labelKey1": "labelValue1",
"labelKey2": "labelValue2",
},
},
Err: nil,
},
expectedSecret: `{"labelKey1":"labelValue1","labelKey2":"labelValue2"}`,
},
{
name: "no property is specified",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Labels: map[string]string{
"label-key": "label-value",
},
Annotations: map[string]string{
"annotation-key": "annotation-value",
},
},
Err: nil,
},
expectedSecret: `{"annotations":{"annotation-key":"annotation-value"},"labels":{"label-key":"label-value"}}`,
},
{
name: "annotation does not exist",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "annotations.unknown",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Annotations: map[string]string{
"managed-by": "external-secrets",
},
},
Err: nil,
},
expectedErr: "annotation with key unknown does not exist in secret bar",
},
{
name: "label does not exist",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "labels.unknown",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Labels: map[string]string{
"managed-by": "external-secrets",
},
},
Err: nil,
},
expectedErr: "label with key unknown does not exist in secret bar",
},
{
name: "invalid property",
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "bar",
MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
Property: "invalid.managed-by",
},
getSecretMockReturn: fakesm.GetSecretMockReturn{
Secret: &secretmanagerpb.Secret{
Name: "projects/foo/secret/bar",
Labels: map[string]string{
"managed-by": "external-secrets",
},
},
Err: nil,
},
expectedErr: "invalid property invalid.managed-by",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
smClient := fakesm.MockSMClient{}
smClient.NewGetSecretFn(tc.getSecretMockReturn)
client := Client{
smClient: &smClient,
store: &esv1beta1.GCPSMProvider{
ProjectID: "foo",
},
}
got, err := client.GetSecret(context.TODO(), tc.ref)
if tc.expectedErr != "" {
if err == nil {
t.Fatalf("expected to receive an error but got nit")
}
if !ErrorContains(err, tc.expectedErr) {
t.Fatalf("unexpected error: %s, expected: '%s'", err.Error(), tc.expectedErr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if gotStr := string(got); gotStr != tc.expectedSecret {
t.Fatalf("unexpected secret: expected %s, got %s", tc.expectedSecret, gotStr)
}
})
}
}
type fakeRef struct {
key string
}
@ -309,6 +510,7 @@ func TestDeleteSecret(t *testing.T) {
})
}
}
func TestSetSecret(t *testing.T) {
ref := fakeRef{key: "/baz"}