1
0
Fork 0
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:
Merlin 2022-04-06 06:02:19 +02:00 committed by Romain DARY
parent 84af221762
commit 4820cc9165
31 changed files with 169 additions and 83 deletions

View file

@ -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"

View file

@ -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)

View file

@ -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 {

View file

@ -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.

View file

@ -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.")
} }

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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
} }

View file

@ -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())

View file

@ -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.

View file

@ -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 {

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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) {

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}
} }
} }

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}
} }

View file

@ -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 {

View file

@ -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 {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {