Skip to content

Commit 769efdc

Browse files
authored
✨ Feature/deletion policies (external-secrets#1914)
Signed-off-by: Gustavo Carvalho <gusfcarvalho@gmail.com>
1 parent 709db58 commit 769efdc

File tree

7 files changed

+102
-20
lines changed

7 files changed

+102
-20
lines changed

docs/introduction/stability-support.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,24 @@ The following table describes the stability level of each provider and who's res
4444

4545
The following table show the support for features across different providers.
4646

47-
| Provider | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret |
48-
|---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: |
49-
| AWS Secrets Manager | x | x | | x | x | |
50-
| AWS Parameter Store | x | x | | x | x | |
51-
| Hashicorp Vault | x | x | | x | x | |
52-
| GCP Secret Manager | x | x | | x | x | |
53-
| Azure Keyvault | x | x | x | x | x | x |
54-
| Kubernetes | x | x | | x | x | |
55-
| IBM Cloud Secrets Manager | | | | | x | |
56-
| Yandex Lockbox | | | | | x | |
57-
| Gitlab Variables | x | x | | | x | |
58-
| Alibaba Cloud KMS | | | | | x | |
59-
| Oracle Vault | | | | | x | |
60-
| Akeyless | | | | | x | |
61-
| 1Password | x | | | | x | |
62-
| Generic Webhook | | | | | | |
63-
| senhasegura DSM | | | | | x | |
64-
| Doppler | x | | | | x | |
47+
| Provider | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret | DeletionPolicy Merge/Delete |
48+
|---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: | :-------------------------:
49+
| AWS Secrets Manager | x | x | | x | x | x | x |
50+
| AWS Parameter Store | x | x | | x | x | x | x |
51+
| Hashicorp Vault | x | x | | x | x | x | x |
52+
| GCP Secret Manager | x | x | | x | x | x | x |
53+
| Azure Keyvault | x | x | x | x | x | x | x |
54+
| Kubernetes | x | x | | x | x | x | x |
55+
| IBM Cloud Secrets Manager | | | | | x | | |
56+
| Yandex Lockbox | | | | | x | | |
57+
| Gitlab Variables | x | x | | | x | | |
58+
| Alibaba Cloud KMS | | | | | x | | |
59+
| Oracle Vault | | | | | x | | |
60+
| Akeyless | | | | | x | | |
61+
| 1Password | x | | | | x | | |
62+
| Generic Webhook | | | | | | | |
63+
| senhasegura DSM | | | | | x | | |
64+
| Doppler | x | | | | x | | |
6565

6666

6767
## Support Policy

pkg/provider/azure/keyvault/keyvault.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ func (a *Azure) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretF
489489
checkName := ref.Name != nil && len(ref.Name.RegExp) > 0
490490

491491
secretListIter, err := basicClient.GetSecretsComplete(context.Background(), *a.provider.VaultURL, nil)
492+
err = parseError(err)
492493
if err != nil {
493494
return nil, err
494495
}
@@ -502,6 +503,7 @@ func (a *Azure) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretF
502503
}
503504

504505
secretResp, err := basicClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, "")
506+
err = parseError(err)
505507
if err != nil {
506508
return nil, err
507509
}
@@ -568,6 +570,14 @@ func getProperty(secret, property, key string) ([]byte, error) {
568570
return []byte(res.String()), nil
569571
}
570572

573+
func parseError(err error) error {
574+
aerr := autorest.DetailedError{}
575+
if errors.As(err, &aerr) && aerr.StatusCode == 404 {
576+
return esv1beta1.NoSecretError{}
577+
}
578+
return err
579+
}
580+
571581
// Implements store.Client.GetSecret Interface.
572582
// Retrieves a secret/Key/Certificate/Tag with the secret name defined in ref.Name
573583
// The Object Type is defined as a prefix in the ref.Name , if no prefix is defined , we assume a secret is required.
@@ -579,6 +589,7 @@ func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataR
579589
// returns a SecretBundle with the secret value
580590
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#SecretBundle
581591
secretResp, err := a.baseClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
592+
err = parseError(err)
582593
if err != nil {
583594
return nil, err
584595
}
@@ -590,6 +601,7 @@ func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataR
590601
// returns a CertBundle. We return CER contents of x509 certificate
591602
// see: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#CertificateBundle
592603
certResp, err := a.baseClient.GetCertificate(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
604+
err = parseError(err)
593605
if err != nil {
594606
return nil, err
595607
}
@@ -602,6 +614,7 @@ func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataR
602614
// azure kv returns only public keys
603615
// see: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#KeyBundle
604616
keyResp, err := a.baseClient.GetKey(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
617+
err = parseError(err)
605618
if err != nil {
606619
return nil, err
607620
}
@@ -618,7 +631,7 @@ func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataR
618631
func (a *Azure) getSecretTags(ref esv1beta1.ExternalSecretDataRemoteRef) (map[string]*string, error) {
619632
_, secretName := getObjType(ref)
620633
secretResp, err := a.baseClient.GetSecret(context.Background(), *a.provider.VaultURL, secretName, ref.Version)
621-
634+
err = parseError(err)
622635
if err != nil {
623636
return nil, err
624637
}

pkg/provider/azure/keyvault/keyvault_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,26 @@ func TestAzureKeyVaultSecretManagerGetSecret(t *testing.T) {
763763
Value: &secretString,
764764
}
765765
}
766+
// good case
767+
secretNotFound := func(smtc *secretManagerTestCase) {
768+
smtc.expectedSecret = ""
769+
smtc.apiErr = autorest.DetailedError{StatusCode: 404}
770+
smtc.expectError = esv1beta1.NoSecretError{}.Error()
771+
}
772+
773+
certNotFound := func(smtc *secretManagerTestCase) {
774+
smtc.expectedSecret = ""
775+
smtc.secretName = certName
776+
smtc.apiErr = autorest.DetailedError{StatusCode: 404}
777+
smtc.expectError = esv1beta1.NoSecretError{}.Error()
778+
}
779+
780+
keyNotFound := func(smtc *secretManagerTestCase) {
781+
smtc.expectedSecret = ""
782+
smtc.secretName = keyName
783+
smtc.apiErr = autorest.DetailedError{StatusCode: 404}
784+
smtc.expectError = esv1beta1.NoSecretError{}.Error()
785+
}
766786

767787
setSecretStringWithVersion := func(smtc *secretManagerTestCase) {
768788
smtc.expectedSecret = secretString
@@ -1062,6 +1082,9 @@ func TestAzureKeyVaultSecretManagerGetSecret(t *testing.T) {
10621082
makeValidSecretManagerTestCaseCustom(badSecretWithProperty),
10631083
makeValidSecretManagerTestCaseCustom(setPubRSAKey),
10641084
makeValidSecretManagerTestCaseCustom(setPubECKey),
1085+
makeValidSecretManagerTestCaseCustom(secretNotFound),
1086+
makeValidSecretManagerTestCaseCustom(certNotFound),
1087+
makeValidSecretManagerTestCaseCustom(keyNotFound),
10651088
makeValidSecretManagerTestCaseCustom(setCertificate),
10661089
makeValidSecretManagerTestCaseCustom(badSecretType),
10671090
makeValidSecretManagerTestCaseCustom(setSecretWithTag),

pkg/provider/gcp/secretmanager/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushRemot
114114
return c.smClient.DeleteSecret(ctx, deleteSecretVersionReq)
115115
}
116116

117+
func parseError(err error) error {
118+
var gerr *apierror.APIError
119+
if errors.As(err, &gerr) && gerr.GRPCStatus().Code() == codes.NotFound {
120+
return esv1beta1.NoSecretError{}
121+
}
122+
return err
123+
}
124+
117125
// PushSecret pushes a kubernetes secret key into gcp provider Secret.
118126
func (c *Client) PushSecret(ctx context.Context, payload []byte, remoteRef esv1beta1.PushRemoteRef) error {
119127
createSecretReq := &secretmanagerpb.CreateSecretRequest{
@@ -325,6 +333,7 @@ func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
325333
Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", c.store.ProjectID, ref.Key, version),
326334
}
327335
result, err := c.smClient.AccessSecretVersion(ctx, req)
336+
err = parseError(err)
328337
if err != nil {
329338
return nil, fmt.Errorf(errClientGetSecretAccess, err)
330339
}

pkg/provider/gcp/secretmanager/client_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ func TestSecretManagerGetSecret(t *testing.T) {
113113
smtc.apiOutput.Payload.Data = []byte("testtesttest")
114114
smtc.expectedSecret = "testtesttest"
115115
}
116+
secretNotFound := func(smtc *secretManagerTestCase) {
117+
fErr := status.Error(codes.NotFound, "failed")
118+
notFoundError, _ := apierror.FromError(fErr)
119+
smtc.apiErr = notFoundError
120+
smtc.expectedSecret = ""
121+
smtc.expectError = esv1beta1.NoSecretErr.Error()
122+
}
116123
// good case: with a dot in the key name
117124
setDotRef := func(smtc *secretManagerTestCase) {
118125
smtc.ref = &esv1beta1.ExternalSecretDataRemoteRef{
@@ -164,6 +171,7 @@ func TestSecretManagerGetSecret(t *testing.T) {
164171
successCases := []*secretManagerTestCase{
165172
makeValidSecretManagerTestCase(),
166173
makeValidSecretManagerTestCaseCustom(setSecretString),
174+
makeValidSecretManagerTestCaseCustom(secretNotFound),
167175
makeValidSecretManagerTestCaseCustom(setCustomVersion),
168176
makeValidSecretManagerTestCaseCustom(setAPIErr),
169177
makeValidSecretManagerTestCaseCustom(setCustomRef),

pkg/provider/kubernetes/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/json"
1919
"fmt"
2020

21+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223
labels "k8s.io/apimachinery/pkg/labels"
2324

@@ -60,6 +61,9 @@ func (c *Client) PushSecret(ctx context.Context, value []byte, remoteRef esv1bet
6061

6162
func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
6263
secret, err := c.userSecretClient.Get(ctx, ref.Key, metav1.GetOptions{})
64+
if apierrors.IsNotFound(err) {
65+
return nil, esv1beta1.NoSecretError{}
66+
}
6367
if err != nil {
6468
return nil, err
6569
}

pkg/provider/kubernetes/client_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import (
2121

2222
"github.com/stretchr/testify/assert"
2323
corev1 "k8s.io/api/core/v1"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/runtime/schema"
2527

2628
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
2729
)
@@ -34,6 +36,7 @@ type fakeClient struct {
3436
t *testing.T
3537
secretMap map[string]corev1.Secret
3638
expectedListOptions metav1.ListOptions
39+
err error
3740
}
3841

3942
func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error) {
@@ -42,7 +45,7 @@ func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOption
4245
if !ok {
4346
return nil, errors.New(errSomethingWentWrong)
4447
}
45-
return &secret, nil
48+
return &secret, fk.err
4649
}
4750

4851
func (fk fakeClient) List(ctx context.Context, opts metav1.ListOptions) (*corev1.SecretList, error) {
@@ -68,6 +71,28 @@ func TestGetSecret(t *testing.T) {
6871
want []byte
6972
wantErr bool
7073
}{
74+
{
75+
name: "secretNotFound",
76+
fields: fields{
77+
Client: fakeClient{
78+
t: t,
79+
secretMap: map[string]corev1.Secret{
80+
"mysec": {
81+
Data: map[string][]byte{
82+
"token": []byte(`foobar`),
83+
},
84+
},
85+
},
86+
err: apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret"),
87+
},
88+
Namespace: "default",
89+
},
90+
ref: esv1beta1.ExternalSecretDataRemoteRef{
91+
Key: "mysec",
92+
Property: "token",
93+
},
94+
wantErr: true,
95+
},
7196
{
7297
name: "err GetSecretMap",
7398
fields: fields{

0 commit comments

Comments
 (0)