diff --git a/apis/externalsecrets/v1alpha1/secretstore_fake_types.go b/apis/externalsecrets/v1alpha1/secretstore_fake_types.go
new file mode 100644
index 000000000..5c21b6fd0
--- /dev/null
+++ b/apis/externalsecrets/v1alpha1/secretstore_fake_types.go
@@ -0,0 +1,27 @@
+/*
+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
+
+// FakeProvider configures a fake provider that returns static values.
+type FakeProvider struct {
+ Data []FakeProviderData `json:"data"`
+}
+
+type FakeProviderData struct {
+ Key string `json:"key"`
+ Value string `json:"value,omitempty"`
+ ValueMap map[string]string `json:"valueMap,omitempty"`
+ Version string `json:"version,omitempty"`
+}
diff --git a/apis/externalsecrets/v1alpha1/secretstore_types.go b/apis/externalsecrets/v1alpha1/secretstore_types.go
index 0018504ae..3f90a5ec3 100644
--- a/apis/externalsecrets/v1alpha1/secretstore_types.go
+++ b/apis/externalsecrets/v1alpha1/secretstore_types.go
@@ -81,6 +81,10 @@ type SecretStoreProvider struct {
// Webhook configures this store to sync secrets using a generic templated webhook
// +optional
Webhook *WebhookProvider `json:"webhook,omitempty"`
+
+ // Fake configures a store with static key/value pairs
+ // +optional
+ Fake *FakeProvider `json:"fake,omitempty"`
}
type SecretStoreRetrySettings struct {
diff --git a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
index 00e4e17c1..cf377f8b2 100644
--- a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
@@ -599,6 +599,50 @@ func (in *ExternalSecretTemplateMetadata) DeepCopy() *ExternalSecretTemplateMeta
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *FakeProvider) DeepCopyInto(out *FakeProvider) {
+ *out = *in
+ if in.Data != nil {
+ in, out := &in.Data, &out.Data
+ *out = make([]FakeProviderData, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProvider.
+func (in *FakeProvider) DeepCopy() *FakeProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(FakeProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *FakeProviderData) DeepCopyInto(out *FakeProviderData) {
+ *out = *in
+ if in.ValueMap != nil {
+ in, out := &in.ValueMap, &out.ValueMap
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProviderData.
+func (in *FakeProviderData) DeepCopy() *FakeProviderData {
+ if in == nil {
+ return nil
+ }
+ out := new(FakeProviderData)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GCPSMAuth) DeepCopyInto(out *GCPSMAuth) {
*out = *in
@@ -939,6 +983,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(WebhookProvider)
(*in).DeepCopyInto(*out)
}
+ if in.Fake != nil {
+ in, out := &in.Fake, &out.Fake
+ *out = new(FakeProvider)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretStoreProvider.
diff --git a/deploy/crds/external-secrets.io_clustersecretstores.yaml b/deploy/crds/external-secrets.io_clustersecretstores.yaml
index a4fa020bb..8a71f2998 100644
--- a/deploy/crds/external-secrets.io_clustersecretstores.yaml
+++ b/deploy/crds/external-secrets.io_clustersecretstores.yaml
@@ -380,6 +380,29 @@ spec:
required:
- vaultUrl
type: object
+ fake:
+ description: Fake configures a store with static key/value pairs
+ properties:
+ data:
+ items:
+ properties:
+ key:
+ type: string
+ value:
+ type: string
+ valueMap:
+ additionalProperties:
+ type: string
+ type: object
+ version:
+ type: string
+ required:
+ - key
+ type: object
+ type: array
+ required:
+ - data
+ type: object
gcpsm:
description: GCPSM configures this store to sync secrets using
Google Cloud Platform Secret Manager provider
diff --git a/deploy/crds/external-secrets.io_secretstores.yaml b/deploy/crds/external-secrets.io_secretstores.yaml
index ac2da8e28..18a60f554 100644
--- a/deploy/crds/external-secrets.io_secretstores.yaml
+++ b/deploy/crds/external-secrets.io_secretstores.yaml
@@ -380,6 +380,29 @@ spec:
required:
- vaultUrl
type: object
+ fake:
+ description: Fake configures a store with static key/value pairs
+ properties:
+ data:
+ items:
+ properties:
+ key:
+ type: string
+ value:
+ type: string
+ valueMap:
+ additionalProperties:
+ type: string
+ type: object
+ version:
+ type: string
+ required:
+ - key
+ type: object
+ type: array
+ required:
+ - data
+ type: object
gcpsm:
description: GCPSM configures this store to sync secrets using
Google Cloud Platform Secret Manager provider
diff --git a/docs/provider-fake.md b/docs/provider-fake.md
new file mode 100644
index 000000000..f538f9fd5
--- /dev/null
+++ b/docs/provider-fake.md
@@ -0,0 +1,26 @@
+We provide a `fake` implementation to help with testing. This provider returns static key/value pairs and nothing else.
+To use the `fake` provider simply create a `SecretStore` or `ClusterSecretStore` and configure it like in the following example:
+
+!!! note inline end
+ The provider returns static data configured in `value` or `valueMap`. You can define a `version`, too. If set the `remoteRef` from an ExternalSecret must match otherwise no value is returned.
+
+```yaml
+{% include 'fake-provider-store.yaml' %}
+```
+
+Please note that `value` is intended for exclusive use with `data` and `valueMap` for `dataFrom`.
+Here is an example `ExternalSecret` that displays this behavior:
+
+!!! warning inline end
+ This provider supports specifying different `data[].version` configurations. However, `data[].property` is ignored.
+
+```yaml
+{% include 'fake-provider-es.yaml' %}
+```
+
+This results in the following secret:
+
+
+```yaml
+{% include 'fake-provider-secret.yaml' %}
+```
diff --git a/docs/snippets/fake-provider-es.yaml b/docs/snippets/fake-provider-es.yaml
new file mode 100644
index 000000000..cb3498836
--- /dev/null
+++ b/docs/snippets/fake-provider-es.yaml
@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+ name: example
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ name: fake
+ kind: ClusterSecretStore
+ target:
+ name: secret-to-be-created
+ data:
+ - secretKey: foo_bar
+ remoteRef:
+ key: /foo/bar
+ version: v1
+ dataFrom:
+ - key: /foo/baz
\ No newline at end of file
diff --git a/docs/snippets/fake-provider-secret.yaml b/docs/snippets/fake-provider-secret.yaml
new file mode 100644
index 000000000..ac4d40d57
--- /dev/null
+++ b/docs/snippets/fake-provider-secret.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: secret-to-be-created
+ namespace: default
+data:
+ foo_bar: SEVMTE8x # HELLO1 (via data)
+ foo: ZXhhbXBsZQ== # example (via dataFrom)
+ other: dGhpbmc= # thing (via dataFrom)
diff --git a/docs/snippets/fake-provider-store.yaml b/docs/snippets/fake-provider-store.yaml
new file mode 100644
index 000000000..6d429235e
--- /dev/null
+++ b/docs/snippets/fake-provider-store.yaml
@@ -0,0 +1,20 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: ClusterSecretStore
+metadata:
+ name: fake
+spec:
+ provider:
+ fake:
+ data:
+ - key: "/foo/bar"
+ value: "HELLO1"
+ version: "v1"
+ - key: "/foo/bar"
+ value: "HELLO2"
+ version: "v2"
+ - key: "/foo/baz"
+ valueMap:
+ foo: example
+ other: thing
+
+
diff --git a/docs/spec.md b/docs/spec.md
index a1b3aa453..80483267a 100644
--- a/docs/spec.md
+++ b/docs/spec.md
@@ -714,6 +714,237 @@ string
|
+ClusterExternalSecret
+
+
+
ClusterExternalSecret is the Schema for the clusterexternalsecrets API.
+
+
+ClusterExternalSecretConditionType
+(string
alias)
+
+(Appears on:
+ClusterExternalSecretStatus)
+
+
+
+
+
+
+Value |
+Description |
+
+
+"NotReady" |
+ |
+
"PartiallyReady" |
+ |
+
"Ready" |
+ |
+
+
+ClusterExternalSecretSpec
+
+
+(Appears on:
+ClusterExternalSecret)
+
+
+
ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+externalSecretSpec
+
+
+ExternalSecretSpec
+
+
+ |
+
+ The spec for the ExternalSecrets to be created
+ |
+
+
+
+externalSecretName
+
+string
+
+ |
+
+(Optional)
+ The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+ |
+
+
+
+namespaceSelector
+
+
+Kubernetes meta/v1.LabelSelector
+
+
+ |
+
+ The labels to select by to find the Namespaces to create the ExternalSecrets in.
+ |
+
+
+
+ClusterExternalSecretStatus
+
+
+(Appears on:
+ClusterExternalSecret)
+
+
+
ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.
+
+
ClusterSecretStore
@@ -1084,6 +1315,7 @@ string
(Appears on:
+ClusterExternalSecretSpec,
ExternalSecret)
@@ -1171,6 +1403,7 @@ If multiple entries are specified, the Secret keys are merged in the specified o
(Appears on:
+ClusterExternalSecretStatus,
ExternalSecret)
@@ -1486,6 +1719,95 @@ map[string]string
+
FakeProvider
+
+
+(Appears on:
+SecretStoreProvider)
+
+
+
FakeProvider configures a fake provider that returns static values
+
+
+FakeProviderData
+
+
+(Appears on:
+FakeProvider)
+
+
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+key
+
+string
+
+ |
+
+ |
+
+
+
+value
+
+string
+
+ |
+
+ |
+
+
+
+valueMap
+
+map[string]string
+
+ |
+
+ |
+
+
+
+version
+
+string
+
+ |
+
+ |
+
+
+
GCPSMAuth
@@ -2310,6 +2632,20 @@ WebhookProvider
Webhook configures this store to sync secrets using a generic templated webhook
+
+
+fake
+
+
+FakeProvider
+
+
+ |
+
+(Optional)
+ Fake configures a store with static key/value pairs
+ |
+
SecretStoreRef
diff --git a/hack/api-docs/mkdocs.yml b/hack/api-docs/mkdocs.yml
index 960e8bf99..5c3695751 100644
--- a/hack/api-docs/mkdocs.yml
+++ b/hack/api-docs/mkdocs.yml
@@ -56,6 +56,7 @@ nav:
- Oracle:
- Oracle Vault: provider-oracle-vault.md
- Webhook: provider-webhook.md
+ - Fake: provider-fake.md
- References:
- API specification: spec.md
- Contributing:
diff --git a/pkg/controllers/externalsecret/externalsecret_controller_test.go b/pkg/controllers/externalsecret/externalsecret_controller_test.go
index b502674f7..64e9a6b60 100644
--- a/pkg/controllers/externalsecret/externalsecret_controller_test.go
+++ b/pkg/controllers/externalsecret/externalsecret_controller_test.go
@@ -31,8 +31,8 @@ import (
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
- "github.com/external-secrets/external-secrets/pkg/provider/fake"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
+ "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
)
var (
diff --git a/pkg/provider/fake/fake.go b/pkg/provider/fake/fake.go
index bbf66fd4b..384feeb7a 100644
--- a/pkg/provider/fake/fake.go
+++ b/pkg/provider/fake/fake.go
@@ -16,6 +16,7 @@ package fake
import (
"context"
+ "fmt"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -24,80 +25,72 @@ import (
"github.com/external-secrets/external-secrets/pkg/provider/schema"
)
-var _ provider.Provider = &Client{}
+var (
+ errNotFound = fmt.Errorf("secret value not found")
+ errMissingStore = fmt.Errorf("missing store provider")
+ errMissingFakeProvider = fmt.Errorf("missing store provider fake")
+)
-// Client is a fake client for testing.
-type Client struct {
- NewFn func(context.Context, esv1alpha1.GenericStore, client.Client,
- string) (provider.SecretsClient, error)
- GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)
- GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
+type Provider struct {
+ config *esv1alpha1.FakeProvider
}
-// New returns a fake provider/client.
-func New() *Client {
- v := &Client{
- GetSecretFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
- return nil, nil
- },
- GetSecretMapFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- return nil, nil
- },
- }
-
- v.NewFn = func(context.Context, esv1alpha1.GenericStore, client.Client, string) (provider.SecretsClient, error) {
- return v, nil
- }
-
- return v
-}
-
-// RegisterAs registers the fake client in the schema.
-func (v *Client) RegisterAs(provider *esv1alpha1.SecretStoreProvider) {
- schema.ForceRegister(v, provider)
-}
-
-// GetSecret implements the provider.Provider interface.
-func (v *Client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
- return v.GetSecretFn(ctx, ref)
-}
-
-// WithGetSecret wraps secret data returned by this provider.
-func (v *Client) WithGetSecret(secData []byte, err error) *Client {
- v.GetSecretFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
- return secData, err
- }
- return v
-}
-
-// GetSecretMap imeplements the provider.Provider interface.
-func (v *Client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- return v.GetSecretMapFn(ctx, ref)
-}
-func (v *Client) Close(ctx context.Context) error {
- return nil
-}
-
-// WithGetSecretMap wraps the secret data map returned by this fake provider.
-func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client {
- v.GetSecretMapFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- return secData, err
- }
- return v
-}
-
-// WithNew wraps the fake provider factory function.
-func (v *Client) WithNew(f func(context.Context, esv1alpha1.GenericStore, client.Client,
- string) (provider.SecretsClient, error)) *Client {
- v.NewFn = f
- return v
-}
-
-// NewClient returns a new fake provider.
-func (v *Client) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
- c, err := v.NewFn(ctx, store, kube, namespace)
+func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
+ cfg, err := getProvider(store)
if err != nil {
return nil, err
}
- return c, nil
+ return &Provider{
+ config: cfg,
+ }, nil
+}
+
+func getProvider(store esv1alpha1.GenericStore) (*esv1alpha1.FakeProvider, error) {
+ if store == nil {
+ return nil, errMissingStore
+ }
+ spc := store.GetSpec()
+ if spc == nil || spc.Provider == nil || spc.Provider.Fake == nil {
+ return nil, errMissingFakeProvider
+ }
+ return spc.Provider.Fake, nil
+}
+
+// GetSecret returns a single secret from the provider.
+func (p *Provider) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ for _, data := range p.config.Data {
+ if data.Key == ref.Key && data.Version == ref.Version {
+ return []byte(data.Value), nil
+ }
+ }
+ return nil, errNotFound
+}
+
+// GetSecretMap returns multiple k/v pairs from the provider.
+func (p *Provider) GetSecretMap(ctx context.Context, ref esv1alpha1.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
+ }
+ return nil, errNotFound
+}
+
+func convertMap(in map[string]string) map[string][]byte {
+ m := make(map[string][]byte)
+ for k, v := range in {
+ m[k] = []byte(v)
+ }
+ return m
+}
+
+func (p *Provider) Close(ctx context.Context) error {
+ return nil
+}
+
+func init() {
+ schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
+ Fake: &esv1alpha1.FakeProvider{},
+ })
}
diff --git a/pkg/provider/fake/fake_test.go b/pkg/provider/fake/fake_test.go
new file mode 100644
index 000000000..7a88af5d5
--- /dev/null
+++ b/pkg/provider/fake/fake_test.go
@@ -0,0 +1,194 @@
+/*
+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 fake
+
+import (
+ "context"
+ "testing"
+
+ "github.com/onsi/gomega"
+
+ esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+)
+
+func TestNewClient(t *testing.T) {
+ p := &Provider{}
+ gomega.RegisterTestingT(t)
+
+ // nil store
+ _, err := p.NewClient(context.Background(), nil, nil, "")
+ gomega.Expect(err).To(gomega.HaveOccurred())
+
+ // missing provider
+ _, err = p.NewClient(context.Background(), &esv1alpha1.SecretStore{}, nil, "")
+ gomega.Expect(err).To(gomega.HaveOccurred())
+}
+
+func TestClose(t *testing.T) {
+ p := &Provider{}
+ gomega.RegisterTestingT(t)
+ err := p.Close(context.TODO())
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+}
+
+type testCase struct {
+ name string
+ input []esv1alpha1.FakeProviderData
+ request esv1alpha1.ExternalSecretDataRemoteRef
+ expValue string
+ expErr string
+}
+
+func TestGetSecret(t *testing.T) {
+ gomega.RegisterTestingT(t)
+ p := &Provider{}
+ tbl := []testCase{
+ {
+ name: "return err when not found",
+ input: []esv1alpha1.FakeProviderData{},
+ request: esv1alpha1.ExternalSecretDataRemoteRef{
+ Key: "/foo",
+ Version: "v2",
+ },
+ expErr: "secret value not found",
+ },
+ {
+ name: "get correct value from multiple versions",
+ input: []esv1alpha1.FakeProviderData{
+ {
+ Key: "/foo",
+ Value: "bar2",
+ Version: "v2",
+ },
+ {
+ Key: "junk",
+ Value: "xxxxx",
+ },
+ {
+ Key: "/foo",
+ Value: "bar1",
+ Version: "v1",
+ },
+ },
+ request: esv1alpha1.ExternalSecretDataRemoteRef{
+ Key: "/foo",
+ Version: "v2",
+ },
+ expValue: "bar2",
+ },
+ }
+
+ for _, row := range tbl {
+ t.Run(row.name, func(t *testing.T) {
+ cl, err := p.NewClient(context.Background(), &esv1alpha1.SecretStore{
+ Spec: esv1alpha1.SecretStoreSpec{
+ Provider: &esv1alpha1.SecretStoreProvider{
+ Fake: &esv1alpha1.FakeProvider{
+ Data: row.input,
+ },
+ },
+ },
+ }, nil, "")
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+ out, err := cl.GetSecret(context.Background(), row.request)
+ if row.expErr != "" {
+ gomega.Expect(err).To(gomega.MatchError(row.expErr))
+ } else {
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+ }
+ gomega.Expect(string(out)).To(gomega.Equal(row.expValue))
+ })
+ }
+}
+
+type testMapCase struct {
+ name string
+ input []esv1alpha1.FakeProviderData
+ request esv1alpha1.ExternalSecretDataRemoteRef
+ expValue map[string][]byte
+ expErr string
+}
+
+func TestGetSecretMap(t *testing.T) {
+ gomega.RegisterTestingT(t)
+ p := &Provider{}
+ tbl := []testMapCase{
+ {
+ name: "return err when not found",
+ input: []esv1alpha1.FakeProviderData{},
+ request: esv1alpha1.ExternalSecretDataRemoteRef{
+ Key: "/foo",
+ Version: "v2",
+ },
+ expErr: "secret value not found",
+ },
+ {
+ name: "get correct value from multiple versions",
+ input: []esv1alpha1.FakeProviderData{
+ {
+ Key: "junk",
+ ValueMap: map[string]string{
+ "junk": "ok",
+ },
+ },
+ {
+ Key: "/foo",
+ ValueMap: map[string]string{
+ "foo": "bar",
+ "baz": "bang",
+ },
+ Version: "v1",
+ },
+ {
+ Key: "/foo",
+ ValueMap: map[string]string{
+ "foo": "bar",
+ "baz": "bang",
+ },
+ Version: "v2",
+ },
+ },
+ request: esv1alpha1.ExternalSecretDataRemoteRef{
+ Key: "/foo",
+ Version: "v2",
+ },
+ expValue: map[string][]byte{
+ "foo": []byte("bar"),
+ "baz": []byte("bang"),
+ },
+ },
+ }
+
+ for _, row := range tbl {
+ t.Run(row.name, func(t *testing.T) {
+ cl, err := p.NewClient(context.Background(), &esv1alpha1.SecretStore{
+ Spec: esv1alpha1.SecretStoreSpec{
+ Provider: &esv1alpha1.SecretStoreProvider{
+ Fake: &esv1alpha1.FakeProvider{
+ Data: row.input,
+ },
+ },
+ },
+ }, nil, "")
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+ out, err := cl.GetSecretMap(context.Background(), row.request)
+ if row.expErr != "" {
+ gomega.Expect(err).To(gomega.MatchError(row.expErr))
+ } else {
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+ }
+ gomega.Expect(out).To(gomega.Equal(row.expValue))
+ })
+ }
+}
diff --git a/pkg/provider/register/register.go b/pkg/provider/register/register.go
index 10760941a..be69679ae 100644
--- a/pkg/provider/register/register.go
+++ b/pkg/provider/register/register.go
@@ -21,6 +21,7 @@ import (
_ "github.com/external-secrets/external-secrets/pkg/provider/alibaba"
_ "github.com/external-secrets/external-secrets/pkg/provider/aws"
_ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/fake"
_ "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
_ "github.com/external-secrets/external-secrets/pkg/provider/gitlab"
_ "github.com/external-secrets/external-secrets/pkg/provider/ibm"
diff --git a/pkg/provider/testing/fake/fake.go b/pkg/provider/testing/fake/fake.go
new file mode 100644
index 000000000..bbf66fd4b
--- /dev/null
+++ b/pkg/provider/testing/fake/fake.go
@@ -0,0 +1,103 @@
+/*
+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 fake
+
+import (
+ "context"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+ "github.com/external-secrets/external-secrets/pkg/provider"
+ "github.com/external-secrets/external-secrets/pkg/provider/schema"
+)
+
+var _ provider.Provider = &Client{}
+
+// Client is a fake client for testing.
+type Client struct {
+ NewFn func(context.Context, esv1alpha1.GenericStore, client.Client,
+ string) (provider.SecretsClient, error)
+ GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)
+ GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
+}
+
+// New returns a fake provider/client.
+func New() *Client {
+ v := &Client{
+ GetSecretFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ return nil, nil
+ },
+ GetSecretMapFn: func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ return nil, nil
+ },
+ }
+
+ v.NewFn = func(context.Context, esv1alpha1.GenericStore, client.Client, string) (provider.SecretsClient, error) {
+ return v, nil
+ }
+
+ return v
+}
+
+// RegisterAs registers the fake client in the schema.
+func (v *Client) RegisterAs(provider *esv1alpha1.SecretStoreProvider) {
+ schema.ForceRegister(v, provider)
+}
+
+// GetSecret implements the provider.Provider interface.
+func (v *Client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ return v.GetSecretFn(ctx, ref)
+}
+
+// WithGetSecret wraps secret data returned by this provider.
+func (v *Client) WithGetSecret(secData []byte, err error) *Client {
+ v.GetSecretFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ return secData, err
+ }
+ return v
+}
+
+// GetSecretMap imeplements the provider.Provider interface.
+func (v *Client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ return v.GetSecretMapFn(ctx, ref)
+}
+func (v *Client) Close(ctx context.Context) error {
+ return nil
+}
+
+// WithGetSecretMap wraps the secret data map returned by this fake provider.
+func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client {
+ v.GetSecretMapFn = func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ return secData, err
+ }
+ return v
+}
+
+// WithNew wraps the fake provider factory function.
+func (v *Client) WithNew(f func(context.Context, esv1alpha1.GenericStore, client.Client,
+ string) (provider.SecretsClient, error)) *Client {
+ v.NewFn = f
+ return v
+}
+
+// NewClient returns a new fake provider.
+func (v *Client) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
+ c, err := v.NewFn(ctx, store, kube, namespace)
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
+}