Просмотр исходного кода

Merge branch 'beach-team' of https://github.com/external-secrets/external-secrets into HEAD

Conch Horse (EngineerBetterCI) 4 лет назад
Родитель
Сommit
c7d50e983c
48 измененных файлов с 4146 добавлено и 48 удалено
  1. 124 0
      apis/externalsecrets/v1alpha1/pushsecret_types.go
  2. 8 0
      apis/externalsecrets/v1alpha1/register.go
  3. 229 0
      apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
  4. 5 0
      apis/externalsecrets/v1beta1/provider.go
  5. 9 0
      apis/externalsecrets/v1beta1/provider_schema_test.go
  6. 24 0
      apis/externalsecrets/v1beta1/pushsecret_interfaces.go
  7. 14 0
      apis/externalsecrets/v1beta1/secretstore_types.go
  8. 11 0
      cmd/root.go
  9. 10 0
      config/crds/bases/external-secrets.io_clustersecretstores.yaml
  10. 142 0
      config/crds/bases/external-secrets.io_secretsinks.yaml
  11. 7 0
      config/crds/bases/external-secrets.io_secretstores.yaml
  12. 6 0
      deploy/charts/external-secrets/templates/rbac.yaml
  13. 159 0
      deploy/crds/bundle.yaml
  14. 8 8
      design/001-secretsink.md
  15. 3 3
      e2e/framework/addon/eso.go
  16. 8 8
      e2e/framework/addon/vault.go
  17. 784 0
      pkg/controllers/pushsecret/internal/fakes/client.go
  18. 1288 0
      pkg/controllers/pushsecret/internal/fakes/manager.go
  19. 196 0
      pkg/controllers/pushsecret/internal/fakes/statuswriter.go
  20. 228 0
      pkg/controllers/pushsecret/pushsecret_controller.go
  21. 316 0
      pkg/controllers/pushsecret/pushsecret_controller_test.go
  22. 44 0
      pkg/controllers/pushsecret/suite_test.go
  23. 9 0
      pkg/controllers/secretstore/common.go
  24. 33 0
      pkg/controllers/secretstore/common_test.go
  25. 9 0
      pkg/provider/akeyless/akeyless.go
  26. 9 0
      pkg/provider/alibaba/kms.go
  27. 6 1
      pkg/provider/aws/parameterstore/parameterstore.go
  28. 5 0
      pkg/provider/aws/provider.go
  29. 5 0
      pkg/provider/aws/secretsmanager/secretsmanager.go
  30. 10 0
      pkg/provider/azure/keyvault/keyvault.go
  31. 68 13
      pkg/provider/fake/fake.go
  32. 74 2
      pkg/provider/fake/fake_test.go
  33. 67 0
      pkg/provider/gcp/secretmanager/fake/fake.go
  34. 45 0
      pkg/provider/gcp/secretmanager/secretsmanager.go
  35. 41 0
      pkg/provider/gcp/secretmanager/secretsmanager_test.go
  36. 10 0
      pkg/provider/gitlab/gitlab.go
  37. 10 0
      pkg/provider/ibm/provider.go
  38. 10 0
      pkg/provider/kubernetes/kubernetes.go
  39. 10 0
      pkg/provider/onepassword/onepassword.go
  40. 10 0
      pkg/provider/oracle/oracle.go
  41. 5 0
      pkg/provider/senhasegura/dsm/dsm.go
  42. 5 0
      pkg/provider/senhasegura/provider.go
  43. 22 0
      pkg/provider/testing/fake/fake.go
  44. 12 3
      pkg/provider/vault/vault.go
  45. 10 0
      pkg/provider/webhook/webhook.go
  46. 6 1
      pkg/provider/yandex/common/provider.go
  47. 14 9
      pkg/provider/yandex/common/secretsclient.go
  48. 18 0
      pkg/provider/yandex/common/secretsetter.go

+ 124 - 0
apis/externalsecrets/v1alpha1/pushsecret_types.go

@@ -0,0 +1,124 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type PushSecretStoreRef struct {
+	// Name of the SecretStore resource
+	Name string `json:"name"`
+
+	// Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
+	// Defaults to `SecretStore`
+	// +optional
+	Kind string `json:"kind,omitempty"`
+}
+
+// PushSecretSpec configures the behavior of the PushSecret.
+type PushSecretSpec struct {
+	SecretStoreRefs []PushSecretStoreRef `json:"secretStoreRefs"`
+	Selector        PushSecretSelector   `json:"selector"`
+	Data            []PushSecretData     `json:"data,omitempty"`
+}
+
+type PushSecretSecret struct {
+	Name string `json:"name"`
+}
+
+type PushSecretSelector struct {
+	Secret PushSecretSecret `json:"secret"`
+}
+
+type PushSecretRemoteRefs struct {
+	RemoteKey string `json:"remoteKey"`
+}
+
+func (r PushSecretRemoteRefs) GetRemoteKey() string {
+	return r.RemoteKey
+}
+
+type PushSecretMatch struct {
+	SecretKey  string                 `json:"secretKey"`
+	RemoteRefs []PushSecretRemoteRefs `json:"remoteRefs"`
+}
+
+type PushSecretData struct {
+	Match []PushSecretMatch `json:"match"`
+}
+
+// PushSecretConditionType indicates the condition of the PushSecret.
+type PushSecretConditionType string
+
+const (
+	PushSecretReady PushSecretConditionType = "Ready"
+)
+
+// PushSecretStatusCondition indicates the status of the PushSecret.
+type PushSecretStatusCondition struct {
+	Type   PushSecretConditionType `json:"type"`
+	Status corev1.ConditionStatus  `json:"status"`
+
+	// +optional
+	Reason string `json:"reason,omitempty"`
+
+	// +optional
+	Message string `json:"message,omitempty"`
+
+	// +optional
+	LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
+}
+
+// PushSecretStatus indicates the history of the status of PushSecret.
+type PushSecretStatus struct {
+	// +nullable
+	// refreshTime is the time and date the external secret was fetched and
+	// the target secret updated
+	RefreshTime metav1.Time `json:"refreshTime,omitempty"`
+
+	// SyncedResourceVersion keeps track of the last synced version.
+	SyncedResourceVersion string `json:"syncedResourceVersion,omitempty"`
+
+	// +optional
+	Conditions []PushSecretStatusCondition `json:"conditions,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// PushSecrets is the Schema for the PushSecrets API.
+// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
+// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Namespaced,categories={PushSecrets}
+
+type PushSecret struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   PushSecretSpec   `json:"spec,omitempty"`
+	Status PushSecretStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
+// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
+// PushSecretList contains a list of PushSecret resources.
+type PushSecretList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []PushSecret `json:"items"`
+}

+ 8 - 0
apis/externalsecrets/v1alpha1/register.go

@@ -60,8 +60,16 @@ var (
 	ClusterSecretStoreGroupVersionKind = SchemeGroupVersion.WithKind(ClusterSecretStoreKind)
 )
 
+var (
+	PushSecretKind             = reflect.TypeOf(PushSecret{}).Name()
+	PushSecretGroupKind        = schema.GroupKind{Group: Group, Kind: PushSecretKind}.String()
+	PushSecretKindAPIVersion   = PushSecretKind + "." + SchemeGroupVersion.String()
+	PushSecretGroupVersionKind = SchemeGroupVersion.WithKind(PushSecretKind)
+)
+
 func init() {
 	SchemeBuilder.Register(&ExternalSecret{}, &ExternalSecretList{})
 	SchemeBuilder.Register(&SecretStore{}, &SecretStoreList{})
 	SchemeBuilder.Register(&ClusterSecretStore{}, &ClusterSecretStoreList{})
+	SchemeBuilder.Register(&PushSecret{}, &PushSecretList{})
 }

+ 229 - 0
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -964,6 +964,235 @@ func (in *OracleSecretRef) DeepCopy() *OracleSecretRef {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecret) DeepCopyInto(out *PushSecret) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecret.
+func (in *PushSecret) DeepCopy() *PushSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PushSecret) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretData) DeepCopyInto(out *PushSecretData) {
+	*out = *in
+	if in.Match != nil {
+		in, out := &in.Match, &out.Match
+		*out = make([]PushSecretMatch, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretData.
+func (in *PushSecretData) DeepCopy() *PushSecretData {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretData)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretList) DeepCopyInto(out *PushSecretList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]PushSecret, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretList.
+func (in *PushSecretList) DeepCopy() *PushSecretList {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PushSecretList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretMatch) DeepCopyInto(out *PushSecretMatch) {
+	*out = *in
+	if in.RemoteRefs != nil {
+		in, out := &in.RemoteRefs, &out.RemoteRefs
+		*out = make([]PushSecretRemoteRefs, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretMatch.
+func (in *PushSecretMatch) DeepCopy() *PushSecretMatch {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretMatch)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretRemoteRefs) DeepCopyInto(out *PushSecretRemoteRefs) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretRemoteRefs.
+func (in *PushSecretRemoteRefs) DeepCopy() *PushSecretRemoteRefs {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretRemoteRefs)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretSecret) DeepCopyInto(out *PushSecretSecret) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretSecret.
+func (in *PushSecretSecret) DeepCopy() *PushSecretSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretSelector) DeepCopyInto(out *PushSecretSelector) {
+	*out = *in
+	out.Secret = in.Secret
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretSelector.
+func (in *PushSecretSelector) DeepCopy() *PushSecretSelector {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretSelector)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretSpec) DeepCopyInto(out *PushSecretSpec) {
+	*out = *in
+	if in.SecretStoreRefs != nil {
+		in, out := &in.SecretStoreRefs, &out.SecretStoreRefs
+		*out = make([]PushSecretStoreRef, len(*in))
+		copy(*out, *in)
+	}
+	out.Selector = in.Selector
+	if in.Data != nil {
+		in, out := &in.Data, &out.Data
+		*out = make([]PushSecretData, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretSpec.
+func (in *PushSecretSpec) DeepCopy() *PushSecretSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretStatus) DeepCopyInto(out *PushSecretStatus) {
+	*out = *in
+	in.RefreshTime.DeepCopyInto(&out.RefreshTime)
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]PushSecretStatusCondition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretStatus.
+func (in *PushSecretStatus) DeepCopy() *PushSecretStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretStatusCondition) DeepCopyInto(out *PushSecretStatusCondition) {
+	*out = *in
+	in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecetStatusCondition.
+func (in *PushSecretStatusCondition) DeepCopy() *PushSecretStatusCondition {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretStatusCondition)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PushSecretStoreRef) DeepCopyInto(out *PushSecretStoreRef) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretStoreRef.
+func (in *PushSecretStoreRef) DeepCopy() *PushSecretStoreRef {
+	if in == nil {
+		return nil
+	}
+	out := new(PushSecretStoreRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *SecretStore) DeepCopyInto(out *SecretStore) {
 	*out = *in

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

@@ -51,6 +51,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
@@ -65,6 +67,9 @@ type SecretsClient interface {
 	// then the secret entry will be deleted depending on the deletionPolicy.
 	GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error)
 
+	// SetSecret will write a single secret into the provider
+	SetSecret(ctx context.Context, value []byte, remoteRef PushRemoteRef) 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.

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

@@ -25,11 +25,20 @@ 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
 }
 
+// SetSecret writes a single secret into a provider.
+func (p *PP) SetSecret(ctx context.Context, value []byte, remoteRef PushRemoteRef) error {
+	return nil
+}
+
 // GetSecret returns a single secret from the provider.
 func (p *PP) GetSecret(ctx context.Context, ref ExternalSecretDataRemoteRef) ([]byte, error) {
 	return []byte("NOOP"), nil

+ 24 - 0
apis/externalsecrets/v1beta1/pushsecret_interfaces.go

@@ -0,0 +1,24 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package v1beta1
+
+// +kubebuilder:object:root=false
+// +kubebuilder:object:generate:false
+// +k8s:deepcopy-gen:interfaces=nil
+// +k8s:deepcopy-gen=nil
+
+// This interface is to allow using v1alpha1 content in Provider registered in v1beta1.
+type PushRemoteRef interface {
+	GetRemoteKey() string
+}

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

+ 11 - 0
cmd/root.go

@@ -37,6 +37,7 @@ import (
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret"
 	"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret"
+	"github.com/external-secrets/external-secrets/pkg/controllers/pushsecret"
 	"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
 )
 
@@ -146,6 +147,16 @@ var rootCmd = &cobra.Command{
 			setupLog.Error(err, errCreateController, "controller", "ExternalSecret")
 			os.Exit(1)
 		}
+		if err = (&pushsecret.Reconciler{
+			Client:          mgr.GetClient(),
+			Log:             ctrl.Log.WithName("controllers").WithName("PushSecret"),
+			Scheme:          mgr.GetScheme(),
+			ControllerClass: controllerClass,
+			RequeueInterval: time.Hour,
+		}).SetupWithManager(mgr); err != nil {
+			setupLog.Error(err, errCreateController, "controller", "PushSecret")
+			os.Exit(1)
+		}
 		if enableClusterExternalSecretReconciler {
 			if err = (&clusterexternalsecret.Reconciler{
 				Client:          mgr.GetClient(),

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

+ 142 - 0
config/crds/bases/external-secrets.io_secretsinks.yaml

@@ -0,0 +1,142 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.0
+  creationTimestamp: null
+  name: pushsecrets.external-secrets.io
+spec:
+  group: external-secrets.io
+  names:
+    categories:
+    - pushsecrets
+    kind: PushSecret
+    listKind: PushSecretList
+    plural: pushsecrets
+    singular: pushsecret
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .metadata.creationTimestamp
+      name: AGE
+      type: date
+    - jsonPath: .status.conditions[?(@.type=="Ready")].reason
+      name: Status
+      type: string
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: PushSecretSpec configures the behavior of the PushSecret.
+            properties:
+              data:
+                items:
+                  properties:
+                    match:
+                      items:
+                        properties:
+                          remoteRefs:
+                            items:
+                              properties:
+                                remoteKey:
+                                  type: string
+                              required:
+                              - remoteKey
+                              type: object
+                            type: array
+                          secretKey:
+                            type: string
+                        required:
+                        - remoteRefs
+                        - secretKey
+                        type: object
+                      type: array
+                  required:
+                  - match
+                  type: object
+                type: array
+              secretStoreRefs:
+                items:
+                  properties:
+                    kind:
+                      description: Kind of the SecretStore resource (SecretStore or
+                        ClusterSecretStore) Defaults to `SecretStore`
+                      type: string
+                    name:
+                      description: Name of the SecretStore resource
+                      type: string
+                  required:
+                  - name
+                  type: object
+                type: array
+              selector:
+                properties:
+                  secret:
+                    properties:
+                      name:
+                        type: string
+                    required:
+                    - name
+                    type: object
+                required:
+                - secret
+                type: object
+            required:
+            - secretStoreRefs
+            - selector
+            type: object
+          status:
+            description: PushSecretStatus indicates the history of the status of PushSecret.
+            properties:
+              conditions:
+                items:
+                  description: PushSecretStatusCondition indicates the status of the
+                    PushSecret.
+                  properties:
+                    lastTransitionTime:
+                      format: date-time
+                      type: string
+                    message:
+                      type: string
+                    reason:
+                      type: string
+                    status:
+                      type: string
+                    type:
+                      description: PushSecretConditionType indicates the condition
+                        of the PushSecret.
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              refreshTime:
+                description: refreshTime is the time and date the external secret
+                  was fetched and the target secret updated
+                format: date-time
+                nullable: true
+                type: string
+              syncedResourceVersion:
+                description: SyncedResourceVersion keeps track of the last synced
+                  version.
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}

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

+ 6 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -20,6 +20,7 @@ rules:
     - "clustersecretstores"
     - "externalsecrets"
     - "clusterexternalsecrets"
+    - "pushsecrets"
     verbs:
     - "get"
     - "list"
@@ -39,6 +40,9 @@ rules:
     - "clusterexternalsecrets"
     - "clusterexternalsecrets/status"
     - "clusterexternalsecrets/finalizers"
+    - "pushsecrets"
+    - "pushsecrets/status"
+    - "pushsecrets/finalizers"
     verbs:
     - "update"
     - "patch"
@@ -115,6 +119,7 @@ rules:
       - "externalsecrets"
       - "secretstores"
       - "clustersecretstores"
+      - "pushsecrets"
     verbs:
       - "get"
       - "watch"
@@ -142,6 +147,7 @@ rules:
       - "externalsecrets"
       - "secretstores"
       - "clustersecretstores"
+      - "pushsecrets"
     verbs:
       - "create"
       - "delete"

+ 159 - 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:
@@ -3001,6 +3010,150 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.0
+  creationTimestamp: null
+  name: pushsecrets.external-secrets.io
+spec:
+  group: external-secrets.io
+  names:
+    categories:
+      - pushsecrets
+    kind: PushSecret
+    listKind: PushSecretList
+    plural: pushsecrets
+    singular: pushsecret
+  scope: Namespaced
+  versions:
+    - additionalPrinterColumns:
+        - jsonPath: .metadata.creationTimestamp
+          name: AGE
+          type: date
+        - jsonPath: .status.conditions[?(@.type=="Ready")].reason
+          name: Status
+          type: string
+      name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: PushSecretSpec configures the behavior of the PushSecret.
+              properties:
+                data:
+                  items:
+                    properties:
+                      match:
+                        items:
+                          properties:
+                            remoteRefs:
+                              items:
+                                properties:
+                                  remoteKey:
+                                    type: string
+                                required:
+                                  - remoteKey
+                                type: object
+                              type: array
+                            secretKey:
+                              type: string
+                          required:
+                            - remoteRefs
+                            - secretKey
+                          type: object
+                        type: array
+                    required:
+                      - match
+                    type: object
+                  type: array
+                secretStoreRefs:
+                  items:
+                    properties:
+                      kind:
+                        description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore`
+                        type: string
+                      name:
+                        description: Name of the SecretStore resource
+                        type: string
+                    required:
+                      - name
+                    type: object
+                  type: array
+                selector:
+                  properties:
+                    secret:
+                      properties:
+                        name:
+                          type: string
+                      required:
+                        - name
+                      type: object
+                  required:
+                    - secret
+                  type: object
+              required:
+                - secretStoreRefs
+                - selector
+              type: object
+            status:
+              description: PushSecretStatus indicates the history of the status of PushSecret.
+              properties:
+                conditions:
+                  items:
+                    description: PushSecretStatusCondition indicates the status of the PushSecret.
+                    properties:
+                      lastTransitionTime:
+                        format: date-time
+                        type: string
+                      message:
+                        type: string
+                      reason:
+                        type: string
+                      status:
+                        type: string
+                      type:
+                        description: PushSecretConditionType indicates the condition of the PushSecret.
+                        type: string
+                    required:
+                      - status
+                      - type
+                    type: object
+                  type: array
+                refreshTime:
+                  description: refreshTime is the time and date the external secret was fetched and the target secret updated
+                  format: date-time
+                  nullable: true
+                  type: string
+                syncedResourceVersion:
+                  description: SyncedResourceVersion keeps track of the last synced version.
+                  type: string
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
+  conversion:
+    strategy: Webhook
+    webhook:
+      conversionReviewVersions:
+        - v1
+      clientConfig:
+        service:
+          name: kubernetes
+          namespace: default
+          path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.9.0
@@ -4030,6 +4183,9 @@ spec:
         - jsonPath: .status.conditions[?(@.type=="Ready")].reason
           name: Status
           type: string
+        - jsonPath: .status.capabilities
+          name: Capabilities
+          type: string
       name: v1beta1
       schema:
         openAPIV3Schema:
@@ -5128,6 +5284,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:

+ 8 - 8
design/001-secretsink.md

@@ -1,6 +1,6 @@
 ```yaml
 ---
-title: SecretSink
+title: PushSecret
 version: v1alpha1
 authors: 
 creation-date: 2022-01-25
@@ -18,13 +18,13 @@ status: draft
 
 
 ## Summary
-The Secret Sink is a feature to allow Secrets from Kubernetes to be saved back into some providers. Where ExternalSecret is responsible to download a Secret from a Provider into Kubernetes (as a K8s Secret), SecretSink will upload a Kubernetes Secret to a Provider.
+The Secret Sink is a feature to allow Secrets from Kubernetes to be saved back into some providers. Where ExternalSecret is responsible to download a Secret from a Provider into Kubernetes (as a K8s Secret), PushSecret will upload a Kubernetes Secret to a Provider.
 
 ## Motivation
 Secret Sink allows some inCluster generated secrets to also be available on a given secret provider. It also allows multiple Providers having the same secret (which means a way to perform failover in case a given secret provider is on downtime or compromised for whatever the reason).
 
 ### Goals
-- CRD Design for the SecretSink
+- CRD Design for the PushSecret
 - Define the need for a SinkStore
 - 
 ### Non-Goals
@@ -113,7 +113,7 @@ spec:
 
 ```yaml
 apiVersion: external-secrets.io/v1alpha1
-kind: SecretSink
+kind: PushSecret
 metadata:
   name: "hello-world"
   namespace: my-ns # Same of the SecretStores
@@ -165,7 +165,7 @@ status:
 ```
 
 ### Behavior
-When checking SecretSink for the Source Secret, check existing labels for SecretStore reference of that particular Secret. If this SecretStore reference is an object in SecretSink SecretStore lists, a SecretSyncError should be emited as we cannot sync the secret to the same SecretStore.
+When checking PushSecret for the Source Secret, check existing labels for SecretStore reference of that particular Secret. If this SecretStore reference is an object in PushSecret SecretStore lists, a SecretSyncError should be emited as we cannot sync the secret to the same SecretStore.
 
 If the SecretStores are all fine or if the Secret has no labels (secret created by user / another tool), for Each SecretStore, get the SyncState of this store (New, SecretSynced, SecretSyncedErr).
 
@@ -177,9 +177,9 @@ We had several discussions on how to implement this feature, and it turns out ju
 
 ### Acceptance Criteria
 + ExternalSecrets create appropriate labels on generated Secrets
-+ SecretSinks can read labels on source Secrets
-+ SecretSinks cannot have same references to SecretStores
-+ SecretSinks respect refreshInterval
++ PushSecrets can read labels on source Secrets
++ PushSecrets cannot have same references to SecretStores
++ PushSecrets respect refreshInterval
 ## Alternatives
 Using some integration with Crossplane can allow to sync the secrets. Cons is this must be either manual or through some integration that would be an independent project on its own.
 

+ 3 - 3
e2e/framework/addon/eso.go

@@ -17,7 +17,7 @@ import (
 	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo/v2"
+	ginkgo "github.com/onsi/ginkgo/v2"
 )
 
 type ESO struct {
@@ -155,7 +155,7 @@ func WithCRDs() MutationFunc {
 }
 
 func (l *ESO) Install() error {
-	By("Installing eso\n")
+	ginkgo.By("Installing eso\n")
 	err := l.HelmChart.Install()
 	if err != nil {
 		return err
@@ -165,7 +165,7 @@ func (l *ESO) Install() error {
 }
 
 func (l *ESO) Uninstall() error {
-	By("Uninstalling eso")
+	ginkgo.By("Uninstalling eso")
 	err := l.HelmChart.Uninstall()
 	if err != nil {
 		return err

+ 8 - 8
e2e/framework/addon/vault.go

@@ -32,7 +32,7 @@ import (
 	vault "github.com/hashicorp/vault/api"
 
 	// nolint
-	. "github.com/onsi/ginkgo/v2"
+	ginkgo "github.com/onsi/ginkgo/v2"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -93,7 +93,7 @@ type OperatorInitResponse struct {
 }
 
 func (l *Vault) Install() error {
-	By("Installing vault in " + l.Namespace)
+	ginkgo.By("Installing vault in " + l.Namespace)
 	err := l.chart.Install()
 	if err != nil {
 		return err
@@ -168,13 +168,13 @@ func (l *Vault) initVault() error {
 	l.KubernetesAuthPath = "mykubernetes"              // see configure-vault.sh
 	l.KubernetesAuthRole = "external-secrets-operator" // see configure-vault.sh
 
-	By("Creating vault TLS secret")
+	ginkgo.By("Creating vault TLS secret")
 	err = l.chart.config.CRClient.Create(context.Background(), sec)
 	if err != nil {
 		return err
 	}
 
-	By("Waiting for vault pods to be running")
+	ginkgo.By("Waiting for vault pods to be running")
 	pl, err := util.WaitForPodsRunning(l.chart.config.KubeClientSet, 1, l.Namespace, metav1.ListOptions{
 		LabelSelector: "app.kubernetes.io/name=vault",
 	})
@@ -183,7 +183,7 @@ func (l *Vault) initVault() error {
 	}
 	l.PodName = pl.Items[0].Name
 
-	By("Initializing vault")
+	ginkgo.By("Initializing vault")
 	out, err := util.ExecCmd(
 		l.chart.config.KubeClientSet,
 		l.chart.config.KubeConfig,
@@ -192,7 +192,7 @@ func (l *Vault) initVault() error {
 		return fmt.Errorf("error initializing vault: %w", err)
 	}
 
-	By("Parsing init response")
+	ginkgo.By("Parsing init response")
 	var res OperatorInitResponse
 	err = json.Unmarshal([]byte(out), &res)
 	if err != nil {
@@ -200,7 +200,7 @@ func (l *Vault) initVault() error {
 	}
 	l.RootToken = res.RootToken
 
-	By("Unsealing vault")
+	ginkgo.By("Unsealing vault")
 	for _, k := range res.UnsealKeysB64 {
 		_, err = util.ExecCmd(
 			l.chart.config.KubeClientSet,
@@ -238,7 +238,7 @@ func (l *Vault) initVault() error {
 }
 
 func (l *Vault) configureVault() error {
-	By("configuring vault")
+	ginkgo.By("configuring vault")
 	cmd := `sh /etc/vault-config/configure-vault.sh %s`
 	_, err := util.ExecCmd(
 		l.chart.config.KubeClientSet,

+ 784 - 0
pkg/controllers/pushsecret/internal/fakes/client.go

@@ -0,0 +1,784 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"context"
+	"sync"
+
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type Client struct {
+	CreateStub        func(context.Context, client.Object, ...client.CreateOption) error
+	createMutex       sync.RWMutex
+	createArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.CreateOption
+	}
+	createReturns struct {
+		result1 error
+	}
+	createReturnsOnCall map[int]struct {
+		result1 error
+	}
+	DeleteStub        func(context.Context, client.Object, ...client.DeleteOption) error
+	deleteMutex       sync.RWMutex
+	deleteArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.DeleteOption
+	}
+	deleteReturns struct {
+		result1 error
+	}
+	deleteReturnsOnCall map[int]struct {
+		result1 error
+	}
+	DeleteAllOfStub        func(context.Context, client.Object, ...client.DeleteAllOfOption) error
+	deleteAllOfMutex       sync.RWMutex
+	deleteAllOfArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.DeleteAllOfOption
+	}
+	deleteAllOfReturns struct {
+		result1 error
+	}
+	deleteAllOfReturnsOnCall map[int]struct {
+		result1 error
+	}
+	GetStub        func(context.Context, types.NamespacedName, client.Object) error
+	getMutex       sync.RWMutex
+	getArgsForCall []struct {
+		arg1 context.Context
+		arg2 types.NamespacedName
+		arg3 client.Object
+	}
+	getReturns struct {
+		result1 error
+	}
+	getReturnsOnCall map[int]struct {
+		result1 error
+	}
+	ListStub        func(context.Context, client.ObjectList, ...client.ListOption) error
+	listMutex       sync.RWMutex
+	listArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.ObjectList
+		arg3 []client.ListOption
+	}
+	listReturns struct {
+		result1 error
+	}
+	listReturnsOnCall map[int]struct {
+		result1 error
+	}
+	PatchStub        func(context.Context, client.Object, client.Patch, ...client.PatchOption) error
+	patchMutex       sync.RWMutex
+	patchArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 client.Patch
+		arg4 []client.PatchOption
+	}
+	patchReturns struct {
+		result1 error
+	}
+	patchReturnsOnCall map[int]struct {
+		result1 error
+	}
+	RESTMapperStub        func() meta.RESTMapper
+	rESTMapperMutex       sync.RWMutex
+	rESTMapperArgsForCall []struct {
+	}
+	rESTMapperReturns struct {
+		result1 meta.RESTMapper
+	}
+	rESTMapperReturnsOnCall map[int]struct {
+		result1 meta.RESTMapper
+	}
+	SchemeStub        func() *runtime.Scheme
+	schemeMutex       sync.RWMutex
+	schemeArgsForCall []struct {
+	}
+	schemeReturns struct {
+		result1 *runtime.Scheme
+	}
+	schemeReturnsOnCall map[int]struct {
+		result1 *runtime.Scheme
+	}
+	StatusStub        func() client.StatusWriter
+	statusMutex       sync.RWMutex
+	statusArgsForCall []struct {
+	}
+	statusReturns struct {
+		result1 client.StatusWriter
+	}
+	statusReturnsOnCall map[int]struct {
+		result1 client.StatusWriter
+	}
+	UpdateStub        func(context.Context, client.Object, ...client.UpdateOption) error
+	updateMutex       sync.RWMutex
+	updateArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.UpdateOption
+	}
+	updateReturns struct {
+		result1 error
+	}
+	updateReturnsOnCall map[int]struct {
+		result1 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *Client) Create(arg1 context.Context, arg2 client.Object, arg3 ...client.CreateOption) error {
+	fake.createMutex.Lock()
+	ret, specificReturn := fake.createReturnsOnCall[len(fake.createArgsForCall)]
+	fake.createArgsForCall = append(fake.createArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.CreateOption
+	}{arg1, arg2, arg3})
+	stub := fake.CreateStub
+	fakeReturns := fake.createReturns
+	fake.recordInvocation("Create", []interface{}{arg1, arg2, arg3})
+	fake.createMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) CreateCallCount() int {
+	fake.createMutex.RLock()
+	defer fake.createMutex.RUnlock()
+	return len(fake.createArgsForCall)
+}
+
+func (fake *Client) CreateCalls(stub func(context.Context, client.Object, ...client.CreateOption) error) {
+	fake.createMutex.Lock()
+	defer fake.createMutex.Unlock()
+	fake.CreateStub = stub
+}
+
+func (fake *Client) CreateArgsForCall(i int) (context.Context, client.Object, []client.CreateOption) {
+	fake.createMutex.RLock()
+	defer fake.createMutex.RUnlock()
+	argsForCall := fake.createArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) CreateReturns(result1 error) {
+	fake.createMutex.Lock()
+	defer fake.createMutex.Unlock()
+	fake.CreateStub = nil
+	fake.createReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) CreateReturnsOnCall(i int, result1 error) {
+	fake.createMutex.Lock()
+	defer fake.createMutex.Unlock()
+	fake.CreateStub = nil
+	if fake.createReturnsOnCall == nil {
+		fake.createReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.createReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) Delete(arg1 context.Context, arg2 client.Object, arg3 ...client.DeleteOption) error {
+	fake.deleteMutex.Lock()
+	ret, specificReturn := fake.deleteReturnsOnCall[len(fake.deleteArgsForCall)]
+	fake.deleteArgsForCall = append(fake.deleteArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.DeleteOption
+	}{arg1, arg2, arg3})
+	stub := fake.DeleteStub
+	fakeReturns := fake.deleteReturns
+	fake.recordInvocation("Delete", []interface{}{arg1, arg2, arg3})
+	fake.deleteMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) DeleteCallCount() int {
+	fake.deleteMutex.RLock()
+	defer fake.deleteMutex.RUnlock()
+	return len(fake.deleteArgsForCall)
+}
+
+func (fake *Client) DeleteCalls(stub func(context.Context, client.Object, ...client.DeleteOption) error) {
+	fake.deleteMutex.Lock()
+	defer fake.deleteMutex.Unlock()
+	fake.DeleteStub = stub
+}
+
+func (fake *Client) DeleteArgsForCall(i int) (context.Context, client.Object, []client.DeleteOption) {
+	fake.deleteMutex.RLock()
+	defer fake.deleteMutex.RUnlock()
+	argsForCall := fake.deleteArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) DeleteReturns(result1 error) {
+	fake.deleteMutex.Lock()
+	defer fake.deleteMutex.Unlock()
+	fake.DeleteStub = nil
+	fake.deleteReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) DeleteReturnsOnCall(i int, result1 error) {
+	fake.deleteMutex.Lock()
+	defer fake.deleteMutex.Unlock()
+	fake.DeleteStub = nil
+	if fake.deleteReturnsOnCall == nil {
+		fake.deleteReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.deleteReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) DeleteAllOf(arg1 context.Context, arg2 client.Object, arg3 ...client.DeleteAllOfOption) error {
+	fake.deleteAllOfMutex.Lock()
+	ret, specificReturn := fake.deleteAllOfReturnsOnCall[len(fake.deleteAllOfArgsForCall)]
+	fake.deleteAllOfArgsForCall = append(fake.deleteAllOfArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.DeleteAllOfOption
+	}{arg1, arg2, arg3})
+	stub := fake.DeleteAllOfStub
+	fakeReturns := fake.deleteAllOfReturns
+	fake.recordInvocation("DeleteAllOf", []interface{}{arg1, arg2, arg3})
+	fake.deleteAllOfMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) DeleteAllOfCallCount() int {
+	fake.deleteAllOfMutex.RLock()
+	defer fake.deleteAllOfMutex.RUnlock()
+	return len(fake.deleteAllOfArgsForCall)
+}
+
+func (fake *Client) DeleteAllOfCalls(stub func(context.Context, client.Object, ...client.DeleteAllOfOption) error) {
+	fake.deleteAllOfMutex.Lock()
+	defer fake.deleteAllOfMutex.Unlock()
+	fake.DeleteAllOfStub = stub
+}
+
+func (fake *Client) DeleteAllOfArgsForCall(i int) (context.Context, client.Object, []client.DeleteAllOfOption) {
+	fake.deleteAllOfMutex.RLock()
+	defer fake.deleteAllOfMutex.RUnlock()
+	argsForCall := fake.deleteAllOfArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) DeleteAllOfReturns(result1 error) {
+	fake.deleteAllOfMutex.Lock()
+	defer fake.deleteAllOfMutex.Unlock()
+	fake.DeleteAllOfStub = nil
+	fake.deleteAllOfReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) DeleteAllOfReturnsOnCall(i int, result1 error) {
+	fake.deleteAllOfMutex.Lock()
+	defer fake.deleteAllOfMutex.Unlock()
+	fake.DeleteAllOfStub = nil
+	if fake.deleteAllOfReturnsOnCall == nil {
+		fake.deleteAllOfReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.deleteAllOfReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) Get(arg1 context.Context, arg2 types.NamespacedName, arg3 client.Object) error {
+	fake.getMutex.Lock()
+	ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
+	fake.getArgsForCall = append(fake.getArgsForCall, struct {
+		arg1 context.Context
+		arg2 types.NamespacedName
+		arg3 client.Object
+	}{arg1, arg2, arg3})
+	stub := fake.GetStub
+	fakeReturns := fake.getReturns
+	fake.recordInvocation("Get", []interface{}{arg1, arg2, arg3})
+	fake.getMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) GetCallCount() int {
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	return len(fake.getArgsForCall)
+}
+
+func (fake *Client) GetCalls(stub func(context.Context, types.NamespacedName, client.Object) error) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = stub
+}
+
+func (fake *Client) GetArgsForCall(i int) (context.Context, types.NamespacedName, client.Object) {
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	argsForCall := fake.getArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) GetReturns(result1 error) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = nil
+	fake.getReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) GetReturnsOnCall(i int, result1 error) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = nil
+	if fake.getReturnsOnCall == nil {
+		fake.getReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.getReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) List(arg1 context.Context, arg2 client.ObjectList, arg3 ...client.ListOption) error {
+	fake.listMutex.Lock()
+	ret, specificReturn := fake.listReturnsOnCall[len(fake.listArgsForCall)]
+	fake.listArgsForCall = append(fake.listArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.ObjectList
+		arg3 []client.ListOption
+	}{arg1, arg2, arg3})
+	stub := fake.ListStub
+	fakeReturns := fake.listReturns
+	fake.recordInvocation("List", []interface{}{arg1, arg2, arg3})
+	fake.listMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) ListCallCount() int {
+	fake.listMutex.RLock()
+	defer fake.listMutex.RUnlock()
+	return len(fake.listArgsForCall)
+}
+
+func (fake *Client) ListCalls(stub func(context.Context, client.ObjectList, ...client.ListOption) error) {
+	fake.listMutex.Lock()
+	defer fake.listMutex.Unlock()
+	fake.ListStub = stub
+}
+
+func (fake *Client) ListArgsForCall(i int) (context.Context, client.ObjectList, []client.ListOption) {
+	fake.listMutex.RLock()
+	defer fake.listMutex.RUnlock()
+	argsForCall := fake.listArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) ListReturns(result1 error) {
+	fake.listMutex.Lock()
+	defer fake.listMutex.Unlock()
+	fake.ListStub = nil
+	fake.listReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) ListReturnsOnCall(i int, result1 error) {
+	fake.listMutex.Lock()
+	defer fake.listMutex.Unlock()
+	fake.ListStub = nil
+	if fake.listReturnsOnCall == nil {
+		fake.listReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.listReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) Patch(arg1 context.Context, arg2 client.Object, arg3 client.Patch, arg4 ...client.PatchOption) error {
+	fake.patchMutex.Lock()
+	ret, specificReturn := fake.patchReturnsOnCall[len(fake.patchArgsForCall)]
+	fake.patchArgsForCall = append(fake.patchArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 client.Patch
+		arg4 []client.PatchOption
+	}{arg1, arg2, arg3, arg4})
+	stub := fake.PatchStub
+	fakeReturns := fake.patchReturns
+	fake.recordInvocation("Patch", []interface{}{arg1, arg2, arg3, arg4})
+	fake.patchMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3, arg4...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) PatchCallCount() int {
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	return len(fake.patchArgsForCall)
+}
+
+func (fake *Client) PatchCalls(stub func(context.Context, client.Object, client.Patch, ...client.PatchOption) error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = stub
+}
+
+func (fake *Client) PatchArgsForCall(i int) (context.Context, client.Object, client.Patch, []client.PatchOption) {
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	argsForCall := fake.patchArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *Client) PatchReturns(result1 error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = nil
+	fake.patchReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) PatchReturnsOnCall(i int, result1 error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = nil
+	if fake.patchReturnsOnCall == nil {
+		fake.patchReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.patchReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) RESTMapper() meta.RESTMapper {
+	fake.rESTMapperMutex.Lock()
+	ret, specificReturn := fake.rESTMapperReturnsOnCall[len(fake.rESTMapperArgsForCall)]
+	fake.rESTMapperArgsForCall = append(fake.rESTMapperArgsForCall, struct {
+	}{})
+	stub := fake.RESTMapperStub
+	fakeReturns := fake.rESTMapperReturns
+	fake.recordInvocation("RESTMapper", []interface{}{})
+	fake.rESTMapperMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) RESTMapperCallCount() int {
+	fake.rESTMapperMutex.RLock()
+	defer fake.rESTMapperMutex.RUnlock()
+	return len(fake.rESTMapperArgsForCall)
+}
+
+func (fake *Client) RESTMapperCalls(stub func() meta.RESTMapper) {
+	fake.rESTMapperMutex.Lock()
+	defer fake.rESTMapperMutex.Unlock()
+	fake.RESTMapperStub = stub
+}
+
+func (fake *Client) RESTMapperReturns(result1 meta.RESTMapper) {
+	fake.rESTMapperMutex.Lock()
+	defer fake.rESTMapperMutex.Unlock()
+	fake.RESTMapperStub = nil
+	fake.rESTMapperReturns = struct {
+		result1 meta.RESTMapper
+	}{result1}
+}
+
+func (fake *Client) RESTMapperReturnsOnCall(i int, result1 meta.RESTMapper) {
+	fake.rESTMapperMutex.Lock()
+	defer fake.rESTMapperMutex.Unlock()
+	fake.RESTMapperStub = nil
+	if fake.rESTMapperReturnsOnCall == nil {
+		fake.rESTMapperReturnsOnCall = make(map[int]struct {
+			result1 meta.RESTMapper
+		})
+	}
+	fake.rESTMapperReturnsOnCall[i] = struct {
+		result1 meta.RESTMapper
+	}{result1}
+}
+
+func (fake *Client) Scheme() *runtime.Scheme {
+	fake.schemeMutex.Lock()
+	ret, specificReturn := fake.schemeReturnsOnCall[len(fake.schemeArgsForCall)]
+	fake.schemeArgsForCall = append(fake.schemeArgsForCall, struct {
+	}{})
+	stub := fake.SchemeStub
+	fakeReturns := fake.schemeReturns
+	fake.recordInvocation("Scheme", []interface{}{})
+	fake.schemeMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) SchemeCallCount() int {
+	fake.schemeMutex.RLock()
+	defer fake.schemeMutex.RUnlock()
+	return len(fake.schemeArgsForCall)
+}
+
+func (fake *Client) SchemeCalls(stub func() *runtime.Scheme) {
+	fake.schemeMutex.Lock()
+	defer fake.schemeMutex.Unlock()
+	fake.SchemeStub = stub
+}
+
+func (fake *Client) SchemeReturns(result1 *runtime.Scheme) {
+	fake.schemeMutex.Lock()
+	defer fake.schemeMutex.Unlock()
+	fake.SchemeStub = nil
+	fake.schemeReturns = struct {
+		result1 *runtime.Scheme
+	}{result1}
+}
+
+func (fake *Client) SchemeReturnsOnCall(i int, result1 *runtime.Scheme) {
+	fake.schemeMutex.Lock()
+	defer fake.schemeMutex.Unlock()
+	fake.SchemeStub = nil
+	if fake.schemeReturnsOnCall == nil {
+		fake.schemeReturnsOnCall = make(map[int]struct {
+			result1 *runtime.Scheme
+		})
+	}
+	fake.schemeReturnsOnCall[i] = struct {
+		result1 *runtime.Scheme
+	}{result1}
+}
+
+func (fake *Client) Status() client.StatusWriter {
+	fake.statusMutex.Lock()
+	ret, specificReturn := fake.statusReturnsOnCall[len(fake.statusArgsForCall)]
+	fake.statusArgsForCall = append(fake.statusArgsForCall, struct {
+	}{})
+	stub := fake.StatusStub
+	fakeReturns := fake.statusReturns
+	fake.recordInvocation("Status", []interface{}{})
+	fake.statusMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) StatusCallCount() int {
+	fake.statusMutex.RLock()
+	defer fake.statusMutex.RUnlock()
+	return len(fake.statusArgsForCall)
+}
+
+func (fake *Client) StatusCalls(stub func() client.StatusWriter) {
+	fake.statusMutex.Lock()
+	defer fake.statusMutex.Unlock()
+	fake.StatusStub = stub
+}
+
+func (fake *Client) StatusReturns(result1 client.StatusWriter) {
+	fake.statusMutex.Lock()
+	defer fake.statusMutex.Unlock()
+	fake.StatusStub = nil
+	fake.statusReturns = struct {
+		result1 client.StatusWriter
+	}{result1}
+}
+
+func (fake *Client) StatusReturnsOnCall(i int, result1 client.StatusWriter) {
+	fake.statusMutex.Lock()
+	defer fake.statusMutex.Unlock()
+	fake.StatusStub = nil
+	if fake.statusReturnsOnCall == nil {
+		fake.statusReturnsOnCall = make(map[int]struct {
+			result1 client.StatusWriter
+		})
+	}
+	fake.statusReturnsOnCall[i] = struct {
+		result1 client.StatusWriter
+	}{result1}
+}
+
+func (fake *Client) Update(arg1 context.Context, arg2 client.Object, arg3 ...client.UpdateOption) error {
+	fake.updateMutex.Lock()
+	ret, specificReturn := fake.updateReturnsOnCall[len(fake.updateArgsForCall)]
+	fake.updateArgsForCall = append(fake.updateArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.UpdateOption
+	}{arg1, arg2, arg3})
+	stub := fake.UpdateStub
+	fakeReturns := fake.updateReturns
+	fake.recordInvocation("Update", []interface{}{arg1, arg2, arg3})
+	fake.updateMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Client) UpdateCallCount() int {
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	return len(fake.updateArgsForCall)
+}
+
+func (fake *Client) UpdateCalls(stub func(context.Context, client.Object, ...client.UpdateOption) error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = stub
+}
+
+func (fake *Client) UpdateArgsForCall(i int) (context.Context, client.Object, []client.UpdateOption) {
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	argsForCall := fake.updateArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *Client) UpdateReturns(result1 error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = nil
+	fake.updateReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) UpdateReturnsOnCall(i int, result1 error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = nil
+	if fake.updateReturnsOnCall == nil {
+		fake.updateReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.updateReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Client) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.createMutex.RLock()
+	defer fake.createMutex.RUnlock()
+	fake.deleteMutex.RLock()
+	defer fake.deleteMutex.RUnlock()
+	fake.deleteAllOfMutex.RLock()
+	defer fake.deleteAllOfMutex.RUnlock()
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	fake.listMutex.RLock()
+	defer fake.listMutex.RUnlock()
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	fake.rESTMapperMutex.RLock()
+	defer fake.rESTMapperMutex.RUnlock()
+	fake.schemeMutex.RLock()
+	defer fake.schemeMutex.RUnlock()
+	fake.statusMutex.RLock()
+	defer fake.statusMutex.RUnlock()
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *Client) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ client.Client = new(Client)

+ 1288 - 0
pkg/controllers/pushsecret/internal/fakes/manager.go

@@ -0,0 +1,1288 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"context"
+	"net/http"
+	"sync"
+
+	"github.com/go-logr/logr"
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/record"
+	"sigs.k8s.io/controller-runtime/pkg/cache"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
+	"sigs.k8s.io/controller-runtime/pkg/healthz"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+type Manager struct {
+	AddStub        func(manager.Runnable) error
+	addMutex       sync.RWMutex
+	addArgsForCall []struct {
+		arg1 manager.Runnable
+	}
+	addReturns struct {
+		result1 error
+	}
+	addReturnsOnCall map[int]struct {
+		result1 error
+	}
+	AddHealthzCheckStub        func(string, healthz.Checker) error
+	addHealthzCheckMutex       sync.RWMutex
+	addHealthzCheckArgsForCall []struct {
+		arg1 string
+		arg2 healthz.Checker
+	}
+	addHealthzCheckReturns struct {
+		result1 error
+	}
+	addHealthzCheckReturnsOnCall map[int]struct {
+		result1 error
+	}
+	AddMetricsExtraHandlerStub        func(string, http.Handler) error
+	addMetricsExtraHandlerMutex       sync.RWMutex
+	addMetricsExtraHandlerArgsForCall []struct {
+		arg1 string
+		arg2 http.Handler
+	}
+	addMetricsExtraHandlerReturns struct {
+		result1 error
+	}
+	addMetricsExtraHandlerReturnsOnCall map[int]struct {
+		result1 error
+	}
+	AddReadyzCheckStub        func(string, healthz.Checker) error
+	addReadyzCheckMutex       sync.RWMutex
+	addReadyzCheckArgsForCall []struct {
+		arg1 string
+		arg2 healthz.Checker
+	}
+	addReadyzCheckReturns struct {
+		result1 error
+	}
+	addReadyzCheckReturnsOnCall map[int]struct {
+		result1 error
+	}
+	ElectedStub        func() <-chan struct{}
+	electedMutex       sync.RWMutex
+	electedArgsForCall []struct {
+	}
+	electedReturns struct {
+		result1 <-chan struct{}
+	}
+	electedReturnsOnCall map[int]struct {
+		result1 <-chan struct{}
+	}
+	GetAPIReaderStub        func() client.Reader
+	getAPIReaderMutex       sync.RWMutex
+	getAPIReaderArgsForCall []struct {
+	}
+	getAPIReaderReturns struct {
+		result1 client.Reader
+	}
+	getAPIReaderReturnsOnCall map[int]struct {
+		result1 client.Reader
+	}
+	GetCacheStub        func() cache.Cache
+	getCacheMutex       sync.RWMutex
+	getCacheArgsForCall []struct {
+	}
+	getCacheReturns struct {
+		result1 cache.Cache
+	}
+	getCacheReturnsOnCall map[int]struct {
+		result1 cache.Cache
+	}
+	GetClientStub        func() client.Client
+	getClientMutex       sync.RWMutex
+	getClientArgsForCall []struct {
+	}
+	getClientReturns struct {
+		result1 client.Client
+	}
+	getClientReturnsOnCall map[int]struct {
+		result1 client.Client
+	}
+	GetConfigStub        func() *rest.Config
+	getConfigMutex       sync.RWMutex
+	getConfigArgsForCall []struct {
+	}
+	getConfigReturns struct {
+		result1 *rest.Config
+	}
+	getConfigReturnsOnCall map[int]struct {
+		result1 *rest.Config
+	}
+	GetControllerOptionsStub        func() v1alpha1.ControllerConfigurationSpec
+	getControllerOptionsMutex       sync.RWMutex
+	getControllerOptionsArgsForCall []struct {
+	}
+	getControllerOptionsReturns struct {
+		result1 v1alpha1.ControllerConfigurationSpec
+	}
+	getControllerOptionsReturnsOnCall map[int]struct {
+		result1 v1alpha1.ControllerConfigurationSpec
+	}
+	GetEventRecorderForStub        func(string) record.EventRecorder
+	getEventRecorderForMutex       sync.RWMutex
+	getEventRecorderForArgsForCall []struct {
+		arg1 string
+	}
+	getEventRecorderForReturns struct {
+		result1 record.EventRecorder
+	}
+	getEventRecorderForReturnsOnCall map[int]struct {
+		result1 record.EventRecorder
+	}
+	GetFieldIndexerStub        func() client.FieldIndexer
+	getFieldIndexerMutex       sync.RWMutex
+	getFieldIndexerArgsForCall []struct {
+	}
+	getFieldIndexerReturns struct {
+		result1 client.FieldIndexer
+	}
+	getFieldIndexerReturnsOnCall map[int]struct {
+		result1 client.FieldIndexer
+	}
+	GetLoggerStub        func() logr.Logger
+	getLoggerMutex       sync.RWMutex
+	getLoggerArgsForCall []struct {
+	}
+	getLoggerReturns struct {
+		result1 logr.Logger
+	}
+	getLoggerReturnsOnCall map[int]struct {
+		result1 logr.Logger
+	}
+	GetRESTMapperStub        func() meta.RESTMapper
+	getRESTMapperMutex       sync.RWMutex
+	getRESTMapperArgsForCall []struct {
+	}
+	getRESTMapperReturns struct {
+		result1 meta.RESTMapper
+	}
+	getRESTMapperReturnsOnCall map[int]struct {
+		result1 meta.RESTMapper
+	}
+	GetSchemeStub        func() *runtime.Scheme
+	getSchemeMutex       sync.RWMutex
+	getSchemeArgsForCall []struct {
+	}
+	getSchemeReturns struct {
+		result1 *runtime.Scheme
+	}
+	getSchemeReturnsOnCall map[int]struct {
+		result1 *runtime.Scheme
+	}
+	GetWebhookServerStub        func() *webhook.Server
+	getWebhookServerMutex       sync.RWMutex
+	getWebhookServerArgsForCall []struct {
+	}
+	getWebhookServerReturns struct {
+		result1 *webhook.Server
+	}
+	getWebhookServerReturnsOnCall map[int]struct {
+		result1 *webhook.Server
+	}
+	SetFieldsStub        func(interface{}) error
+	setFieldsMutex       sync.RWMutex
+	setFieldsArgsForCall []struct {
+		arg1 interface{}
+	}
+	setFieldsReturns struct {
+		result1 error
+	}
+	setFieldsReturnsOnCall map[int]struct {
+		result1 error
+	}
+	StartStub        func(context.Context) error
+	startMutex       sync.RWMutex
+	startArgsForCall []struct {
+		arg1 context.Context
+	}
+	startReturns struct {
+		result1 error
+	}
+	startReturnsOnCall map[int]struct {
+		result1 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *Manager) Add(arg1 manager.Runnable) error {
+	fake.addMutex.Lock()
+	ret, specificReturn := fake.addReturnsOnCall[len(fake.addArgsForCall)]
+	fake.addArgsForCall = append(fake.addArgsForCall, struct {
+		arg1 manager.Runnable
+	}{arg1})
+	stub := fake.AddStub
+	fakeReturns := fake.addReturns
+	fake.recordInvocation("Add", []interface{}{arg1})
+	fake.addMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) AddCallCount() int {
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	return len(fake.addArgsForCall)
+}
+
+func (fake *Manager) AddCalls(stub func(manager.Runnable) error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = stub
+}
+
+func (fake *Manager) AddArgsForCall(i int) manager.Runnable {
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	argsForCall := fake.addArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *Manager) AddReturns(result1 error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = nil
+	fake.addReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddReturnsOnCall(i int, result1 error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = nil
+	if fake.addReturnsOnCall == nil {
+		fake.addReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.addReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddHealthzCheck(arg1 string, arg2 healthz.Checker) error {
+	fake.addHealthzCheckMutex.Lock()
+	ret, specificReturn := fake.addHealthzCheckReturnsOnCall[len(fake.addHealthzCheckArgsForCall)]
+	fake.addHealthzCheckArgsForCall = append(fake.addHealthzCheckArgsForCall, struct {
+		arg1 string
+		arg2 healthz.Checker
+	}{arg1, arg2})
+	stub := fake.AddHealthzCheckStub
+	fakeReturns := fake.addHealthzCheckReturns
+	fake.recordInvocation("AddHealthzCheck", []interface{}{arg1, arg2})
+	fake.addHealthzCheckMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) AddHealthzCheckCallCount() int {
+	fake.addHealthzCheckMutex.RLock()
+	defer fake.addHealthzCheckMutex.RUnlock()
+	return len(fake.addHealthzCheckArgsForCall)
+}
+
+func (fake *Manager) AddHealthzCheckCalls(stub func(string, healthz.Checker) error) {
+	fake.addHealthzCheckMutex.Lock()
+	defer fake.addHealthzCheckMutex.Unlock()
+	fake.AddHealthzCheckStub = stub
+}
+
+func (fake *Manager) AddHealthzCheckArgsForCall(i int) (string, healthz.Checker) {
+	fake.addHealthzCheckMutex.RLock()
+	defer fake.addHealthzCheckMutex.RUnlock()
+	argsForCall := fake.addHealthzCheckArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *Manager) AddHealthzCheckReturns(result1 error) {
+	fake.addHealthzCheckMutex.Lock()
+	defer fake.addHealthzCheckMutex.Unlock()
+	fake.AddHealthzCheckStub = nil
+	fake.addHealthzCheckReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddHealthzCheckReturnsOnCall(i int, result1 error) {
+	fake.addHealthzCheckMutex.Lock()
+	defer fake.addHealthzCheckMutex.Unlock()
+	fake.AddHealthzCheckStub = nil
+	if fake.addHealthzCheckReturnsOnCall == nil {
+		fake.addHealthzCheckReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.addHealthzCheckReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddMetricsExtraHandler(arg1 string, arg2 http.Handler) error {
+	fake.addMetricsExtraHandlerMutex.Lock()
+	ret, specificReturn := fake.addMetricsExtraHandlerReturnsOnCall[len(fake.addMetricsExtraHandlerArgsForCall)]
+	fake.addMetricsExtraHandlerArgsForCall = append(fake.addMetricsExtraHandlerArgsForCall, struct {
+		arg1 string
+		arg2 http.Handler
+	}{arg1, arg2})
+	stub := fake.AddMetricsExtraHandlerStub
+	fakeReturns := fake.addMetricsExtraHandlerReturns
+	fake.recordInvocation("AddMetricsExtraHandler", []interface{}{arg1, arg2})
+	fake.addMetricsExtraHandlerMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) AddMetricsExtraHandlerCallCount() int {
+	fake.addMetricsExtraHandlerMutex.RLock()
+	defer fake.addMetricsExtraHandlerMutex.RUnlock()
+	return len(fake.addMetricsExtraHandlerArgsForCall)
+}
+
+func (fake *Manager) AddMetricsExtraHandlerCalls(stub func(string, http.Handler) error) {
+	fake.addMetricsExtraHandlerMutex.Lock()
+	defer fake.addMetricsExtraHandlerMutex.Unlock()
+	fake.AddMetricsExtraHandlerStub = stub
+}
+
+func (fake *Manager) AddMetricsExtraHandlerArgsForCall(i int) (string, http.Handler) {
+	fake.addMetricsExtraHandlerMutex.RLock()
+	defer fake.addMetricsExtraHandlerMutex.RUnlock()
+	argsForCall := fake.addMetricsExtraHandlerArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *Manager) AddMetricsExtraHandlerReturns(result1 error) {
+	fake.addMetricsExtraHandlerMutex.Lock()
+	defer fake.addMetricsExtraHandlerMutex.Unlock()
+	fake.AddMetricsExtraHandlerStub = nil
+	fake.addMetricsExtraHandlerReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddMetricsExtraHandlerReturnsOnCall(i int, result1 error) {
+	fake.addMetricsExtraHandlerMutex.Lock()
+	defer fake.addMetricsExtraHandlerMutex.Unlock()
+	fake.AddMetricsExtraHandlerStub = nil
+	if fake.addMetricsExtraHandlerReturnsOnCall == nil {
+		fake.addMetricsExtraHandlerReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.addMetricsExtraHandlerReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddReadyzCheck(arg1 string, arg2 healthz.Checker) error {
+	fake.addReadyzCheckMutex.Lock()
+	ret, specificReturn := fake.addReadyzCheckReturnsOnCall[len(fake.addReadyzCheckArgsForCall)]
+	fake.addReadyzCheckArgsForCall = append(fake.addReadyzCheckArgsForCall, struct {
+		arg1 string
+		arg2 healthz.Checker
+	}{arg1, arg2})
+	stub := fake.AddReadyzCheckStub
+	fakeReturns := fake.addReadyzCheckReturns
+	fake.recordInvocation("AddReadyzCheck", []interface{}{arg1, arg2})
+	fake.addReadyzCheckMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) AddReadyzCheckCallCount() int {
+	fake.addReadyzCheckMutex.RLock()
+	defer fake.addReadyzCheckMutex.RUnlock()
+	return len(fake.addReadyzCheckArgsForCall)
+}
+
+func (fake *Manager) AddReadyzCheckCalls(stub func(string, healthz.Checker) error) {
+	fake.addReadyzCheckMutex.Lock()
+	defer fake.addReadyzCheckMutex.Unlock()
+	fake.AddReadyzCheckStub = stub
+}
+
+func (fake *Manager) AddReadyzCheckArgsForCall(i int) (string, healthz.Checker) {
+	fake.addReadyzCheckMutex.RLock()
+	defer fake.addReadyzCheckMutex.RUnlock()
+	argsForCall := fake.addReadyzCheckArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *Manager) AddReadyzCheckReturns(result1 error) {
+	fake.addReadyzCheckMutex.Lock()
+	defer fake.addReadyzCheckMutex.Unlock()
+	fake.AddReadyzCheckStub = nil
+	fake.addReadyzCheckReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) AddReadyzCheckReturnsOnCall(i int, result1 error) {
+	fake.addReadyzCheckMutex.Lock()
+	defer fake.addReadyzCheckMutex.Unlock()
+	fake.AddReadyzCheckStub = nil
+	if fake.addReadyzCheckReturnsOnCall == nil {
+		fake.addReadyzCheckReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.addReadyzCheckReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) Elected() <-chan struct{} {
+	fake.electedMutex.Lock()
+	ret, specificReturn := fake.electedReturnsOnCall[len(fake.electedArgsForCall)]
+	fake.electedArgsForCall = append(fake.electedArgsForCall, struct {
+	}{})
+	stub := fake.ElectedStub
+	fakeReturns := fake.electedReturns
+	fake.recordInvocation("Elected", []interface{}{})
+	fake.electedMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) ElectedCallCount() int {
+	fake.electedMutex.RLock()
+	defer fake.electedMutex.RUnlock()
+	return len(fake.electedArgsForCall)
+}
+
+func (fake *Manager) ElectedCalls(stub func() <-chan struct{}) {
+	fake.electedMutex.Lock()
+	defer fake.electedMutex.Unlock()
+	fake.ElectedStub = stub
+}
+
+func (fake *Manager) ElectedReturns(result1 <-chan struct{}) {
+	fake.electedMutex.Lock()
+	defer fake.electedMutex.Unlock()
+	fake.ElectedStub = nil
+	fake.electedReturns = struct {
+		result1 <-chan struct{}
+	}{result1}
+}
+
+func (fake *Manager) ElectedReturnsOnCall(i int, result1 <-chan struct{}) {
+	fake.electedMutex.Lock()
+	defer fake.electedMutex.Unlock()
+	fake.ElectedStub = nil
+	if fake.electedReturnsOnCall == nil {
+		fake.electedReturnsOnCall = make(map[int]struct {
+			result1 <-chan struct{}
+		})
+	}
+	fake.electedReturnsOnCall[i] = struct {
+		result1 <-chan struct{}
+	}{result1}
+}
+
+func (fake *Manager) GetAPIReader() client.Reader {
+	fake.getAPIReaderMutex.Lock()
+	ret, specificReturn := fake.getAPIReaderReturnsOnCall[len(fake.getAPIReaderArgsForCall)]
+	fake.getAPIReaderArgsForCall = append(fake.getAPIReaderArgsForCall, struct {
+	}{})
+	stub := fake.GetAPIReaderStub
+	fakeReturns := fake.getAPIReaderReturns
+	fake.recordInvocation("GetAPIReader", []interface{}{})
+	fake.getAPIReaderMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetAPIReaderCallCount() int {
+	fake.getAPIReaderMutex.RLock()
+	defer fake.getAPIReaderMutex.RUnlock()
+	return len(fake.getAPIReaderArgsForCall)
+}
+
+func (fake *Manager) GetAPIReaderCalls(stub func() client.Reader) {
+	fake.getAPIReaderMutex.Lock()
+	defer fake.getAPIReaderMutex.Unlock()
+	fake.GetAPIReaderStub = stub
+}
+
+func (fake *Manager) GetAPIReaderReturns(result1 client.Reader) {
+	fake.getAPIReaderMutex.Lock()
+	defer fake.getAPIReaderMutex.Unlock()
+	fake.GetAPIReaderStub = nil
+	fake.getAPIReaderReturns = struct {
+		result1 client.Reader
+	}{result1}
+}
+
+func (fake *Manager) GetAPIReaderReturnsOnCall(i int, result1 client.Reader) {
+	fake.getAPIReaderMutex.Lock()
+	defer fake.getAPIReaderMutex.Unlock()
+	fake.GetAPIReaderStub = nil
+	if fake.getAPIReaderReturnsOnCall == nil {
+		fake.getAPIReaderReturnsOnCall = make(map[int]struct {
+			result1 client.Reader
+		})
+	}
+	fake.getAPIReaderReturnsOnCall[i] = struct {
+		result1 client.Reader
+	}{result1}
+}
+
+func (fake *Manager) GetCache() cache.Cache {
+	fake.getCacheMutex.Lock()
+	ret, specificReturn := fake.getCacheReturnsOnCall[len(fake.getCacheArgsForCall)]
+	fake.getCacheArgsForCall = append(fake.getCacheArgsForCall, struct {
+	}{})
+	stub := fake.GetCacheStub
+	fakeReturns := fake.getCacheReturns
+	fake.recordInvocation("GetCache", []interface{}{})
+	fake.getCacheMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetCacheCallCount() int {
+	fake.getCacheMutex.RLock()
+	defer fake.getCacheMutex.RUnlock()
+	return len(fake.getCacheArgsForCall)
+}
+
+func (fake *Manager) GetCacheCalls(stub func() cache.Cache) {
+	fake.getCacheMutex.Lock()
+	defer fake.getCacheMutex.Unlock()
+	fake.GetCacheStub = stub
+}
+
+func (fake *Manager) GetCacheReturns(result1 cache.Cache) {
+	fake.getCacheMutex.Lock()
+	defer fake.getCacheMutex.Unlock()
+	fake.GetCacheStub = nil
+	fake.getCacheReturns = struct {
+		result1 cache.Cache
+	}{result1}
+}
+
+func (fake *Manager) GetCacheReturnsOnCall(i int, result1 cache.Cache) {
+	fake.getCacheMutex.Lock()
+	defer fake.getCacheMutex.Unlock()
+	fake.GetCacheStub = nil
+	if fake.getCacheReturnsOnCall == nil {
+		fake.getCacheReturnsOnCall = make(map[int]struct {
+			result1 cache.Cache
+		})
+	}
+	fake.getCacheReturnsOnCall[i] = struct {
+		result1 cache.Cache
+	}{result1}
+}
+
+func (fake *Manager) GetClient() client.Client {
+	fake.getClientMutex.Lock()
+	ret, specificReturn := fake.getClientReturnsOnCall[len(fake.getClientArgsForCall)]
+	fake.getClientArgsForCall = append(fake.getClientArgsForCall, struct {
+	}{})
+	stub := fake.GetClientStub
+	fakeReturns := fake.getClientReturns
+	fake.recordInvocation("GetClient", []interface{}{})
+	fake.getClientMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetClientCallCount() int {
+	fake.getClientMutex.RLock()
+	defer fake.getClientMutex.RUnlock()
+	return len(fake.getClientArgsForCall)
+}
+
+func (fake *Manager) GetClientCalls(stub func() client.Client) {
+	fake.getClientMutex.Lock()
+	defer fake.getClientMutex.Unlock()
+	fake.GetClientStub = stub
+}
+
+func (fake *Manager) GetClientReturns(result1 client.Client) {
+	fake.getClientMutex.Lock()
+	defer fake.getClientMutex.Unlock()
+	fake.GetClientStub = nil
+	fake.getClientReturns = struct {
+		result1 client.Client
+	}{result1}
+}
+
+func (fake *Manager) GetClientReturnsOnCall(i int, result1 client.Client) {
+	fake.getClientMutex.Lock()
+	defer fake.getClientMutex.Unlock()
+	fake.GetClientStub = nil
+	if fake.getClientReturnsOnCall == nil {
+		fake.getClientReturnsOnCall = make(map[int]struct {
+			result1 client.Client
+		})
+	}
+	fake.getClientReturnsOnCall[i] = struct {
+		result1 client.Client
+	}{result1}
+}
+
+func (fake *Manager) GetConfig() *rest.Config {
+	fake.getConfigMutex.Lock()
+	ret, specificReturn := fake.getConfigReturnsOnCall[len(fake.getConfigArgsForCall)]
+	fake.getConfigArgsForCall = append(fake.getConfigArgsForCall, struct {
+	}{})
+	stub := fake.GetConfigStub
+	fakeReturns := fake.getConfigReturns
+	fake.recordInvocation("GetConfig", []interface{}{})
+	fake.getConfigMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetConfigCallCount() int {
+	fake.getConfigMutex.RLock()
+	defer fake.getConfigMutex.RUnlock()
+	return len(fake.getConfigArgsForCall)
+}
+
+func (fake *Manager) GetConfigCalls(stub func() *rest.Config) {
+	fake.getConfigMutex.Lock()
+	defer fake.getConfigMutex.Unlock()
+	fake.GetConfigStub = stub
+}
+
+func (fake *Manager) GetConfigReturns(result1 *rest.Config) {
+	fake.getConfigMutex.Lock()
+	defer fake.getConfigMutex.Unlock()
+	fake.GetConfigStub = nil
+	fake.getConfigReturns = struct {
+		result1 *rest.Config
+	}{result1}
+}
+
+func (fake *Manager) GetConfigReturnsOnCall(i int, result1 *rest.Config) {
+	fake.getConfigMutex.Lock()
+	defer fake.getConfigMutex.Unlock()
+	fake.GetConfigStub = nil
+	if fake.getConfigReturnsOnCall == nil {
+		fake.getConfigReturnsOnCall = make(map[int]struct {
+			result1 *rest.Config
+		})
+	}
+	fake.getConfigReturnsOnCall[i] = struct {
+		result1 *rest.Config
+	}{result1}
+}
+
+func (fake *Manager) GetControllerOptions() v1alpha1.ControllerConfigurationSpec {
+	fake.getControllerOptionsMutex.Lock()
+	ret, specificReturn := fake.getControllerOptionsReturnsOnCall[len(fake.getControllerOptionsArgsForCall)]
+	fake.getControllerOptionsArgsForCall = append(fake.getControllerOptionsArgsForCall, struct {
+	}{})
+	stub := fake.GetControllerOptionsStub
+	fakeReturns := fake.getControllerOptionsReturns
+	fake.recordInvocation("GetControllerOptions", []interface{}{})
+	fake.getControllerOptionsMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetControllerOptionsCallCount() int {
+	fake.getControllerOptionsMutex.RLock()
+	defer fake.getControllerOptionsMutex.RUnlock()
+	return len(fake.getControllerOptionsArgsForCall)
+}
+
+func (fake *Manager) GetControllerOptionsCalls(stub func() v1alpha1.ControllerConfigurationSpec) {
+	fake.getControllerOptionsMutex.Lock()
+	defer fake.getControllerOptionsMutex.Unlock()
+	fake.GetControllerOptionsStub = stub
+}
+
+func (fake *Manager) GetControllerOptionsReturns(result1 v1alpha1.ControllerConfigurationSpec) {
+	fake.getControllerOptionsMutex.Lock()
+	defer fake.getControllerOptionsMutex.Unlock()
+	fake.GetControllerOptionsStub = nil
+	fake.getControllerOptionsReturns = struct {
+		result1 v1alpha1.ControllerConfigurationSpec
+	}{result1}
+}
+
+func (fake *Manager) GetControllerOptionsReturnsOnCall(i int, result1 v1alpha1.ControllerConfigurationSpec) {
+	fake.getControllerOptionsMutex.Lock()
+	defer fake.getControllerOptionsMutex.Unlock()
+	fake.GetControllerOptionsStub = nil
+	if fake.getControllerOptionsReturnsOnCall == nil {
+		fake.getControllerOptionsReturnsOnCall = make(map[int]struct {
+			result1 v1alpha1.ControllerConfigurationSpec
+		})
+	}
+	fake.getControllerOptionsReturnsOnCall[i] = struct {
+		result1 v1alpha1.ControllerConfigurationSpec
+	}{result1}
+}
+
+func (fake *Manager) GetEventRecorderFor(arg1 string) record.EventRecorder {
+	fake.getEventRecorderForMutex.Lock()
+	ret, specificReturn := fake.getEventRecorderForReturnsOnCall[len(fake.getEventRecorderForArgsForCall)]
+	fake.getEventRecorderForArgsForCall = append(fake.getEventRecorderForArgsForCall, struct {
+		arg1 string
+	}{arg1})
+	stub := fake.GetEventRecorderForStub
+	fakeReturns := fake.getEventRecorderForReturns
+	fake.recordInvocation("GetEventRecorderFor", []interface{}{arg1})
+	fake.getEventRecorderForMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetEventRecorderForCallCount() int {
+	fake.getEventRecorderForMutex.RLock()
+	defer fake.getEventRecorderForMutex.RUnlock()
+	return len(fake.getEventRecorderForArgsForCall)
+}
+
+func (fake *Manager) GetEventRecorderForCalls(stub func(string) record.EventRecorder) {
+	fake.getEventRecorderForMutex.Lock()
+	defer fake.getEventRecorderForMutex.Unlock()
+	fake.GetEventRecorderForStub = stub
+}
+
+func (fake *Manager) GetEventRecorderForArgsForCall(i int) string {
+	fake.getEventRecorderForMutex.RLock()
+	defer fake.getEventRecorderForMutex.RUnlock()
+	argsForCall := fake.getEventRecorderForArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *Manager) GetEventRecorderForReturns(result1 record.EventRecorder) {
+	fake.getEventRecorderForMutex.Lock()
+	defer fake.getEventRecorderForMutex.Unlock()
+	fake.GetEventRecorderForStub = nil
+	fake.getEventRecorderForReturns = struct {
+		result1 record.EventRecorder
+	}{result1}
+}
+
+func (fake *Manager) GetEventRecorderForReturnsOnCall(i int, result1 record.EventRecorder) {
+	fake.getEventRecorderForMutex.Lock()
+	defer fake.getEventRecorderForMutex.Unlock()
+	fake.GetEventRecorderForStub = nil
+	if fake.getEventRecorderForReturnsOnCall == nil {
+		fake.getEventRecorderForReturnsOnCall = make(map[int]struct {
+			result1 record.EventRecorder
+		})
+	}
+	fake.getEventRecorderForReturnsOnCall[i] = struct {
+		result1 record.EventRecorder
+	}{result1}
+}
+
+func (fake *Manager) GetFieldIndexer() client.FieldIndexer {
+	fake.getFieldIndexerMutex.Lock()
+	ret, specificReturn := fake.getFieldIndexerReturnsOnCall[len(fake.getFieldIndexerArgsForCall)]
+	fake.getFieldIndexerArgsForCall = append(fake.getFieldIndexerArgsForCall, struct {
+	}{})
+	stub := fake.GetFieldIndexerStub
+	fakeReturns := fake.getFieldIndexerReturns
+	fake.recordInvocation("GetFieldIndexer", []interface{}{})
+	fake.getFieldIndexerMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetFieldIndexerCallCount() int {
+	fake.getFieldIndexerMutex.RLock()
+	defer fake.getFieldIndexerMutex.RUnlock()
+	return len(fake.getFieldIndexerArgsForCall)
+}
+
+func (fake *Manager) GetFieldIndexerCalls(stub func() client.FieldIndexer) {
+	fake.getFieldIndexerMutex.Lock()
+	defer fake.getFieldIndexerMutex.Unlock()
+	fake.GetFieldIndexerStub = stub
+}
+
+func (fake *Manager) GetFieldIndexerReturns(result1 client.FieldIndexer) {
+	fake.getFieldIndexerMutex.Lock()
+	defer fake.getFieldIndexerMutex.Unlock()
+	fake.GetFieldIndexerStub = nil
+	fake.getFieldIndexerReturns = struct {
+		result1 client.FieldIndexer
+	}{result1}
+}
+
+func (fake *Manager) GetFieldIndexerReturnsOnCall(i int, result1 client.FieldIndexer) {
+	fake.getFieldIndexerMutex.Lock()
+	defer fake.getFieldIndexerMutex.Unlock()
+	fake.GetFieldIndexerStub = nil
+	if fake.getFieldIndexerReturnsOnCall == nil {
+		fake.getFieldIndexerReturnsOnCall = make(map[int]struct {
+			result1 client.FieldIndexer
+		})
+	}
+	fake.getFieldIndexerReturnsOnCall[i] = struct {
+		result1 client.FieldIndexer
+	}{result1}
+}
+
+func (fake *Manager) GetLogger() logr.Logger {
+	fake.getLoggerMutex.Lock()
+	ret, specificReturn := fake.getLoggerReturnsOnCall[len(fake.getLoggerArgsForCall)]
+	fake.getLoggerArgsForCall = append(fake.getLoggerArgsForCall, struct {
+	}{})
+	stub := fake.GetLoggerStub
+	fakeReturns := fake.getLoggerReturns
+	fake.recordInvocation("GetLogger", []interface{}{})
+	fake.getLoggerMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetLoggerCallCount() int {
+	fake.getLoggerMutex.RLock()
+	defer fake.getLoggerMutex.RUnlock()
+	return len(fake.getLoggerArgsForCall)
+}
+
+func (fake *Manager) GetLoggerCalls(stub func() logr.Logger) {
+	fake.getLoggerMutex.Lock()
+	defer fake.getLoggerMutex.Unlock()
+	fake.GetLoggerStub = stub
+}
+
+func (fake *Manager) GetLoggerReturns(result1 logr.Logger) {
+	fake.getLoggerMutex.Lock()
+	defer fake.getLoggerMutex.Unlock()
+	fake.GetLoggerStub = nil
+	fake.getLoggerReturns = struct {
+		result1 logr.Logger
+	}{result1}
+}
+
+func (fake *Manager) GetLoggerReturnsOnCall(i int, result1 logr.Logger) {
+	fake.getLoggerMutex.Lock()
+	defer fake.getLoggerMutex.Unlock()
+	fake.GetLoggerStub = nil
+	if fake.getLoggerReturnsOnCall == nil {
+		fake.getLoggerReturnsOnCall = make(map[int]struct {
+			result1 logr.Logger
+		})
+	}
+	fake.getLoggerReturnsOnCall[i] = struct {
+		result1 logr.Logger
+	}{result1}
+}
+
+func (fake *Manager) GetRESTMapper() meta.RESTMapper {
+	fake.getRESTMapperMutex.Lock()
+	ret, specificReturn := fake.getRESTMapperReturnsOnCall[len(fake.getRESTMapperArgsForCall)]
+	fake.getRESTMapperArgsForCall = append(fake.getRESTMapperArgsForCall, struct {
+	}{})
+	stub := fake.GetRESTMapperStub
+	fakeReturns := fake.getRESTMapperReturns
+	fake.recordInvocation("GetRESTMapper", []interface{}{})
+	fake.getRESTMapperMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetRESTMapperCallCount() int {
+	fake.getRESTMapperMutex.RLock()
+	defer fake.getRESTMapperMutex.RUnlock()
+	return len(fake.getRESTMapperArgsForCall)
+}
+
+func (fake *Manager) GetRESTMapperCalls(stub func() meta.RESTMapper) {
+	fake.getRESTMapperMutex.Lock()
+	defer fake.getRESTMapperMutex.Unlock()
+	fake.GetRESTMapperStub = stub
+}
+
+func (fake *Manager) GetRESTMapperReturns(result1 meta.RESTMapper) {
+	fake.getRESTMapperMutex.Lock()
+	defer fake.getRESTMapperMutex.Unlock()
+	fake.GetRESTMapperStub = nil
+	fake.getRESTMapperReturns = struct {
+		result1 meta.RESTMapper
+	}{result1}
+}
+
+func (fake *Manager) GetRESTMapperReturnsOnCall(i int, result1 meta.RESTMapper) {
+	fake.getRESTMapperMutex.Lock()
+	defer fake.getRESTMapperMutex.Unlock()
+	fake.GetRESTMapperStub = nil
+	if fake.getRESTMapperReturnsOnCall == nil {
+		fake.getRESTMapperReturnsOnCall = make(map[int]struct {
+			result1 meta.RESTMapper
+		})
+	}
+	fake.getRESTMapperReturnsOnCall[i] = struct {
+		result1 meta.RESTMapper
+	}{result1}
+}
+
+func (fake *Manager) GetScheme() *runtime.Scheme {
+	fake.getSchemeMutex.Lock()
+	ret, specificReturn := fake.getSchemeReturnsOnCall[len(fake.getSchemeArgsForCall)]
+	fake.getSchemeArgsForCall = append(fake.getSchemeArgsForCall, struct {
+	}{})
+	stub := fake.GetSchemeStub
+	fakeReturns := fake.getSchemeReturns
+	fake.recordInvocation("GetScheme", []interface{}{})
+	fake.getSchemeMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetSchemeCallCount() int {
+	fake.getSchemeMutex.RLock()
+	defer fake.getSchemeMutex.RUnlock()
+	return len(fake.getSchemeArgsForCall)
+}
+
+func (fake *Manager) GetSchemeCalls(stub func() *runtime.Scheme) {
+	fake.getSchemeMutex.Lock()
+	defer fake.getSchemeMutex.Unlock()
+	fake.GetSchemeStub = stub
+}
+
+func (fake *Manager) GetSchemeReturns(result1 *runtime.Scheme) {
+	fake.getSchemeMutex.Lock()
+	defer fake.getSchemeMutex.Unlock()
+	fake.GetSchemeStub = nil
+	fake.getSchemeReturns = struct {
+		result1 *runtime.Scheme
+	}{result1}
+}
+
+func (fake *Manager) GetSchemeReturnsOnCall(i int, result1 *runtime.Scheme) {
+	fake.getSchemeMutex.Lock()
+	defer fake.getSchemeMutex.Unlock()
+	fake.GetSchemeStub = nil
+	if fake.getSchemeReturnsOnCall == nil {
+		fake.getSchemeReturnsOnCall = make(map[int]struct {
+			result1 *runtime.Scheme
+		})
+	}
+	fake.getSchemeReturnsOnCall[i] = struct {
+		result1 *runtime.Scheme
+	}{result1}
+}
+
+func (fake *Manager) GetWebhookServer() *webhook.Server {
+	fake.getWebhookServerMutex.Lock()
+	ret, specificReturn := fake.getWebhookServerReturnsOnCall[len(fake.getWebhookServerArgsForCall)]
+	fake.getWebhookServerArgsForCall = append(fake.getWebhookServerArgsForCall, struct {
+	}{})
+	stub := fake.GetWebhookServerStub
+	fakeReturns := fake.getWebhookServerReturns
+	fake.recordInvocation("GetWebhookServer", []interface{}{})
+	fake.getWebhookServerMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) GetWebhookServerCallCount() int {
+	fake.getWebhookServerMutex.RLock()
+	defer fake.getWebhookServerMutex.RUnlock()
+	return len(fake.getWebhookServerArgsForCall)
+}
+
+func (fake *Manager) GetWebhookServerCalls(stub func() *webhook.Server) {
+	fake.getWebhookServerMutex.Lock()
+	defer fake.getWebhookServerMutex.Unlock()
+	fake.GetWebhookServerStub = stub
+}
+
+func (fake *Manager) GetWebhookServerReturns(result1 *webhook.Server) {
+	fake.getWebhookServerMutex.Lock()
+	defer fake.getWebhookServerMutex.Unlock()
+	fake.GetWebhookServerStub = nil
+	fake.getWebhookServerReturns = struct {
+		result1 *webhook.Server
+	}{result1}
+}
+
+func (fake *Manager) GetWebhookServerReturnsOnCall(i int, result1 *webhook.Server) {
+	fake.getWebhookServerMutex.Lock()
+	defer fake.getWebhookServerMutex.Unlock()
+	fake.GetWebhookServerStub = nil
+	if fake.getWebhookServerReturnsOnCall == nil {
+		fake.getWebhookServerReturnsOnCall = make(map[int]struct {
+			result1 *webhook.Server
+		})
+	}
+	fake.getWebhookServerReturnsOnCall[i] = struct {
+		result1 *webhook.Server
+	}{result1}
+}
+
+func (fake *Manager) SetFields(arg1 interface{}) error {
+	fake.setFieldsMutex.Lock()
+	ret, specificReturn := fake.setFieldsReturnsOnCall[len(fake.setFieldsArgsForCall)]
+	fake.setFieldsArgsForCall = append(fake.setFieldsArgsForCall, struct {
+		arg1 interface{}
+	}{arg1})
+	stub := fake.SetFieldsStub
+	fakeReturns := fake.setFieldsReturns
+	fake.recordInvocation("SetFields", []interface{}{arg1})
+	fake.setFieldsMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) SetFieldsCallCount() int {
+	fake.setFieldsMutex.RLock()
+	defer fake.setFieldsMutex.RUnlock()
+	return len(fake.setFieldsArgsForCall)
+}
+
+func (fake *Manager) SetFieldsCalls(stub func(interface{}) error) {
+	fake.setFieldsMutex.Lock()
+	defer fake.setFieldsMutex.Unlock()
+	fake.SetFieldsStub = stub
+}
+
+func (fake *Manager) SetFieldsArgsForCall(i int) interface{} {
+	fake.setFieldsMutex.RLock()
+	defer fake.setFieldsMutex.RUnlock()
+	argsForCall := fake.setFieldsArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *Manager) SetFieldsReturns(result1 error) {
+	fake.setFieldsMutex.Lock()
+	defer fake.setFieldsMutex.Unlock()
+	fake.SetFieldsStub = nil
+	fake.setFieldsReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) SetFieldsReturnsOnCall(i int, result1 error) {
+	fake.setFieldsMutex.Lock()
+	defer fake.setFieldsMutex.Unlock()
+	fake.SetFieldsStub = nil
+	if fake.setFieldsReturnsOnCall == nil {
+		fake.setFieldsReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.setFieldsReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) Start(arg1 context.Context) error {
+	fake.startMutex.Lock()
+	ret, specificReturn := fake.startReturnsOnCall[len(fake.startArgsForCall)]
+	fake.startArgsForCall = append(fake.startArgsForCall, struct {
+		arg1 context.Context
+	}{arg1})
+	stub := fake.StartStub
+	fakeReturns := fake.startReturns
+	fake.recordInvocation("Start", []interface{}{arg1})
+	fake.startMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *Manager) StartCallCount() int {
+	fake.startMutex.RLock()
+	defer fake.startMutex.RUnlock()
+	return len(fake.startArgsForCall)
+}
+
+func (fake *Manager) StartCalls(stub func(context.Context) error) {
+	fake.startMutex.Lock()
+	defer fake.startMutex.Unlock()
+	fake.StartStub = stub
+}
+
+func (fake *Manager) StartArgsForCall(i int) context.Context {
+	fake.startMutex.RLock()
+	defer fake.startMutex.RUnlock()
+	argsForCall := fake.startArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *Manager) StartReturns(result1 error) {
+	fake.startMutex.Lock()
+	defer fake.startMutex.Unlock()
+	fake.StartStub = nil
+	fake.startReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) StartReturnsOnCall(i int, result1 error) {
+	fake.startMutex.Lock()
+	defer fake.startMutex.Unlock()
+	fake.StartStub = nil
+	if fake.startReturnsOnCall == nil {
+		fake.startReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.startReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *Manager) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	fake.addHealthzCheckMutex.RLock()
+	defer fake.addHealthzCheckMutex.RUnlock()
+	fake.addMetricsExtraHandlerMutex.RLock()
+	defer fake.addMetricsExtraHandlerMutex.RUnlock()
+	fake.addReadyzCheckMutex.RLock()
+	defer fake.addReadyzCheckMutex.RUnlock()
+	fake.electedMutex.RLock()
+	defer fake.electedMutex.RUnlock()
+	fake.getAPIReaderMutex.RLock()
+	defer fake.getAPIReaderMutex.RUnlock()
+	fake.getCacheMutex.RLock()
+	defer fake.getCacheMutex.RUnlock()
+	fake.getClientMutex.RLock()
+	defer fake.getClientMutex.RUnlock()
+	fake.getConfigMutex.RLock()
+	defer fake.getConfigMutex.RUnlock()
+	fake.getControllerOptionsMutex.RLock()
+	defer fake.getControllerOptionsMutex.RUnlock()
+	fake.getEventRecorderForMutex.RLock()
+	defer fake.getEventRecorderForMutex.RUnlock()
+	fake.getFieldIndexerMutex.RLock()
+	defer fake.getFieldIndexerMutex.RUnlock()
+	fake.getLoggerMutex.RLock()
+	defer fake.getLoggerMutex.RUnlock()
+	fake.getRESTMapperMutex.RLock()
+	defer fake.getRESTMapperMutex.RUnlock()
+	fake.getSchemeMutex.RLock()
+	defer fake.getSchemeMutex.RUnlock()
+	fake.getWebhookServerMutex.RLock()
+	defer fake.getWebhookServerMutex.RUnlock()
+	fake.setFieldsMutex.RLock()
+	defer fake.setFieldsMutex.RUnlock()
+	fake.startMutex.RLock()
+	defer fake.startMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *Manager) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ manager.Manager = new(Manager)

+ 196 - 0
pkg/controllers/pushsecret/internal/fakes/statuswriter.go

@@ -0,0 +1,196 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"context"
+	"sync"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type StatusWriter struct {
+	PatchStub        func(context.Context, client.Object, client.Patch, ...client.PatchOption) error
+	patchMutex       sync.RWMutex
+	patchArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 client.Patch
+		arg4 []client.PatchOption
+	}
+	patchReturns struct {
+		result1 error
+	}
+	patchReturnsOnCall map[int]struct {
+		result1 error
+	}
+	UpdateStub        func(context.Context, client.Object, ...client.UpdateOption) error
+	updateMutex       sync.RWMutex
+	updateArgsForCall []struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.UpdateOption
+	}
+	updateReturns struct {
+		result1 error
+	}
+	updateReturnsOnCall map[int]struct {
+		result1 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *StatusWriter) Patch(arg1 context.Context, arg2 client.Object, arg3 client.Patch, arg4 ...client.PatchOption) error {
+	fake.patchMutex.Lock()
+	ret, specificReturn := fake.patchReturnsOnCall[len(fake.patchArgsForCall)]
+	fake.patchArgsForCall = append(fake.patchArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 client.Patch
+		arg4 []client.PatchOption
+	}{arg1, arg2, arg3, arg4})
+	stub := fake.PatchStub
+	fakeReturns := fake.patchReturns
+	fake.recordInvocation("Patch", []interface{}{arg1, arg2, arg3, arg4})
+	fake.patchMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3, arg4...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *StatusWriter) PatchCallCount() int {
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	return len(fake.patchArgsForCall)
+}
+
+func (fake *StatusWriter) PatchCalls(stub func(context.Context, client.Object, client.Patch, ...client.PatchOption) error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = stub
+}
+
+func (fake *StatusWriter) PatchArgsForCall(i int) (context.Context, client.Object, client.Patch, []client.PatchOption) {
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	argsForCall := fake.patchArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *StatusWriter) PatchReturns(result1 error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = nil
+	fake.patchReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *StatusWriter) PatchReturnsOnCall(i int, result1 error) {
+	fake.patchMutex.Lock()
+	defer fake.patchMutex.Unlock()
+	fake.PatchStub = nil
+	if fake.patchReturnsOnCall == nil {
+		fake.patchReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.patchReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *StatusWriter) Update(arg1 context.Context, arg2 client.Object, arg3 ...client.UpdateOption) error {
+	fake.updateMutex.Lock()
+	ret, specificReturn := fake.updateReturnsOnCall[len(fake.updateArgsForCall)]
+	fake.updateArgsForCall = append(fake.updateArgsForCall, struct {
+		arg1 context.Context
+		arg2 client.Object
+		arg3 []client.UpdateOption
+	}{arg1, arg2, arg3})
+	stub := fake.UpdateStub
+	fakeReturns := fake.updateReturns
+	fake.recordInvocation("Update", []interface{}{arg1, arg2, arg3})
+	fake.updateMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *StatusWriter) UpdateCallCount() int {
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	return len(fake.updateArgsForCall)
+}
+
+func (fake *StatusWriter) UpdateCalls(stub func(context.Context, client.Object, ...client.UpdateOption) error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = stub
+}
+
+func (fake *StatusWriter) UpdateArgsForCall(i int) (context.Context, client.Object, []client.UpdateOption) {
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	argsForCall := fake.updateArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *StatusWriter) UpdateReturns(result1 error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = nil
+	fake.updateReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *StatusWriter) UpdateReturnsOnCall(i int, result1 error) {
+	fake.updateMutex.Lock()
+	defer fake.updateMutex.Unlock()
+	fake.UpdateStub = nil
+	if fake.updateReturnsOnCall == nil {
+		fake.updateReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.updateReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *StatusWriter) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.patchMutex.RLock()
+	defer fake.patchMutex.RUnlock()
+	fake.updateMutex.RLock()
+	defer fake.updateMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *StatusWriter) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ client.StatusWriter = new(StatusWriter)

+ 228 - 0
pkg/controllers/pushsecret/pushsecret_controller.go

@@ -0,0 +1,228 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package pushsecret
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/go-logr/logr"
+	v1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/tools/record"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	v1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+	errFailedGetSecret        = "could not get source secret"
+	errPatchStatus            = "error merging"
+	errGetSecretStore         = "could not get SecretStore %q, %w"
+	errGetClusterSecretStore  = "could not get ClusterSecretStore %q, %w"
+	errGetProviderFailed      = "could not start provider"
+	errGetSecretsClientFailed = "could not start secrets client"
+	errCloseStoreClient       = "error when calling provider close method"
+	errSetSecretFailed        = "could not write remote ref %v to target secretstore %v: %v"
+	errFailedSetSecret        = "set secret failed: %v"
+)
+
+type Reconciler struct {
+	client.Client
+	Log             logr.Logger
+	Scheme          *runtime.Scheme
+	recorder        record.EventRecorder
+	RequeueInterval time.Duration
+	ControllerClass string
+}
+
+func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	log := r.Log.WithValues("pushsecret", req.NamespacedName)
+	var ss esapi.PushSecret
+	err := r.Get(ctx, req.NamespacedName, &ss)
+	if apierrors.IsNotFound(err) {
+		return ctrl.Result{}, nil
+	} else if err != nil {
+		log.Error(err, "unable to get PushSecret")
+		return ctrl.Result{}, fmt.Errorf("get resource: %w", err)
+	}
+
+	p := client.MergeFrom(ss.DeepCopy())
+	defer func() {
+		err := r.Client.Status().Patch(ctx, &ss, p)
+		if err != nil {
+			log.Error(err, errPatchStatus)
+		}
+	}()
+	secret, err := r.GetSecret(ctx, ss)
+	if err != nil {
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, "SecretSyncFailed", errFailedGetSecret)
+		ss = SetPushSecretCondition(ss, *cond)
+		return ctrl.Result{}, err
+	}
+	secretStores, err := r.GetSecretStores(ctx, ss)
+	if err != nil {
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, "SecretSyncFailed", err.Error())
+		ss = SetPushSecretCondition(ss, *cond)
+	}
+	err = r.SetSecretToProviders(ctx, secretStores, ss, secret)
+	if err != nil {
+		msg := fmt.Sprintf(errFailedSetSecret, err)
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, "SecretSyncFailed", msg)
+		ss = SetPushSecretCondition(ss, *cond)
+		return ctrl.Result{}, err
+	}
+	cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionTrue, "SecretSynced", "PushSecret synced successfully")
+	ss = SetPushSecretCondition(ss, *cond)
+	// Set status for PushSecret
+	return ctrl.Result{}, nil
+}
+
+func (r *Reconciler) SetSecretToProviders(ctx context.Context, stores []v1beta1.GenericStore, ss esapi.PushSecret, secret *v1.Secret) error {
+	for _, store := range stores {
+		provider, err := v1beta1.GetProvider(store)
+		if err != nil {
+			return fmt.Errorf(errGetProviderFailed)
+		}
+		client, err := provider.NewClient(ctx, store, r.Client, ss.Namespace)
+		if err != nil {
+			return fmt.Errorf(errGetSecretsClientFailed)
+		}
+		defer func() {
+			err := client.Close(ctx)
+			if err != nil {
+				r.Log.Error(err, errCloseStoreClient)
+			}
+		}()
+		for _, ref := range ss.Spec.Data {
+			for _, match := range ref.Match {
+				secretValue, ok := secret.Data[match.SecretKey]
+				if !ok {
+					return fmt.Errorf("secret key %v does not exist", match.SecretKey)
+				}
+				for _, rK := range match.RemoteRefs {
+					err := client.SetSecret(ctx, secretValue, rK)
+					if err != nil {
+						return fmt.Errorf(errSetSecretFailed, match.SecretKey, store.GetName(), err)
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (r *Reconciler) GetSecret(ctx context.Context, ss esapi.PushSecret) (*v1.Secret, error) {
+	secretName := types.NamespacedName{Name: ss.Spec.Selector.Secret.Name, Namespace: ss.Namespace}
+	secret := &v1.Secret{}
+	err := r.Client.Get(ctx, secretName, secret)
+	if err != nil {
+		return nil, err
+	}
+	return secret, nil
+}
+
+func (r *Reconciler) GetSecretStores(ctx context.Context, ss esapi.PushSecret) ([]v1beta1.GenericStore, error) {
+	stores := make([]v1beta1.GenericStore, 0)
+	for _, refStore := range ss.Spec.SecretStoreRefs {
+		ref := types.NamespacedName{
+			Name: refStore.Name,
+		}
+
+		if refStore.Kind == v1beta1.ClusterSecretStoreKind {
+			var store v1beta1.ClusterSecretStore
+			err := r.Get(ctx, ref, &store)
+			if err != nil {
+				return nil, fmt.Errorf(errGetClusterSecretStore, ref.Name, err)
+			}
+			stores = append(stores, &store)
+		} else {
+			ref.Namespace = ss.Namespace
+
+			var store v1beta1.SecretStore
+			err := r.Get(ctx, ref, &store)
+			if err != nil {
+				return nil, fmt.Errorf(errGetSecretStore, ref.Name, err)
+			}
+			stores = append(stores, &store)
+		}
+	}
+	return stores, nil
+}
+
+func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
+	r.recorder = mgr.GetEventRecorderFor("secret-sink")
+
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&esapi.PushSecret{}).
+		Complete(r)
+}
+
+func NewPushSecretCondition(condType esapi.PushSecretConditionType, status v1.ConditionStatus, reason, message string) *esapi.PushSecretStatusCondition {
+	return &esapi.PushSecretStatusCondition{
+		Type:               condType,
+		Status:             status,
+		LastTransitionTime: metav1.Now(),
+		Reason:             reason,
+		Message:            message,
+	}
+}
+
+func SetPushSecretCondition(gs esapi.PushSecret, condition esapi.PushSecretStatusCondition) esapi.PushSecret {
+	status := gs.Status
+	currentCond := GetPushSecretCondition(status, condition.Type)
+	if currentCond != nil && currentCond.Status == condition.Status &&
+		currentCond.Reason == condition.Reason && currentCond.Message == condition.Message {
+		return gs
+	}
+
+	// Do not update lastTransitionTime if the status of the condition doesn't change.
+	if currentCond != nil && currentCond.Status == condition.Status {
+		condition.LastTransitionTime = currentCond.LastTransitionTime
+	}
+
+	status.Conditions = append(filterOutCondition(status.Conditions, condition.Type), condition)
+	gs.Status = status
+	return gs
+}
+
+// filterOutCondition returns an empty set of conditions with the provided type.
+func filterOutCondition(conditions []esapi.PushSecretStatusCondition, condType esapi.PushSecretConditionType) []esapi.PushSecretStatusCondition {
+	newConditions := make([]esapi.PushSecretStatusCondition, 0, len(conditions))
+	for _, c := range conditions {
+		if c.Type == condType {
+			continue
+		}
+		newConditions = append(newConditions, c)
+	}
+	return newConditions
+}
+
+// GetSecretStoreCondition returns the condition with the provided type.
+func GetPushSecretCondition(status esapi.PushSecretStatus, condType esapi.PushSecretConditionType) *esapi.PushSecretStatusCondition {
+	for i := range status.Conditions {
+		c := status.Conditions[i]
+		if c.Type == condType {
+			return &c
+		}
+	}
+	return nil
+}

+ 316 - 0
pkg/controllers/pushsecret/pushsecret_controller_test.go

@@ -0,0 +1,316 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package pushsecret
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"github.com/go-logr/logr"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/types"
+	ctrl "sigs.k8s.io/controller-runtime"
+	kubeclient "sigs.k8s.io/controller-runtime/pkg/client"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	v1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/controllers/pushsecret/internal/fakes"
+	"github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
+)
+
+var fakeProvider *fake.Client
+
+var _ = Describe("pushsecret", func() {
+	var (
+		reconciler *Reconciler
+		client     *fakes.Client
+	)
+	BeforeEach(func() {
+		client = new(fakes.Client)
+		reconciler = &Reconciler{client, logr.Discard(), nil, nil, 0, ""}
+	})
+	Describe("#Reconcile", func() {
+		var (
+			statusWriter *fakes.StatusWriter
+		)
+
+		BeforeEach(func() {
+			statusWriter = new(fakes.StatusWriter)
+			client.StatusReturns(statusWriter)
+		})
+
+		It("succeeds", func() {
+			namspacedName := types.NamespacedName{Namespace: "foo", Name: "Bar"}
+			_, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: namspacedName})
+			Expect(err).NotTo(HaveOccurred())
+			Expect(client.GetCallCount()).To(Equal(2))
+			Expect(client.StatusCallCount()).To(Equal(1))
+
+			_, gotNamespacedName, _ := client.GetArgsForCall(0)
+			Expect(gotNamespacedName).To(Equal(namspacedName))
+
+			Expect(statusWriter.PatchCallCount()).To(Equal(1))
+			_, _, patch, _ := statusWriter.PatchArgsForCall(0)
+			Expect(patch.Type()).To(Equal(types.MergePatchType))
+		})
+
+		When("an error returns in get", func() {
+			BeforeEach(func() {
+				client.GetReturns(errors.New("UnknownError"))
+			})
+
+			It("returns the error", func() {
+				namspacedName := types.NamespacedName{Namespace: "foo", Name: "Bar"}
+				_, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: namspacedName})
+
+				Expect(err).To(MatchError("get resource: UnknownError"))
+				Expect(client.GetCallCount()).To(Equal(1))
+				Expect(client.StatusCallCount()).To(Equal(0))
+			})
+		})
+
+		When("an object is not found", func() {
+			BeforeEach(func() {
+				client.GetReturns(statusErrorNotFound{})
+			})
+
+			It("returns an empty result without error", func() {
+				namspacedName := types.NamespacedName{Namespace: "foo", Name: "Bar"}
+				_, err := reconciler.Reconcile(context.Background(), ctrl.Request{NamespacedName: namspacedName})
+
+				Expect(err).NotTo(HaveOccurred())
+			})
+		})
+	})
+
+	Describe("#GetPushSecretCondition", func() {
+		It("returns nil for empty secret sink status", func() {
+			pushSecretStatus := new(esapi.PushSecretStatus)
+			pushSecretConditionType := new(esapi.PushSecretConditionType)
+
+			Expect(GetPushSecretCondition(*pushSecretStatus, *pushSecretConditionType)).To(BeNil())
+		})
+
+		It("returns correct condition for secret sink status", func() {
+			pushSecretStatusCondition := esapi.PushSecretStatusCondition{Type: esapi.PushSecretReady}
+			pushSecretStatus := esapi.PushSecretStatus{Conditions: []esapi.PushSecretStatusCondition{pushSecretStatusCondition}}
+			pushSecretConditionType := esapi.PushSecretReady
+
+			Expect(GetPushSecretCondition(pushSecretStatus, pushSecretConditionType)).To(Equal(&pushSecretStatusCondition))
+		})
+	})
+
+	Describe("#SetPushSecretCondition", func() {
+		It("appends a condition", func() {
+			pushSecret := esapi.PushSecret{}
+
+			pushSecretStatusCondition := esapi.PushSecretStatusCondition{}
+			pushSecretStatus := esapi.PushSecretStatus{Conditions: []esapi.PushSecretStatusCondition{pushSecretStatusCondition}}
+			expected := esapi.PushSecret{Status: pushSecretStatus}
+			Expect(SetPushSecretCondition(pushSecret, pushSecretStatusCondition)).To(Equal(expected))
+		})
+
+		It("changes an existing condition", func() {
+			conditionStatusTrue := v1.ConditionTrue
+			pushSecretWithCondition := esapi.PushSecret{Status: esapi.PushSecretStatus{Conditions: []esapi.PushSecretStatusCondition{
+				{
+					Status: conditionStatusTrue,
+					Type:   esapi.PushSecretReady,
+				},
+			}},
+			}
+			pushSecretStatusConditionTrue := esapi.PushSecretStatusCondition{Status: conditionStatusTrue,
+				Type:    esapi.PushSecretReady,
+				Message: "Update status",
+			}
+
+			got := SetPushSecretCondition(pushSecretWithCondition, pushSecretStatusConditionTrue)
+			Expect(len(got.Status.Conditions)).To(Equal(1))
+			Expect(got.Status.Conditions[0]).To(Equal(pushSecretStatusConditionTrue))
+		})
+	})
+	Describe("#GetSecret", func() {
+		It("returns a secret if it exists", func() {
+			sink := esapi.PushSecret{
+				Spec: esapi.PushSecretSpec{
+					Selector: esapi.PushSecretSelector{
+						Secret: esapi.PushSecretSecret{
+							Name: "foo",
+						},
+					},
+				},
+			}
+			sink.Namespace = "foobar"
+			_, err := reconciler.GetSecret(context.TODO(), sink)
+			Expect(err).To(BeNil())
+			_, name, _ := client.GetArgsForCall(0)
+			Expect(name.Namespace).To(Equal("foobar"))
+			Expect(name.Name).To(Equal("foo"))
+
+		})
+
+		It("returns an error if it doesn't exist", func() {
+			client.GetReturns(errors.New("secret not found"))
+			_, err := reconciler.GetSecret(context.TODO(), esapi.PushSecret{})
+			Expect(err).To(HaveOccurred())
+		})
+	})
+
+	Describe("#GetSecretStore", func() {
+		sink := esapi.PushSecret{
+			Spec: esapi.PushSecretSpec{
+				SecretStoreRefs: []esapi.PushSecretStoreRef{
+					{
+						Name: "foo",
+					},
+				},
+			},
+		}
+		sink.Namespace = "bar"
+
+		clusterSink := esapi.PushSecret{
+			Spec: esapi.PushSecretSpec{
+				SecretStoreRefs: []esapi.PushSecretStoreRef{
+					{
+						Name: "foo",
+						Kind: "ClusterSecretStore",
+					},
+				},
+			},
+		}
+
+		It("returns a secretstore if it exists", func() {
+			_, err := reconciler.GetSecretStores(context.TODO(), sink)
+			Expect(err).To(BeNil())
+			Expect(client.GetCallCount()).To(Equal(1))
+			_, name, store := client.GetArgsForCall(0)
+			Expect(name.Namespace).To(Equal("bar"))
+			Expect(name.Name).To(Equal("foo"))
+			Expect(store).To(BeAssignableToTypeOf(&v1beta1.SecretStore{}))
+		})
+
+		It("returns an error if it doesn't exist", func() {
+			client.GetReturns(errors.New("secretstore not found"))
+			_, err := reconciler.GetSecretStores(context.TODO(), sink)
+			Expect(err).To(HaveOccurred())
+		})
+
+		It("returns a clustersecretstore if it exists", func() {
+			_, err := reconciler.GetSecretStores(context.TODO(), clusterSink)
+			Expect(err).To(BeNil())
+			Expect(client.GetCallCount()).To(Equal(1))
+			_, name, store := client.GetArgsForCall(0)
+			Expect(store).To(BeAssignableToTypeOf(&v1beta1.ClusterSecretStore{}))
+			Expect(name.Name).To(Equal("foo"))
+		})
+	})
+	Describe("#SetSecretToProviders", func() {
+		val := "supersecret"
+		secret := &v1.Secret{
+			Data: map[string][]byte{
+				"foo": []byte(val),
+			},
+		}
+		sink := esapi.PushSecret{
+			Spec: esapi.PushSecretSpec{
+				SecretStoreRefs: []esapi.PushSecretStoreRef{
+					{
+						Name: "foo",
+					},
+				},
+				Data: []esapi.PushSecretData{
+					{
+						Match: []esapi.PushSecretMatch{
+							{
+								SecretKey: "foo",
+								RemoteRefs: []esapi.PushSecretRemoteRefs{
+									{
+										RemoteKey: "bar",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		}
+		sink.Namespace = "bar"
+
+		secretStore := v1beta1.SecretStore{}
+		stores := make([]v1beta1.GenericStore, 0)
+		stores = append(stores, &secretStore)
+
+		It("gets the provider and client and then sets the secret", func() {
+
+			Expect(reconciler.SetSecretToProviders(context.TODO(), []v1beta1.GenericStore{}, sink, secret)).To(BeNil())
+		})
+
+		It("returns an error if it can't get a provider", func() {
+			err := reconciler.SetSecretToProviders(context.TODO(), stores, sink, secret)
+
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(Equal(errGetProviderFailed))
+		})
+
+		It("returns an if it can't get a client", func() {
+			specWithProvider := v1beta1.SecretStoreSpec{
+				Provider: &v1beta1.SecretStoreProvider{
+					Fake: &v1beta1.FakeProvider{},
+				},
+			}
+			fakeProvider.WithNew(func(context.Context, v1beta1.GenericStore, kubeclient.Client,
+				string) (v1beta1.SecretsClient, error) {
+				return nil, fmt.Errorf("Something went wrong")
+			})
+			secretStore = v1beta1.SecretStore{
+				Spec: specWithProvider,
+			}
+
+			stores[0] = &secretStore
+			err := reconciler.SetSecretToProviders(context.TODO(), stores, sink, secret)
+
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(Equal(errGetSecretsClientFailed))
+		})
+		It("returns an error if set secret fails", func() {
+			specWithProvider := v1beta1.SecretStoreSpec{
+				Provider: &v1beta1.SecretStoreProvider{
+					Fake: &v1beta1.FakeProvider{},
+				},
+			}
+			fakeProvider.Reset()
+			fakeProvider.WithSetSecret(fmt.Errorf("something went wrong"))
+			secretStore = v1beta1.SecretStore{
+				Spec: specWithProvider,
+			}
+
+			stores[0] = &secretStore
+			err := reconciler.SetSecretToProviders(context.TODO(), stores, sink, secret)
+
+			Expect(err).To(HaveOccurred())
+			Expect(err.Error()).To(Equal(fmt.Sprintf(errSetSecretFailed, "foo", "", "something went wrong")))
+		})
+	})
+})
+
+func init() {
+	fakeProvider = fake.New()
+	v1beta1.ForceRegister(fakeProvider, &v1beta1.SecretStoreProvider{
+		Fake: &v1beta1.FakeProvider{},
+	})
+}

+ 44 - 0
pkg/controllers/pushsecret/suite_test.go

@@ -0,0 +1,44 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package pushsecret
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestAPIs(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Controller Suite")
+}
+
+type statusErrorNotFound struct {
+}
+
+func (statusErrorNotFound) Status() metav1.Status {
+	return metav1.Status{
+		Reason: metav1.StatusReasonNotFound,
+	}
+}
+
+func (statusErrorNotFound) Error() string {
+	return "Blurb"
+}
+
+var _ errors.APIStatus = statusErrorNotFound{}

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

@@ -67,6 +67,15 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
 		log.Error(err, "unable to validate store")
 		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)

+ 33 - 0
pkg/controllers/secretstore/common_test.go

@@ -125,6 +125,37 @@ var _ = Describe("SecretStore reconcile", func() {
 
 	}
 
+	readWrite := func(tc *testCase) {
+		spc := tc.store.GetSpec()
+		spc.Provider.Vault = nil
+		spc.Provider.Fake = &esapi.FakeProvider{
+			Data: []esapi.FakeProviderData{},
+		}
+
+		tc.assert = func() {
+			Eventually(func() bool {
+				ss := tc.store.Copy()
+				err := k8sClient.Get(context.Background(), types.NamespacedName{
+					Name:      defaultStoreName,
+					Namespace: ss.GetNamespace(),
+				}, ss)
+				if err != nil {
+					return false
+				}
+
+				if ss.GetStatus().Capabilities != esapi.SecretStoreReadWrite {
+					return false
+				}
+
+				return true
+			}).
+				WithTimeout(time.Second * 10).
+				WithPolling(time.Second).
+				Should(BeTrue())
+		}
+
+	}
+
 	DescribeTable("Controller Reconcile logic", func(muts ...func(tc *testCase)) {
 		for _, mut := range muts {
 			mut(test)
@@ -137,11 +168,13 @@ var _ = Describe("SecretStore reconcile", func() {
 		Entry("[namespace] invalid provider with secretStore should set InvalidStore condition", invalidProvider),
 		Entry("[namespace] ignore stores with non-matching class", ignoreControllerClass),
 		Entry("[namespace] valid provider has status=ready", validProvider),
+		Entry("[namespace] valid provider has capabilities=ReadWrite", readWrite),
 
 		// cluster store
 		Entry("[cluster] invalid provider with secretStore should set InvalidStore condition", invalidProvider, useClusterStore),
 		Entry("[cluster] ignore stores with non-matching class", ignoreControllerClass, useClusterStore),
 		Entry("[cluster] valid provider has status=ready", validProvider, useClusterStore),
+		Entry("[cluster] valid provider has capabilities=ReadWrite", readWrite, useClusterStore),
 	)
 
 })

+ 9 - 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)
@@ -165,6 +170,10 @@ func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
 	return esv1beta1.ValidationResultReady, nil
 }
 
+func (a *Akeyless) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Implements store.Client.GetSecret Interface.
 // Retrieves a secret with the secret name defined in ref.Name.
 func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {

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

@@ -114,6 +114,10 @@ func (c *Client) setAuth(ctx context.Context) error {
 	return nil
 }
 
+func (kms *KeyManagementService) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Empty GetAllSecrets.
 func (kms *KeyManagementService) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented
@@ -168,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()

+ 6 - 1
pkg/provider/aws/parameterstore/parameterstore.go

@@ -60,7 +60,12 @@ func New(sess *session.Session) (*ParameterStore, error) {
 	}, nil
 }
 
-// Empty GetAllSecrets.
+// Not Implemented SetSecret.
+func (pm *ParameterStore) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
+// GetAllSecrets fetches information from multiple secrets into a single kubernetes secret.
 func (pm *ParameterStore) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	if ref.Name != nil {
 		return pm.findByName(ref)

+ 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/aws/secretsmanager/secretsmanager.go

@@ -104,6 +104,11 @@ func (sm *SecretsManager) fetch(_ context.Context, ref esv1beta1.ExternalSecretD
 	return secretOut, nil
 }
 
+// Not Implemented SetSecret.
+func (sm *SecretsManager) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // GetAllSecrets syncs multiple secrets from aws provider into a single Kubernetes Secret.
 func (sm *SecretsManager) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	if ref.Name != nil {

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

@@ -107,6 +107,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)
@@ -196,6 +201,11 @@ func (a *Azure) ValidateStore(store esv1beta1.GenericStore) error {
 	return nil
 }
 
+// Not Implemented SetSecret.
+func (a *Azure) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Implements store.Client.GetAllSecrets Interface.
 // Retrieves a map[string][]byte with the secret names as key and the secret itself as the calue.
 func (a *Azure) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {

+ 68 - 13
pkg/provider/fake/fake.go

@@ -30,15 +30,54 @@ var (
 	errMissingValueField   = "at least one of value or valueMap must be set in data %v"
 )
 
+type SourceOrigin string
+
+const (
+	FakeSecretStore SourceOrigin = "SecretStore"
+	FakeSetSecret   SourceOrigin = "SetSecret"
+)
+
+type Data struct {
+	Value    string
+	Version  string
+	ValueMap map[string]string
+	Origin   SourceOrigin
+}
+type Config map[string]*Data
 type Provider struct {
-	config *esv1beta1.FakeProvider
+	config   Config
+	database map[string]Config
+}
+
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadWrite
 }
 
 func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
-	cfg, err := getProvider(store)
+	if p.database == nil {
+		p.database = make(map[string]Config)
+	}
+	c, err := getProvider(store)
 	if err != nil {
 		return nil, err
 	}
+	cfg := p.database[store.GetName()]
+	if cfg == nil {
+		cfg = Config{}
+	}
+	for _, data := range c.Data {
+		mapKey := fmt.Sprintf("%v%v", data.Key, data.Version)
+		cfg[mapKey] = &Data{
+			Value:   data.Value,
+			Version: data.Version,
+			Origin:  FakeSecretStore,
+		}
+		if data.ValueMap != nil {
+			cfg[mapKey].ValueMap = data.ValueMap
+		}
+	}
+	p.database[store.GetName()] = cfg
 	return &Provider{
 		config: cfg,
 	}, nil
@@ -55,6 +94,23 @@ func getProvider(store esv1beta1.GenericStore) (*esv1beta1.FakeProvider, error)
 	return spc.Provider.Fake, nil
 }
 
+// Not Implemented SetSecret.
+func (p *Provider) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	currentData, ok := p.config[remoteRef.GetRemoteKey()]
+	if !ok {
+		p.config[remoteRef.GetRemoteKey()] = &Data{
+			Value:  string(value),
+			Origin: FakeSetSecret,
+		}
+		return nil
+	}
+	if currentData.Origin != FakeSetSecret {
+		return fmt.Errorf("key already exists")
+	}
+	currentData.Value = string(value)
+	return nil
+}
+
 // Empty GetAllSecrets.
 func (p *Provider) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented
@@ -63,23 +119,22 @@ func (p *Provider) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecr
 
 // GetSecret returns a single secret from the provider.
 func (p *Provider) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	for _, data := range p.config.Data {
-		if data.Key == ref.Key && data.Version == ref.Version {
-			return []byte(data.Value), nil
-		}
+	mapKey := fmt.Sprintf("%v%v", ref.Key, ref.Version)
+	data, ok := p.config[mapKey]
+	if !ok || data.Version != ref.Version {
+		return nil, esv1beta1.NoSecretErr
 	}
-	return nil, esv1beta1.NoSecretErr
+	return []byte(data.Value), nil
 }
 
 // GetSecretMap returns multiple k/v pairs from the provider.
 func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
-	for _, data := range p.config.Data {
-		if data.Key != ref.Key || data.Version != ref.Version || data.ValueMap == nil {
-			continue
-		}
-		return convertMap(data.ValueMap), nil
+	mapKey := fmt.Sprintf("%v%v", ref.Key, ref.Version)
+	data, ok := p.config[mapKey]
+	if !ok || data.Version != ref.Version || data.ValueMap == nil {
+		return nil, esv1beta1.NoSecretErr
 	}
-	return nil, esv1beta1.NoSecretErr
+	return convertMap(data.ValueMap), nil
 }
 
 func convertMap(in map[string]string) map[string][]byte {

+ 74 - 2
pkg/provider/fake/fake_test.go

@@ -15,11 +15,14 @@ package fake
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"testing"
 
 	"github.com/onsi/gomega"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 
@@ -123,9 +126,12 @@ func TestGetSecret(t *testing.T) {
 		},
 	}
 
-	for _, row := range tbl {
+	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{
@@ -146,6 +152,69 @@ func TestGetSecret(t *testing.T) {
 	}
 }
 
+type setSecretTestCase struct {
+	name       string
+	input      []esv1beta1.FakeProviderData
+	requestKey string
+	expValue   string
+	expErr     string
+}
+
+func TestSetSecret(t *testing.T) {
+	gomega.RegisterTestingT(t)
+	p := &Provider{}
+	tbl := []setSecretTestCase{
+		{
+			name:       "return nil if no existing secret",
+			input:      []esv1beta1.FakeProviderData{},
+			requestKey: "/foo",
+			expValue:   "my-secret-value",
+		},
+		{
+			name: "return err if existing secret",
+			input: []esv1beta1.FakeProviderData{
+				{
+					Key:   "/foo",
+					Value: "bar2",
+				},
+			},
+			requestKey: "/foo",
+			expErr:     errors.New("key already exists").Error(),
+		},
+	}
+
+	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())
+			err = cl.SetSecret(context.TODO(), []byte(row.expValue), esv1alpha1.PushSecretRemoteRefs{
+				RemoteKey: row.requestKey,
+			})
+			if row.expErr != "" {
+				gomega.Expect(err).To(gomega.MatchError(row.expErr))
+			} else {
+				gomega.Expect(err).ToNot(gomega.HaveOccurred())
+				out, err := cl.GetSecret(context.Background(), esv1beta1.ExternalSecretDataRemoteRef{
+					Key: row.requestKey,
+				})
+				gomega.Expect(err).ToNot(gomega.HaveOccurred())
+				gomega.Expect(string(out)).To(gomega.Equal(row.expValue))
+			}
+		})
+	}
+}
+
 type testMapCase struct {
 	name     string
 	input    []esv1beta1.FakeProviderData
@@ -204,9 +273,12 @@ func TestGetSecretMap(t *testing.T) {
 		},
 	}
 
-	for _, row := range tbl {
+	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{

+ 67 - 0
pkg/provider/gcp/secretmanager/fake/fake.go

@@ -15,6 +15,7 @@ package fake
 
 import (
 	"context"
+	"errors"
 	"fmt"
 
 	secretmanager "cloud.google.com/go/secretmanager/apiv1"
@@ -27,6 +28,8 @@ import (
 type MockSMClient struct {
 	accessSecretFn func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
 	ListSecretsFn  func(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
+	addSecretFn    func(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
+	createSecretFn func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
 	closeFn        func() error
 }
 
@@ -41,12 +44,76 @@ func (mc *MockSMClient) Close() error {
 	return mc.closeFn()
 }
 
+func (mc *MockSMClient) AddSecretVersion(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error) {
+	return mc.addSecretFn(ctx, req)
+}
+
+func (mc *MockSMClient) CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+	return mc.createSecretFn(ctx, req)
+}
+
 func (mc *MockSMClient) NilClose() {
 	mc.closeFn = func() error {
 		return nil
 	}
 }
 
+func (mc *MockSMClient) CreateSecretError() {
+	mc.createSecretFn = func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+		return nil, errors.New("something went wrong")
+	}
+}
+
+func (mc *MockSMClient) CreateSecretGetError() {
+	mc.createSecretFn = func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+		mc.accessSecretFn = func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
+			return nil, errors.New("no, this broke")
+		}
+		return nil, nil
+	}
+}
+
+func (mc *MockSMClient) DefaultCreateSecret(wantedSecretID, wantedParent string) {
+	mc.createSecretFn = func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+		if req.SecretId != wantedSecretID {
+			return nil, fmt.Errorf("create secret req wrong key: got %v want %v", req.SecretId, wantedSecretID)
+		}
+		if req.Parent != wantedParent {
+			return nil, fmt.Errorf("create secret req wrong parent: got %v want %v", req.Parent, wantedParent)
+		}
+		return &secretmanagerpb.Secret{
+			Name: fmt.Sprintf("%s/%s", req.Parent, req.SecretId),
+		}, nil
+	}
+}
+
+func (mc *MockSMClient) DefaultAddSecretVersion(wantedData, wantedParent, versionName string) {
+	mc.addSecretFn = func(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error) {
+		if string(req.Payload.Data) != wantedData {
+			return nil, fmt.Errorf("add version req wrong data got: %v want %v ", req.Payload.Data, wantedData)
+		}
+		if req.Parent != wantedParent {
+			return nil, fmt.Errorf("add version req has wrong parent: got %v want %v", req.Parent, wantedParent)
+		}
+		return &secretmanagerpb.SecretVersion{
+			Name: versionName,
+		}, nil
+	}
+}
+
+func (mc *MockSMClient) DefaultAccessSecretVersion(wantedVersionName string) {
+	mc.accessSecretFn = func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
+		if req.Name != wantedVersionName {
+			return nil, fmt.Errorf("access req has wrong version name: got %v want %v", req.Name, wantedVersionName)
+		}
+		return &secretmanagerpb.AccessSecretVersionResponse{
+			Name: req.Name,
+		}, nil
+	}
+}
+
+// TODO: func (mc...) DefaultAccessSecretVersion (similar to above)
+
 func (mc *MockSMClient) WithValue(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, val *secretmanagerpb.AccessSecretVersionResponse, err error) {
 	if mc != nil {
 		mc.accessSecretFn = func(paramCtx context.Context, paramReq *secretmanagerpb.AccessSecretVersionRequest, paramOpts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {

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

@@ -71,6 +71,8 @@ var log = ctrl.Log.WithName("provider").WithName("gcp").WithName("secretsmanager
 type GoogleSecretManagerClient interface {
 	AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
 	ListSecrets(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
+	AddSecretVersion(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
+	CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
 	Close() error
 }
 
@@ -160,6 +162,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()
@@ -214,6 +221,44 @@ func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1beta1.GenericSto
 	return sm, nil
 }
 
+// SetSecret pushes a kubernetes secret key into gcp provider Secret.
+// funcName(variable type_of_variable, ...)
+func (sm *ProviderGCP) SetSecret(ctx context.Context, payload []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	createSecretReq := &secretmanagerpb.CreateSecretRequest{
+		Parent:   fmt.Sprintf("projects/%s", sm.projectID),
+		SecretId: remoteRef.GetRemoteKey(),
+	}
+
+	secret, err := sm.SecretManagerClient.CreateSecret(ctx, createSecretReq)
+
+	if err != nil {
+		return err
+	}
+
+	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
+		Parent: secret.Name,
+		Payload: &secretmanagerpb.SecretPayload{
+			Data: payload,
+		},
+	}
+
+	version, err := sm.SecretManagerClient.AddSecretVersion(ctx, addSecretVersionReq)
+
+	if err != nil {
+		return err
+	}
+
+	accessRequest := secretmanagerpb.AccessSecretVersionRequest{
+		Name: version.Name,
+	}
+
+	if _, err := sm.SecretManagerClient.AccessSecretVersion(ctx, &accessRequest); err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // GetAllSecrets syncs multiple secrets from gcp provider into a single Kubernetes Secret.
 func (sm *ProviderGCP) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	if ref.Name != nil {

+ 41 - 0
pkg/provider/gcp/secretmanager/secretsmanager_test.go

@@ -23,6 +23,7 @@ import (
 	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
 	"k8s.io/utils/pointer"
 
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 	fakesm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager/fake"
@@ -181,6 +182,46 @@ func TestSecretManagerGetSecret(t *testing.T) {
 	}
 }
 
+func TestSecretManagerSetSecret(t *testing.T) {
+	secretManagerClient := fakesm.MockSMClient{}
+	secretManagerClient.NilClose()
+	secretManagerClient.WithValue(context.Background(), nil, nil, nil)
+	secretManagerClient.CreateSecretError()
+
+	key := "foo"
+	want := []byte("bar")
+	projectID := "default"
+
+	wantedSecretParent := fmt.Sprintf("projects/%s", projectID)
+	wantedVersionParent := fmt.Sprintf("%s/%s", wantedSecretParent, key)
+	wantedVersion := "latest"
+
+	p := ProviderGCP{
+		SecretManagerClient: &secretManagerClient,
+		projectID:           projectID,
+	}
+	err := p.SetSecret(context.TODO(), want, esv1alpha1.PushSecretRemoteRefs{RemoteKey: key})
+	if err == nil {
+		t.Errorf("expected err got nil from SetSecret")
+	}
+
+	secretManagerClient.DefaultCreateSecret(key, wantedSecretParent)
+	secretManagerClient.DefaultAddSecretVersion(string(want), wantedVersionParent, wantedVersion)
+	secretManagerClient.DefaultAccessSecretVersion(wantedVersion)
+
+	err = p.SetSecret(context.TODO(), want, esv1alpha1.PushSecretRemoteRefs{RemoteKey: key})
+	if err != nil {
+		t.Errorf("expected nil got err from SetSecret: %v", err)
+	}
+	err = p.SetSecret(context.TODO(), want, esv1alpha1.PushSecretRemoteRefs{RemoteKey: "wrong"})
+	if err == nil {
+		t.Errorf("expected err got nil")
+	}
+	err = p.SetSecret(context.TODO(), []byte("potato"), esv1alpha1.PushSecretRemoteRefs{RemoteKey: key})
+	if err == nil {
+		t.Errorf("expected err got nil")
+	}
+}
 func TestGetSecretMap(t *testing.T) {
 	// good case: default version & deserialization
 	setDeserialization := func(smtc *secretManagerTestCase) {

+ 10 - 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()
@@ -156,6 +161,11 @@ func (g *Gitlab) NewClient(ctx context.Context, store esv1beta1.GenericStore, ku
 	return g, nil
 }
 
+// Not Implemented SetSecret.
+func (g *Gitlab) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Empty GetAllSecrets.
 func (g *Gitlab) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented

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

@@ -100,6 +100,11 @@ func (c *client) setAuth(ctx context.Context) error {
 	return nil
 }
 
+// Not Implemented SetSecret.
+func (ibm *providerIBM) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Empty GetAllSecrets.
 func (ibm *providerIBM) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented
@@ -564,6 +569,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

+ 10 - 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()
@@ -125,6 +130,11 @@ func (k *ProviderKubernetes) Close(ctx context.Context) error {
 	return nil
 }
 
+// Not Implemented SetSecret.
+func (k *ProviderKubernetes) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 func (k *ProviderKubernetes) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	if ref.Property == "" {
 		return nil, fmt.Errorf(errPropertyNotFound)

+ 10 - 0
pkg/provider/onepassword/onepassword.go

@@ -69,6 +69,11 @@ type ProviderOnePassword struct {
 var _ esv1beta1.SecretsClient = &ProviderOnePassword{}
 var _ esv1beta1.Provider = &ProviderOnePassword{}
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (provider *ProviderOnePassword) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a 1Password Provider.
 func (provider *ProviderOnePassword) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	config := store.GetSpec().Provider.OnePassword
@@ -148,6 +153,11 @@ func validateStore(store esv1beta1.GenericStore) error {
 	return nil
 }
 
+// Not Implemented SetSecret.
+func (provider *ProviderOnePassword) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // GetSecret returns a single secret from the provider.
 func (provider *ProviderOnePassword) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	if ref.Version != "" {

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

@@ -65,6 +65,11 @@ type VMInterface interface {
 	GetSecretBundleByName(ctx context.Context, request secrets.GetSecretBundleByNameRequest) (secrets.GetSecretBundleByNameResponse, error)
 }
 
+// Not Implemented SetSecret.
+func (vms *VaultManagementService) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Empty GetAllSecrets.
 func (vms *VaultManagementService) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented
@@ -125,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/dsm/dsm.go

@@ -90,6 +90,11 @@ func New(isoSession *senhaseguraAuth.SenhaseguraIsoSession) (*DSM, error) {
 	}, nil
 }
 
+// Not Implemented SetSecret.
+func (dsm *DSM) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 /*
 	GetSecret implements ESO interface and get a single secret from senhasegura provider with DSM service
 */

+ 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
 */

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

@@ -30,6 +30,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)
+	SetSecretFn     func() error
 }
 
 // New returns a fake provider/client.
@@ -44,6 +45,9 @@ func New() *Client {
 		GetAllSecretsFn: func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 			return nil, nil
 		},
+		SetSecretFn: func() error {
+			return nil
+		},
 	}
 
 	v.NewFn = func(context.Context, esv1beta1.GenericStore, client.Client, string) (esv1beta1.SecretsClient, error) {
@@ -63,6 +67,11 @@ func (v *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecret
 	return v.GetAllSecretsFn(ctx, ref)
 }
 
+// Not Implemented SetSecret.
+func (v *Client) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return v.SetSecretFn()
+}
+
 // GetSecret implements the provider.Provider interface.
 func (v *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	return v.GetSecretFn(ctx, ref)
@@ -109,6 +118,14 @@ func (v *Client) WithGetAllSecrets(secData map[string][]byte, err error) *Client
 	return v
 }
 
+// WithSetSecret wraps the secret response to the fake provider.
+func (v *Client) WithSetSecret(err error) *Client {
+	v.SetSecretFn = func() error {
+		return err
+	}
+	return v
+}
+
 // WithNew wraps the fake provider factory function.
 func (v *Client) WithNew(f func(context.Context, esv1beta1.GenericStore, client.Client,
 	string) (esv1beta1.SecretsClient, error)) *Client {
@@ -116,6 +133,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)

+ 12 - 3
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
@@ -355,9 +360,13 @@ func (c *connector) ValidateStore(store esv1beta1.GenericStore) error {
 	return nil
 }
 
-// Empty GetAllSecrets.
-// GetAllSecrets
-// First load all secrets from secretStore path configuration.
+// Not Implemented SetSecret.
+func (v *client) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
+// GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
+// First load all secrets from secretStore path configuration
 // Then, gets secrets from a matching name or matching custom_metadata.
 func (v *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	if v.store.Version == esv1beta1.VaultKVStoreV1 {

+ 10 - 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,
@@ -111,6 +116,11 @@ func (w *WebHook) getStoreSecret(ctx context.Context, ref esmeta.SecretKeySelect
 	return secret, nil
 }
 
+// Not Implemented SetSecret.
+func (w *WebHook) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 // Empty GetAllSecrets.
 func (w *WebHook) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented

+ 6 - 1
pkg/provider/yandex/common/provider.go

@@ -88,6 +88,7 @@ func InitYandexCloudProvider(
 	return provider
 }
 
+type NewSecretSetterFunc func()
 type AdaptInputFunc func(store esv1beta1.GenericStore) (*SecretsClientInput, error)
 type NewSecretGetterFunc func(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (SecretGetter, error)
 type NewIamTokenFunc func(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (*IamToken, error)
@@ -103,6 +104,10 @@ type SecretsClientInput struct {
 	CACertificate *esmeta.SecretKeySelector
 }
 
+func (p *YandexCloudProvider) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a Yandex.Cloud Provider.
 func (p *YandexCloudProvider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	input, err := p.adaptInputFunc(store)
@@ -177,7 +182,7 @@ func (p *YandexCloudProvider) NewClient(ctx context.Context, store esv1beta1.Gen
 		return nil, fmt.Errorf("failed to create IAM token: %w", err)
 	}
 
-	return &yandexCloudSecretsClient{secretGetter, iamToken.Token}, nil
+	return &yandexCloudSecretsClient{secretGetter, nil, iamToken.Token}, nil
 }
 
 func (p *YandexCloudProvider) getOrCreateSecretGetter(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (SecretGetter, error) {

+ 14 - 9
pkg/provider/yandex/common/secretsclient.go

@@ -26,26 +26,31 @@ var _ esv1beta1.SecretsClient = &yandexCloudSecretsClient{}
 // Implementation of v1beta1.SecretsClient.
 type yandexCloudSecretsClient struct {
 	secretGetter SecretGetter
+	secretSetter SecretSetter
 	iamToken     string
 }
 
-func (c *yandexCloudSecretsClient) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
-	// TO be implemented
-	return nil, fmt.Errorf("GetAllSecrets not supported")
-}
-
 func (c *yandexCloudSecretsClient) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	return c.secretGetter.GetSecret(ctx, c.iamToken, ref.Key, ref.Version, ref.Property)
 }
 
+func (c *yandexCloudSecretsClient) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
+func (c *yandexCloudSecretsClient) Validate() (esv1beta1.ValidationResult, error) {
+	return esv1beta1.ValidationResultReady, nil
+}
+
 func (c *yandexCloudSecretsClient) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
 	return c.secretGetter.GetSecretMap(ctx, c.iamToken, ref.Key, ref.Version)
 }
 
-func (c *yandexCloudSecretsClient) Close(ctx context.Context) error {
-	return nil
+func (c *yandexCloudSecretsClient) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	// TO be implemented
+	return nil, fmt.Errorf("GetAllSecrets not supported")
 }
 
-func (c *yandexCloudSecretsClient) Validate() (esv1beta1.ValidationResult, error) {
-	return esv1beta1.ValidationResultReady, nil
+func (c *yandexCloudSecretsClient) Close(ctx context.Context) error {
+	return nil
 }

+ 18 - 0
pkg/provider/yandex/common/secretsetter.go

@@ -0,0 +1,18 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package common
+
+type SecretSetter interface {
+	SetSecret() error
+}