Преглед изворни кода

Implemented Checks for Capabilities on the SecretStore definition

Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>
Signed-off-by: Marcin Kubica <marcin.kubica@engineerbetter.com>
Signed-off-by: William Young <will.young@engineerbetter.com>
Signed-off-by: Dominic Meddick <dom.meddick@engineerbetter.com>
Gustavo Carvalho пре 4 година
родитељ
комит
c41d43e083

+ 2 - 0
apis/externalsecrets/v1beta1/provider.go

@@ -44,6 +44,8 @@ type Provider interface {
 
 	// ValidateStore checks if the provided store is valid
 	ValidateStore(store GenericStore) error
+	// Capabilities returns the provider Capabilities (Read, Write, ReadWrite)
+	Capabilities() SecretStoreCapabilities
 }
 
 // +kubebuilder:object:root=false

+ 4 - 0
apis/externalsecrets/v1beta1/provider_schema_test.go

@@ -25,6 +25,10 @@ type PP struct{}
 
 const shouldBeRegistered = "provider should be registered"
 
+func (p *PP) Capabilities() SecretStoreCapabilities {
+	return SecretStoreReadOnly
+}
+
 // New constructs a SecretsManager Provider.
 func (p *PP) NewClient(ctx context.Context, store GenericStore, kube client.Client, namespace string) (SecretsClient, error) {
 	return p, nil

+ 14 - 0
apis/externalsecrets/v1beta1/secretstore_types.go

@@ -137,10 +137,21 @@ type SecretStoreStatusCondition struct {
 	LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
 }
 
+// SecretStoreCapabilities defines the possible operations a SecretStore can do.
+type SecretStoreCapabilities string
+
+const (
+	SecretStoreReadOnly  SecretStoreCapabilities = "ReadOnly"
+	SecretStoreWriteOnly SecretStoreCapabilities = "WriteOnly"
+	SecretStoreReadWrite SecretStoreCapabilities = "ReadWrite"
+)
+
 // SecretStoreStatus defines the observed state of the SecretStore.
 type SecretStoreStatus struct {
 	// +optional
 	Conditions []SecretStoreStatusCondition `json:"conditions"`
+	// +optional
+	Capabilities SecretStoreCapabilities `json:"capabilities"`
 }
 
 // +kubebuilder:object:root=true
@@ -149,6 +160,7 @@ type SecretStoreStatus struct {
 // SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields.
 // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
 // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
+// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities`
 // +kubebuilder:subresource:status
 // +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=ss
 type SecretStore struct {
@@ -173,6 +185,8 @@ type SecretStoreList struct {
 
 // ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields.
 // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
+// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
+// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities`
 // +kubebuilder:subresource:status
 // +kubebuilder:resource:scope=Cluster,categories={externalsecrets},shortName=css
 type ClusterSecretStore struct {

+ 10 - 0
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -1387,6 +1387,12 @@ spec:
     - jsonPath: .metadata.creationTimestamp
       name: AGE
       type: date
+    - jsonPath: .status.conditions[?(@.type=="Ready")].reason
+      name: Status
+      type: string
+    - jsonPath: .status.capabilities
+      name: Capabilities
+      type: string
     name: v1beta1
     schema:
       openAPIV3Schema:
@@ -2885,6 +2891,10 @@ spec:
           status:
             description: SecretStoreStatus defines the observed state of the SecretStore.
             properties:
+              capabilities:
+                description: SecretStoreCapabilities defines the possible operations
+                  a SecretStore can do.
+                type: string
               conditions:
                 items:
                   properties:

+ 7 - 0
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -1390,6 +1390,9 @@ spec:
     - jsonPath: .status.conditions[?(@.type=="Ready")].reason
       name: Status
       type: string
+    - jsonPath: .status.capabilities
+      name: Capabilities
+      type: string
     name: v1beta1
     schema:
       openAPIV3Schema:
@@ -2888,6 +2891,10 @@ spec:
           status:
             description: SecretStoreStatus defines the observed state of the SecretStore.
             properties:
+              capabilities:
+                description: SecretStoreCapabilities defines the possible operations
+                  a SecretStore can do.
+                type: string
               conditions:
                 items:
                   properties:

+ 15 - 0
deploy/crds/bundle.yaml

@@ -1358,6 +1358,12 @@ spec:
         - jsonPath: .metadata.creationTimestamp
           name: AGE
           type: date
+        - jsonPath: .status.conditions[?(@.type=="Ready")].reason
+          name: Status
+          type: string
+        - jsonPath: .status.capabilities
+          name: Capabilities
+          type: string
       name: v1beta1
       schema:
         openAPIV3Schema:
@@ -2456,6 +2462,9 @@ spec:
             status:
               description: SecretStoreStatus defines the observed state of the SecretStore.
               properties:
+                capabilities:
+                  description: SecretStoreCapabilities defines the possible operations a SecretStore can do.
+                  type: string
                 conditions:
                   items:
                     properties:
@@ -4179,6 +4188,9 @@ spec:
         - jsonPath: .status.conditions[?(@.type=="Ready")].reason
           name: Status
           type: string
+        - jsonPath: .status.capabilities
+          name: Capabilities
+          type: string
       name: v1beta1
       schema:
         openAPIV3Schema:
@@ -5277,6 +5289,9 @@ spec:
             status:
               description: SecretStoreStatus defines the observed state of the SecretStore.
               properties:
+                capabilities:
+                  description: SecretStoreCapabilities defines the possible operations a SecretStore can do.
+                  type: string
                 conditions:
                   items:
                     properties:

+ 9 - 0
pkg/controllers/secretstore/common.go

@@ -66,6 +66,15 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
 	if err != nil {
 		return ctrl.Result{}, err
 	}
+	storeProvider, err := esapi.GetProvider(ss)
+	if err != nil {
+		return ctrl.Result{}, err
+	}
+	capStatus := esapi.SecretStoreStatus{
+		Capabilities: storeProvider.Capabilities(),
+		Conditions:   ss.GetStatus().Conditions,
+	}
+	ss.SetStatus(capStatus)
 
 	recorder.Event(ss, v1.EventTypeNormal, esapi.ReasonStoreValid, msgStoreValidated)
 	cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionTrue, esapi.ReasonStoreValid, msgStoreValidated)

+ 5 - 0
pkg/provider/akeyless/akeyless.go

@@ -66,6 +66,11 @@ func init() {
 	})
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a new secrets client based on the provided store.
 func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	return newClient(ctx, store, kube, namespace)

+ 5 - 0
pkg/provider/alibaba/kms.go

@@ -172,6 +172,11 @@ func (kms *KeyManagementService) GetSecretMap(ctx context.Context, ref esv1beta1
 	return secretData, nil
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (kms *KeyManagementService) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a new secrets client based on the provided store.
 func (kms *KeyManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()

+ 5 - 0
pkg/provider/aws/provider.go

@@ -41,6 +41,11 @@ const (
 	errRegionNotFound         = "region not found: %s"
 )
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a new secrets client based on the provided store.
 func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	return newClient(ctx, store, kube, namespace, awsauth.DefaultSTSProvider)

+ 5 - 0
pkg/provider/azure/keyvault/keyvault.go

@@ -106,6 +106,11 @@ func init() {
 	})
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (a *Azure) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a new secrets client based on the provided store.
 func (a *Azure) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	return newClient(ctx, store, kube, namespace)

+ 5 - 0
pkg/provider/fake/fake.go

@@ -34,6 +34,11 @@ type Provider struct {
 	config *esv1beta1.FakeProvider
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	cfg, err := getProvider(store)
 	if err != nil {

+ 5 - 0
pkg/provider/gcp/secretmanager/secretsmanager.go

@@ -160,6 +160,11 @@ func serviceAccountTokenSource(ctx context.Context, store esv1beta1.GenericStore
 	return config.TokenSource(ctx), nil
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (sm *ProviderGCP) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a GCP Provider.
 func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()

+ 5 - 0
pkg/provider/gitlab/gitlab.go

@@ -114,6 +114,11 @@ func NewGitlabProvider() *Gitlab {
 	return &Gitlab{}
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (g *Gitlab) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // Method on Gitlab Provider to set up client with credentials and populate projectID.
 func (g *Gitlab) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()

+ 5 - 0
pkg/provider/ibm/provider.go

@@ -525,6 +525,11 @@ func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
 	return nil
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (ibm *providerIBM) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()
 	ibmSpec := storeSpec.Provider.IBM

+ 5 - 0
pkg/provider/kubernetes/kubernetes.go

@@ -79,6 +79,11 @@ func init() {
 	})
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (k *ProviderKubernetes) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a Kubernetes Provider.
 func (k *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()

+ 5 - 0
pkg/provider/oracle/oracle.go

@@ -130,6 +130,11 @@ func (vms *VaultManagementService) GetSecretMap(ctx context.Context, ref esv1bet
 	return secretData, nil
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (vms *VaultManagementService) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a new secrets client based on the provided store.
 func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()

+ 5 - 0
pkg/provider/senhasegura/provider.go

@@ -43,6 +43,11 @@ const (
 	errMissingClientID            = "missing senhasegura authentication Client ID"
 )
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 /*
 	Construct a new secrets client based on provided store
 */

+ 5 - 0
pkg/provider/testing/fake/fake.go

@@ -122,6 +122,11 @@ func (v *Client) WithNew(f func(context.Context, esv1beta1.GenericStore, client.
 	return v
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (v *Client) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient returns a new fake provider.
 func (v *Client) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	c, err := v.NewFn(ctx, store, kube, namespace)

+ 5 - 0
pkg/provider/vault/vault.go

@@ -224,6 +224,11 @@ type connector struct {
 	newVaultClient func(c *vault.Config) (Client, error)
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (c *connector) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 func (c *connector) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	// controller-runtime/client does not support TokenRequest or other subresource APIs
 	// so we need to construct our own client and use it to fetch tokens

+ 5 - 0
pkg/provider/webhook/webhook.go

@@ -61,6 +61,11 @@ func init() {
 	})
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	whClient := &WebHook{
 		kube:      kube,

+ 35 - 1
pkg/provider/yandex/lockbox/lockbox.go

@@ -30,7 +30,41 @@ import (
 
 var log = ctrl.Log.WithName("provider").WithName("yandex").WithName("lockbox")
 
-func adaptInput(store esv1beta1.GenericStore) (*common.SecretsClientInput, error) {
+type iamTokenKey struct {
+	authorizedKeyID  string
+	serviceAccountID string
+	privateKeyHash   string
+}
+
+// https://github.com/external-secrets/external-secrets/issues/644
+var _ esv1beta1.SecretsClient = &lockboxSecretsClient{}
+var _ esv1beta1.Provider = &lockboxProvider{}
+
+// lockboxProvider is a provider for Yandex Lockbox.
+type lockboxProvider struct {
+	yandexCloudCreator client.YandexCloudCreator
+
+	lockboxClientMap      map[string]client.LockboxClient // apiEndpoint -> LockboxClient
+	lockboxClientMapMutex sync.Mutex
+	iamTokenMap           map[iamTokenKey]*client.IamToken
+	iamTokenMapMutex      sync.Mutex
+}
+
+func newLockboxProvider(yandexCloudCreator client.YandexCloudCreator) *lockboxProvider {
+	return &lockboxProvider{
+		yandexCloudCreator: yandexCloudCreator,
+		lockboxClientMap:   make(map[string]client.LockboxClient),
+		iamTokenMap:        make(map[iamTokenKey]*client.IamToken),
+	}
+}
+
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *lockboxProvider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
+// NewClient constructs a Yandex Lockbox Provider.
+func (p *lockboxProvider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()
 	if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.YandexLockbox == nil {
 		return nil, fmt.Errorf("received invalid Yandex Lockbox SecretStore resource")