mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Ignore ExternalSecret processing if the store is not usuable (e.g.
NotReady).
This commit is contained in:
parent
84af221762
commit
4820cc9165
31 changed files with 169 additions and 83 deletions
|
@ -273,6 +273,7 @@ const (
|
||||||
ConditionReasonSecretDeleted = "SecretDeleted"
|
ConditionReasonSecretDeleted = "SecretDeleted"
|
||||||
|
|
||||||
ReasonInvalidStoreRef = "InvalidStoreRef"
|
ReasonInvalidStoreRef = "InvalidStoreRef"
|
||||||
|
ReasonUnavailableStore = "UnavailableStore"
|
||||||
ReasonProviderClientConfig = "InvalidProviderClientConfig"
|
ReasonProviderClientConfig = "InvalidProviderClientConfig"
|
||||||
ReasonUpdateFailed = "UpdateFailed"
|
ReasonUpdateFailed = "UpdateFailed"
|
||||||
ReasonUpdated = "Updated"
|
ReasonUpdated = "Updated"
|
||||||
|
|
|
@ -20,6 +20,18 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ValidationResultReady ValidationResult = iota
|
||||||
|
ValidationResultUnknown
|
||||||
|
ValidationResultError
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidationResult uint8
|
||||||
|
|
||||||
|
func (v ValidationResult) String() string {
|
||||||
|
return [...]string{"Ready", "Unknown", "Error"}[v]
|
||||||
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=false
|
// +kubebuilder:object:root=false
|
||||||
// +kubebuilder:object:generate:false
|
// +kubebuilder:object:generate:false
|
||||||
// +k8s:deepcopy-gen:interfaces=nil
|
// +k8s:deepcopy-gen:interfaces=nil
|
||||||
|
@ -47,8 +59,9 @@ type SecretsClient interface {
|
||||||
GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error)
|
GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error)
|
||||||
|
|
||||||
// Validate checks if the client is configured correctly
|
// Validate checks if the client is configured correctly
|
||||||
// and is able to retrieve secrets from the provider
|
// and is able to retrieve secrets from the provider.
|
||||||
Validate() error
|
// If the validation result is unknown it will be ignored.
|
||||||
|
Validate() (ValidationResult, error)
|
||||||
|
|
||||||
// GetSecretMap returns multiple k/v pairs from the provider
|
// GetSecretMap returns multiple k/v pairs from the provider
|
||||||
GetSecretMap(ctx context.Context, ref ExternalSecretDataRemoteRef) (map[string][]byte, error)
|
GetSecretMap(ctx context.Context, ref ExternalSecretDataRemoteRef) (map[string][]byte, error)
|
||||||
|
|
|
@ -50,8 +50,8 @@ func (p *PP) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PP) Validate() error {
|
func (p *PP) Validate() (ValidationResult, error) {
|
||||||
return nil
|
return ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PP) ValidateStore(store GenericStore) error {
|
func (p *PP) ValidateStore(store GenericStore) error {
|
||||||
|
|
|
@ -32,6 +32,10 @@ type SecretStoreSpec struct {
|
||||||
// Used to configure http retries if failed
|
// Used to configure http retries if failed
|
||||||
// +optional
|
// +optional
|
||||||
RetrySettings *SecretStoreRetrySettings `json:"retrySettings,omitempty"`
|
RetrySettings *SecretStoreRetrySettings `json:"retrySettings,omitempty"`
|
||||||
|
|
||||||
|
// Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
|
||||||
|
// +optional
|
||||||
|
RefreshInterval int `json:"refreshInterval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretStoreProvider contains the provider-specific configration.
|
// SecretStoreProvider contains the provider-specific configration.
|
||||||
|
|
|
@ -55,6 +55,7 @@ var (
|
||||||
namespace string
|
namespace string
|
||||||
enableClusterStoreReconciler bool
|
enableClusterStoreReconciler bool
|
||||||
enableClusterExternalSecretReconciler bool
|
enableClusterExternalSecretReconciler bool
|
||||||
|
enableFloodGate bool
|
||||||
storeRequeueInterval time.Duration
|
storeRequeueInterval time.Duration
|
||||||
serviceName, serviceNamespace string
|
serviceName, serviceNamespace string
|
||||||
secretName, secretNamespace string
|
secretName, secretNamespace string
|
||||||
|
@ -138,6 +139,7 @@ var rootCmd = &cobra.Command{
|
||||||
ControllerClass: controllerClass,
|
ControllerClass: controllerClass,
|
||||||
RequeueInterval: time.Hour,
|
RequeueInterval: time.Hour,
|
||||||
ClusterSecretStoreEnabled: enableClusterStoreReconciler,
|
ClusterSecretStoreEnabled: enableClusterStoreReconciler,
|
||||||
|
EnableFloodGate: enableFloodGate,
|
||||||
}).SetupWithManager(mgr, controller.Options{
|
}).SetupWithManager(mgr, controller.Options{
|
||||||
MaxConcurrentReconciles: concurrent,
|
MaxConcurrentReconciles: concurrent,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -181,5 +183,6 @@ func init() {
|
||||||
rootCmd.Flags().StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
|
rootCmd.Flags().StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
|
||||||
rootCmd.Flags().BoolVar(&enableClusterStoreReconciler, "enable-cluster-store-reconciler", true, "Enable cluster store reconciler.")
|
rootCmd.Flags().BoolVar(&enableClusterStoreReconciler, "enable-cluster-store-reconciler", true, "Enable cluster store reconciler.")
|
||||||
rootCmd.Flags().BoolVar(&enableClusterExternalSecretReconciler, "enable-cluster-external-secret-reconciler", true, "Enable cluster external secret reconciler.")
|
rootCmd.Flags().BoolVar(&enableClusterExternalSecretReconciler, "enable-cluster-external-secret-reconciler", true, "Enable cluster external secret reconciler.")
|
||||||
rootCmd.Flags().DurationVar(&storeRequeueInterval, "store-requeue-interval", time.Minute*5, "Time duration between reconciling (Cluster)SecretStores")
|
rootCmd.Flags().DurationVar(&storeRequeueInterval, "store-requeue-interval", time.Minute*5, "Default Time duration between reconciling (Cluster)SecretStores")
|
||||||
|
rootCmd.Flags().BoolVar(&enableFloodGate, "enable-flood-gate", true, "Enable flood gate. External secret will be reconciled only if the ClusterStore or Store have an healthy or unknown state.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2704,6 +2704,10 @@ spec:
|
||||||
- auth
|
- auth
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
refreshInterval:
|
||||||
|
description: Used to configure store refresh interval in seconds.
|
||||||
|
Empty or 0 will default to the controller config.
|
||||||
|
type: integer
|
||||||
retrySettings:
|
retrySettings:
|
||||||
description: Used to configure http retries if failed
|
description: Used to configure http retries if failed
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -2707,6 +2707,10 @@ spec:
|
||||||
- auth
|
- auth
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
refreshInterval:
|
||||||
|
description: Used to configure store refresh interval in seconds.
|
||||||
|
Empty or 0 will default to the controller config.
|
||||||
|
type: integer
|
||||||
retrySettings:
|
retrySettings:
|
||||||
description: Used to configure http retries if failed
|
description: Used to configure http retries if failed
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -2316,6 +2316,9 @@ spec:
|
||||||
- auth
|
- auth
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
refreshInterval:
|
||||||
|
description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
|
||||||
|
type: integer
|
||||||
retrySettings:
|
retrySettings:
|
||||||
description: Used to configure http retries if failed
|
description: Used to configure http retries if failed
|
||||||
properties:
|
properties:
|
||||||
|
@ -4870,6 +4873,9 @@ spec:
|
||||||
- auth
|
- auth
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
refreshInterval:
|
||||||
|
description: Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
|
||||||
|
type: integer
|
||||||
retrySettings:
|
retrySettings:
|
||||||
description: Used to configure http retries if failed
|
description: Used to configure http retries if failed
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -18,8 +18,6 @@ import (
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
// nolint
|
|
||||||
. "github.com/onsi/ginkgo/v2/extensions/table"
|
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
|
|
|
@ -18,8 +18,6 @@ import (
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
// nolint
|
|
||||||
. "github.com/onsi/ginkgo/v2/extensions/table"
|
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
|
|
|
@ -21,8 +21,6 @@ import (
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
// nolint
|
|
||||||
. "github.com/onsi/ginkgo/v2/extensions/table"
|
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
|
|
|
@ -16,8 +16,6 @@ import (
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
// nolint
|
|
||||||
. "github.com/onsi/ginkgo/v2/extensions/table"
|
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
|
|
|
@ -53,8 +53,10 @@ const (
|
||||||
errUpdateSecret = "could not update Secret"
|
errUpdateSecret = "could not update Secret"
|
||||||
errPatchStatus = "unable to patch status"
|
errPatchStatus = "unable to patch status"
|
||||||
errGetSecretStore = "could not get SecretStore %q, %w"
|
errGetSecretStore = "could not get SecretStore %q, %w"
|
||||||
|
errSecretStoreNotReady = "the desired SecretStore %s is not ready"
|
||||||
errGetClusterSecretStore = "could not get ClusterSecretStore %q, %w"
|
errGetClusterSecretStore = "could not get ClusterSecretStore %q, %w"
|
||||||
errStoreRef = "could not get store reference"
|
errStoreRef = "could not get store reference"
|
||||||
|
errStoreUsability = "could not use store reference"
|
||||||
errStoreProvider = "could not get store provider"
|
errStoreProvider = "could not get store provider"
|
||||||
errStoreClient = "could not get provider client"
|
errStoreClient = "could not get provider client"
|
||||||
errGetExistingSecret = "could not get existing secret: %w"
|
errGetExistingSecret = "could not get existing secret: %w"
|
||||||
|
@ -82,6 +84,7 @@ type Reconciler struct {
|
||||||
ControllerClass string
|
ControllerClass string
|
||||||
RequeueInterval time.Duration
|
RequeueInterval time.Duration
|
||||||
ClusterSecretStoreEnabled bool
|
ClusterSecretStoreEnabled bool
|
||||||
|
EnableFloodGate bool
|
||||||
recorder record.EventRecorder
|
recorder record.EventRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +136,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||||
conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionFalse, esv1beta1.ConditionReasonSecretSyncedError, errStoreRef)
|
conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionFalse, esv1beta1.ConditionReasonSecretSyncedError, errStoreRef)
|
||||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log = log.WithValues("SecretStore", store.GetNamespacedName())
|
log = log.WithValues("SecretStore", store.GetNamespacedName())
|
||||||
|
@ -144,6 +147,17 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.EnableFloodGate {
|
||||||
|
if err = assertStoreIsUsable(store); err != nil {
|
||||||
|
log.Error(err, errStoreUsability)
|
||||||
|
r.recorder.Event(&externalSecret, v1.EventTypeWarning, esv1beta1.ReasonUnavailableStore, err.Error())
|
||||||
|
conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionFalse, esv1beta1.ConditionReasonSecretSyncedError, errStoreUsability)
|
||||||
|
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||||
|
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
storeProvider, err := esv1beta1.GetProvider(store)
|
storeProvider, err := esv1beta1.GetProvider(store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, errStoreProvider)
|
log.Error(err, errStoreProvider)
|
||||||
|
@ -158,7 +172,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||||
r.recorder.Event(&externalSecret, v1.EventTypeWarning, esv1beta1.ReasonProviderClientConfig, err.Error())
|
r.recorder.Event(&externalSecret, v1.EventTypeWarning, esv1beta1.ReasonProviderClientConfig, err.Error())
|
||||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -467,7 +481,14 @@ func isSecretValid(existingSecret v1.Secret) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStore returns the store with the provided ExternalSecret.
|
// assertStoreIsUsable assert that the store is ready to use.
|
||||||
|
func assertStoreIsUsable(store esv1beta1.GenericStore) error {
|
||||||
|
if secretstore.GetSecretStoreCondition(store.GetStatus(), esv1beta1.SecretStoreReady).Status != v1.ConditionTrue {
|
||||||
|
return fmt.Errorf(errSecretStoreNotReady, store.GetName())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1beta1.ExternalSecret) (esv1beta1.GenericStore, error) {
|
func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1beta1.ExternalSecret) (esv1beta1.GenericStore, error) {
|
||||||
ref := types.NamespacedName{
|
ref := types.NamespacedName{
|
||||||
Name: externalSecret.Spec.SecretStoreRef.Name,
|
Name: externalSecret.Spec.SecretStoreRef.Name,
|
||||||
|
@ -479,7 +500,6 @@ func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1beta1.Ext
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errGetClusterSecretStore, ref.Name, err)
|
return nil, fmt.Errorf(errGetClusterSecretStore, ref.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &store, nil
|
return &store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,6 +510,7 @@ func (r *Reconciler) getStore(ctx context.Context, externalSecret *esv1beta1.Ext
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errGetSecretStore, ref.Name, err)
|
return nil, fmt.Errorf(errGetSecretStore, ref.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &store, nil
|
return &store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ss.GetSpec().RefreshInterval != 0 {
|
||||||
|
requeueInterval = time.Second * time.Duration(ss.GetSpec().RefreshInterval)
|
||||||
|
}
|
||||||
|
|
||||||
// patch status when done processing
|
// patch status when done processing
|
||||||
p := client.MergeFrom(ss.Copy())
|
p := client.MergeFrom(ss.Copy())
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -93,8 +97,8 @@ func validateStore(ctx context.Context, namespace string, store esapi.GenericSto
|
||||||
}
|
}
|
||||||
defer cl.Close(ctx)
|
defer cl.Close(ctx)
|
||||||
|
|
||||||
err = cl.Validate()
|
validationResult, err := cl.Validate()
|
||||||
if err != nil {
|
if err != nil && validationResult != esapi.ValidationResultUnknown {
|
||||||
cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, errUnableValidateStore)
|
cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, errUnableValidateStore)
|
||||||
SetExternalSecretCondition(store, *cond)
|
SetExternalSecretCondition(store, *cond)
|
||||||
recorder.Event(store, v1.EventTypeWarning, esapi.ReasonValidationFailed, err.Error())
|
recorder.Event(store, v1.EventTypeWarning, esapi.ReasonValidationFailed, err.Error())
|
||||||
|
|
|
@ -111,11 +111,15 @@ func (a *Akeyless) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Akeyless) Validate() error {
|
func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
timeout := 15 * time.Second
|
timeout := 15 * time.Second
|
||||||
url := a.url
|
url := a.url
|
||||||
|
|
||||||
return utils.NetworkValidate(url, timeout)
|
if err := utils.NetworkValidate(url, timeout); err != nil {
|
||||||
|
return esv1beta1.ValidationResultError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements store.Client.GetSecret Interface.
|
// Implements store.Client.GetSecret Interface.
|
||||||
|
|
|
@ -197,11 +197,14 @@ func (kms *KeyManagementService) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kms *KeyManagementService) Validate() error {
|
func (kms *KeyManagementService) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
timeout := 15 * time.Second
|
timeout := 15 * time.Second
|
||||||
url := kms.url
|
url := kms.url
|
||||||
|
|
||||||
return utils.NetworkValidate(url, timeout)
|
if err := utils.NetworkValidate(url, timeout); err != nil {
|
||||||
|
return esv1beta1.ValidationResultError, err
|
||||||
|
}
|
||||||
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kms *KeyManagementService) ValidateStore(store esv1beta1.GenericStore) error {
|
func (kms *KeyManagementService) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -225,7 +225,10 @@ func (pm *ParameterStore) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ParameterStore) Validate() error {
|
func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
_, err := pm.sess.Config.Credentials.Get()
|
_, err := pm.sess.Config.Credentials.Get()
|
||||||
return err
|
if err != nil {
|
||||||
|
return esv1beta1.ValidationResultError, err
|
||||||
|
}
|
||||||
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,7 +287,10 @@ func (sm *SecretsManager) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *SecretsManager) Validate() error {
|
func (sm *SecretsManager) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
_, err := sm.sess.Config.Credentials.Get()
|
_, err := sm.sess.Config.Credentials.Get()
|
||||||
return err
|
if err != nil {
|
||||||
|
return esv1beta1.ValidationResultError, err
|
||||||
|
}
|
||||||
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -480,8 +480,8 @@ func (a *Azure) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Azure) Validate() error {
|
func (a *Azure) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getObjType(ref esv1beta1.ExternalSecretDataRemoteRef) (string, string) {
|
func getObjType(ref esv1beta1.ExternalSecretDataRemoteRef) (string, string) {
|
||||||
|
|
|
@ -94,8 +94,8 @@ func (p *Provider) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) Validate() error {
|
func (p *Provider) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
|
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -423,8 +423,8 @@ func (sm *ProviderGCP) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *ProviderGCP) Validate() error {
|
func (sm *ProviderGCP) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *ProviderGCP) ValidateStore(store esv1beta1.GenericStore) error {
|
func (sm *ProviderGCP) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -226,14 +226,14 @@ func (g *Gitlab) Close(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate will use the gitlab client to validate the gitlab provider using the ListVariable call to ensure get permissions without needing a specific key.
|
// Validate will use the gitlab client to validate the gitlab provider using the ListVariable call to ensure get permissions without needing a specific key.
|
||||||
func (g *Gitlab) Validate() error {
|
func (g *Gitlab) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
_, resp, err := g.client.ListVariables(g.projectID, nil)
|
_, resp, err := g.client.ListVariables(g.projectID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(errList, err)
|
return esv1beta1.ValidationResultError, fmt.Errorf(errList, err)
|
||||||
} else if resp == nil || resp.StatusCode != http.StatusOK {
|
} else if resp == nil || resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf(errAuth)
|
return esv1beta1.ValidationResultError, fmt.Errorf(errAuth)
|
||||||
}
|
}
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gitlab) ValidateStore(store esv1beta1.GenericStore) error {
|
func (g *Gitlab) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -28,33 +28,35 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type secretManagerTestCase struct {
|
type secretManagerTestCase struct {
|
||||||
mockClient *fakegitlab.GitlabMockClient
|
mockClient *fakegitlab.GitlabMockClient
|
||||||
apiInputProjectID string
|
apiInputProjectID string
|
||||||
apiInputKey string
|
apiInputKey string
|
||||||
apiOutput *gitlab.ProjectVariable
|
apiOutput *gitlab.ProjectVariable
|
||||||
apiResponse *gitlab.Response
|
apiResponse *gitlab.Response
|
||||||
ref *esv1beta1.ExternalSecretDataRemoteRef
|
ref *esv1beta1.ExternalSecretDataRemoteRef
|
||||||
projectID *string
|
projectID *string
|
||||||
apiErr error
|
apiErr error
|
||||||
expectError string
|
expectError string
|
||||||
expectedSecret string
|
expectedSecret string
|
||||||
|
expectedValidationResult esv1beta1.ValidationResult
|
||||||
// for testing secretmap
|
// for testing secretmap
|
||||||
expectedData map[string][]byte
|
expectedData map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeValidSecretManagerTestCase() *secretManagerTestCase {
|
func makeValidSecretManagerTestCase() *secretManagerTestCase {
|
||||||
smtc := secretManagerTestCase{
|
smtc := secretManagerTestCase{
|
||||||
mockClient: &fakegitlab.GitlabMockClient{},
|
mockClient: &fakegitlab.GitlabMockClient{},
|
||||||
apiInputProjectID: makeValidAPIInputProjectID(),
|
apiInputProjectID: makeValidAPIInputProjectID(),
|
||||||
apiInputKey: makeValidAPIInputKey(),
|
apiInputKey: makeValidAPIInputKey(),
|
||||||
ref: makeValidRef(),
|
ref: makeValidRef(),
|
||||||
projectID: nil,
|
projectID: nil,
|
||||||
apiOutput: makeValidAPIOutput(),
|
apiOutput: makeValidAPIOutput(),
|
||||||
apiResponse: makeValidAPIResponse(),
|
apiResponse: makeValidAPIResponse(),
|
||||||
apiErr: nil,
|
apiErr: nil,
|
||||||
expectError: "",
|
expectError: "",
|
||||||
expectedSecret: "",
|
expectedSecret: "",
|
||||||
expectedData: map[string][]byte{},
|
expectedValidationResult: esv1beta1.ValidationResultReady,
|
||||||
|
expectedData: map[string][]byte{},
|
||||||
}
|
}
|
||||||
smtc.mockClient.WithValue(smtc.apiInputProjectID, smtc.apiInputKey, smtc.apiOutput, smtc.apiResponse, smtc.apiErr)
|
smtc.mockClient.WithValue(smtc.apiInputProjectID, smtc.apiInputKey, smtc.apiOutput, smtc.apiResponse, smtc.apiErr)
|
||||||
return &smtc
|
return &smtc
|
||||||
|
@ -104,22 +106,26 @@ func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTest
|
||||||
var setAPIErr = func(smtc *secretManagerTestCase) {
|
var setAPIErr = func(smtc *secretManagerTestCase) {
|
||||||
smtc.apiErr = fmt.Errorf("oh no")
|
smtc.apiErr = fmt.Errorf("oh no")
|
||||||
smtc.expectError = "oh no"
|
smtc.expectError = "oh no"
|
||||||
|
smtc.expectedValidationResult = esv1beta1.ValidationResultError
|
||||||
}
|
}
|
||||||
|
|
||||||
var setListAPIErr = func(smtc *secretManagerTestCase) {
|
var setListAPIErr = func(smtc *secretManagerTestCase) {
|
||||||
err := fmt.Errorf("oh no")
|
err := fmt.Errorf("oh no")
|
||||||
smtc.apiErr = err
|
smtc.apiErr = err
|
||||||
smtc.expectError = fmt.Errorf(errList, err).Error()
|
smtc.expectError = fmt.Errorf(errList, err).Error()
|
||||||
|
smtc.expectedValidationResult = esv1beta1.ValidationResultError
|
||||||
}
|
}
|
||||||
|
|
||||||
var setListAPIRespNil = func(smtc *secretManagerTestCase) {
|
var setListAPIRespNil = func(smtc *secretManagerTestCase) {
|
||||||
smtc.apiResponse = nil
|
smtc.apiResponse = nil
|
||||||
smtc.expectError = errAuth
|
smtc.expectError = errAuth
|
||||||
|
smtc.expectedValidationResult = esv1beta1.ValidationResultError
|
||||||
}
|
}
|
||||||
|
|
||||||
var setListAPIRespBadCode = func(smtc *secretManagerTestCase) {
|
var setListAPIRespBadCode = func(smtc *secretManagerTestCase) {
|
||||||
smtc.apiResponse.StatusCode = http.StatusUnauthorized
|
smtc.apiResponse.StatusCode = http.StatusUnauthorized
|
||||||
smtc.expectError = errAuth
|
smtc.expectError = errAuth
|
||||||
|
smtc.expectedValidationResult = esv1beta1.ValidationResultError
|
||||||
}
|
}
|
||||||
|
|
||||||
var setNilMockClient = func(smtc *secretManagerTestCase) {
|
var setNilMockClient = func(smtc *secretManagerTestCase) {
|
||||||
|
@ -172,10 +178,13 @@ func TestValidate(t *testing.T) {
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
sm.client = v.mockClient
|
sm.client = v.mockClient
|
||||||
t.Logf("%+v", v)
|
t.Logf("%+v", v)
|
||||||
err := sm.Validate()
|
validationResult, err := sm.Validate()
|
||||||
if !ErrorContains(err, v.expectError) {
|
if !ErrorContains(err, v.expectError) {
|
||||||
t.Errorf("[%d], unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
|
t.Errorf("[%d], unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
|
||||||
}
|
}
|
||||||
|
if validationResult != v.expectedValidationResult {
|
||||||
|
t.Errorf("[%d], unexpected validationResult: %s, expected: '%s'", k, validationResult, v.expectedValidationResult)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -496,8 +496,8 @@ func (ibm *providerIBM) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ibm *providerIBM) Validate() error {
|
func (ibm *providerIBM) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
|
func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -241,7 +241,7 @@ func (k *BaseClient) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySel
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *ProviderKubernetes) Validate() error {
|
func (k *ProviderKubernetes) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
authReview, err := k.ReviewClient.Create(ctx, &authv1.SelfSubjectAccessReview{
|
authReview, err := k.ReviewClient.Create(ctx, &authv1.SelfSubjectAccessReview{
|
||||||
|
@ -255,14 +255,14 @@ func (k *ProviderKubernetes) Validate() error {
|
||||||
}, metav1.CreateOptions{})
|
}, metav1.CreateOptions{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not verify if client is valid: %w", err)
|
return esv1beta1.ValidationResultUnknown, fmt.Errorf("could not verify if client is valid: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !authReview.Status.Allowed {
|
if !authReview.Status.Allowed {
|
||||||
return fmt.Errorf("client is not allowed to get secrets")
|
return esv1beta1.ValidationResultError, fmt.Errorf("client is not allowed to get secrets")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *ProviderKubernetes) ValidateStore(store esv1beta1.GenericStore) error {
|
func (k *ProviderKubernetes) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -373,10 +373,14 @@ func TestValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
fakeClient := fakeReviewClient{authReview: &authReview}
|
fakeClient := fakeReviewClient{authReview: &authReview}
|
||||||
k := ProviderKubernetes{ReviewClient: fakeClient}
|
k := ProviderKubernetes{ReviewClient: fakeClient}
|
||||||
err := k.Validate()
|
validationResult, err := k.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Test Failed! %v", err)
|
t.Errorf("Test Failed! %v", err)
|
||||||
}
|
}
|
||||||
|
if validationResult != esv1beta1.ValidationResultReady {
|
||||||
|
t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultReady, validationResult)
|
||||||
|
}
|
||||||
|
|
||||||
authReview = authv1.SelfSubjectAccessReview{
|
authReview = authv1.SelfSubjectAccessReview{
|
||||||
Status: authv1.SubjectAccessReviewStatus{
|
Status: authv1.SubjectAccessReviewStatus{
|
||||||
Allowed: false,
|
Allowed: false,
|
||||||
|
@ -384,15 +388,21 @@ func TestValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
fakeClient = fakeReviewClient{authReview: &authReview}
|
fakeClient = fakeReviewClient{authReview: &authReview}
|
||||||
k = ProviderKubernetes{ReviewClient: fakeClient}
|
k = ProviderKubernetes{ReviewClient: fakeClient}
|
||||||
err = k.Validate()
|
validationResult, err = k.Validate()
|
||||||
if err.Error() != "client is not allowed to get secrets" {
|
if err.Error() != "client is not allowed to get secrets" {
|
||||||
t.Errorf("Test Failed! Wanted client is not allowed to get secrets got: %v", err)
|
t.Errorf("Test Failed! Wanted client is not allowed to get secrets got: %v", err)
|
||||||
}
|
}
|
||||||
|
if validationResult != esv1beta1.ValidationResultError {
|
||||||
|
t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultError, validationResult)
|
||||||
|
}
|
||||||
|
|
||||||
fakeClient = fakeReviewClient{}
|
fakeClient = fakeReviewClient{}
|
||||||
k = ProviderKubernetes{ReviewClient: fakeClient}
|
k = ProviderKubernetes{ReviewClient: fakeClient}
|
||||||
err = k.Validate()
|
validationResult, err = k.Validate()
|
||||||
if err.Error() != "could not verify if client is valid: Something went wrong" {
|
if err.Error() != "could not verify if client is valid: Something went wrong" {
|
||||||
t.Errorf("Test Failed! Wanted could not verify if client is valid: Something went wrong got: %v", err)
|
t.Errorf("Test Failed! Wanted could not verify if client is valid: Something went wrong got: %v", err)
|
||||||
}
|
}
|
||||||
|
if validationResult != esv1beta1.ValidationResultUnknown {
|
||||||
|
t.Errorf("Test Failed! Wanted could not indicate validationResult is %s, got: %s", esv1beta1.ValidationResultUnknown, validationResult)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,8 +223,8 @@ func (vms *VaultManagementService) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vms *VaultManagementService) Validate() error {
|
func (vms *VaultManagementService) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) error {
|
func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -26,8 +26,7 @@ var _ esv1beta1.Provider = &Client{}
|
||||||
|
|
||||||
// Client is a fake client for testing.
|
// Client is a fake client for testing.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
NewFn func(context.Context, esv1beta1.GenericStore, client.Client,
|
NewFn func(context.Context, esv1beta1.GenericStore, client.Client, string) (esv1beta1.SecretsClient, error)
|
||||||
string) (esv1beta1.SecretsClient, error)
|
|
||||||
GetSecretFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error)
|
GetSecretFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error)
|
||||||
GetSecretMapFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
|
GetSecretMapFn func(context.Context, esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
|
||||||
GetAllSecretsFn func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error)
|
GetAllSecretsFn func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error)
|
||||||
|
@ -59,7 +58,7 @@ func (v *Client) RegisterAs(provider *esv1beta1.SecretStoreProvider) {
|
||||||
esv1beta1.ForceRegister(v, provider)
|
esv1beta1.ForceRegister(v, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecret implements the provider.Provider interface.
|
// GetAllSecrets implements the provider.Provider interface.
|
||||||
func (v *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
func (v *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
||||||
return v.GetAllSecretsFn(ctx, ref)
|
return v.GetAllSecretsFn(ctx, ref)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +76,7 @@ func (v *Client) WithGetSecret(secData []byte, err error) *Client {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecretMap imeplements the provider.Provider interface.
|
// GetSecretMap implements the provider.Provider interface.
|
||||||
func (v *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
func (v *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
||||||
return v.GetSecretMapFn(ctx, ref)
|
return v.GetSecretMapFn(ctx, ref)
|
||||||
}
|
}
|
||||||
|
@ -86,8 +85,8 @@ func (v *Client) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Client) Validate() error {
|
func (v *Client) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Client) ValidateStore(store esv1beta1.GenericStore) error {
|
func (v *Client) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
|
@ -576,12 +576,12 @@ func (v *client) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *client) Validate() error {
|
func (v *client) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
_, err := checkToken(context.Background(), v)
|
_, err := checkToken(context.Background(), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(errInvalidCredentials, err)
|
return esv1beta1.ValidationResultError, fmt.Errorf(errInvalidCredentials, err)
|
||||||
}
|
}
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *client) buildMetadataPath(path string) (string, error) {
|
func (v *client) buildMetadataPath(path string) (string, error) {
|
||||||
|
|
|
@ -394,11 +394,14 @@ func (w *WebHook) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebHook) Validate() error {
|
func (w *WebHook) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
timeout := 15 * time.Second
|
timeout := 15 * time.Second
|
||||||
url := w.url
|
url := w.url
|
||||||
|
|
||||||
return utils.NetworkValidate(url, timeout)
|
if err := utils.NetworkValidate(url, timeout); err != nil {
|
||||||
|
return esv1beta1.ValidationResultError, err
|
||||||
|
}
|
||||||
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeTemplateString(tmpl string, data map[string]map[string]string) (string, error) {
|
func executeTemplateString(tmpl string, data map[string]map[string]string) (string, error) {
|
||||||
|
|
|
@ -288,8 +288,8 @@ func (c *lockboxSecretsClient) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *lockboxSecretsClient) Validate() error {
|
func (c *lockboxSecretsClient) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
return nil
|
return esv1beta1.ValidationResultReady, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getValueAsIs(entry *lockbox.Payload_Entry) (interface{}, error) {
|
func getValueAsIs(entry *lockbox.Payload_Entry) (interface{}, error) {
|
||||||
|
|
Loading…
Reference in a new issue