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:
commit
410908dd9f
16 changed files with 898 additions and 71 deletions
27
apis/externalsecrets/v1alpha1/secretstore_fake_types.go
Normal file
27
apis/externalsecrets/v1alpha1/secretstore_fake_types.go
Normal 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"`
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
26
docs/provider-fake.md
Normal 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' %}
|
||||
```
|
18
docs/snippets/fake-provider-es.yaml
Normal file
18
docs/snippets/fake-provider-es.yaml
Normal 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
|
9
docs/snippets/fake-provider-secret.yaml
Normal file
9
docs/snippets/fake-provider-secret.yaml
Normal 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)
|
20
docs/snippets/fake-provider-store.yaml
Normal file
20
docs/snippets/fake-provider-store.yaml
Normal 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
|
||||
|
||||
|
336
docs/spec.md
336
docs/spec.md
|
@ -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>"NotReady"</p></td>
|
||||
<td></td>
|
||||
</tr><tr><td><p>"PartiallyReady"</p></td>
|
||||
<td></td>
|
||||
</tr><tr><td><p>"Ready"</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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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{},
|
||||
})
|
||||
}
|
||||
|
|
194
pkg/provider/fake/fake_test.go
Normal file
194
pkg/provider/fake/fake_test.go
Normal 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))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
103
pkg/provider/testing/fake/fake.go
Normal file
103
pkg/provider/testing/fake/fake.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue