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.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +ClusterExternalSecretSpec + + +
+
+
+ + + + + + + + + + + + + +
+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.

+
+
+status
+ + +ClusterExternalSecretStatus + + +
+
+

ClusterExternalSecretConditionType +(string alias)

+

+(Appears on: +ClusterExternalSecretStatus) +

+

+

+ + + + + + + + + + + + + + +
ValueDescription

"NotReady"

"PartiallyReady"

"Ready"

+

ClusterExternalSecretSpec +

+

+(Appears on: +ClusterExternalSecret) +

+

+

ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+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.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+type
+ + +ClusterExternalSecretConditionType + + +
+
+status
+ + +Kubernetes core/v1.ConditionStatus + + +
+
+externalSecretStatuses
+ + +[]ExternalSecretStatus + + +
+(Optional) +

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

+

+ + + + + + + + + + + + + +
FieldDescription
+data
+ + +[]FakeProviderData + + +
+
+

FakeProviderData +

+

+(Appears on: +FakeProvider) +

+

+

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