1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Merge pull request #661 from external-secrets/feature/fake-provider

feat(provider): implement fake provider
This commit is contained in:
paul-the-alien[bot] 2022-02-01 18:55:59 +00:00 committed by GitHub
commit 410908dd9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 898 additions and 71 deletions

View file

@ -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"`
}

View file

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

View file

@ -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.

View file

@ -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

View file

@ -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

26
docs/provider-fake.md Normal file
View file

@ -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' %}
```

View file

@ -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

View file

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

View file

@ -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

View file

@ -714,6 +714,237 @@ string
<td></td>
</tr></tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterExternalSecret">ClusterExternalSecret
</h3>
<p>
<p>ClusterExternalSecret is the Schema for the clusterexternalsecrets API.</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>metadata</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta">
Kubernetes meta/v1.ObjectMeta
</a>
</em>
</td>
<td>
Refer to the Kubernetes API documentation for the fields of the
<code>metadata</code> field.
</td>
</tr>
<tr>
<td>
<code>spec</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretSpec">
ClusterExternalSecretSpec
</a>
</em>
</td>
<td>
<br/>
<br/>
<table>
<tr>
<td>
<code>externalSecretSpec</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ExternalSecretSpec">
ExternalSecretSpec
</a>
</em>
</td>
<td>
<p>The spec for the ExternalSecrets to be created</p>
</td>
</tr>
<tr>
<td>
<code>externalSecretName</code></br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>The name of the external secrets to be created defaults to the name of the ClusterExternalSecret</p>
</td>
</tr>
<tr>
<td>
<code>namespaceSelector</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#labelselector-v1-meta">
Kubernetes meta/v1.LabelSelector
</a>
</em>
</td>
<td>
<p>The labels to select by to find the Namespaces to create the ExternalSecrets in.</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<code>status</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretStatus">
ClusterExternalSecretStatus
</a>
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterExternalSecretConditionType">ClusterExternalSecretConditionType
(<code>string</code> alias)</p></h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretStatus">ClusterExternalSecretStatus</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr><td><p>&#34;NotReady&#34;</p></td>
<td></td>
</tr><tr><td><p>&#34;PartiallyReady&#34;</p></td>
<td></td>
</tr><tr><td><p>&#34;Ready&#34;</p></td>
<td></td>
</tr></tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterExternalSecretSpec">ClusterExternalSecretSpec
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecret">ClusterExternalSecret</a>)
</p>
<p>
<p>ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>externalSecretSpec</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ExternalSecretSpec">
ExternalSecretSpec
</a>
</em>
</td>
<td>
<p>The spec for the ExternalSecrets to be created</p>
</td>
</tr>
<tr>
<td>
<code>externalSecretName</code></br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>The name of the external secrets to be created defaults to the name of the ClusterExternalSecret</p>
</td>
</tr>
<tr>
<td>
<code>namespaceSelector</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#labelselector-v1-meta">
Kubernetes meta/v1.LabelSelector
</a>
</em>
</td>
<td>
<p>The labels to select by to find the Namespaces to create the ExternalSecrets in.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterExternalSecretStatus">ClusterExternalSecretStatus
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecret">ClusterExternalSecret</a>)
</p>
<p>
<p>ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>type</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretConditionType">
ClusterExternalSecretConditionType
</a>
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>status</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#conditionstatus-v1-core">
Kubernetes core/v1.ConditionStatus
</a>
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>externalSecretStatuses</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.ExternalSecretStatus">
[]ExternalSecretStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.ClusterSecretStore">ClusterSecretStore
</h3>
<p>
@ -1084,6 +1315,7 @@ string
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretSpec">ClusterExternalSecretSpec</a>,
<a href="#external-secrets.io/v1alpha1.ExternalSecret">ExternalSecret</a>)
</p>
<p>
@ -1171,6 +1403,7 @@ If multiple entries are specified, the Secret keys are merged in the specified o
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.ClusterExternalSecretStatus">ClusterExternalSecretStatus</a>,
<a href="#external-secrets.io/v1alpha1.ExternalSecret">ExternalSecret</a>)
</p>
<p>
@ -1486,6 +1719,95 @@ map[string]string
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.FakeProvider">FakeProvider
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.SecretStoreProvider">SecretStoreProvider</a>)
</p>
<p>
<p>FakeProvider configures a fake provider that returns static values</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>data</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.FakeProviderData">
[]FakeProviderData
</a>
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.FakeProviderData">FakeProviderData
</h3>
<p>
(<em>Appears on:</em>
<a href="#external-secrets.io/v1alpha1.FakeProvider">FakeProvider</a>)
</p>
<p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>key</code></br>
<em>
string
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>value</code></br>
<em>
string
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>valueMap</code></br>
<em>
map[string]string
</em>
</td>
<td>
</td>
</tr>
<tr>
<td>
<code>version</code></br>
<em>
string
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.GCPSMAuth">GCPSMAuth
</h3>
<p>
@ -2310,6 +2632,20 @@ WebhookProvider
<p>Webhook configures this store to sync secrets using a generic templated webhook</p>
</td>
</tr>
<tr>
<td>
<code>fake</code></br>
<em>
<a href="#external-secrets.io/v1alpha1.FakeProvider">
FakeProvider
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Fake configures a store with static key/value pairs</p>
</td>
</tr>
</tbody>
</table>
<h3 id="external-secrets.io/v1alpha1.SecretStoreRef">SecretStoreRef

View file

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

View file

@ -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 (

View file

@ -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{},
})
}

View file

@ -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))
})
}
}

View file

@ -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"

View file

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