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

feat: implement provider interface

adds the provider interface, generic store and schema registration.
mostly taken from  itscontained/secret-manager

Co-authored-by: Moritz Johner <beller.moritz@googlemail.com>
This commit is contained in:
Kellin McAvoy 2020-11-30 21:56:51 +01:00 committed by Moritz Johner
parent ffd4a220d1
commit f1fb6cfa06
17 changed files with 725 additions and 257 deletions

View file

@ -0,0 +1,52 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// +kubebuilder:object:root=false
// +kubebuilder:object:generate:false
// +k8s:deepcopy-gen:interfaces=nil
// +k8s:deepcopy-gen=nil
// GenericStore is a common interface for interacting with ClusterSecretStore
// or a namespaced SecretStore
type GenericStore interface {
runtime.Object
metav1.Object
GetProvider() *SecretStoreProvider
}
// +kubebuilder:object:root:false
// +kubebuilder:object:generate:false
var _ GenericStore = &SecretStore{}
// GetProvider returns the underlying provider
func (c *SecretStore) GetProvider() *SecretStoreProvider {
return c.Spec.Provider
}
// SetProvider sets the underlying provider
func (c *SecretStore) SetProvider(provider SecretStoreProvider) {
c.Spec.Provider = &provider
}
// Copy returns a DeepCopy of the Store
func (c *SecretStore) Copy() GenericStore {
return c.DeepCopy()
}

View file

@ -18,6 +18,8 @@ limitations under the License.
package v1alpha1
import (
"reflect"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
@ -32,3 +34,9 @@ var (
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
// SecretStore type metadata.
var (
SecretStoreKind = reflect.TypeOf(SecretStore{}).Name()
SecretStoreKindAPIVersion = SecretStoreKind + "." + GroupVersion.String()
)

View file

@ -34,6 +34,14 @@ type SecretStoreSpec struct {
// +optional
Controller string `json:"controller"`
// Used to configure the provider. Only one provider may be set
Provider *SecretStoreProvider `json:"provider"`
}
// SecretStoreProvider contains the provider-specific configration
// +kubebuilder:validation:MinProperties=1
// +kubebuilder:validation:MaxProperties=1
type SecretStoreProvider struct {
// AWSSM configures this store to sync secrets using AWS Secret Manager provider
// +optional
AWSSM *AWSSMProvider `json:"awssm,omitempty"`

View file

@ -1,8 +1,6 @@
// +build !ignore_autogenerated
/*
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
@ -21,7 +19,7 @@ limitations under the License.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -368,6 +366,26 @@ func (in *SecretStoreList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = *in
if in.AWSSM != nil {
in, out := &in.AWSSM, &out.AWSSM
*out = new(AWSSMProvider)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretStoreProvider.
func (in *SecretStoreProvider) DeepCopy() *SecretStoreProvider {
if in == nil {
return nil
}
out := new(SecretStoreProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretStoreRef) DeepCopyInto(out *SecretStoreRef) {
*out = *in
@ -386,9 +404,9 @@ func (in *SecretStoreRef) DeepCopy() *SecretStoreRef {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretStoreSpec) DeepCopyInto(out *SecretStoreSpec) {
*out = *in
if in.AWSSM != nil {
in, out := &in.AWSSM, &out.AWSSM
*out = new(AWSSMProvider)
if in.Provider != nil {
in, out := &in.Provider, &out.Provider
*out = new(SecretStoreProvider)
(*in).DeepCopyInto(*out)
}
}

View file

@ -1,10 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
controller-gen.kubebuilder.io/version: v0.4.1
creationTimestamp: null
name: externalsecrets.external-secrets.io
spec:
@ -15,150 +15,150 @@ spec:
plural: externalsecrets
singular: externalsecret
scope: Namespaced
validation:
openAPIV3Schema:
description: ExternalSecret is the Schema for the externalsecrets API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ExternalSecretSpec defines the desired state of ExternalSecret
properties:
data:
description: Data defines the connection between the Kubernetes Secret
keys and the Provider data
items:
description: ExternalSecretData defines the connection between the
Kubernetes Secret key (spec.data.<key>) and the Provider data
properties:
remoteRef:
description: ExternalSecretDataRemoteRef defines Provider data
location
properties:
key:
description: Key is the key used in the Provider, mandatory
type: string
property:
description: Used to select a specific property of the Provider
value (if a map), if supported
type: string
version:
description: Used to select a specific version of the Provider
value, if supported
type: string
required:
- key
type: object
secretKey:
type: string
required:
- remoteRef
- secretKey
type: object
type: array
dataFrom:
description: DataFrom is used to fetch all properties from a specific
Provider data If multiple entries are specified, the Secret keys are
merged in the specified order
items:
description: ExternalSecretDataRemoteRef defines Provider data location
properties:
key:
description: Key is the key used in the Provider, mandatory
type: string
property:
description: Used to select a specific property of the Provider
value (if a map), if supported
type: string
version:
description: Used to select a specific version of the Provider
value, if supported
type: string
required:
- key
type: object
type: array
refreshInterval:
description: 'RefreshInterval is the amount of time before the values
reading again from the SecretStore provider Valid time units are "ns",
"us" (or "µs"), "ms", "s", "m", "h" (from time.ParseDuration) May
be set to zero to fetch and create it once TODO: Default to some value?'
type: string
secretStoreRef:
description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret
data
properties:
kind:
description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
type: string
name:
description: Name of the SecretStore resource
type: string
required:
- name
type: object
target:
description: ExternalSecretTarget defines the Kubernetes Secret to be
created There can be only one target per ExternalSecret
properties:
creationPolicy:
description: CreationPolicy defines rules on how to create the resulting
Secret Defaults to 'Owner'
type: string
name:
description: Name defines the name of the Secret resource to be
managed This field is immutable Defaults to the .metadata.name
of the ExternalSecret resource
type: string
type: object
required:
- secretStoreRef
- target
type: object
status:
properties:
conditions:
items:
properties:
lastSyncTime:
format: date-time
type: string
lastTransitionTime:
format: date-time
type: string
message:
type: string
reason:
type: string
status:
type: string
type:
type: string
required:
- status
- type
type: object
type: array
phase:
description: ExternalSecretStatusPhase represents the current phase
of the Secret sync
type: string
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: ExternalSecret is the Schema for the externalsecrets API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ExternalSecretSpec defines the desired state of ExternalSecret
properties:
data:
description: Data defines the connection between the Kubernetes Secret
keys and the Provider data
items:
description: ExternalSecretData defines the connection between the
Kubernetes Secret key (spec.data.<key>) and the Provider data
properties:
remoteRef:
description: ExternalSecretDataRemoteRef defines Provider data
location
properties:
key:
description: Key is the key used in the Provider, mandatory
type: string
property:
description: Used to select a specific property of the Provider
value (if a map), if supported
type: string
version:
description: Used to select a specific version of the Provider
value, if supported
type: string
required:
- key
type: object
secretKey:
type: string
required:
- remoteRef
- secretKey
type: object
type: array
dataFrom:
description: DataFrom is used to fetch all properties from a specific
Provider data If multiple entries are specified, the Secret keys
are merged in the specified order
items:
description: ExternalSecretDataRemoteRef defines Provider data location
properties:
key:
description: Key is the key used in the Provider, mandatory
type: string
property:
description: Used to select a specific property of the Provider
value (if a map), if supported
type: string
version:
description: Used to select a specific version of the Provider
value, if supported
type: string
required:
- key
type: object
type: array
refreshInterval:
description: 'RefreshInterval is the amount of time before the values
reading again from the SecretStore provider Valid time units are
"ns", "us" (or "µs"), "ms", "s", "m", "h" (from time.ParseDuration)
May be set to zero to fetch and create it once TODO: Default to
some value?'
type: string
secretStoreRef:
description: SecretStoreRef defines which SecretStore to fetch the
ExternalSecret data
properties:
kind:
description: Kind of the SecretStore resource (SecretStore or
ClusterSecretStore) Defaults to `SecretStore`
type: string
name:
description: Name of the SecretStore resource
type: string
required:
- name
type: object
target:
description: ExternalSecretTarget defines the Kubernetes Secret to
be created There can be only one target per ExternalSecret
properties:
creationPolicy:
description: CreationPolicy defines rules on how to create the
resulting Secret Defaults to 'Owner'
type: string
name:
description: Name defines the name of the Secret resource to be
managed This field is immutable Defaults to the .metadata.name
of the ExternalSecret resource
type: string
type: object
required:
- secretStoreRef
- target
type: object
status:
properties:
conditions:
items:
properties:
lastSyncTime:
format: date-time
type: string
lastTransitionTime:
format: date-time
type: string
message:
type: string
reason:
type: string
status:
type: string
type:
type: string
required:
- status
- type
type: object
type: array
phase:
description: ExternalSecretStatusPhase represents the current phase
of the Secret sync
type: string
type: object
type: object
served: true
storage: true
status:

View file

@ -1,10 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
controller-gen.kubebuilder.io/version: v0.4.1
creationTimestamp: null
name: secretstores.external-secrets.io
spec:
@ -15,111 +15,119 @@ spec:
plural: secretstores
singular: secretstore
scope: Namespaced
validation:
openAPIV3Schema:
description: SecretStore is the Schema for the secretstores API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: SecretStoreSpec defines the desired state of SecretStore
properties:
awssm:
description: AWSSM configures this store to sync secrets using AWS Secret
Manager provider
properties:
auth:
description: Auth defines the information necessary to authenticate
against AWS
properties:
secretRef:
properties:
accessKeyIDSecretRef:
description: The AccessKeyID is used for authentication
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- key
- name
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- key
- name
type: object
type: object
required:
- secretRef
type: object
region:
description: AWS Region to be used for the provider
type: string
role:
description: Role is a Role ARN which the SecretManager provider
will assume
type: string
required:
- auth
- region
type: object
controller:
description: 'Used to select the correct KES controller (think: ingress.ingressClassName)
The KES controller is instantiated with a specific controller name
and filters ES based on this property'
type: string
type: object
status:
description: SecretStoreStatus defines the observed state of the SecretStore
properties:
conditions:
items:
properties:
lastTransitionTime:
format: date-time
type: string
message:
type: string
reason:
type: string
status:
type: string
type:
type: string
required:
- status
- type
type: object
type: array
phase:
type: string
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: SecretStore is the Schema for the secretstores API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: SecretStoreSpec defines the desired state of SecretStore
properties:
controller:
description: 'Used to select the correct KES controller (think: ingress.ingressClassName)
The KES controller is instantiated with a specific controller name
and filters ES based on this property'
type: string
provider:
description: Used to configure the provider. Only one provider may
be set
maxProperties: 1
minProperties: 1
properties:
awssm:
description: AWSSM configures this store to sync secrets using
AWS Secret Manager provider
properties:
auth:
description: Auth defines the information necessary to authenticate
against AWS
properties:
secretRef:
properties:
accessKeyIDSecretRef:
description: The AccessKeyID is used for authentication
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- key
- name
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- key
- name
type: object
type: object
required:
- secretRef
type: object
region:
description: AWS Region to be used for the provider
type: string
role:
description: Role is a Role ARN which the SecretManager provider
will assume
type: string
required:
- auth
- region
type: object
type: object
required:
- provider
type: object
status:
description: SecretStoreStatus defines the observed state of the SecretStore
properties:
conditions:
items:
properties:
lastTransitionTime:
format: date-time
type: string
message:
type: string
reason:
type: string
status:
type: string
type:
type: string
required:
- status
- type
type: object
type: array
phase:
type: string
type: object
type: object
served: true
storage: true
status:

1
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/go-logr/logr v0.1.0
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1
github.com/stretchr/testify v1.4.0
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.17.2

2
go.sum
View file

@ -36,6 +36,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@ -277,6 +278,7 @@ github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=

View file

@ -0,0 +1,49 @@
/*
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 secretsmanager
import (
"context"
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// SecretsManager is a provider for AWS SecretsManager
type SecretsManager struct{}
// New constructs a SecretsManager Provider
func (sm *SecretsManager) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
return sm, nil // stub
}
// GetSecret returns a single secret from the provider
func (sm *SecretsManager) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
return []byte("NOOP"), nil
}
// GetSecretMap returns multiple k/v pairs from the provider
func (sm *SecretsManager) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
return map[string][]byte{
"noop": []byte("NOOP"),
}, nil
}
func init() {
schema.Register(&SecretsManager{}, &esv1alpha1.SecretStoreProvider{
AWSSM: &esv1alpha1.AWSSMProvider{},
})
}

View file

@ -0,0 +1 @@
package secretsmanager

100
pkg/provider/fake/fake.go Normal file
View file

@ -0,0 +1,100 @@
/*
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"
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/external-secrets/external-secrets/pkg/provider/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var _ provider.Provider = &Client{}
// Client is a fake client for testing
type Client struct {
NewFn func(context.Context, esv1alpha1.SecretStoreProvider, client.Client,
string) (provider.Provider, error)
GetSecretFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)
GetSecretMapFn func(context.Context, esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
}
// New returns a fake 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.SecretStoreProvider, client.Client, string) (provider.Provider, error) {
return nil, 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)
}
// 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.SecretStoreProvider, client.Client,
string) (provider.Provider, error)) *Client {
v.NewFn = f
return v
}
// New returns a new fake provider
func (v *Client) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
client, err := v.NewFn(ctx, store, kube, namespace)
if err != nil {
return nil, err
}
return client, nil
}

35
pkg/provider/provider.go Normal file
View file

@ -0,0 +1,35 @@
/*
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 provider
import (
"context"
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Provider is a common interface for interacting with secret backends
type Provider interface {
// New constructs a SecretsManager Provider
New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (Provider, error)
// GetSecret returns a single secret from the provider
GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error)
// GetSecretMap returns multiple k/v pairs from the provider
GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error)
}

View file

@ -0,0 +1,21 @@
/*
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 register
// packages imported here are registered to the controller schema
import (
// register awssm provider
_ "github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
)

View file

@ -0,0 +1,114 @@
/*
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 schema
import (
"encoding/json"
"fmt"
"sync"
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
)
var builder map[string]provider.Provider
var buildlock sync.RWMutex
func init() {
builder = make(map[string]provider.Provider)
}
// Register a store backend type. Register panics if a
// backend with the same store is already registered
func Register(s provider.Provider, storeSpec *esv1alpha1.SecretStoreProvider) {
storeName, err := getProviderName(storeSpec)
if err != nil {
panic(fmt.Sprintf("store error registering schema: %s", err.Error()))
}
buildlock.Lock()
defer buildlock.Unlock()
_, exists := builder[storeName]
if exists {
panic(fmt.Sprintf("store %q already registered", storeName))
}
builder[storeName] = s
}
// ForceRegister adds to store schema, overwriting a store if
// already registered. Should only be used for testing
func ForceRegister(s provider.Provider, storeSpec *esv1alpha1.SecretStoreProvider) {
storeName, err := getProviderName(storeSpec)
if err != nil {
panic(fmt.Sprintf("store error registering schema: %s", err.Error()))
}
buildlock.Lock()
builder[storeName] = s
buildlock.Unlock()
}
// GetProviderByName returns the provider implementation by name
func GetProviderByName(name string) (provider.Provider, bool) {
buildlock.RLock()
f, ok := builder[name]
buildlock.RUnlock()
return f, ok
}
// GetProvider returns the provider from the generic store
func GetProvider(s esv1alpha1.GenericStore) (provider.Provider, error) {
provider := s.GetProvider()
storeName, err := getProviderName(provider)
if err != nil {
return nil, fmt.Errorf("store error for %s: %w", s.GetName(), err)
}
buildlock.RLock()
f, ok := builder[storeName]
buildlock.RUnlock()
if !ok {
return nil, fmt.Errorf("failed to find registered store backend for type: %s, name: %s", storeName, s.GetName())
}
return f, nil
}
// getProviderName returns the name of the configured provider
// or an error if the provider is not configured
func getProviderName(storeSpec *esv1alpha1.SecretStoreProvider) (string, error) {
storeBytes, err := json.Marshal(storeSpec)
if err != nil {
return "", fmt.Errorf("failed to marshal store spec: %w", err)
}
storeMap := make(map[string]interface{})
err = json.Unmarshal(storeBytes, &storeMap)
if err != nil {
return "", fmt.Errorf("failed to unmarshal store spec: %w", err)
}
if len(storeMap) != 1 {
return "", fmt.Errorf("secret stores must only have exactly one backend specified, found %d", len(storeMap))
}
for k := range storeMap {
return k, nil
}
return "", fmt.Errorf("failed to find registered store backend")
}

View file

@ -0,0 +1,53 @@
/*
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 schema
import (
"context"
"testing"
esv1alpha1 "github.com/external-secrets/external-secrets/api/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type PP struct{}
// New constructs a SecretsManager Provider
func (p *PP) New(ctx context.Context, store esv1alpha1.SecretStoreProvider, kube client.Client, namespace string) (provider.Provider, error) {
return p, nil
}
// GetSecret returns a single secret from the provider
func (p *PP) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
return []byte("NOOP"), nil
}
// GetSecretMap returns multiple k/v pairs from the provider
func (p *PP) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
return map[string][]byte{}, nil
}
func TestRegister(t *testing.T) {
p, ok := GetProviderByName("awssm")
assert.Nil(t, p)
assert.False(t, ok)
ForceRegister(&PP{}, &esv1alpha1.SecretStoreProvider{
AWSSM: &esv1alpha1.AWSSMProvider{},
})
p, ok = GetProviderByName("awssm")
assert.NotNil(t, p)
assert.True(t, ok)
}

View file

@ -1 +0,0 @@
package awssm

View file

@ -1 +0,0 @@
package awssm