mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
* Add PushSecret UpdatePolicy Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Adjust description of UpdatePolicy in PushSecret Spec Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Restructure PushSecret Status Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Refactor PushSecret controller method Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Add missing methods for new providers Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Add missing method to onboardbase client Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Add docs on PushSecret UpdatePolicy Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> * Use constant for error message Signed-off-by: Carolin Dohmen <carodohmen@gmail.com> --------- Signed-off-by: Carolin Dohmen <carodohmen@gmail.com>
This commit is contained in:
parent
de78ea175f
commit
29e5f71d8b
38 changed files with 734 additions and 109 deletions
|
@ -41,6 +41,14 @@ type PushSecretStoreRef struct {
|
|||
Kind string `json:"kind,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=Replace;IfNotExists
|
||||
type PushSecretUpdatePolicy string
|
||||
|
||||
const (
|
||||
PushSecretUpdatePolicyReplace PushSecretUpdatePolicy = "Replace"
|
||||
PushSecretUpdatePolicyIfNotExists PushSecretUpdatePolicy = "IfNotExists"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Enum=Delete;None
|
||||
type PushSecretDeletionPolicy string
|
||||
|
||||
|
@ -54,6 +62,10 @@ type PushSecretSpec struct {
|
|||
// The Interval to which External Secrets will try to push a secret definition
|
||||
RefreshInterval *metav1.Duration `json:"refreshInterval,omitempty"`
|
||||
SecretStoreRefs []PushSecretStoreRef `json:"secretStoreRefs"`
|
||||
// UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".
|
||||
// +kubebuilder:default="Replace"
|
||||
// +optional
|
||||
UpdatePolicy PushSecretUpdatePolicy `json:"updatePolicy,omitempty"`
|
||||
// Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".
|
||||
// +kubebuilder:default="None"
|
||||
// +optional
|
||||
|
@ -148,6 +160,7 @@ type PushSecretStatusCondition struct {
|
|||
// +optional
|
||||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||
}
|
||||
|
||||
type SyncedPushSecretsMap map[string]map[string]PushSecretData
|
||||
|
||||
// PushSecretStatus indicates the history of the status of PushSecret.
|
||||
|
@ -159,7 +172,8 @@ type PushSecretStatus struct {
|
|||
|
||||
// SyncedResourceVersion keeps track of the last synced version.
|
||||
SyncedResourceVersion string `json:"syncedResourceVersion,omitempty"`
|
||||
// Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore.
|
||||
// Synced PushSecrets, including secrets that already exist in provider.
|
||||
// Matches secret stores to PushSecretData that was stored to that secret store.
|
||||
// +optional
|
||||
SyncedPushSecrets SyncedPushSecretsMap `json:"syncedPushSecrets,omitempty"`
|
||||
// +optional
|
||||
|
|
|
@ -79,6 +79,9 @@ type SecretsClient interface {
|
|||
// DeleteSecret will delete the secret from a provider
|
||||
DeleteSecret(ctx context.Context, remoteRef PushSecretRemoteRef) error
|
||||
|
||||
// SecretExists checks if a secret is already present in the provider at the given location.
|
||||
SecretExists(ctx context.Context, remoteRef PushSecretRemoteRef) (bool, error)
|
||||
|
||||
// Validate checks if the client is configured correctly
|
||||
// and is able to retrieve secrets from the provider.
|
||||
// If the validation result is unknown it will be ignored.
|
||||
|
|
|
@ -47,6 +47,11 @@ func (p *PP) DeleteSecret(_ context.Context, _ PushSecretRemoteRef) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Exists checks if a secret is already present in the provider at the given location.
|
||||
func (p *PP) SecretExists(_ context.Context, _ PushSecretRemoteRef) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GetSecret returns a single secret from the provider.
|
||||
func (p *PP) GetSecret(_ context.Context, _ ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||
return []byte("NOOP"), nil
|
||||
|
|
|
@ -266,6 +266,14 @@ spec:
|
|||
type:
|
||||
type: string
|
||||
type: object
|
||||
updatePolicy:
|
||||
default: Replace
|
||||
description: 'UpdatePolicy to handle Secrets in the provider. Possible
|
||||
Values: "Replace/IfNotExists". Defaults to "Replace".'
|
||||
enum:
|
||||
- Replace
|
||||
- IfNotExists
|
||||
type: string
|
||||
required:
|
||||
- secretStoreRefs
|
||||
- selector
|
||||
|
@ -339,8 +347,9 @@ spec:
|
|||
- match
|
||||
type: object
|
||||
type: object
|
||||
description: Synced Push Secrets for later deletion. Matches Secret
|
||||
Stores to PushSecretData that was stored to that secretStore.
|
||||
description: |-
|
||||
Synced PushSecrets, including secrets that already exist in provider.
|
||||
Matches secret stores to PushSecretData that was stored to that secret store.
|
||||
type: object
|
||||
syncedResourceVersion:
|
||||
description: SyncedResourceVersion keeps track of the last synced
|
||||
|
|
|
@ -5668,6 +5668,13 @@ spec:
|
|||
type:
|
||||
type: string
|
||||
type: object
|
||||
updatePolicy:
|
||||
default: Replace
|
||||
description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".'
|
||||
enum:
|
||||
- Replace
|
||||
- IfNotExists
|
||||
type: string
|
||||
required:
|
||||
- secretStoreRefs
|
||||
- selector
|
||||
|
@ -5737,7 +5744,9 @@ spec:
|
|||
- match
|
||||
type: object
|
||||
type: object
|
||||
description: Synced Push Secrets for later deletion. Matches Secret Stores to PushSecretData that was stored to that secretStore.
|
||||
description: |-
|
||||
Synced PushSecrets, including secrets that already exist in provider.
|
||||
Matches secret stores to PushSecretData that was stored to that secret store.
|
||||
type: object
|
||||
syncedResourceVersion:
|
||||
description: SyncedResourceVersion keeps track of the last synced version.
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
Contrary to what `ExternalSecret` does by pulling secrets from secret providers and creating `kind=Secret` in your cluster, `PushSecret` reads a local `kind=Secret` and pushes its content to a secret provider.
|
||||
|
||||
If there's already a secret in the secrets provided with the intended name of the secret to be created by the `PushSecret` you'll see the `PushSecret` in Error state, and when described you'll see a message saying `secret not managed by external-secrets`.
|
||||
The update behavior of `PushSecret` is controlled by `spec.updatePolicy`. The default policy is `Replace`, such that secrets are overwritten in the provider, regardless of whether there already is a secret present in the provider at the given location. If you do not want `PushSecret` to overwrite existing secrets in the provider, you can set `spec.UpdatePolicy` to `IfNotExists`. With this policy, the provider becomes the source of truth. Please note that with using `spec.updatePolicy=IfNotExists` it is possible that the secret value referenced by the `PushSecret` within the cluster differs from the secret value at the given location in the provider.
|
||||
|
||||
By default, the secret created in the secret provided will not be deleted even after deleting the `PushSecret`, unless you set `spec.deletionPolicy` to `Delete`.
|
||||
|
||||
By default, the secret created in the secret provided will not be deleted even after deleting the `PushSecret`, unless you set `spec.deletionPolicy` to Delete.
|
||||
|
||||
``` yaml
|
||||
{% include 'full-pushsecret.yaml' %}
|
||||
|
|
|
@ -5,6 +5,7 @@ metadata:
|
|||
name: pushsecret-example # Customisable
|
||||
namespace: default # Same of the SecretStores
|
||||
spec:
|
||||
updatePolicy: Replace # Policy to overwrite existing secrets in the provider on sync
|
||||
deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted
|
||||
refreshInterval: 10s # Refresh interval for which push secret will reconcile
|
||||
secretStoreRefs: # A list of secret stores to push secrets to
|
||||
|
|
|
@ -185,24 +185,27 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
return ctrl.Result{RequeueAfter: refreshInt}, nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) markAsFailed(msg string, ps *esapi.PushSecret, badSyncState esapi.SyncedPushSecretsMap) {
|
||||
func (r *Reconciler) markAsFailed(msg string, ps *esapi.PushSecret, syncState esapi.SyncedPushSecretsMap) {
|
||||
cond := newPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, esapi.ReasonErrored, msg)
|
||||
setPushSecretCondition(ps, *cond)
|
||||
if badSyncState != nil {
|
||||
r.setSyncedSecrets(ps, badSyncState)
|
||||
if syncState != nil {
|
||||
r.setSecrets(ps, syncState)
|
||||
}
|
||||
r.recorder.Event(ps, v1.EventTypeWarning, esapi.ReasonErrored, msg)
|
||||
}
|
||||
|
||||
func (r *Reconciler) markAsDone(ps *esapi.PushSecret, syncedSecrets esapi.SyncedPushSecretsMap) {
|
||||
func (r *Reconciler) markAsDone(ps *esapi.PushSecret, secrets esapi.SyncedPushSecretsMap) {
|
||||
msg := "PushSecret synced successfully"
|
||||
if ps.Spec.UpdatePolicy == esapi.PushSecretUpdatePolicyIfNotExists {
|
||||
msg += ". Existing secrets in providers unchanged."
|
||||
}
|
||||
cond := newPushSecretCondition(esapi.PushSecretReady, v1.ConditionTrue, esapi.ReasonSynced, msg)
|
||||
setPushSecretCondition(ps, *cond)
|
||||
r.setSyncedSecrets(ps, syncedSecrets)
|
||||
r.setSecrets(ps, secrets)
|
||||
r.recorder.Event(ps, v1.EventTypeNormal, esapi.ReasonSynced, msg)
|
||||
}
|
||||
|
||||
func (r *Reconciler) setSyncedSecrets(ps *esapi.PushSecret, status esapi.SyncedPushSecretsMap) {
|
||||
func (r *Reconciler) setSecrets(ps *esapi.PushSecret, status esapi.SyncedPushSecretsMap) {
|
||||
ps.Status.SyncedPushSecrets = status
|
||||
}
|
||||
|
||||
|
@ -269,35 +272,57 @@ func (r *Reconciler) DeleteSecretFromStore(ctx context.Context, client v1beta1.S
|
|||
}
|
||||
|
||||
func (r *Reconciler) PushSecretToProviders(ctx context.Context, stores map[esapi.PushSecretStoreRef]v1beta1.GenericStore, ps esapi.PushSecret, secret *v1.Secret, mgr *secretstore.Manager) (esapi.SyncedPushSecretsMap, error) {
|
||||
out := esapi.SyncedPushSecretsMap{}
|
||||
out := make(esapi.SyncedPushSecretsMap)
|
||||
for ref, store := range stores {
|
||||
storeKey := fmt.Sprintf("%v/%v", ref.Kind, store.GetName())
|
||||
out[storeKey] = make(map[string]esapi.PushSecretData)
|
||||
storeRef := v1beta1.SecretStoreRef{
|
||||
Name: store.GetName(),
|
||||
Kind: ref.Kind,
|
||||
}
|
||||
secretClient, err := mgr.Get(ctx, storeRef, ps.GetNamespace(), nil)
|
||||
out, err := r.handlePushSecretDataForStore(ctx, ps, secret, out, mgr, store.GetName(), ref.Kind)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("could not get secrets client for store %v: %w", store.GetName(), err)
|
||||
}
|
||||
for _, data := range ps.Spec.Data {
|
||||
if data.Match.SecretKey != "" {
|
||||
if _, ok := secret.Data[data.Match.SecretKey]; !ok {
|
||||
return out, fmt.Errorf("secret key %v does not exist", data.Match.SecretKey)
|
||||
}
|
||||
}
|
||||
|
||||
if err := secretClient.PushSecret(ctx, secret, data); err != nil {
|
||||
return out, fmt.Errorf(errSetSecretFailed, data.Match.SecretKey, store.GetName(), err)
|
||||
}
|
||||
|
||||
out[storeKey][statusRef(data)] = data
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) handlePushSecretDataForStore(ctx context.Context, ps esapi.PushSecret, secret *v1.Secret, out esapi.SyncedPushSecretsMap, mgr *secretstore.Manager, storeName, refKind string) (esapi.SyncedPushSecretsMap, error) {
|
||||
storeKey := fmt.Sprintf("%v/%v", refKind, storeName)
|
||||
out[storeKey] = make(map[string]esapi.PushSecretData)
|
||||
storeRef := v1beta1.SecretStoreRef{
|
||||
Name: storeName,
|
||||
Kind: refKind,
|
||||
}
|
||||
secretClient, err := mgr.Get(ctx, storeRef, ps.GetNamespace(), nil)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("could not get secrets client for store %v: %w", storeName, err)
|
||||
}
|
||||
for _, data := range ps.Spec.Data {
|
||||
key := data.GetSecretKey()
|
||||
if !secretKeyExists(key, secret) {
|
||||
return out, fmt.Errorf("secret key %v does not exist", key)
|
||||
}
|
||||
switch ps.Spec.UpdatePolicy {
|
||||
case esapi.PushSecretUpdatePolicyIfNotExists:
|
||||
exists, err := secretClient.SecretExists(ctx, data.Match.RemoteRef)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("could not verify if secret exists in store: %w", err)
|
||||
} else if exists {
|
||||
out[storeKey][statusRef(data)] = data
|
||||
continue
|
||||
}
|
||||
case esapi.PushSecretUpdatePolicyReplace:
|
||||
default:
|
||||
}
|
||||
if err := secretClient.PushSecret(ctx, secret, data); err != nil {
|
||||
return out, fmt.Errorf(errSetSecretFailed, key, storeName, err)
|
||||
}
|
||||
out[storeKey][statusRef(data)] = data
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func secretKeyExists(key string, secret *v1.Secret) bool {
|
||||
_, ok := secret.Data[key]
|
||||
return key == "" || ok
|
||||
}
|
||||
|
||||
func (r *Reconciler) GetSecret(ctx context.Context, ps esapi.PushSecret) (*v1.Secret, error) {
|
||||
secretName := types.NamespacedName{Name: ps.Spec.Selector.Secret.Name, Namespace: ps.Namespace}
|
||||
secret := &v1.Secret{}
|
||||
|
|
|
@ -128,6 +128,18 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
})).To(Succeed())
|
||||
})
|
||||
|
||||
const (
|
||||
defaultKey = "key"
|
||||
defaultVal = "value"
|
||||
defaultPath = "path/to/key"
|
||||
otherKey = "other-key"
|
||||
otherVal = "other-value"
|
||||
otherPath = "path/to/other-key"
|
||||
newKey = "new-key"
|
||||
newVal = "new-value"
|
||||
storePrefixTemplate = "SecretStore/%v"
|
||||
)
|
||||
|
||||
makeDefaultTestcase := func() *testCase {
|
||||
return &testCase{
|
||||
pushsecret: &v1alpha1.PushSecret{
|
||||
|
@ -150,9 +162,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -165,7 +177,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Namespace: PushSecretNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
defaultKey: []byte(defaultVal),
|
||||
},
|
||||
},
|
||||
store: &v1beta1.SecretStore{
|
||||
|
@ -195,7 +207,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
Eventually(func() bool {
|
||||
By("checking if Provider value got updated")
|
||||
secretValue := secret.Data["key"]
|
||||
secretValue := secret.Data[defaultKey]
|
||||
providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -207,6 +219,157 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
}
|
||||
|
||||
updateIfNotExists := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
return nil
|
||||
}
|
||||
fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
_, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
|
||||
return ok, nil
|
||||
}
|
||||
tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
|
||||
initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
tc.secret.Data[defaultKey] = []byte(newVal)
|
||||
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
Eventually(func() bool {
|
||||
By("checking if Provider value did not get updated")
|
||||
Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
|
||||
providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
got := providerValue.Value
|
||||
return bytes.Equal(got, initialValue)
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
updateIfNotExistsPartialSecrets := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
return nil
|
||||
}
|
||||
fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
_, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
|
||||
return ok, nil
|
||||
}
|
||||
tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
|
||||
tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: otherKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: otherPath,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
tc.secret.Data[defaultKey] = []byte(newVal) // change initial value in secret
|
||||
tc.secret.Data[otherKey] = []byte(otherVal)
|
||||
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
Eventually(func() bool {
|
||||
By("checking if only not existing Provider value got updated")
|
||||
Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
|
||||
providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
got := providerValue.Value
|
||||
otherProviderValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[1].Match.RemoteRef.RemoteKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
gotOther := otherProviderValue.Value
|
||||
|
||||
return bytes.Equal(gotOther, tc.secret.Data[otherKey]) && bytes.Equal(got, initialValue)
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
updateIfNotExistsSyncStatus := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
return nil
|
||||
}
|
||||
fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
_, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
|
||||
return ok, nil
|
||||
}
|
||||
tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
|
||||
tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: otherKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: otherPath,
|
||||
},
|
||||
},
|
||||
})
|
||||
tc.secret.Data[defaultKey] = []byte(newVal)
|
||||
tc.secret.Data[otherKey] = []byte(otherVal)
|
||||
updatedPS := &v1alpha1.PushSecret{}
|
||||
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
Eventually(func() bool {
|
||||
By("checking if PushSecret status gets updated correctly with UpdatePolicy=IfNotExists")
|
||||
Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
|
||||
psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
|
||||
err := k8sClient.Get(context.Background(), psKey, updatedPS)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][otherPath]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
expected := v1alpha1.PushSecretStatusCondition{
|
||||
Type: v1alpha1.PushSecretReady,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: v1alpha1.ReasonSynced,
|
||||
Message: "PushSecret synced successfully. Existing secrets in providers unchanged.",
|
||||
}
|
||||
return checkCondition(ps.Status, expected)
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
updateIfNotExistsSyncFailed := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
return nil
|
||||
}
|
||||
fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("don't know")
|
||||
}
|
||||
tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
|
||||
initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
tc.secret.Data[defaultKey] = []byte(newVal)
|
||||
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
Eventually(func() bool {
|
||||
By("checking if sync failed if secret existence cannot be verified in Provider")
|
||||
providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
got := providerValue.Value
|
||||
expected := v1alpha1.PushSecretStatusCondition{
|
||||
Type: v1alpha1.PushSecretReady,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: v1alpha1.ReasonErrored,
|
||||
Message: "set secret failed: could not verify if secret exists in store: don't know",
|
||||
}
|
||||
return checkCondition(ps.Status, expected) && bytes.Equal(got, initialValue)
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// if target Secret name is not specified it should use the ExternalSecret name.
|
||||
syncSuccessfullyWithTemplate := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
|
@ -232,9 +395,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -251,7 +414,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Type: v1.SecretTypeOpaque,
|
||||
EngineVersion: v1beta1.TemplateEngineV2,
|
||||
Data: map[string]string{
|
||||
"key": "{{ .key | toString | upper }} was templated",
|
||||
defaultKey: "{{ .key | toString | upper }} was templated",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -269,6 +432,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// if target Secret name is not specified it should use the ExternalSecret name.
|
||||
syncAndDeleteSuccessfully := func(tc *testCase) {
|
||||
fakeProvider.SetSecretFn = func() error {
|
||||
|
@ -295,9 +459,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -305,7 +469,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
},
|
||||
}
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
ps.Spec.Data[0].Match.RemoteRef.RemoteKey = "different-key"
|
||||
ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
|
||||
updatedPS := &v1alpha1.PushSecret{}
|
||||
Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
|
||||
Eventually(func() bool {
|
||||
|
@ -315,11 +479,11 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
key, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["different-key"]
|
||||
key, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return key.Match.SecretKey == "key"
|
||||
return key.Match.SecretKey == defaultKey
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
|
@ -352,9 +516,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -362,7 +526,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
},
|
||||
}
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
ps.Spec.Data[0].Match.RemoteRef.RemoteKey = "different-key"
|
||||
ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
|
||||
updatedPS := &v1alpha1.PushSecret{}
|
||||
Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
|
||||
Eventually(func() bool {
|
||||
|
@ -372,11 +536,11 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["different-key"]
|
||||
_, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["path/to/key"]
|
||||
_, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
|
||||
return ok
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
|
@ -460,7 +624,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
key, ok := updatedPS.Status.SyncedPushSecrets["SecretStore/new-store"]["path/to/key"]
|
||||
key, ok := updatedPS.Status.SyncedPushSecrets["SecretStore/new-store"][defaultPath]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
@ -468,7 +632,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
if syncedLen != 1 {
|
||||
return false
|
||||
}
|
||||
return key.Match.SecretKey == "key"
|
||||
return key.Match.SecretKey == defaultKey
|
||||
}, time.Second*10, time.Second).Should(BeTrue())
|
||||
return true
|
||||
}
|
||||
|
@ -505,9 +669,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -534,7 +698,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
},
|
||||
}
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
secretValue := secret.Data["key"]
|
||||
secretValue := secret.Data[defaultKey]
|
||||
providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
expected := v1alpha1.PushSecretStatusCondition{
|
||||
Type: v1alpha1.PushSecretReady,
|
||||
|
@ -566,7 +730,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
secretValue := secret.Data["key"]
|
||||
secretValue := secret.Data[defaultKey]
|
||||
providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
expected := v1alpha1.PushSecretStatusCondition{
|
||||
Type: v1alpha1.PushSecretReady,
|
||||
|
@ -606,9 +770,9 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
Data: []v1alpha1.PushSecretData{
|
||||
{
|
||||
Match: v1alpha1.PushSecretMatch{
|
||||
SecretKey: "key",
|
||||
SecretKey: defaultKey,
|
||||
RemoteRef: v1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "path/to/key",
|
||||
RemoteKey: defaultPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -631,7 +795,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
},
|
||||
}
|
||||
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
|
||||
secretValue := secret.Data["key"]
|
||||
secretValue := secret.Data[defaultKey]
|
||||
providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
|
||||
expected := v1alpha1.PushSecretStatusCondition{
|
||||
Type: v1alpha1.PushSecretReady,
|
||||
|
@ -768,6 +932,10 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
// this must be optional so we can test faulty es configuration
|
||||
},
|
||||
Entry("should sync", syncSuccessfully),
|
||||
Entry("should not update existing secret if UpdatePolicy=IfNotExists", updateIfNotExists),
|
||||
Entry("should only update parts of secret that don't already exist if UpdatePolicy=IfNotExists", updateIfNotExistsPartialSecrets),
|
||||
Entry("should update the PushSecret status correctly if UpdatePolicy=IfNotExists", updateIfNotExistsSyncStatus),
|
||||
Entry("should fail if secret existence cannot be verified if UpdatePolicy=IfNotExists", updateIfNotExistsSyncFailed),
|
||||
Entry("should sync with template", syncSuccessfullyWithTemplate),
|
||||
Entry("should delete if DeletionPolicy=Delete", syncAndDeleteSuccessfully),
|
||||
Entry("should track deletion tasks if Delete fails", failDelete),
|
||||
|
|
|
@ -349,6 +349,10 @@ func (c *MockFakeClient) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretR
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *MockFakeClient) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (c *MockFakeClient) GetSecret(_ context.Context, _ esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultAPIUrl = "https://api.akeyless.io"
|
||||
defaultAPIUrl = "https://api.akeyless.io"
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
// https://github.com/external-secrets/external-secrets/issues/644
|
||||
|
@ -236,11 +237,15 @@ func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
|
|||
}
|
||||
|
||||
func (a *Akeyless) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (a *Akeyless) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (a *Akeyless) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Implements store.Client.GetSecret Interface.
|
||||
|
|
|
@ -39,6 +39,7 @@ const (
|
|||
errUninitalizedAlibabaProvider = "provider Alibaba is not initialized"
|
||||
errFetchAccessKeyID = "could not fetch AccessKeyID secret: %w"
|
||||
errFetchAccessKeySecret = "could not fetch AccessKeySecret secret: %w"
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
// https://github.com/external-secrets/external-secrets/issues/644
|
||||
|
@ -56,17 +57,21 @@ type SMInterface interface {
|
|||
}
|
||||
|
||||
func (kms *KeyManagementService) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (kms *KeyManagementService) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (kms *KeyManagementService) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Empty GetAllSecrets.
|
||||
func (kms *KeyManagementService) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
// TO be implemented
|
||||
return nil, fmt.Errorf("GetAllSecrets not implemented")
|
||||
return nil, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// GetSecret returns a single secret from the provider.
|
||||
|
|
|
@ -133,6 +133,10 @@ func (pm *ParameterStore) DeleteSecret(ctx context.Context, remoteRef esv1beta1.
|
|||
return nil
|
||||
}
|
||||
|
||||
func (pm *ParameterStore) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
|
||||
parameterType := "String"
|
||||
overwrite := true
|
||||
|
|
|
@ -204,6 +204,10 @@ func (sm *SecretsManager) DeleteSecret(ctx context.Context, remoteRef esv1beta1.
|
|||
return err
|
||||
}
|
||||
|
||||
func (sm *SecretsManager) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (sm *SecretsManager) PushSecret(ctx context.Context, secret *corev1.Secret, psd esv1beta1.PushSecretData) error {
|
||||
if psd.GetSecretKey() == "" {
|
||||
return fmt.Errorf("pushing the whole secret is not yet implemented")
|
||||
|
|
|
@ -310,6 +310,10 @@ func (a *Azure) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecret
|
|||
}
|
||||
}
|
||||
|
||||
func (a *Azure) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func getCertificateFromValue(value []byte) (*x509.Certificate, error) {
|
||||
// 1st: try decode pkcs12
|
||||
_, localCert, err := pkcs12.Decode(value, "")
|
||||
|
|
|
@ -59,6 +59,7 @@ const (
|
|||
errStoreValidateFailed = "unable to validate provided store. Check if username, serverUrl and privateKey are correct"
|
||||
errServerURLNoEndSlash = "serverurl does not end with slash(/)"
|
||||
errInvalidDataform = "invalid key format in dataForm section. Expected only 'databagName'"
|
||||
errNotImplemented = "not implemented"
|
||||
|
||||
ProviderChef = "Chef"
|
||||
CallChefGetDataBagItem = "GetDataBagItem"
|
||||
|
@ -329,12 +330,16 @@ func getChefProvider(store v1beta1.GenericStore) (*v1beta1.ChefProvider, error)
|
|||
|
||||
// Not Implemented DeleteSecret.
|
||||
func (providerchef *Providerchef) DeleteSecret(_ context.Context, _ v1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Not Implemented PushSecret.
|
||||
func (providerchef *Providerchef) PushSecret(_ context.Context, _ *corev1.Secret, _ v1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (providerchef *Providerchef) SecretExists(_ context.Context, _ v1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
|
||||
|
|
|
@ -193,6 +193,10 @@ func (p *Client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// GetSecretMap returns multiple k/v pairs from the provider.
|
||||
func (p *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
||||
// Gets a secret as normal, expecting secret value to be a json object
|
||||
|
|
|
@ -71,6 +71,10 @@ func (c *client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef
|
|||
return errors.New("deleting secrets is not supported by Delinea DevOps Secrets Vault")
|
||||
}
|
||||
|
||||
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (c *client) Validate() (esv1beta1.ValidationResult, error) {
|
||||
return esv1beta1.ValidationResultReady, nil
|
||||
}
|
||||
|
|
|
@ -118,6 +118,10 @@ func (c *Client) DeleteSecret(_ context.Context, ref esv1beta1.PushSecretRemoteR
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
|
||||
value := secret.Data[data.GetSecretKey()]
|
||||
|
||||
|
|
|
@ -112,6 +112,11 @@ func (p *Provider) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteR
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) SecretExists(_ context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
_, ok := p.config[ref.GetRemoteKey()]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (p *Provider) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
|
||||
value := secret.Data[data.GetSecretKey()]
|
||||
currentData, ok := p.config[data.GetRemoteKey()]
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
|
||||
)
|
||||
|
@ -393,6 +394,62 @@ func TestSetSecret(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type secretExistsTestCase struct {
|
||||
name string
|
||||
input []esv1beta1.FakeProviderData
|
||||
request esv1alpha1.PushSecretRemoteRef
|
||||
expExists bool
|
||||
}
|
||||
|
||||
func TestSecretExists(t *testing.T) {
|
||||
gomega.RegisterTestingT(t)
|
||||
p := &Provider{}
|
||||
tbl := []secretExistsTestCase{
|
||||
{
|
||||
name: "return false, nil if no existing secret",
|
||||
input: []esv1beta1.FakeProviderData{},
|
||||
request: esv1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "/foo",
|
||||
},
|
||||
expExists: false,
|
||||
},
|
||||
{
|
||||
name: "return true, nil if existing secret",
|
||||
input: []esv1beta1.FakeProviderData{
|
||||
{
|
||||
Key: "/foo",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
request: esv1alpha1.PushSecretRemoteRef{
|
||||
RemoteKey: "/foo",
|
||||
},
|
||||
expExists: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, row := range tbl {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
cl, err := p.NewClient(context.Background(), &esv1beta1.SecretStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("secret-store-%v", i),
|
||||
},
|
||||
Spec: esv1beta1.SecretStoreSpec{
|
||||
Provider: &esv1beta1.SecretStoreProvider{
|
||||
Fake: &esv1beta1.FakeProvider{
|
||||
Data: row.input,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil, "")
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
exists, err := cl.SecretExists(context.TODO(), row.request)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
gomega.Expect(exists).To(gomega.Equal(row.expExists))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testMapCase struct {
|
||||
name string
|
||||
input []esv1beta1.FakeProviderData
|
||||
|
|
|
@ -78,6 +78,10 @@ func (c *client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.Pus
|
|||
return errors.New(errPushSecretsNotSupported)
|
||||
}
|
||||
|
||||
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, errors.New(errPushSecretsNotSupported)
|
||||
}
|
||||
|
||||
func (c *client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return errors.New(errDeleteSecretsNotSupported)
|
||||
}
|
||||
|
|
|
@ -129,6 +129,10 @@ func parseError(err error) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// PushSecret pushes a kubernetes secret key into gcp provider Secret.
|
||||
func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecretData esv1beta1.PushSecretData) error {
|
||||
if pushSecretData.GetSecretKey() == "" {
|
||||
|
|
|
@ -49,6 +49,7 @@ const (
|
|||
errTagsOnlyEnvironmentSupported = "'find.tags' only supports 'environment_scope'"
|
||||
errPathNotImplemented = "'find.path' is not implemented in the GitLab provider"
|
||||
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
// https://github.com/external-secrets/external-secrets/issues/644
|
||||
|
@ -88,11 +89,15 @@ func (g *gitlabBase) getAuth(ctx context.Context) (string, error) {
|
|||
}
|
||||
|
||||
func (g *gitlabBase) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (g *gitlabBase) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (g *gitlabBase) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// GetAllSecrets syncs all gitlab project and group variables into a single Kubernetes Secret.
|
||||
|
|
|
@ -60,6 +60,7 @@ const (
|
|||
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
|
||||
errJSONSecretMarshal = "unable to marshal secret: %w"
|
||||
errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
var contextTimeout = time.Minute * 2
|
||||
|
@ -97,18 +98,22 @@ func (c *client) setAuth(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (ibm *providerIBM) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (ibm *providerIBM) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Not Implemented PushSecret.
|
||||
func (ibm *providerIBM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Empty GetAllSecrets.
|
||||
func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
// TO be implemented
|
||||
return nil, fmt.Errorf("GetAllSecrets not implemented")
|
||||
return nil, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||
|
|
|
@ -212,6 +212,10 @@ func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretR
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) buildSecretNameAndKey(remoteRef esv1beta1.PushSecretRemoteRef) ([]string, error) {
|
||||
parts := strings.Split(remoteRef.GetRemoteKey(), "/")
|
||||
if len(parts) != 2 {
|
||||
|
|
|
@ -100,6 +100,10 @@ func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecre
|
|||
return c.fullDelete(ctx, remoteRef.GetRemoteKey())
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *Client) PushSecret(ctx context.Context, secret *v1.Secret, data esv1beta1.PushSecretData) error {
|
||||
if data.GetProperty() == "" && data.GetSecretKey() != "" {
|
||||
return fmt.Errorf("requires property in RemoteRef to push secret value if secret key is defined")
|
||||
|
|
|
@ -126,6 +126,11 @@ func (c *Client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
// not implemented
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (c *Client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
// not implemented
|
||||
return nil
|
||||
|
|
|
@ -208,6 +208,10 @@ func (provider *ProviderOnePassword) DeleteSecret(_ context.Context, ref esv1bet
|
|||
return nil
|
||||
}
|
||||
|
||||
func (provider *ProviderOnePassword) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
const (
|
||||
passwordLabel = "password"
|
||||
)
|
||||
|
|
|
@ -159,6 +159,10 @@ func (vms *VaultManagementService) DeleteSecret(ctx context.Context, remoteRef e
|
|||
}
|
||||
}
|
||||
|
||||
func (vms *VaultManagementService) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (vms *VaultManagementService) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
var page *string
|
||||
var summaries []vault.SecretSummary
|
||||
|
|
|
@ -61,6 +61,10 @@ func (c *client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.Pus
|
|||
return errors.New(errPushSecretsNotSupported)
|
||||
}
|
||||
|
||||
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, errors.New(errPushSecretsNotSupported)
|
||||
}
|
||||
|
||||
func (c *client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return errors.New(errDeleteSecretsNotSupported)
|
||||
}
|
||||
|
|
|
@ -264,6 +264,10 @@ func (c *client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecre
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *client) Validate() (esv1beta1.ValidationResult, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -80,6 +79,7 @@ var (
|
|||
errInvalidResponseBody = errors.New("invalid HTTP response body received from senhasegura")
|
||||
errInvalidHTTPCode = errors.New("received invalid HTTP code from senhasegura")
|
||||
errApplicationError = errors.New("received application error from senhasegura")
|
||||
errNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -93,12 +93,16 @@ func New(isoSession *senhaseguraAuth.SenhaseguraIsoSession) (*DSM, error) {
|
|||
}
|
||||
|
||||
func (dsm *DSM) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
func (dsm *DSM) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, errNotImplemented
|
||||
}
|
||||
|
||||
// Not Implemented PushSecret.
|
||||
func (dsm *DSM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -165,7 +169,7 @@ TODO: GetAllSecrets functionality is to get secrets from either regexp-matching
|
|||
https://github.com/external-secrets/external-secrets/pull/830#discussion_r858657107
|
||||
*/
|
||||
func (dsm *DSM) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (secretData map[string][]byte, err error) {
|
||||
return nil, fmt.Errorf("GetAllSecrets not implemented yet")
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -38,6 +38,7 @@ type Client struct {
|
|||
GetSecretFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error)
|
||||
GetSecretMapFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
|
||||
GetAllSecretsFn func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error)
|
||||
SecretExistsFn func(context.Context, esv1beta1.PushSecretRemoteRef) (bool, error)
|
||||
SetSecretFn func() error
|
||||
DeleteSecretFn func() error
|
||||
}
|
||||
|
@ -54,6 +55,9 @@ func New() *Client {
|
|||
GetAllSecretsFn: func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
return nil, nil
|
||||
},
|
||||
SecretExistsFn: func(context.Context, esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, nil
|
||||
},
|
||||
SetSecretFn: func() error {
|
||||
return nil
|
||||
},
|
||||
|
@ -92,6 +96,10 @@ func (v *Client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef
|
|||
return v.DeleteSecretFn()
|
||||
}
|
||||
|
||||
func (v *Client) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return v.SecretExistsFn(ctx, ref)
|
||||
}
|
||||
|
||||
// GetSecret implements the provider.Provider interface.
|
||||
func (v *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||
return v.GetSecretFn(ctx, ref)
|
||||
|
|
|
@ -70,32 +70,7 @@ func (c *client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
|
|||
}
|
||||
}
|
||||
|
||||
// Return nil if secret value is null
|
||||
if data == nil {
|
||||
return nil, esv1beta1.NoSecretError{}
|
||||
}
|
||||
jsonStr, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// (1): return raw json if no property is defined
|
||||
if ref.Property == "" {
|
||||
return jsonStr, nil
|
||||
}
|
||||
|
||||
// For backwards compatibility we want the
|
||||
// actual keys to take precedence over gjson syntax
|
||||
// (2): extract key from secret with property
|
||||
if _, ok := data[ref.Property]; ok {
|
||||
return utils.GetByteValueFromMap(data, ref.Property)
|
||||
}
|
||||
|
||||
// (3): extract key from secret using gjson
|
||||
val := gjson.Get(string(jsonStr), ref.Property)
|
||||
if !val.Exists() {
|
||||
return nil, fmt.Errorf(errSecretKeyFmt, ref.Property)
|
||||
}
|
||||
return []byte(val.String()), nil
|
||||
return getSecretValue(data, ref.Property)
|
||||
}
|
||||
|
||||
// GetSecretMap supports two modes of operation:
|
||||
|
@ -123,6 +98,25 @@ func (c *client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretD
|
|||
return byteMap, nil
|
||||
}
|
||||
|
||||
func (c *client) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
path := c.buildPath(ref.GetRemoteKey())
|
||||
data, err := c.readSecret(ctx, path, "")
|
||||
if err != nil {
|
||||
if errors.Is(err, esv1beta1.NoSecretError{}) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
value, err := getSecretValue(data, ref.GetProperty())
|
||||
if err != nil {
|
||||
if errors.Is(err, esv1beta1.NoSecretError{}) || err.Error() == fmt.Sprintf(errSecretKeyFmt, ref.GetProperty()) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return value != nil, nil
|
||||
}
|
||||
|
||||
func (c *client) readSecret(ctx context.Context, path, version string) (map[string]interface{}, error) {
|
||||
dataPath := c.buildPath(path)
|
||||
|
||||
|
@ -162,6 +156,34 @@ func (c *client) readSecret(ctx context.Context, path, version string) (map[stri
|
|||
return secretData, nil
|
||||
}
|
||||
|
||||
func getSecretValue(data map[string]interface{}, property string) ([]byte, error) {
|
||||
if data == nil {
|
||||
return nil, esv1beta1.NoSecretError{}
|
||||
}
|
||||
jsonStr, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// (1): return raw json if no property is defined
|
||||
if property == "" {
|
||||
return jsonStr, nil
|
||||
}
|
||||
|
||||
// For backwards compatibility we want the
|
||||
// actual keys to take precedence over gjson syntax
|
||||
// (2): extract key from secret with property
|
||||
if _, ok := data[property]; ok {
|
||||
return utils.GetByteValueFromMap(data, property)
|
||||
}
|
||||
|
||||
// (3): extract key from secret using gjson
|
||||
val := gjson.Get(string(jsonStr), property)
|
||||
if !val.Exists() {
|
||||
return nil, fmt.Errorf(errSecretKeyFmt, property)
|
||||
}
|
||||
return []byte(val.String()), nil
|
||||
}
|
||||
|
||||
func (c *client) readSecretMetadata(ctx context.Context, path string) (map[string]string, error) {
|
||||
metadata := make(map[string]string)
|
||||
url, err := c.buildMetadataPath(path)
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
kclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
|
||||
"github.com/external-secrets/external-secrets/pkg/provider/vault/fake"
|
||||
"github.com/external-secrets/external-secrets/pkg/provider/vault/util"
|
||||
)
|
||||
|
@ -695,6 +696,193 @@ func TestGetSecretPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSecretExists(t *testing.T) {
|
||||
secret := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}
|
||||
secretWithNil := map[string]interface{}{
|
||||
"hi": nil,
|
||||
}
|
||||
errNope := errors.New("nope")
|
||||
type args struct {
|
||||
store *esv1beta1.VaultProvider
|
||||
vClient util.Logical
|
||||
}
|
||||
type want struct {
|
||||
exists bool
|
||||
err error
|
||||
}
|
||||
tests := map[string]struct {
|
||||
reason string
|
||||
args args
|
||||
ref *testingfake.PushSecretData
|
||||
want want
|
||||
}{
|
||||
"NoExistingSecretV1": {
|
||||
reason: "Should return false, nil if secret does not exist in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, esv1beta1.NoSecretError{}),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"NoExistingSecretV2": {
|
||||
reason: "Should return false, nil if secret does not exist in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, esv1beta1.NoSecretError{}),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"NoExistingSecretWithPropertyV2": {
|
||||
reason: "Should return false, nil if secret with property does not exist in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
|
||||
"data": secret,
|
||||
}, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret", Property: "different"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"NoExistingSecretWithPropertyV1": {
|
||||
reason: "Should return false, nil if secret with property does not exist in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret", Property: "different"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"ExistingSecretV1": {
|
||||
reason: "Should return true, nil if secret exists in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: true,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"ExistingSecretV2": {
|
||||
reason: "Should return true, nil if secret exists in provider.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
|
||||
"data": secret,
|
||||
}, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: true,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"ExistingSecretWithNilV1": {
|
||||
reason: "Should return false, nil if secret in provider has nil value.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNil, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret", Property: "hi"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"ExistingSecretWithNilV2": {
|
||||
reason: "Should return false, nil if secret in provider has nil value.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
|
||||
"data": secretWithNil,
|
||||
}, nil),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret", Property: "hi"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"ErrorReadingSecretV1": {
|
||||
reason: "Should return error if secret existence cannot be verified.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errNope),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: fmt.Errorf(errReadSecret, errNope),
|
||||
},
|
||||
},
|
||||
"ErrorReadingSecretV2": {
|
||||
reason: "Should return error if secret existence cannot be verified.",
|
||||
args: args{
|
||||
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||
vClient: &fake.Logical{
|
||||
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errNope),
|
||||
},
|
||||
},
|
||||
ref: &testingfake.PushSecretData{RemoteKey: "secret"},
|
||||
want: want{
|
||||
exists: false,
|
||||
err: fmt.Errorf(errReadSecret, errNope),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
client := &client{
|
||||
logical: tc.args.vClient,
|
||||
store: tc.args.store,
|
||||
}
|
||||
exists, err := client.SecretExists(context.Background(), tc.ref)
|
||||
if diff := cmp.Diff(exists, tc.want.exists); diff != "" {
|
||||
t.Errorf("\n%s\nvault.SecretExists(...): -want exists, +got exists:\n%s", tc.reason, diff)
|
||||
}
|
||||
if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nvault.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// EquateErrors returns true if the supplied errors are of the same type and
|
||||
// produce identical strings. This mirrors the error comparison behavior of
|
||||
// https://github.com/go-test/deep, which most Crossplane tests targeted before
|
||||
|
|
|
@ -31,6 +31,10 @@ import (
|
|||
"github.com/external-secrets/external-secrets/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
// https://github.com/external-secrets/external-secrets/issues/644
|
||||
var _ esv1beta1.SecretsClient = &WebHook{}
|
||||
var _ esv1beta1.Provider = &Provider{}
|
||||
|
@ -101,18 +105,22 @@ func getProvider(store esv1beta1.GenericStore) (*webhook.Spec, error) {
|
|||
}
|
||||
|
||||
func (w *WebHook) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (w *WebHook) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Not Implemented PushSecret.
|
||||
func (w *WebHook) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
// Empty GetAllSecrets.
|
||||
func (w *WebHook) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
// TO be implemented
|
||||
return nil, fmt.Errorf("GetAllSecrets not implemented")
|
||||
return nil, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (w *WebHook) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||
|
|
|
@ -23,6 +23,10 @@ import (
|
|||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
errNotImplemented = "not implemented"
|
||||
)
|
||||
|
||||
// https://github.com/external-secrets/external-secrets/issues/644
|
||||
var _ esv1beta1.SecretsClient = &yandexCloudSecretsClient{}
|
||||
|
||||
|
@ -38,11 +42,15 @@ func (c *yandexCloudSecretsClient) GetSecret(ctx context.Context, ref esv1beta1.
|
|||
}
|
||||
|
||||
func (c *yandexCloudSecretsClient) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (c *yandexCloudSecretsClient) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
return false, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (c *yandexCloudSecretsClient) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
return fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (c *yandexCloudSecretsClient) Validate() (esv1beta1.ValidationResult, error) {
|
||||
|
@ -55,7 +63,7 @@ func (c *yandexCloudSecretsClient) GetSecretMap(ctx context.Context, ref esv1bet
|
|||
|
||||
func (c *yandexCloudSecretsClient) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||
// TO be implemented
|
||||
return nil, fmt.Errorf("GetAllSecrets not supported")
|
||||
return nil, fmt.Errorf(errNotImplemented)
|
||||
}
|
||||
|
||||
func (c *yandexCloudSecretsClient) Close(_ context.Context) error {
|
||||
|
|
Loading…
Reference in a new issue