Browse Source

feat: awssm refactoring (#57)

* fix: refactor awssm provider
Moritz Johner 5 years ago
parent
commit
640978ca9e

+ 24 - 8
apis/externalsecrets/v1alpha1/secretstore_awssm_types.go

@@ -18,14 +18,14 @@ import (
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 
-// AWSSMAuth contains a secretRef for credentials.
-type AWSSMAuth struct {
-	SecretRef AWSSMAuthSecretRef `json:"secretRef"`
+// AWSAuth contains a secretRef for credentials.
+type AWSAuth struct {
+	SecretRef AWSAuthSecretRef `json:"secretRef"`
 }
 
-// AWSSMAuthSecretRef holds secret references for aws credentials
+// AWSAuthSecretRef holds secret references for aws credentials
 // both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
-type AWSSMAuthSecretRef struct {
+type AWSAuthSecretRef struct {
 	// The AccessKeyID is used for authentication
 	AccessKeyID esmeta.SecretKeySelector `json:"accessKeyIDSecretRef,omitempty"`
 
@@ -33,14 +33,30 @@ type AWSSMAuthSecretRef struct {
 	SecretAccessKey esmeta.SecretKeySelector `json:"secretAccessKeySecretRef,omitempty"`
 }
 
-// AWSSMProvider configures a store to sync secrets using the AWS Secret Manager provider.
-type AWSSMProvider struct {
+// AWSServiceType is a enum that defines the service/API that is used to fetch the secrets.
+// +kubebuilder:validation:Enum=SecretsManager;ParameterStore
+type AWSServiceType string
+
+const (
+	// AWSServiceSecretsManager is the AWS SecretsManager.
+	// see: https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html
+	AWSServiceSecretsManager AWSServiceType = "SecretsManager"
+	// AWSServiceParameterStore is the AWS SystemsManager ParameterStore.
+	// see: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html
+	AWSServiceParameterStore AWSServiceType = "ParameterStore"
+)
+
+// AWSProvider configures a store to sync secrets with AWS.
+type AWSProvider struct {
+	// Service defines which service should be used to fetch the secrets
+	Service AWSServiceType `json:"service"`
+
 	// Auth defines the information necessary to authenticate against AWS
 	// if not set aws sdk will infer credentials from your environment
 	// see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials
 	// +nullable
 	// +optional
-	Auth *AWSSMAuth `json:"auth"`
+	Auth *AWSAuth `json:"auth"`
 
 	// Role is a Role ARN which the SecretManager provider will assume
 	// +optional

+ 2 - 10
apis/externalsecrets/v1alpha1/secretstore_types.go

@@ -19,14 +19,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-type StoreProvider string
-
-const (
-	AWSSM StoreProvider = "AWSSM"
-	GCPSM StoreProvider = "GCPSM"
-	Vault StoreProvider = "VAULT"
-)
-
 // SecretStoreSpec defines the desired state of SecretStore.
 type SecretStoreSpec struct {
 	// Used to select the correct KES controller (think: ingress.ingressClassName)
@@ -42,9 +34,9 @@ type SecretStoreSpec struct {
 // +kubebuilder:validation:MinProperties=1
 // +kubebuilder:validation:MaxProperties=1
 type SecretStoreProvider struct {
-	// AWSSM configures this store to sync secrets using AWS Secret Manager provider
+	// AWS configures this store to sync secrets using AWS Secret Manager provider
 	// +optional
-	AWSSM *AWSSMProvider `json:"awssm,omitempty"`
+	AWS *AWSProvider `json:"aws,omitempty"`
 }
 
 type SecretStoreConditionType string

+ 16 - 16
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -24,54 +24,54 @@ import (
 )
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AWSSMAuth) DeepCopyInto(out *AWSSMAuth) {
+func (in *AWSAuth) DeepCopyInto(out *AWSAuth) {
 	*out = *in
 	in.SecretRef.DeepCopyInto(&out.SecretRef)
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSMAuth.
-func (in *AWSSMAuth) DeepCopy() *AWSSMAuth {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSAuth.
+func (in *AWSAuth) DeepCopy() *AWSAuth {
 	if in == nil {
 		return nil
 	}
-	out := new(AWSSMAuth)
+	out := new(AWSAuth)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AWSSMAuthSecretRef) DeepCopyInto(out *AWSSMAuthSecretRef) {
+func (in *AWSAuthSecretRef) DeepCopyInto(out *AWSAuthSecretRef) {
 	*out = *in
 	in.AccessKeyID.DeepCopyInto(&out.AccessKeyID)
 	in.SecretAccessKey.DeepCopyInto(&out.SecretAccessKey)
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSMAuthSecretRef.
-func (in *AWSSMAuthSecretRef) DeepCopy() *AWSSMAuthSecretRef {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSAuthSecretRef.
+func (in *AWSAuthSecretRef) DeepCopy() *AWSAuthSecretRef {
 	if in == nil {
 		return nil
 	}
-	out := new(AWSSMAuthSecretRef)
+	out := new(AWSAuthSecretRef)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AWSSMProvider) DeepCopyInto(out *AWSSMProvider) {
+func (in *AWSProvider) DeepCopyInto(out *AWSProvider) {
 	*out = *in
 	if in.Auth != nil {
 		in, out := &in.Auth, &out.Auth
-		*out = new(AWSSMAuth)
+		*out = new(AWSAuth)
 		(*in).DeepCopyInto(*out)
 	}
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSMProvider.
-func (in *AWSSMProvider) DeepCopy() *AWSSMProvider {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSProvider.
+func (in *AWSProvider) DeepCopy() *AWSProvider {
 	if in == nil {
 		return nil
 	}
-	out := new(AWSSMProvider)
+	out := new(AWSProvider)
 	in.DeepCopyInto(out)
 	return out
 }
@@ -417,9 +417,9 @@ 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)
+	if in.AWS != nil {
+		in, out := &in.AWS, &out.AWS
+		*out = new(AWSProvider)
 		(*in).DeepCopyInto(*out)
 	}
 }

+ 12 - 4
config/crd/bases/external-secrets.io_clustersecretstores.yaml

@@ -54,9 +54,9 @@ spec:
                 maxProperties: 1
                 minProperties: 1
                 properties:
-                  awssm:
-                    description: AWSSM configures this store to sync secrets using
-                      AWS Secret Manager provider
+                  aws:
+                    description: AWS configures this store to sync secrets using AWS
+                      Secret Manager provider
                     properties:
                       auth:
                         description: 'Auth defines the information necessary to authenticate
@@ -65,7 +65,7 @@ spec:
                         nullable: true
                         properties:
                           secretRef:
-                            description: AWSSMAuthSecretRef holds secret references
+                            description: AWSAuthSecretRef holds secret references
                               for aws credentials both AccessKeyID and SecretAccessKey
                               must be defined in order to properly authenticate.
                             properties:
@@ -124,8 +124,16 @@ spec:
                         description: Role is a Role ARN which the SecretManager provider
                           will assume
                         type: string
+                      service:
+                        description: Service defines which service should be used
+                          to fetch the secrets
+                        enum:
+                        - SecretsManager
+                        - ParameterStore
+                        type: string
                     required:
                     - region
+                    - service
                     type: object
                 type: object
             required:

+ 12 - 4
config/crd/bases/external-secrets.io_secretstores.yaml

@@ -54,9 +54,9 @@ spec:
                 maxProperties: 1
                 minProperties: 1
                 properties:
-                  awssm:
-                    description: AWSSM configures this store to sync secrets using
-                      AWS Secret Manager provider
+                  aws:
+                    description: AWS configures this store to sync secrets using AWS
+                      Secret Manager provider
                     properties:
                       auth:
                         description: 'Auth defines the information necessary to authenticate
@@ -65,7 +65,7 @@ spec:
                         nullable: true
                         properties:
                           secretRef:
-                            description: AWSSMAuthSecretRef holds secret references
+                            description: AWSAuthSecretRef holds secret references
                               for aws credentials both AccessKeyID and SecretAccessKey
                               must be defined in order to properly authenticate.
                             properties:
@@ -124,8 +124,16 @@ spec:
                         description: Role is a Role ARN which the SecretManager provider
                           will assume
                         type: string
+                      service:
+                        description: Service defines which service should be used
+                          to fetch the secrets
+                        enum:
+                        - SecretsManager
+                        - ParameterStore
+                        type: string
                     required:
                     - region
+                    - service
                     type: object
                 type: object
             required:

+ 4 - 4
config/samples/external-secrets_v1alpha1_secretstore.yaml

@@ -6,7 +6,10 @@ spec:
   controller: dev
 
   provider:
-    awssm:
+    aws:
+      service: SecretsManager
+      role: iam-role
+      region: eu-central-1
       auth:
         secretRef:
           accessKeyIDSecretRef:
@@ -16,6 +19,3 @@ spec:
           secretAccessKeySecretRef:
             name: awssm-secret
             key: secret-access-key
-
-      role: iam-role
-      region: eu-central-1

+ 1 - 1
docs/provider-aws-secrets-manager.md

@@ -9,7 +9,7 @@
 
 A `SecretStore` points to AWS Secrets Manager in a certain account within a
 defined region. You should define Roles that allow fine-grained access to
-individual secrets and pass them to ESO using `spec.provider.awssm.role`. This
+individual secrets and pass them to ESO using `spec.provider.aws.role`. This
 way users of the `SecretStore` can only access the secrets necessary.
 
 ``` yaml

+ 2 - 1
docs/snippets/aws-sm-store.yaml

@@ -5,7 +5,8 @@ metadata:
 spec:
   controller: dev
   provider:
-    awssm:
+    aws:
+      service: SecretsManager
       # define a specific role to limit access
       # to certain secrets
       role: iam-role

+ 2 - 1
docs/snippets/basic-secret-store.yaml

@@ -5,7 +5,8 @@ metadata:
 spec:
   controller: dev
   provider:
-    awssm:
+    aws:
+      service: SecretsManager
       role: arn:aws:iam::123456789012:role/team-a-reader
       region: us-east-1
       auth:

+ 4 - 3
docs/snippets/full-secret-store.yaml

@@ -1,7 +1,7 @@
 apiVerson: external-secrets.io/v1alpha1
 kind: SecretStore
 metadata:
-  name: vault
+  name: example
   namespace: example-ns
 spec:
 
@@ -15,8 +15,9 @@ spec:
   provider:
 
     # (1): AWS Secrets Manager
-    # AWSSM configures this store to sync secrets using AWS Secret Manager provider
-    awssm:
+    # aws configures this store to sync secrets using AWS Secret Manager provider
+    aws:
+      service: SecretsManager
       # Role is a Role ARN which the SecretManager provider will assume
       role: iam-role
       # AWS Region to be used for the provider

+ 73 - 47
docs/spec.md

@@ -10,14 +10,14 @@
 </p>
 Resource Types:
 <ul></ul>
-<h3 id="external-secrets.io/v1alpha1.AWSSMAuth">AWSSMAuth
+<h3 id="external-secrets.io/v1alpha1.AWSAuth">AWSAuth
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1alpha1.AWSSMProvider">AWSSMProvider</a>)
+<a href="#external-secrets.io/v1alpha1.AWSProvider">AWSProvider</a>)
 </p>
 <p>
-<p>AWSSMAuth contains a secretRef for credentials.</p>
+<p>AWSAuth contains a secretRef for credentials.</p>
 </p>
 <table>
 <thead>
@@ -31,8 +31,8 @@ Resource Types:
 <td>
 <code>secretRef</code></br>
 <em>
-<a href="#external-secrets.io/v1alpha1.AWSSMAuthSecretRef">
-AWSSMAuthSecretRef
+<a href="#external-secrets.io/v1alpha1.AWSAuthSecretRef">
+AWSAuthSecretRef
 </a>
 </em>
 </td>
@@ -41,14 +41,14 @@ AWSSMAuthSecretRef
 </tr>
 </tbody>
 </table>
-<h3 id="external-secrets.io/v1alpha1.AWSSMAuthSecretRef">AWSSMAuthSecretRef
+<h3 id="external-secrets.io/v1alpha1.AWSAuthSecretRef">AWSAuthSecretRef
 </h3>
 <p>
 (<em>Appears on:</em>
-<a href="#external-secrets.io/v1alpha1.AWSSMAuth">AWSSMAuth</a>)
+<a href="#external-secrets.io/v1alpha1.AWSAuth">AWSAuth</a>)
 </p>
 <p>
-<p>AWSSMAuthSecretRef holds secret references for aws credentials
+<p>AWSAuthSecretRef holds secret references for aws credentials
 both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.</p>
 </p>
 <table>
@@ -83,14 +83,14 @@ github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
 </tr>
 </tbody>
 </table>
-<h3 id="external-secrets.io/v1alpha1.AWSSMProvider">AWSSMProvider
+<h3 id="external-secrets.io/v1alpha1.AWSProvider">AWSProvider
 </h3>
 <p>
 (<em>Appears on:</em>
 <a href="#external-secrets.io/v1alpha1.SecretStoreProvider">SecretStoreProvider</a>)
 </p>
 <p>
-<p>AWSSMProvider configures a store to sync secrets using the AWS Secret Manager provider.</p>
+<p>AWSProvider configures a store to sync secrets using the AWS Secret Manager provider.</p>
 </p>
 <table>
 <thead>
@@ -102,10 +102,23 @@ github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
 <tbody>
 <tr>
 <td>
+<code>service</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.AWSServiceType">
+AWSServiceType
+</a>
+</em>
+</td>
+<td>
+<p>Service defines which service should be used to fetch the secrets</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>auth</code></br>
 <em>
-<a href="#external-secrets.io/v1alpha1.AWSSMAuth">
-AWSSMAuth
+<a href="#external-secrets.io/v1alpha1.AWSAuth">
+AWSAuth
 </a>
 </em>
 </td>
@@ -141,6 +154,32 @@ string
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1alpha1.AWSServiceType">AWSServiceType
+(<code>string</code> alias)</p></h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.AWSProvider">AWSProvider</a>)
+</p>
+<p>
+<p>AWSServiceType is a enum that defines the service/API that is used to fetch the secrets</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Value</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody><tr><td><p>&#34;ParameterStore&#34;</p></td>
+<td><p>AWSServiceParameterStore is the AWS SystemsManager ParameterStore
+see: <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html</a></p>
+</td>
+</tr><tr><td><p>&#34;SecretsManager&#34;</p></td>
+<td><p>AWSServiceSecretsManager is the AWS SecretsManager
+see: <a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html">https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html</a></p>
+</td>
+</tr></tbody>
+</table>
 <h3 id="external-secrets.io/v1alpha1.ClusterSecretStore">ClusterSecretStore
 </h3>
 <p>
@@ -280,15 +319,15 @@ ExternalSecretTarget
 <td>
 <code>refreshInterval</code></br>
 <em>
-string
+<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#duration-v1-meta">
+Kubernetes meta/v1.Duration
+</a>
 </em>
 </td>
 <td>
-<em>(Optional)</em>
-<p>RefreshInterval is the amount of time before the values reading again from the SecretStore provider
-Valid time units are &ldquo;ns&rdquo;, &ldquo;us&rdquo; (or &ldquo;µs&rdquo;), &ldquo;ms&rdquo;, &ldquo;s&rdquo;, &ldquo;m&rdquo;, &ldquo;h&rdquo; (from time.ParseDuration)
-May be set to zero to fetch and create it once
-TODO: Default to some value?</p>
+<p>RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+Valid time units are &ldquo;ns&rdquo;, &ldquo;us&rdquo; (or &ldquo;µs&rdquo;), &ldquo;ms&rdquo;, &ldquo;s&rdquo;, &ldquo;m&rdquo;, &ldquo;h&rdquo;
+May be set to zero to fetch and create it once. Defaults to 1h.</p>
 </td>
 </tr>
 <tr>
@@ -524,15 +563,15 @@ ExternalSecretTarget
 <td>
 <code>refreshInterval</code></br>
 <em>
-string
+<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#duration-v1-meta">
+Kubernetes meta/v1.Duration
+</a>
 </em>
 </td>
 <td>
-<em>(Optional)</em>
-<p>RefreshInterval is the amount of time before the values reading again from the SecretStore provider
-Valid time units are &ldquo;ns&rdquo;, &ldquo;us&rdquo; (or &ldquo;µs&rdquo;), &ldquo;ms&rdquo;, &ldquo;s&rdquo;, &ldquo;m&rdquo;, &ldquo;h&rdquo; (from time.ParseDuration)
-May be set to zero to fetch and create it once
-TODO: Default to some value?</p>
+<p>RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+Valid time units are &ldquo;ns&rdquo;, &ldquo;us&rdquo; (or &ldquo;µs&rdquo;), &ldquo;ms&rdquo;, &ldquo;s&rdquo;, &ldquo;m&rdquo;, &ldquo;h&rdquo;
+May be set to zero to fetch and create it once. Defaults to 1h.</p>
 </td>
 </tr>
 <tr>
@@ -825,6 +864,12 @@ map[string]string
 <p>GenericStore is a common interface for interacting with ClusterSecretStore
 or a namespaced SecretStore.</p>
 </p>
+<h3 id="external-secrets.io/v1alpha1.ProviderIdentity">ProviderIdentity
+</h3>
+<p>
+<p>ProviderIdentity returns the name of a secret store provider
+this interface must be implemented by every provider</p>
+</p>
 <h3 id="external-secrets.io/v1alpha1.SecretStore">SecretStore
 </h3>
 <p>
@@ -946,16 +991,16 @@ SecretStoreStatus
 <tbody>
 <tr>
 <td>
-<code>awssm</code></br>
+<code>aws</code></br>
 <em>
-<a href="#external-secrets.io/v1alpha1.AWSSMProvider">
-AWSSMProvider
+<a href="#external-secrets.io/v1alpha1.AWSProvider">
+AWSProvider
 </a>
 </em>
 </td>
 <td>
 <em>(Optional)</em>
-<p>AWSSM configures this store to sync secrets using AWS Secret Manager provider</p>
+<p>AWS configures this store to sync secrets using AWS Secret Manager provider</p>
 </td>
 </tr>
 </tbody>
@@ -1158,25 +1203,6 @@ Kubernetes meta/v1.Time
 </tr>
 </tbody>
 </table>
-<h3 id="external-secrets.io/v1alpha1.StoreProvider">StoreProvider
-(<code>string</code> alias)</p></h3>
-<p>
-</p>
-<table>
-<thead>
-<tr>
-<th>Value</th>
-<th>Description</th>
-</tr>
-</thead>
-<tbody><tr><td><p>&#34;AWSSM&#34;</p></td>
-<td></td>
-</tr><tr><td><p>&#34;GCPSM&#34;</p></td>
-<td></td>
-</tr><tr><td><p>&#34;VAULT&#34;</p></td>
-<td></td>
-</tr></tbody>
-</table>
 <hr/>
 <p><em>
 Generated with <code>gen-crd-api-reference-docs</code>.

+ 9 - 3
pkg/controllers/externalsecret/externalsecret_controller_test.go

@@ -56,7 +56,9 @@ var _ = Describe("ExternalSecret controller", func() {
 			},
 			Spec: esv1alpha1.SecretStoreSpec{
 				Provider: &esv1alpha1.SecretStoreProvider{
-					AWSSM: &esv1alpha1.AWSSMProvider{},
+					AWS: &esv1alpha1.AWSProvider{
+						Service: esv1alpha1.AWSServiceSecretsManager,
+					},
 				},
 			},
 		})).To(Succeed())
@@ -427,7 +429,9 @@ var _ = Describe("ExternalSecret controller", func() {
 				Spec: esv1alpha1.SecretStoreSpec{
 					Controller: "some-other-controller",
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{},
+						AWS: &esv1alpha1.AWSProvider{
+							Service: esv1alpha1.AWSServiceSecretsManager,
+						},
 					},
 				},
 			})).To(Succeed())
@@ -509,6 +513,8 @@ func CreateNamespace(baseName string, c client.Client) (string, error) {
 func init() {
 	fakeProvider = fake.New()
 	schema.ForceRegister(fakeProvider, &esv1alpha1.SecretStoreProvider{
-		AWSSM: &esv1alpha1.AWSSMProvider{},
+		AWS: &esv1alpha1.AWSProvider{
+			Service: esv1alpha1.AWSServiceSecretsManager,
+		},
 	})
 }

+ 60 - 0
pkg/provider/aws/parameterstore/parameterstore.go

@@ -0,0 +1,60 @@
+/*
+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 parameterstore
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go/service/ssm"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"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"
+	awssess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
+)
+
+// ParameterStore is a provider for AWS ParameterStore.
+type ParameterStore struct {
+	stsProvider awssess.STSProvider
+	// session     *session.Session
+	// client      PMInterface
+}
+
+// PMInterface is a subset of the parameterstore api.
+// see: https://docs.aws.amazon.com/sdk-for-go/api/service/ssm/ssmiface/
+type PMInterface interface {
+	GetParameter(*ssm.GetParameterInput) (*ssm.GetParameterOutput, error)
+}
+
+var log = ctrl.Log.WithName("provider").WithName("aws").WithName("parameterstore")
+
+// New constructs a ParameterStore Provider that is specific to a store.
+func New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string, stsProvider awssess.STSProvider) (provider.SecretsClient, error) {
+	pm := &ParameterStore{
+		stsProvider: stsProvider,
+	}
+	return pm, nil
+}
+
+// GetSecret returns a single secret from the provider.
+func (pm *ParameterStore) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
+	log.Info("fetching secret value", "key", ref.Key)
+	return []byte("NOOP"), nil
+}
+
+// GetSecretMap returns multiple k/v pairs from the provider.
+func (pm *ParameterStore) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+	return map[string][]byte{"NOOP": []byte("NOOP")}, nil
+}

+ 10 - 3
pkg/provider/aws/provider.go

@@ -8,6 +8,7 @@ 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/aws/parameterstore"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/secretsmanager"
 	awssess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
 	"github.com/external-secrets/external-secrets/pkg/provider/schema"
@@ -28,14 +29,20 @@ func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore,
 	if spec.Provider == nil {
 		return nil, fmt.Errorf("storeSpec is missing provider")
 	}
-	if spec.Provider.AWSSM != nil {
+	if spec.Provider.AWS == nil {
+		return nil, fmt.Errorf("storeSpec is missing aws spec")
+	}
+	switch spec.Provider.AWS.Service {
+	case esv1alpha1.AWSServiceSecretsManager:
 		return secretsmanager.New(ctx, store, kube, namespace, awssess.DefaultSTSProvider)
+	case esv1alpha1.AWSServiceParameterStore:
+		return parameterstore.New(ctx, store, kube, namespace, awssess.DefaultSTSProvider)
 	}
-	return nil, fmt.Errorf("AWS Provider spec missing")
+	return nil, fmt.Errorf("unknown AWS Provider Service: %s", spec.Provider.AWS.Service)
 }
 
 func init() {
 	schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
-		AWSSM: &esv1alpha1.AWSSMProvider{},
+		AWS: &esv1alpha1.AWSProvider{},
 	})
 }

+ 3 - 1
pkg/provider/aws/provider_test.go

@@ -47,7 +47,9 @@ func TestProvider(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{},
+						AWS: &esv1alpha1.AWSProvider{
+							Service: esv1alpha1.AWSServiceParameterStore,
+						},
 					},
 				},
 			},

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

@@ -60,7 +60,7 @@ func New(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client,
 	if spc.Provider == nil {
 		return nil, fmt.Errorf("storeSpec is missing provider")
 	}
-	smProvider := spc.Provider.AWSSM
+	smProvider := spc.Provider.AWS
 	if smProvider == nil {
 		return nil, fmt.Errorf("invalid provider spec. Missing AWSSM field in store %s", store.GetObjectMeta().String())
 	}

+ 22 - 22
pkg/provider/aws/secretsmanager/secretsmanager_test.go

@@ -71,7 +71,7 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{},
+						AWS: &esv1alpha1.AWSProvider{},
 					},
 				},
 			},
@@ -107,7 +107,7 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
+						AWS: &esv1alpha1.AWSProvider{
 							Role: "foo-bar-baz",
 						},
 					},
@@ -127,9 +127,9 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name: "othersecret",
 										Key:  "one",
@@ -152,9 +152,9 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name: "onesecret",
 										// Namespace is not set
@@ -193,9 +193,9 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name: "brokensecret",
 										Key:  "one",
@@ -227,9 +227,9 @@ func TestConstructor(t *testing.T) {
 			store: &esv1alpha1.SecretStore{
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name:      "onesecret",
 										Namespace: aws.String("evil"), // this should not be possible!
@@ -270,9 +270,9 @@ func TestConstructor(t *testing.T) {
 				},
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name:      "onesecret",
 										Namespace: aws.String("platform-team-ns"),
@@ -315,9 +315,9 @@ func TestConstructor(t *testing.T) {
 				},
 				Spec: esv1alpha1.SecretStoreSpec{
 					Provider: &esv1alpha1.SecretStoreProvider{
-						AWSSM: &esv1alpha1.AWSSMProvider{
-							Auth: &esv1alpha1.AWSSMAuth{
-								SecretRef: esv1alpha1.AWSSMAuthSecretRef{
+						AWS: &esv1alpha1.AWSProvider{
+							Auth: &esv1alpha1.AWSAuth{
+								SecretRef: esv1alpha1.AWSAuthSecretRef{
 									AccessKeyID: esmeta.SecretKeySelector{
 										Name: "onesecret",
 										Key:  "one",
@@ -397,7 +397,7 @@ func TestSMEnvCredentials(t *testing.T) {
 		Spec: esv1alpha1.SecretStoreSpec{
 			Provider: &esv1alpha1.SecretStoreProvider{
 				// defaults
-				AWSSM: &esv1alpha1.AWSSMProvider{},
+				AWS: &esv1alpha1.AWSProvider{},
 			},
 		},
 	}, k8sClient, "example-ns", awssess.DefaultSTSProvider)
@@ -439,7 +439,7 @@ func TestSMAssumeRole(t *testing.T) {
 		Spec: esv1alpha1.SecretStoreSpec{
 			Provider: &esv1alpha1.SecretStoreProvider{
 				// do assume role!
-				AWSSM: &esv1alpha1.AWSSMProvider{
+				AWS: &esv1alpha1.AWSProvider{
 					Role: "my-awesome-role",
 				},
 			},

+ 86 - 9
pkg/provider/schema/schema_test.go

@@ -41,25 +41,102 @@ func (p *PP) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretData
 	return map[string][]byte{}, nil
 }
 
+// TestRegister tests if the Register function
+// (1) panics if it tries to register something invalid
+// (2) stores the correct provider.
 func TestRegister(t *testing.T) {
-	p, ok := GetProviderByName("awssm")
-	assert.Nil(t, p)
-	assert.False(t, ok, "provider should not be registered")
+	tbl := []struct {
+		test      string
+		name      string
+		expPanic  bool
+		expExists bool
+		provider  *esv1alpha1.SecretStoreProvider
+	}{
+		{
+			test:      "should panic when given an invalid provider",
+			name:      "aws",
+			expPanic:  true,
+			expExists: false,
+			provider:  &esv1alpha1.SecretStoreProvider{},
+		},
+		{
+			test:      "should register an correct provider",
+			name:      "aws",
+			expExists: false,
+			provider: &esv1alpha1.SecretStoreProvider{
+				AWS: &esv1alpha1.AWSProvider{
+					Service: esv1alpha1.AWSServiceSecretsManager,
+				},
+			},
+		},
+		{
+			test:      "should panic if already exists",
+			name:      "aws",
+			expPanic:  true,
+			expExists: true,
+			provider: &esv1alpha1.SecretStoreProvider{
+				AWS: &esv1alpha1.AWSProvider{
+					Service: esv1alpha1.AWSServiceSecretsManager,
+				},
+			},
+		},
+	}
+	for i := range tbl {
+		row := tbl[i]
+		t.Run(row.test, func(t *testing.T) {
+			runTest(t,
+				row.name,
+				row.provider,
+				row.expPanic,
+			)
+		})
+	}
+}
 
+func runTest(t *testing.T, name string, provider *esv1alpha1.SecretStoreProvider, expPanic bool) {
 	testProvider := &PP{}
 	secretStore := &esv1alpha1.SecretStore{
 		Spec: esv1alpha1.SecretStoreSpec{
-			Provider: &esv1alpha1.SecretStoreProvider{
-				AWSSM: &esv1alpha1.AWSSMProvider{},
-			},
+			Provider: provider,
 		},
 	}
-
-	ForceRegister(testProvider, secretStore.Spec.Provider)
-	p1, ok := GetProviderByName("awssm")
+	if expPanic {
+		defer func() {
+			if r := recover(); r == nil {
+				t.Errorf("Register should panic")
+			}
+		}()
+	}
+	Register(testProvider, secretStore.Spec.Provider)
+	p1, ok := GetProviderByName(name)
 	assert.True(t, ok, "provider should be registered")
 	assert.Equal(t, testProvider, p1)
+	p2, err := GetProvider(secretStore)
+	assert.Nil(t, err)
+	assert.Equal(t, testProvider, p2)
+}
 
+// ForceRegister is used by other tests, we should ensure it works as expected.
+func TestForceRegister(t *testing.T) {
+	testProvider := &PP{}
+	provider := &esv1alpha1.SecretStoreProvider{
+		AWS: &esv1alpha1.AWSProvider{
+			Service: esv1alpha1.AWSServiceParameterStore,
+		},
+	}
+	secretStore := &esv1alpha1.SecretStore{
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: provider,
+		},
+	}
+	ForceRegister(testProvider, &esv1alpha1.SecretStoreProvider{
+		AWS: &esv1alpha1.AWSProvider{
+			Service: esv1alpha1.AWSServiceParameterStore,
+		},
+	})
+	p1, ok := GetProviderByName("aws")
+	assert.True(t, ok, "provider should be registered")
+	assert.Equal(t, testProvider, p1)
 	p2, err := GetProvider(secretStore)
 	assert.Nil(t, err)
 	assert.Equal(t, testProvider, p2)