Browse Source

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>
Kellin McAvoy 5 years ago
parent
commit
f1fb6cfa06

+ 52 - 0
api/v1alpha1/generic_store.go

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

+ 8 - 0
api/v1alpha1/groupversion_info.go

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

+ 8 - 0
api/v1alpha1/secretstore_types.go

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

+ 24 - 6
api/v1alpha1/zz_generated.deepcopy.go

@@ -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.
@@ -369,6 +367,26 @@ func (in *SecretStoreList) DeepCopyObject() runtime.Object {
 }
 
 // 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)
 	}
 }

+ 137 - 137
config/crd/bases/external-secrets.io_externalsecrets.yaml

@@ -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
+  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:
-                  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
+                  kind:
+                    description: Kind of the SecretStore resource (SecretStore or
+                      ClusterSecretStore) Defaults to `SecretStore`
                     type: string
-                  version:
-                    description: Used to select a specific version of the Provider
-                      value, if supported
+                  name:
+                    description: Name of the SecretStore resource
                     type: string
                 required:
-                - key
+                - name
                 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:
+              target:
+                description: ExternalSecretTarget defines the Kubernetes Secret to
+                  be created There can be only one target per ExternalSecret
                 properties:
-                  lastSyncTime:
-                    format: date-time
+                  creationPolicy:
+                    description: CreationPolicy defines rules on how to create the
+                      resulting Secret Defaults to 'Owner'
                     type: string
-                  lastTransitionTime:
-                    format: date-time
+                  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
-                  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
+            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:

+ 112 - 104
config/crd/bases/external-secrets.io_secretstores.yaml

@@ -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
+  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:
-                    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
+                    lastTransitionTime:
+                      format: date-time
+                      type: string
+                    message:
+                      type: string
+                    reason:
+                      type: string
+                    status:
+                      type: string
+                    type:
+                      type: string
                   required:
-                  - secretRef
+                  - status
+                  - type
                   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
+                type: array
+              phase:
+                type: string
+            type: object
+        type: object
     served: true
     storage: true
 status:

+ 1 - 0
go.mod

@@ -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 - 0
go.sum

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

+ 49 - 0
pkg/provider/aws/secretsmanager/secretsmanager.go

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

+ 1 - 0
pkg/provider/aws/secretsmanager/secretsmanager_test.go

@@ -0,0 +1 @@
+package secretsmanager

+ 100 - 0
pkg/provider/fake/fake.go

@@ -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 - 0
pkg/provider/provider.go

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

+ 21 - 0
pkg/provider/register/register.go

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

+ 114 - 0
pkg/provider/schema/schema.go

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

+ 53 - 0
pkg/provider/schema/schema_test.go

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

+ 0 - 1
pkg/providers/awssm/awssm.go

@@ -1 +0,0 @@
-package awssm

+ 0 - 1
pkg/providers/awssm/awssm_test.go

@@ -1 +0,0 @@
-package awssm