Sfoglia il codice sorgente

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

Conch Horse (EngineerBetterCI) 4 anni fa
parent
commit
95472ef932
54 ha cambiato i file con 5248 aggiunte e 71 eliminazioni
  1. 130 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. 102 0
      apis/externalsecrets/v1beta1/fakes/pushremoteref.go
  5. 5 0
      apis/externalsecrets/v1beta1/provider.go
  6. 9 0
      apis/externalsecrets/v1beta1/provider_schema_test.go
  7. 24 0
      apis/externalsecrets/v1beta1/pushsecret_interfaces.go
  8. 14 0
      apis/externalsecrets/v1beta1/secretstore_types.go
  9. 11 0
      cmd/root.go
  10. 10 0
      config/crds/bases/external-secrets.io_clustersecretstores.yaml
  11. 142 0
      config/crds/bases/external-secrets.io_pushsecrets.yaml
  12. 7 0
      config/crds/bases/external-secrets.io_secretstores.yaml
  13. 6 0
      deploy/charts/external-secrets/templates/rbac.yaml
  14. 159 0
      deploy/crds/bundle.yaml
  15. 8 8
      design/001-secretsink.md
  16. 3 3
      e2e/framework/addon/eso.go
  17. 8 8
      e2e/framework/addon/vault.go
  18. 2 1
      go.mod
  19. 8 4
      go.sum
  20. 784 0
      pkg/controllers/pushsecret/internal/fakes/client.go
  21. 1288 0
      pkg/controllers/pushsecret/internal/fakes/manager.go
  22. 179 0
      pkg/controllers/pushsecret/internal/fakes/recorder.go
  23. 196 0
      pkg/controllers/pushsecret/internal/fakes/statuswriter.go
  24. 236 0
      pkg/controllers/pushsecret/pushsecret_controller.go
  25. 407 0
      pkg/controllers/pushsecret/pushsecret_controller_test.go
  26. 44 0
      pkg/controllers/pushsecret/suite_test.go
  27. 9 0
      pkg/controllers/secretstore/common.go
  28. 33 0
      pkg/controllers/secretstore/common_test.go
  29. 9 0
      pkg/provider/akeyless/akeyless.go
  30. 9 0
      pkg/provider/alibaba/kms.go
  31. 6 1
      pkg/provider/aws/parameterstore/parameterstore.go
  32. 5 0
      pkg/provider/aws/provider.go
  33. 5 0
      pkg/provider/aws/secretsmanager/secretsmanager.go
  34. 10 0
      pkg/provider/azure/keyvault/keyvault.go
  35. 68 13
      pkg/provider/fake/fake.go
  36. 74 2
      pkg/provider/fake/fake_test.go
  37. 78 0
      pkg/provider/gcp/secretmanager/fake/fake.go
  38. 516 0
      pkg/provider/gcp/secretmanager/internal/fakes/client.go
  39. 98 11
      pkg/provider/gcp/secretmanager/secretsmanager.go
  40. 166 7
      pkg/provider/gcp/secretmanager/secretsmanager_test.go
  41. 10 0
      pkg/provider/gitlab/gitlab.go
  42. 10 0
      pkg/provider/ibm/provider.go
  43. 10 0
      pkg/provider/kubernetes/kubernetes.go
  44. 10 0
      pkg/provider/onepassword/onepassword.go
  45. 10 0
      pkg/provider/oracle/oracle.go
  46. 5 0
      pkg/provider/senhasegura/dsm/dsm.go
  47. 5 0
      pkg/provider/senhasegura/provider.go
  48. 22 0
      pkg/provider/testing/fake/fake.go
  49. 12 3
      pkg/provider/vault/vault.go
  50. 10 0
      pkg/provider/webhook/webhook.go
  51. 6 1
      pkg/provider/yandex/common/provider.go
  52. 14 9
      pkg/provider/yandex/common/secretsclient.go
  53. 18 0
      pkg/provider/yandex/common/secretsetter.go
  54. 1 0
      tools.go

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

@@ -0,0 +1,130 @@
+/*
+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"
+)
+
+const (
+	ReasonSynced    = "Synced"
+	ReasonNotSynced = "NotSynced"
+	ReasonErrored   = "Errored"
+)
+
+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 PushSecretStatusCondition.
+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

+ 102 - 0
apis/externalsecrets/v1beta1/fakes/pushremoteref.go

@@ -0,0 +1,102 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"sync"
+
+	"github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+type PushRemoteRef struct {
+	GetRemoteKeyStub        func() string
+	getRemoteKeyMutex       sync.RWMutex
+	getRemoteKeyArgsForCall []struct {
+	}
+	getRemoteKeyReturns struct {
+		result1 string
+	}
+	getRemoteKeyReturnsOnCall map[int]struct {
+		result1 string
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *PushRemoteRef) GetRemoteKey() string {
+	fake.getRemoteKeyMutex.Lock()
+	ret, specificReturn := fake.getRemoteKeyReturnsOnCall[len(fake.getRemoteKeyArgsForCall)]
+	fake.getRemoteKeyArgsForCall = append(fake.getRemoteKeyArgsForCall, struct {
+	}{})
+	stub := fake.GetRemoteKeyStub
+	fakeReturns := fake.getRemoteKeyReturns
+	fake.recordInvocation("GetRemoteKey", []interface{}{})
+	fake.getRemoteKeyMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *PushRemoteRef) GetRemoteKeyCallCount() int {
+	fake.getRemoteKeyMutex.RLock()
+	defer fake.getRemoteKeyMutex.RUnlock()
+	return len(fake.getRemoteKeyArgsForCall)
+}
+
+func (fake *PushRemoteRef) GetRemoteKeyCalls(stub func() string) {
+	fake.getRemoteKeyMutex.Lock()
+	defer fake.getRemoteKeyMutex.Unlock()
+	fake.GetRemoteKeyStub = stub
+}
+
+func (fake *PushRemoteRef) GetRemoteKeyReturns(result1 string) {
+	fake.getRemoteKeyMutex.Lock()
+	defer fake.getRemoteKeyMutex.Unlock()
+	fake.GetRemoteKeyStub = nil
+	fake.getRemoteKeyReturns = struct {
+		result1 string
+	}{result1}
+}
+
+func (fake *PushRemoteRef) GetRemoteKeyReturnsOnCall(i int, result1 string) {
+	fake.getRemoteKeyMutex.Lock()
+	defer fake.getRemoteKeyMutex.Unlock()
+	fake.GetRemoteKeyStub = nil
+	if fake.getRemoteKeyReturnsOnCall == nil {
+		fake.getRemoteKeyReturnsOnCall = make(map[int]struct {
+			result1 string
+		})
+	}
+	fake.getRemoteKeyReturnsOnCall[i] = struct {
+		result1 string
+	}{result1}
+}
+
+func (fake *PushRemoteRef) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.getRemoteKeyMutex.RLock()
+	defer fake.getRemoteKeyMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *PushRemoteRef) 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 _ v1beta1.PushRemoteRef = new(PushRemoteRef)

+ 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"
 	awsauth "github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
 )
@@ -148,6 +149,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:
@@ -2880,6 +2886,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_pushsecrets.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:
@@ -2883,6 +2886,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:
@@ -2452,6 +2458,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:
@@ -2997,6 +3006,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
@@ -4026,6 +4179,9 @@ spec:
         - jsonPath: .status.conditions[?(@.type=="Ready")].reason
           name: Status
           type: string
+        - jsonPath: .status.capabilities
+          name: Capabilities
+          type: string
       name: v1beta1
       schema:
         openAPIV3Schema:
@@ -5120,6 +5276,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,

+ 2 - 1
go.mod

@@ -91,7 +91,7 @@ require (
 	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
 	sigs.k8s.io/controller-runtime v0.11.2
 	sigs.k8s.io/controller-tools v0.9.0
-	software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
+	software.sslmate.com/src/go-pkcs12 v0.2.0
 )
 
 require github.com/1Password/connect-sdk-go v1.4.0
@@ -101,6 +101,7 @@ require (
 	github.com/fluxcd/helm-controller/api v0.22.0
 	github.com/fluxcd/pkg/apis/meta v0.14.2
 	github.com/fluxcd/source-controller/api v0.25.6
+	github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
 	sigs.k8s.io/yaml v1.3.0
 )
 

+ 8 - 4
go.sum

@@ -996,6 +996,8 @@ github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpe
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0 h1:rBhB9Rls+yb8kA4x5a/cWxOufWfXt24E+kq4YlbGj3g=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0/go.mod h1:fJ0UAZc1fx3xZhU4eSHQDJ1ApFmTVhp5VTpV9tm2ogg=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@@ -1234,6 +1236,8 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH
 github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
+github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
+github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -1479,7 +1483,6 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -1487,6 +1490,7 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1773,13 +1777,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@@ -2272,6 +2276,6 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
-software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
-software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
+software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
+software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

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

+ 179 - 0
pkg/controllers/pushsecret/internal/fakes/recorder.go

@@ -0,0 +1,179 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"sync"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/tools/record"
+)
+
+type FakeEventRecorder struct {
+	AnnotatedEventfStub        func(runtime.Object, map[string]string, string, string, string, ...interface{})
+	annotatedEventfMutex       sync.RWMutex
+	annotatedEventfArgsForCall []struct {
+		arg1 runtime.Object
+		arg2 map[string]string
+		arg3 string
+		arg4 string
+		arg5 string
+		arg6 []interface{}
+	}
+	EventStub        func(runtime.Object, string, string, string)
+	eventMutex       sync.RWMutex
+	eventArgsForCall []struct {
+		arg1 runtime.Object
+		arg2 string
+		arg3 string
+		arg4 string
+	}
+	EventfStub        func(runtime.Object, string, string, string, ...interface{})
+	eventfMutex       sync.RWMutex
+	eventfArgsForCall []struct {
+		arg1 runtime.Object
+		arg2 string
+		arg3 string
+		arg4 string
+		arg5 []interface{}
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeEventRecorder) AnnotatedEventf(arg1 runtime.Object, arg2 map[string]string, arg3 string, arg4 string, arg5 string, arg6 ...interface{}) {
+	fake.annotatedEventfMutex.Lock()
+	fake.annotatedEventfArgsForCall = append(fake.annotatedEventfArgsForCall, struct {
+		arg1 runtime.Object
+		arg2 map[string]string
+		arg3 string
+		arg4 string
+		arg5 string
+		arg6 []interface{}
+	}{arg1, arg2, arg3, arg4, arg5, arg6})
+	stub := fake.AnnotatedEventfStub
+	fake.recordInvocation("AnnotatedEventf", []interface{}{arg1, arg2, arg3, arg4, arg5, arg6})
+	fake.annotatedEventfMutex.Unlock()
+	if stub != nil {
+		fake.AnnotatedEventfStub(arg1, arg2, arg3, arg4, arg5, arg6...)
+	}
+}
+
+func (fake *FakeEventRecorder) AnnotatedEventfCallCount() int {
+	fake.annotatedEventfMutex.RLock()
+	defer fake.annotatedEventfMutex.RUnlock()
+	return len(fake.annotatedEventfArgsForCall)
+}
+
+func (fake *FakeEventRecorder) AnnotatedEventfCalls(stub func(runtime.Object, map[string]string, string, string, string, ...interface{})) {
+	fake.annotatedEventfMutex.Lock()
+	defer fake.annotatedEventfMutex.Unlock()
+	fake.AnnotatedEventfStub = stub
+}
+
+func (fake *FakeEventRecorder) AnnotatedEventfArgsForCall(i int) (runtime.Object, map[string]string, string, string, string, []interface{}) {
+	fake.annotatedEventfMutex.RLock()
+	defer fake.annotatedEventfMutex.RUnlock()
+	argsForCall := fake.annotatedEventfArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5, argsForCall.arg6
+}
+
+func (fake *FakeEventRecorder) Event(arg1 runtime.Object, arg2 string, arg3 string, arg4 string) {
+	fake.eventMutex.Lock()
+	fake.eventArgsForCall = append(fake.eventArgsForCall, struct {
+		arg1 runtime.Object
+		arg2 string
+		arg3 string
+		arg4 string
+	}{arg1, arg2, arg3, arg4})
+	stub := fake.EventStub
+	fake.recordInvocation("Event", []interface{}{arg1, arg2, arg3, arg4})
+	fake.eventMutex.Unlock()
+	if stub != nil {
+		fake.EventStub(arg1, arg2, arg3, arg4)
+	}
+}
+
+func (fake *FakeEventRecorder) EventCallCount() int {
+	fake.eventMutex.RLock()
+	defer fake.eventMutex.RUnlock()
+	return len(fake.eventArgsForCall)
+}
+
+func (fake *FakeEventRecorder) EventCalls(stub func(runtime.Object, string, string, string)) {
+	fake.eventMutex.Lock()
+	defer fake.eventMutex.Unlock()
+	fake.EventStub = stub
+}
+
+func (fake *FakeEventRecorder) EventArgsForCall(i int) (runtime.Object, string, string, string) {
+	fake.eventMutex.RLock()
+	defer fake.eventMutex.RUnlock()
+	argsForCall := fake.eventArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *FakeEventRecorder) Eventf(arg1 runtime.Object, arg2 string, arg3 string, arg4 string, arg5 ...interface{}) {
+	fake.eventfMutex.Lock()
+	fake.eventfArgsForCall = append(fake.eventfArgsForCall, struct {
+		arg1 runtime.Object
+		arg2 string
+		arg3 string
+		arg4 string
+		arg5 []interface{}
+	}{arg1, arg2, arg3, arg4, arg5})
+	stub := fake.EventfStub
+	fake.recordInvocation("Eventf", []interface{}{arg1, arg2, arg3, arg4, arg5})
+	fake.eventfMutex.Unlock()
+	if stub != nil {
+		fake.EventfStub(arg1, arg2, arg3, arg4, arg5...)
+	}
+}
+
+func (fake *FakeEventRecorder) EventfCallCount() int {
+	fake.eventfMutex.RLock()
+	defer fake.eventfMutex.RUnlock()
+	return len(fake.eventfArgsForCall)
+}
+
+func (fake *FakeEventRecorder) EventfCalls(stub func(runtime.Object, string, string, string, ...interface{})) {
+	fake.eventfMutex.Lock()
+	defer fake.eventfMutex.Unlock()
+	fake.EventfStub = stub
+}
+
+func (fake *FakeEventRecorder) EventfArgsForCall(i int) (runtime.Object, string, string, string, []interface{}) {
+	fake.eventfMutex.RLock()
+	defer fake.eventfMutex.RUnlock()
+	argsForCall := fake.eventfArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
+}
+
+func (fake *FakeEventRecorder) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.annotatedEventfMutex.RLock()
+	defer fake.annotatedEventfMutex.RUnlock()
+	fake.eventMutex.RLock()
+	defer fake.eventMutex.RUnlock()
+	fake.eventfMutex.RLock()
+	defer fake.eventfMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeEventRecorder) 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 _ record.EventRecorder = new(FakeEventRecorder)

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

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

@@ -0,0 +1,236 @@
+/*
+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 ps esapi.PushSecret
+	err := r.Get(ctx, req.NamespacedName, &ps)
+	if apierrors.IsNotFound(err) {
+		return ctrl.Result{}, nil
+	} else if err != nil {
+		msg := "unable to get PushSecret"
+		r.recorder.Event(&ps, v1.EventTypeWarning, esapi.ReasonErrored, msg)
+		log.Error(err, msg)
+		return ctrl.Result{}, fmt.Errorf("get resource: %w", err)
+	}
+
+	p := client.MergeFrom(ps.DeepCopy())
+	defer func() {
+		err := r.Client.Status().Patch(ctx, &ps, p)
+		if err != nil {
+			log.Error(err, errPatchStatus)
+		}
+	}()
+	secret, err := r.GetSecret(ctx, ps)
+	if err != nil {
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, esapi.ReasonErrored, errFailedGetSecret)
+		ps = SetPushSecretCondition(ps, *cond)
+		r.recorder.Event(&ps, v1.EventTypeWarning, esapi.ReasonErrored, errFailedGetSecret)
+		return ctrl.Result{}, err
+	}
+	secretStores, err := r.GetSecretStores(ctx, ps)
+	if err != nil {
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, esapi.ReasonErrored, err.Error())
+		ps = SetPushSecretCondition(ps, *cond)
+		r.recorder.Event(&ps, v1.EventTypeWarning, esapi.ReasonErrored, err.Error())
+		return ctrl.Result{}, err
+	}
+	err = r.SetSecretToProviders(ctx, secretStores, ps, secret)
+	if err != nil {
+		msg := fmt.Sprintf(errFailedSetSecret, err)
+		cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionFalse, esapi.ReasonErrored, msg)
+		ps = SetPushSecretCondition(ps, *cond)
+		r.recorder.Event(&ps, v1.EventTypeWarning, esapi.ReasonErrored, msg)
+		return ctrl.Result{}, err
+	}
+	msg := "PushSecret synced successfully"
+	cond := NewPushSecretCondition(esapi.PushSecretReady, v1.ConditionTrue, esapi.ReasonSynced, msg)
+	ps = SetPushSecretCondition(ps, *cond)
+	// Set status for PushSecret
+	r.recorder.Event(&ps, v1.EventTypeNormal, esapi.ReasonSynced, msg)
+	return ctrl.Result{}, nil
+}
+
+func (r *Reconciler) SetSecretToProviders(ctx context.Context, stores []v1beta1.GenericStore, ps 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, ps.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 ps.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, ps esapi.PushSecret) (*v1.Secret, error) {
+	secretName := types.NamespacedName{Name: ps.Spec.Selector.Secret.Name, Namespace: ps.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, ps esapi.PushSecret) ([]v1beta1.GenericStore, error) {
+	stores := make([]v1beta1.GenericStore, 0)
+	for _, refStore := range ps.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 = ps.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("pushsecret")
+
+	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
+}

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

@@ -0,0 +1,407 @@
+/*
+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
+		recorder   *fakes.FakeEventRecorder
+	)
+	BeforeEach(func() {
+		client = new(fakes.Client)
+		recorder = &fakes.FakeEventRecorder{}
+		reconciler = &Reconciler{client, logr.Discard(), nil, recorder, 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))
+			Expect(recorder.EventCallCount()).To(Equal(1))
+			_, _, reason, message := recorder.EventArgsForCall(0)
+			Expect(reason).To(Equal(esapi.ReasonSynced))
+			Expect(message).To(Equal("PushSecret synced successfully"))
+		})
+
+		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))
+				_, _, reason, message := recorder.EventArgsForCall(0)
+				Expect(reason).To(Equal(esapi.ReasonErrored))
+				Expect(message).To(Equal("unable to get PushSecret"))
+			})
+		})
+		When("an error returns in get secret", func() {
+			BeforeEach(func() {
+				client.GetStub = func(context context.Context, name types.NamespacedName, obj kubeclient.Object) error {
+					switch obj.(type) {
+					case *v1.Secret:
+						return fmt.Errorf("GetSecretError")
+					default:
+						return nil
+					}
+				}
+			})
+
+			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("GetSecretError"))
+				_, _, reason, message := recorder.EventArgsForCall(0)
+				Expect(reason).To(Equal(esapi.ReasonErrored))
+				Expect(message).To(Equal(errFailedGetSecret))
+			})
+		})
+
+		When("an error returns in get secret store", func() {
+			BeforeEach(func() {
+				client.GetStub = func(context context.Context, name types.NamespacedName, obj kubeclient.Object) error {
+					switch v := obj.(type) {
+					case *esapi.PushSecret:
+						v.Spec.SecretStoreRefs = []esapi.PushSecretStoreRef{
+							{Name: "a", Kind: "secretstore"},
+						}
+					}
+					switch obj.(type) {
+					case *v1beta1.SecretStore:
+						return fmt.Errorf("BORK")
+					default:
+						return nil
+					}
+
+				}
+			})
+
+			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("could not get SecretStore \"a\", BORK"))
+				_, _, reason, message := recorder.EventArgsForCall(0)
+				Expect(reason).To(Equal(esapi.ReasonErrored))
+				Expect(message).To(Equal("could not get SecretStore \"a\", BORK"))
+			})
+		})
+
+		When("an error returns in set secret to providers", func() {
+			BeforeEach(func() {
+				client.GetStub = func(context context.Context, name types.NamespacedName, obj kubeclient.Object) error {
+					switch v := obj.(type) {
+					case *esapi.PushSecret:
+						v.Spec.SecretStoreRefs = []esapi.PushSecretStoreRef{
+							{Name: "a", Kind: "secretstore"},
+						}
+					case *v1beta1.SecretStore:
+						v.Kind = "PotatoStore"
+					}
+					switch obj.(type) {
+					default:
+						return nil
+					}
+
+				}
+			})
+
+			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("could not start provider"))
+				_, _, reason, message := recorder.EventArgsForCall(0)
+				Expect(reason).To(Equal(esapi.ReasonErrored))
+				Expect(message).To(Equal("set secret failed: could not start provider"))
+			})
+		})
+
+		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{

+ 78 - 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,9 +28,15 @@ 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
 }
 
+func (mc *MockSMClient) GetSecret(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+	return nil, nil
+}
+
 func (mc *MockSMClient) AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
 	return mc.accessSecretFn(ctx, req)
 }
@@ -41,12 +48,83 @@ 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,
+			Payload: &secretmanagerpb.SecretPayload{Data: []byte("bar")},
+		}, nil
+	}
+}
+
+func (mc *MockSMClient) AccessSecretVersionWithError(err error) {
+	mc.accessSecretFn = func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
+		return nil, err
+	}
+}
+
+// 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) {

+ 516 - 0
pkg/provider/gcp/secretmanager/internal/fakes/client.go

@@ -0,0 +1,516 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package fakes
+
+import (
+	"context"
+	"sync"
+
+	secretmanagerb "cloud.google.com/go/secretmanager/apiv1"
+	"github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
+	gax "github.com/googleapis/gax-go/v2"
+	secretmanagera "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
+)
+
+type GoogleSecretManagerClient struct {
+	AccessSecretVersionStub        func(context.Context, *secretmanagera.AccessSecretVersionRequest, ...gax.CallOption) (*secretmanagera.AccessSecretVersionResponse, error)
+	accessSecretVersionMutex       sync.RWMutex
+	accessSecretVersionArgsForCall []struct {
+		arg1 context.Context
+		arg2 *secretmanagera.AccessSecretVersionRequest
+		arg3 []gax.CallOption
+	}
+	accessSecretVersionReturns struct {
+		result1 *secretmanagera.AccessSecretVersionResponse
+		result2 error
+	}
+	accessSecretVersionReturnsOnCall map[int]struct {
+		result1 *secretmanagera.AccessSecretVersionResponse
+		result2 error
+	}
+	AddSecretVersionStub        func(context.Context, *secretmanagera.AddSecretVersionRequest, ...gax.CallOption) (*secretmanagera.SecretVersion, error)
+	addSecretVersionMutex       sync.RWMutex
+	addSecretVersionArgsForCall []struct {
+		arg1 context.Context
+		arg2 *secretmanagera.AddSecretVersionRequest
+		arg3 []gax.CallOption
+	}
+	addSecretVersionReturns struct {
+		result1 *secretmanagera.SecretVersion
+		result2 error
+	}
+	addSecretVersionReturnsOnCall map[int]struct {
+		result1 *secretmanagera.SecretVersion
+		result2 error
+	}
+	CloseStub        func() error
+	closeMutex       sync.RWMutex
+	closeArgsForCall []struct {
+	}
+	closeReturns struct {
+		result1 error
+	}
+	closeReturnsOnCall map[int]struct {
+		result1 error
+	}
+	CreateSecretStub        func(context.Context, *secretmanagera.CreateSecretRequest, ...gax.CallOption) (*secretmanagera.Secret, error)
+	createSecretMutex       sync.RWMutex
+	createSecretArgsForCall []struct {
+		arg1 context.Context
+		arg2 *secretmanagera.CreateSecretRequest
+		arg3 []gax.CallOption
+	}
+	createSecretReturns struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}
+	createSecretReturnsOnCall map[int]struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}
+	GetSecretStub        func(context.Context, *secretmanagera.GetSecretRequest, ...gax.CallOption) (*secretmanagera.Secret, error)
+	getSecretMutex       sync.RWMutex
+	getSecretArgsForCall []struct {
+		arg1 context.Context
+		arg2 *secretmanagera.GetSecretRequest
+		arg3 []gax.CallOption
+	}
+	getSecretReturns struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}
+	getSecretReturnsOnCall map[int]struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}
+	ListSecretsStub        func(context.Context, *secretmanagera.ListSecretsRequest, ...gax.CallOption) *secretmanagerb.SecretIterator
+	listSecretsMutex       sync.RWMutex
+	listSecretsArgsForCall []struct {
+		arg1 context.Context
+		arg2 *secretmanagera.ListSecretsRequest
+		arg3 []gax.CallOption
+	}
+	listSecretsReturns struct {
+		result1 *secretmanagerb.SecretIterator
+	}
+	listSecretsReturnsOnCall map[int]struct {
+		result1 *secretmanagerb.SecretIterator
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersion(arg1 context.Context, arg2 *secretmanagera.AccessSecretVersionRequest, arg3 ...gax.CallOption) (*secretmanagera.AccessSecretVersionResponse, error) {
+	fake.accessSecretVersionMutex.Lock()
+	ret, specificReturn := fake.accessSecretVersionReturnsOnCall[len(fake.accessSecretVersionArgsForCall)]
+	fake.accessSecretVersionArgsForCall = append(fake.accessSecretVersionArgsForCall, struct {
+		arg1 context.Context
+		arg2 *secretmanagera.AccessSecretVersionRequest
+		arg3 []gax.CallOption
+	}{arg1, arg2, arg3})
+	stub := fake.AccessSecretVersionStub
+	fakeReturns := fake.accessSecretVersionReturns
+	fake.recordInvocation("AccessSecretVersion", []interface{}{arg1, arg2, arg3})
+	fake.accessSecretVersionMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersionCallCount() int {
+	fake.accessSecretVersionMutex.RLock()
+	defer fake.accessSecretVersionMutex.RUnlock()
+	return len(fake.accessSecretVersionArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersionCalls(stub func(context.Context, *secretmanagera.AccessSecretVersionRequest, ...gax.CallOption) (*secretmanagera.AccessSecretVersionResponse, error)) {
+	fake.accessSecretVersionMutex.Lock()
+	defer fake.accessSecretVersionMutex.Unlock()
+	fake.AccessSecretVersionStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersionArgsForCall(i int) (context.Context, *secretmanagera.AccessSecretVersionRequest, []gax.CallOption) {
+	fake.accessSecretVersionMutex.RLock()
+	defer fake.accessSecretVersionMutex.RUnlock()
+	argsForCall := fake.accessSecretVersionArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersionReturns(result1 *secretmanagera.AccessSecretVersionResponse, result2 error) {
+	fake.accessSecretVersionMutex.Lock()
+	defer fake.accessSecretVersionMutex.Unlock()
+	fake.AccessSecretVersionStub = nil
+	fake.accessSecretVersionReturns = struct {
+		result1 *secretmanagera.AccessSecretVersionResponse
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) AccessSecretVersionReturnsOnCall(i int, result1 *secretmanagera.AccessSecretVersionResponse, result2 error) {
+	fake.accessSecretVersionMutex.Lock()
+	defer fake.accessSecretVersionMutex.Unlock()
+	fake.AccessSecretVersionStub = nil
+	if fake.accessSecretVersionReturnsOnCall == nil {
+		fake.accessSecretVersionReturnsOnCall = make(map[int]struct {
+			result1 *secretmanagera.AccessSecretVersionResponse
+			result2 error
+		})
+	}
+	fake.accessSecretVersionReturnsOnCall[i] = struct {
+		result1 *secretmanagera.AccessSecretVersionResponse
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersion(arg1 context.Context, arg2 *secretmanagera.AddSecretVersionRequest, arg3 ...gax.CallOption) (*secretmanagera.SecretVersion, error) {
+	fake.addSecretVersionMutex.Lock()
+	ret, specificReturn := fake.addSecretVersionReturnsOnCall[len(fake.addSecretVersionArgsForCall)]
+	fake.addSecretVersionArgsForCall = append(fake.addSecretVersionArgsForCall, struct {
+		arg1 context.Context
+		arg2 *secretmanagera.AddSecretVersionRequest
+		arg3 []gax.CallOption
+	}{arg1, arg2, arg3})
+	stub := fake.AddSecretVersionStub
+	fakeReturns := fake.addSecretVersionReturns
+	fake.recordInvocation("AddSecretVersion", []interface{}{arg1, arg2, arg3})
+	fake.addSecretVersionMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersionCallCount() int {
+	fake.addSecretVersionMutex.RLock()
+	defer fake.addSecretVersionMutex.RUnlock()
+	return len(fake.addSecretVersionArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersionCalls(stub func(context.Context, *secretmanagera.AddSecretVersionRequest, ...gax.CallOption) (*secretmanagera.SecretVersion, error)) {
+	fake.addSecretVersionMutex.Lock()
+	defer fake.addSecretVersionMutex.Unlock()
+	fake.AddSecretVersionStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersionArgsForCall(i int) (context.Context, *secretmanagera.AddSecretVersionRequest, []gax.CallOption) {
+	fake.addSecretVersionMutex.RLock()
+	defer fake.addSecretVersionMutex.RUnlock()
+	argsForCall := fake.addSecretVersionArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersionReturns(result1 *secretmanagera.SecretVersion, result2 error) {
+	fake.addSecretVersionMutex.Lock()
+	defer fake.addSecretVersionMutex.Unlock()
+	fake.AddSecretVersionStub = nil
+	fake.addSecretVersionReturns = struct {
+		result1 *secretmanagera.SecretVersion
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) AddSecretVersionReturnsOnCall(i int, result1 *secretmanagera.SecretVersion, result2 error) {
+	fake.addSecretVersionMutex.Lock()
+	defer fake.addSecretVersionMutex.Unlock()
+	fake.AddSecretVersionStub = nil
+	if fake.addSecretVersionReturnsOnCall == nil {
+		fake.addSecretVersionReturnsOnCall = make(map[int]struct {
+			result1 *secretmanagera.SecretVersion
+			result2 error
+		})
+	}
+	fake.addSecretVersionReturnsOnCall[i] = struct {
+		result1 *secretmanagera.SecretVersion
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) Close() error {
+	fake.closeMutex.Lock()
+	ret, specificReturn := fake.closeReturnsOnCall[len(fake.closeArgsForCall)]
+	fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
+	}{})
+	stub := fake.CloseStub
+	fakeReturns := fake.closeReturns
+	fake.recordInvocation("Close", []interface{}{})
+	fake.closeMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *GoogleSecretManagerClient) CloseCallCount() int {
+	fake.closeMutex.RLock()
+	defer fake.closeMutex.RUnlock()
+	return len(fake.closeArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) CloseCalls(stub func() error) {
+	fake.closeMutex.Lock()
+	defer fake.closeMutex.Unlock()
+	fake.CloseStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) CloseReturns(result1 error) {
+	fake.closeMutex.Lock()
+	defer fake.closeMutex.Unlock()
+	fake.CloseStub = nil
+	fake.closeReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *GoogleSecretManagerClient) CloseReturnsOnCall(i int, result1 error) {
+	fake.closeMutex.Lock()
+	defer fake.closeMutex.Unlock()
+	fake.CloseStub = nil
+	if fake.closeReturnsOnCall == nil {
+		fake.closeReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.closeReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecret(arg1 context.Context, arg2 *secretmanagera.CreateSecretRequest, arg3 ...gax.CallOption) (*secretmanagera.Secret, error) {
+	fake.createSecretMutex.Lock()
+	ret, specificReturn := fake.createSecretReturnsOnCall[len(fake.createSecretArgsForCall)]
+	fake.createSecretArgsForCall = append(fake.createSecretArgsForCall, struct {
+		arg1 context.Context
+		arg2 *secretmanagera.CreateSecretRequest
+		arg3 []gax.CallOption
+	}{arg1, arg2, arg3})
+	stub := fake.CreateSecretStub
+	fakeReturns := fake.createSecretReturns
+	fake.recordInvocation("CreateSecret", []interface{}{arg1, arg2, arg3})
+	fake.createSecretMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecretCallCount() int {
+	fake.createSecretMutex.RLock()
+	defer fake.createSecretMutex.RUnlock()
+	return len(fake.createSecretArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecretCalls(stub func(context.Context, *secretmanagera.CreateSecretRequest, ...gax.CallOption) (*secretmanagera.Secret, error)) {
+	fake.createSecretMutex.Lock()
+	defer fake.createSecretMutex.Unlock()
+	fake.CreateSecretStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecretArgsForCall(i int) (context.Context, *secretmanagera.CreateSecretRequest, []gax.CallOption) {
+	fake.createSecretMutex.RLock()
+	defer fake.createSecretMutex.RUnlock()
+	argsForCall := fake.createSecretArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecretReturns(result1 *secretmanagera.Secret, result2 error) {
+	fake.createSecretMutex.Lock()
+	defer fake.createSecretMutex.Unlock()
+	fake.CreateSecretStub = nil
+	fake.createSecretReturns = struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) CreateSecretReturnsOnCall(i int, result1 *secretmanagera.Secret, result2 error) {
+	fake.createSecretMutex.Lock()
+	defer fake.createSecretMutex.Unlock()
+	fake.CreateSecretStub = nil
+	if fake.createSecretReturnsOnCall == nil {
+		fake.createSecretReturnsOnCall = make(map[int]struct {
+			result1 *secretmanagera.Secret
+			result2 error
+		})
+	}
+	fake.createSecretReturnsOnCall[i] = struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) GetSecret(arg1 context.Context, arg2 *secretmanagera.GetSecretRequest, arg3 ...gax.CallOption) (*secretmanagera.Secret, error) {
+	fake.getSecretMutex.Lock()
+	ret, specificReturn := fake.getSecretReturnsOnCall[len(fake.getSecretArgsForCall)]
+	fake.getSecretArgsForCall = append(fake.getSecretArgsForCall, struct {
+		arg1 context.Context
+		arg2 *secretmanagera.GetSecretRequest
+		arg3 []gax.CallOption
+	}{arg1, arg2, arg3})
+	stub := fake.GetSecretStub
+	fakeReturns := fake.getSecretReturns
+	fake.recordInvocation("GetSecret", []interface{}{arg1, arg2, arg3})
+	fake.getSecretMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *GoogleSecretManagerClient) GetSecretCallCount() int {
+	fake.getSecretMutex.RLock()
+	defer fake.getSecretMutex.RUnlock()
+	return len(fake.getSecretArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) GetSecretCalls(stub func(context.Context, *secretmanagera.GetSecretRequest, ...gax.CallOption) (*secretmanagera.Secret, error)) {
+	fake.getSecretMutex.Lock()
+	defer fake.getSecretMutex.Unlock()
+	fake.GetSecretStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) GetSecretArgsForCall(i int) (context.Context, *secretmanagera.GetSecretRequest, []gax.CallOption) {
+	fake.getSecretMutex.RLock()
+	defer fake.getSecretMutex.RUnlock()
+	argsForCall := fake.getSecretArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *GoogleSecretManagerClient) GetSecretReturns(result1 *secretmanagera.Secret, result2 error) {
+	fake.getSecretMutex.Lock()
+	defer fake.getSecretMutex.Unlock()
+	fake.GetSecretStub = nil
+	fake.getSecretReturns = struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) GetSecretReturnsOnCall(i int, result1 *secretmanagera.Secret, result2 error) {
+	fake.getSecretMutex.Lock()
+	defer fake.getSecretMutex.Unlock()
+	fake.GetSecretStub = nil
+	if fake.getSecretReturnsOnCall == nil {
+		fake.getSecretReturnsOnCall = make(map[int]struct {
+			result1 *secretmanagera.Secret
+			result2 error
+		})
+	}
+	fake.getSecretReturnsOnCall[i] = struct {
+		result1 *secretmanagera.Secret
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *GoogleSecretManagerClient) ListSecrets(arg1 context.Context, arg2 *secretmanagera.ListSecretsRequest, arg3 ...gax.CallOption) *secretmanagerb.SecretIterator {
+	fake.listSecretsMutex.Lock()
+	ret, specificReturn := fake.listSecretsReturnsOnCall[len(fake.listSecretsArgsForCall)]
+	fake.listSecretsArgsForCall = append(fake.listSecretsArgsForCall, struct {
+		arg1 context.Context
+		arg2 *secretmanagera.ListSecretsRequest
+		arg3 []gax.CallOption
+	}{arg1, arg2, arg3})
+	stub := fake.ListSecretsStub
+	fakeReturns := fake.listSecretsReturns
+	fake.recordInvocation("ListSecrets", []interface{}{arg1, arg2, arg3})
+	fake.listSecretsMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3...)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *GoogleSecretManagerClient) ListSecretsCallCount() int {
+	fake.listSecretsMutex.RLock()
+	defer fake.listSecretsMutex.RUnlock()
+	return len(fake.listSecretsArgsForCall)
+}
+
+func (fake *GoogleSecretManagerClient) ListSecretsCalls(stub func(context.Context, *secretmanagera.ListSecretsRequest, ...gax.CallOption) *secretmanagerb.SecretIterator) {
+	fake.listSecretsMutex.Lock()
+	defer fake.listSecretsMutex.Unlock()
+	fake.ListSecretsStub = stub
+}
+
+func (fake *GoogleSecretManagerClient) ListSecretsArgsForCall(i int) (context.Context, *secretmanagera.ListSecretsRequest, []gax.CallOption) {
+	fake.listSecretsMutex.RLock()
+	defer fake.listSecretsMutex.RUnlock()
+	argsForCall := fake.listSecretsArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *GoogleSecretManagerClient) ListSecretsReturns(result1 *secretmanagerb.SecretIterator) {
+	fake.listSecretsMutex.Lock()
+	defer fake.listSecretsMutex.Unlock()
+	fake.ListSecretsStub = nil
+	fake.listSecretsReturns = struct {
+		result1 *secretmanagerb.SecretIterator
+	}{result1}
+}
+
+func (fake *GoogleSecretManagerClient) ListSecretsReturnsOnCall(i int, result1 *secretmanagerb.SecretIterator) {
+	fake.listSecretsMutex.Lock()
+	defer fake.listSecretsMutex.Unlock()
+	fake.ListSecretsStub = nil
+	if fake.listSecretsReturnsOnCall == nil {
+		fake.listSecretsReturnsOnCall = make(map[int]struct {
+			result1 *secretmanagerb.SecretIterator
+		})
+	}
+	fake.listSecretsReturnsOnCall[i] = struct {
+		result1 *secretmanagerb.SecretIterator
+	}{result1}
+}
+
+func (fake *GoogleSecretManagerClient) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.accessSecretVersionMutex.RLock()
+	defer fake.accessSecretVersionMutex.RUnlock()
+	fake.addSecretVersionMutex.RLock()
+	defer fake.addSecretVersionMutex.RUnlock()
+	fake.closeMutex.RLock()
+	defer fake.closeMutex.RUnlock()
+	fake.createSecretMutex.RLock()
+	defer fake.createSecretMutex.RUnlock()
+	fake.getSecretMutex.RLock()
+	defer fake.getSecretMutex.RUnlock()
+	fake.listSecretsMutex.RLock()
+	defer fake.listSecretsMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *GoogleSecretManagerClient) 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 _ secretmanager.GoogleSecretManagerClient = new(GoogleSecretManagerClient)

+ 98 - 11
pkg/provider/gcp/secretmanager/secretsmanager.go

@@ -24,12 +24,14 @@ import (
 
 	secretmanager "cloud.google.com/go/secretmanager/apiv1"
 	"github.com/googleapis/gax-go/v2"
+	"github.com/googleapis/gax-go/v2/apierror"
 	"github.com/tidwall/gjson"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/google"
 	"google.golang.org/api/iterator"
 	"google.golang.org/api/option"
 	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
+	"google.golang.org/grpc/codes"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -71,7 +73,10 @@ 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
+	GetSecret(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
 }
 
 /*
@@ -89,7 +94,7 @@ var _ esv1beta1.Provider = &ProviderGCP{}
 
 // ProviderGCP is a provider for GCP Secret Manager.
 type ProviderGCP struct {
-	projectID           string
+	ProjectID           string
 	SecretManagerClient GoogleSecretManagerClient
 	gClient             *gClient
 }
@@ -160,6 +165,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.SecretStoreReadWrite
+}
+
 // 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()
@@ -190,7 +200,7 @@ func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1beta1.GenericSto
 		}
 	}()
 
-	sm.projectID = cliStore.store.ProjectID
+	sm.ProjectID = cliStore.store.ProjectID
 
 	ts, err := cliStore.getTokenSource(ctx, store, kube, namespace)
 	if err != nil {
@@ -214,6 +224,82 @@ 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: &secretmanagerpb.Secret{
+			Labels: map[string]string{
+				"managed-by": "external-secrets",
+			},
+			Replication: &secretmanagerpb.Replication{
+				Replication: &secretmanagerpb.Replication_Automatic_{
+					Automatic: &secretmanagerpb.Replication_Automatic{},
+				},
+			},
+		},
+	}
+
+	var gcpSecret *secretmanagerpb.Secret
+	var err error
+
+	gcpSecret, err = sm.SecretManagerClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
+		Name: fmt.Sprintf("projects/%s/secrets/%s", sm.ProjectID, remoteRef.GetRemoteKey()),
+	})
+
+	var gErr *apierror.APIError
+
+	if errors.As(err, &gErr) {
+		if err != nil && gErr.GRPCStatus().Code() == codes.NotFound {
+			gcpSecret, err = sm.SecretManagerClient.CreateSecret(ctx, createSecretReq)
+			if err != nil {
+				return err
+			}
+		}
+
+		if err != nil {
+			return err
+		}
+	}
+
+	manager, ok := gcpSecret.Labels["managed-by"]
+
+	if !ok || manager != "external-secrets" {
+		return fmt.Errorf("secret %v is not managed by external secrets", remoteRef.GetRemoteKey())
+	}
+
+	gcpVersion, err := sm.SecretManagerClient.AccessSecretVersion(ctx, &secretmanagerpb.AccessSecretVersionRequest{
+		Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", sm.ProjectID, remoteRef.GetRemoteKey()),
+	})
+
+	if errors.As(err, &gErr) {
+		if err != nil && gErr.GRPCStatus().Code() != codes.NotFound {
+			return err
+		}
+	}
+
+	if gcpVersion != nil && gcpVersion.Payload != nil && string(payload) == string(gcpVersion.Payload.Data) {
+		return nil
+	}
+
+	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
+		Parent: fmt.Sprintf("projects/%s/secrets/%s", sm.ProjectID, remoteRef.GetRemoteKey()),
+		Payload: &secretmanagerpb.SecretPayload{
+			Data: payload,
+		},
+	}
+
+	_, err = sm.SecretManagerClient.AddSecretVersion(ctx, addSecretVersionReq)
+
+	if 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 {
@@ -222,6 +308,7 @@ func (sm *ProviderGCP) GetAllSecrets(ctx context.Context, ref esv1beta1.External
 	if len(ref.Tags) > 0 {
 		return sm.findByTags(ctx, ref)
 	}
+
 	return nil, errors.New(errUnexpectedFindOperator)
 }
 
@@ -232,7 +319,7 @@ func (sm *ProviderGCP) findByName(ctx context.Context, ref esv1beta1.ExternalSec
 		return nil, err
 	}
 	req := &secretmanagerpb.ListSecretsRequest{
-		Parent: fmt.Sprintf("projects/%s", sm.projectID),
+		Parent: fmt.Sprintf("projects/%s", sm.ProjectID),
 	}
 	if ref.Path != nil {
 		req.Filter = fmt.Sprintf("name:%s", *ref.Path)
@@ -290,7 +377,7 @@ func (sm *ProviderGCP) findByTags(ctx context.Context, ref esv1beta1.ExternalSec
 		tagFilter = fmt.Sprintf("%s name:%s", tagFilter, *ref.Path)
 	}
 	req := &secretmanagerpb.ListSecretsRequest{
-		Parent: fmt.Sprintf("projects/%s", sm.projectID),
+		Parent: fmt.Sprintf("projects/%s", sm.ProjectID),
 	}
 	log.V(1).Info("gcp sm findByTags", "tagFilter", tagFilter)
 	req.Filter = tagFilter
@@ -320,8 +407,8 @@ func (sm *ProviderGCP) findByTags(ctx context.Context, ref esv1beta1.ExternalSec
 }
 
 func (sm *ProviderGCP) trimName(name string) string {
-	projectIDNumuber := sm.extractProjectIDNumber(name)
-	key := strings.TrimPrefix(name, fmt.Sprintf("projects/%s/secrets/", projectIDNumuber))
+	ProjectIDNumuber := sm.extractProjectIDNumber(name)
+	key := strings.TrimPrefix(name, fmt.Sprintf("projects/%s/secrets/", ProjectIDNumuber))
 	return key
 }
 
@@ -330,13 +417,13 @@ func (sm *ProviderGCP) trimName(name string) string {
 // (and users would always use the name, while requests accept both).
 func (sm *ProviderGCP) extractProjectIDNumber(secretFullName string) string {
 	s := strings.Split(secretFullName, "/")
-	projectIDNumuber := s[1]
-	return projectIDNumuber
+	ProjectIDNumuber := s[1]
+	return ProjectIDNumuber
 }
 
 // GetSecret returns a single secret from the provider.
 func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	if utils.IsNil(sm.SecretManagerClient) || sm.projectID == "" {
+	if utils.IsNil(sm.SecretManagerClient) || sm.ProjectID == "" {
 		return nil, fmt.Errorf(errUninitalizedGCPProvider)
 	}
 
@@ -346,7 +433,7 @@ func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecr
 	}
 
 	req := &secretmanagerpb.AccessSecretVersionRequest{
-		Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", sm.projectID, ref.Key, version),
+		Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", sm.ProjectID, ref.Key, version),
 	}
 	result, err := sm.SecretManagerClient.AccessSecretVersion(ctx, req)
 	if err != nil {
@@ -382,7 +469,7 @@ func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecr
 
 // GetSecretMap returns multiple k/v pairs from the provider.
 func (sm *ProviderGCP) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
-	if sm.SecretManagerClient == nil || sm.projectID == "" {
+	if sm.SecretManagerClient == nil || sm.ProjectID == "" {
 		return nil, fmt.Errorf(errUninitalizedGCPProvider)
 	}
 

+ 166 - 7
pkg/provider/gcp/secretmanager/secretsmanager_test.go

@@ -11,7 +11,7 @@ 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 secretmanager
+package secretmanager_test
 
 import (
 	"context"
@@ -20,12 +20,19 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/googleapis/gax-go/v2/apierror"
+	"github.com/stretchr/testify/assert"
 	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 	"k8s.io/utils/pointer"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	fakeprr "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1/fakes"
 	v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
 	fakesm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager/fake"
+	"github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager/internal/fakes"
 )
 
 type secretManagerTestCase struct {
@@ -97,7 +104,7 @@ var setAPIErr = func(smtc *secretManagerTestCase) {
 
 var setNilMockClient = func(smtc *secretManagerTestCase) {
 	smtc.mockClient = nil
-	smtc.expectError = errUninitalizedGCPProvider
+	smtc.expectError = "provider GCP is not initialized"
 }
 
 // test the sm<->gcp interface
@@ -167,9 +174,9 @@ func TestSecretManagerGetSecret(t *testing.T) {
 		makeValidSecretManagerTestCaseCustom(setNilMockClient),
 	}
 
-	sm := ProviderGCP{}
+	sm := secretmanager.ProviderGCP{}
 	for k, v := range successCases {
-		sm.projectID = v.projectID
+		sm.ProjectID = v.projectID
 		sm.SecretManagerClient = v.mockClient
 		out, err := sm.GetSecret(context.Background(), *v.ref)
 		if !ErrorContains(err, v.expectError) {
@@ -181,6 +188,128 @@ func TestSecretManagerGetSecret(t *testing.T) {
 	}
 }
 
+func TestSetSecret(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	secret := newSecret()
+	p := newProvider(client)
+
+	client.GetSecretReturns(&secret, nil)
+
+	err := p.SetSecret(context.Background(), nil, pushRemoteRef)
+	assert.Equal(t, err, nil)
+}
+
+func TestSetSecretAddSecretVersion(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	secret := newSecret()
+	p := newProvider(client)
+
+	expectedErr := "rpc error: code = Aborted desc = failed"
+	newStatus := status.Error(codes.Aborted, "failed")
+	err, _ := apierror.FromError(newStatus)
+
+	client.GetSecretReturns(&secret, nil)
+	client.AddSecretVersionReturns(nil, err)
+
+	expect := p.SetSecret(context.TODO(), nil, pushRemoteRef)
+	if assert.Error(t, expect) {
+		assert.Equal(t, expect.Error(), expectedErr)
+	}
+}
+
+func TestSetSecretAccessSecretVersion(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	secret := newSecret()
+	p := newProvider(client)
+
+	expectedErr := "rpc error: code = Aborted desc = failed"
+	newStatus := status.Error(codes.Aborted, "failed")
+	err, _ := apierror.FromError(newStatus)
+
+	client.AccessSecretVersionReturns(nil, err)
+	client.GetSecretReturns(nil, err)
+	client.CreateSecretReturns(&secret, nil)
+
+	expect := p.SetSecret(context.Background(), nil, pushRemoteRef)
+	if assert.Error(t, expect) {
+		assert.Equal(t, expect.Error(), expectedErr)
+	}
+}
+
+func TestSetSecretGetSecret404(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	secret := newSecret()
+	p := newProvider(client)
+
+	newStatus := status.Error(codes.NotFound, "failed")
+	err, _ := apierror.FromError(newStatus)
+
+	client.GetSecretReturns(nil, err)
+	client.CreateSecretReturns(&secret, nil)
+	client.AccessSecretVersionReturns(nil, err)
+
+	p.SetSecret(context.Background(), nil, pushRemoteRef)
+	if client.AddSecretVersionCallCount() != 1 {
+		t.Error("expected addSecretVersion to be called")
+	}
+	if client.CreateSecretCallCount() != 1 {
+		t.Error("expected CreateSecret to be called")
+	}
+}
+
+func TestSetSecretWrongLabel(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	p := newProvider(client)
+	secret := secretmanagerpb.Secret{
+		Name: "projects/default/secrets/foo-bar",
+		Replication: &secretmanagerpb.Replication{
+			Replication: &secretmanagerpb.Replication_Automatic_{
+				Automatic: &secretmanagerpb.Replication_Automatic{},
+			},
+		},
+		Labels: map[string]string{
+			"managed-by": "not-external-secrets",
+		},
+	}
+
+	pushRemoteRef.GetRemoteKeyReturns("foo-bar")
+	client.GetSecretReturns(&secret, nil)
+
+	expectedErr := "secret foo-bar is not managed by external secrets"
+	err := p.SetSecret(context.Background(), nil, pushRemoteRef)
+
+	if assert.Error(t, err) {
+		assert.Equal(t, err.Error(), expectedErr)
+	}
+}
+
+func TestSetSecretAlreadyExists(t *testing.T) {
+	client := newClient()
+	pushRemoteRef := newPushRemoteRef()
+	secret := newSecret()
+	p := newProvider(client)
+	payload := &secretmanagerpb.SecretPayload{Data: []byte("bar")}
+
+	client.AccessSecretVersionReturns(&secretmanagerpb.AccessSecretVersionResponse{
+		Name:    "projects/default/secrets/foo-bar",
+		Payload: payload,
+	}, nil)
+	client.GetSecretReturns(&secret, nil)
+
+	err := p.SetSecret(context.TODO(), []byte("bar"), pushRemoteRef)
+	if client.AddSecretVersionCallCount() != 0 {
+		t.Error("expected addSecretVersion to not be called")
+	}
+	if err != nil {
+		t.Errorf("expected nil got error")
+	}
+}
+
 func TestGetSecretMap(t *testing.T) {
 	// good case: default version & deserialization
 	setDeserialization := func(smtc *secretManagerTestCase) {
@@ -209,9 +338,9 @@ func TestGetSecretMap(t *testing.T) {
 		makeValidSecretManagerTestCaseCustom(setNestedJSON),
 	}
 
-	sm := ProviderGCP{}
+	sm := secretmanager.ProviderGCP{}
 	for k, v := range successCases {
-		sm.projectID = v.projectID
+		sm.ProjectID = v.projectID
 		sm.SecretManagerClient = v.mockClient
 		out, err := sm.GetSecretMap(context.Background(), *v.ref)
 		if !ErrorContains(err, v.expectError) {
@@ -278,7 +407,7 @@ func TestValidateStore(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			sm := &ProviderGCP{}
+			sm := &secretmanager.ProviderGCP{}
 			store := &esv1beta1.SecretStore{
 				Spec: esv1beta1.SecretStoreSpec{
 					Provider: &esv1beta1.SecretStoreProvider{
@@ -294,3 +423,33 @@ func TestValidateStore(t *testing.T) {
 		})
 	}
 }
+
+// counterfeiter helper methods.
+func newClient() *fakes.GoogleSecretManagerClient {
+	return new(fakes.GoogleSecretManagerClient)
+}
+
+func newPushRemoteRef() *fakeprr.PushRemoteRef {
+	return new(fakeprr.PushRemoteRef)
+}
+
+func newSecret() secretmanagerpb.Secret {
+	return secretmanagerpb.Secret{
+		Name: "projects/default/secrets/foo-bar",
+		Replication: &secretmanagerpb.Replication{
+			Replication: &secretmanagerpb.Replication_Automatic_{
+				Automatic: &secretmanagerpb.Replication_Automatic{},
+			},
+		},
+		Labels: map[string]string{
+			"managed-by": "external-secrets",
+		},
+	}
+}
+
+func newProvider(client secretmanager.GoogleSecretManagerClient) secretmanager.ProviderGCP {
+	return secretmanager.ProviderGCP{
+		SecretManagerClient: client,
+		ProjectID:           "default",
+	}
+}

+ 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

@@ -73,6 +73,11 @@ func init() {
 	})
 }
 
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *ProviderKubernetes) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
 // NewClient constructs a Kubernetes Provider.
 func (p *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	storeSpec := store.GetSpec()
@@ -147,6 +152,11 @@ func (p *ProviderKubernetes) Close(ctx context.Context) error {
 	return nil
 }
 
+// Not Implemented SetSecret.
+func (p *ProviderKubernetes) SetSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
+	return fmt.Errorf("not implemented")
+}
+
 func (p *ProviderKubernetes) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	secretMap, err := p.GetSecretMap(ctx, ref)
 	if err != nil {

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

+ 1 - 0
tools.go

@@ -5,6 +5,7 @@ package tools
 
 import (
 	_ "github.com/ahmetb/gen-crd-api-reference-docs"
+	_ "github.com/maxbrunsfeld/counterfeiter/v6"
 	_ "github.com/onsi/ginkgo/v2/ginkgo"
 	_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
 )