|
|
@@ -17,18 +17,21 @@ package parameterstore
|
|
|
import (
|
|
|
"context"
|
|
|
"errors"
|
|
|
+ "fmt"
|
|
|
"strings"
|
|
|
"testing"
|
|
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
"github.com/aws/aws-sdk-go-v2/service/ssm"
|
|
|
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
|
|
|
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
"github.com/stretchr/testify/require"
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
+ "k8s.io/utils/ptr"
|
|
|
|
|
|
esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
|
|
|
fakeps "github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore/fake"
|
|
|
@@ -339,10 +342,12 @@ func TestPushSecret(t *testing.T) {
|
|
|
args: args{
|
|
|
store: makeValidParameterStore().Spec.Provider.AWS,
|
|
|
client: fakeps.Client{
|
|
|
- PutParameterFn: fakeps.NewPutParameterFn(putParameterOutput, nil),
|
|
|
- GetParameterFn: fakeps.NewGetParameterFn(getParameterOutput, nil),
|
|
|
- DescribeParametersFn: fakeps.NewDescribeParametersFn(describeParameterOutput, nil),
|
|
|
- ListTagsForResourceFn: fakeps.NewListTagsForResourceFn(validListTagsForResourceOutput, nil),
|
|
|
+ PutParameterFn: fakeps.NewPutParameterFn(putParameterOutput, nil),
|
|
|
+ GetParameterFn: fakeps.NewGetParameterFn(getParameterOutput, nil),
|
|
|
+ DescribeParametersFn: fakeps.NewDescribeParametersFn(describeParameterOutput, nil),
|
|
|
+ ListTagsForResourceFn: fakeps.NewListTagsForResourceFn(validListTagsForResourceOutput, nil),
|
|
|
+ RemoveTagsFromResourceFn: fakeps.NewRemoveTagsFromResourceFn(&ssm.RemoveTagsFromResourceOutput{}, nil),
|
|
|
+ AddTagsToResourceFn: fakeps.NewAddTagsToResourceFn(&ssm.AddTagsToResourceOutput{}, nil),
|
|
|
},
|
|
|
},
|
|
|
want: want{
|
|
|
@@ -586,11 +591,40 @@ func TestPushSecret(t *testing.T) {
|
|
|
err: errors.New("unable to compare 'sensitive' result, ensure to request a decrypted value"),
|
|
|
},
|
|
|
},
|
|
|
+ "SecretWithTags": {
|
|
|
+ reason: "test if we can configure tags for the secret",
|
|
|
+ args: args{
|
|
|
+ store: makeValidParameterStore().Spec.Provider.AWS,
|
|
|
+ metadata: &apiextensionsv1.JSON{
|
|
|
+ Raw: []byte(`{
|
|
|
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
|
|
|
+ "kind": "PushSecretMetadata",
|
|
|
+ "spec": {
|
|
|
+ "tags": {
|
|
|
+ "tagname1": "value1"
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }`),
|
|
|
+ },
|
|
|
+ client: fakeps.Client{
|
|
|
+ PutParameterFn: fakeps.NewPutParameterFn(putParameterOutput, nil),
|
|
|
+ GetParameterFn: fakeps.NewGetParameterFn(sameGetParameterOutput, nil),
|
|
|
+ DescribeParametersFn: fakeps.NewDescribeParametersFn(describeParameterOutput, nil),
|
|
|
+ ListTagsForResourceFn: fakeps.NewListTagsForResourceFn(&ssm.ListTagsForResourceOutput{
|
|
|
+ TagList: []ssmtypes.Tag{managedByESO, {Key: ptr.To("tagname1"), Value: ptr.To("value1")}},
|
|
|
+ }, nil),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ want: want{
|
|
|
+ err: nil,
|
|
|
+ },
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
for name, tc := range tests {
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
psd := fake.PushSecretData{SecretKey: fakeSecretKey, RemoteKey: remoteKey}
|
|
|
+ fmt.Println("line 635:", psd, tc.args.metadata)
|
|
|
if tc.args.metadata != nil {
|
|
|
psd.Metadata = tc.args.metadata
|
|
|
}
|
|
|
@@ -599,6 +633,8 @@ func TestPushSecret(t *testing.T) {
|
|
|
}
|
|
|
err := ps.PushSecret(context.TODO(), fakeSecret, psd)
|
|
|
|
|
|
+ fmt.Println("line 644:", err)
|
|
|
+
|
|
|
// Error nil XOR tc.want.err nil
|
|
|
if ((err == nil) || (tc.want.err == nil)) && !((err == nil) && (tc.want.err == nil)) {
|
|
|
t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error: %v", name, tc.reason, tc.want.err, err)
|
|
|
@@ -806,7 +842,7 @@ func TestGetSecret(t *testing.T) {
|
|
|
TagList: getTagSlice(),
|
|
|
}
|
|
|
pstc.fakeClient.ListTagsForResourceFn = fakeps.NewListTagsForResourceFn(&output, nil)
|
|
|
- pstc.expectedSecret, _ = util.ParameterTagsToJSONString(getTagSlice())
|
|
|
+ pstc.expectedSecret, _ = util.ParameterTagsToJSONString(normaliseTags(getTagSlice()))
|
|
|
}
|
|
|
|
|
|
// good case: metadata property returned
|
|
|
@@ -952,6 +988,16 @@ func getTagSlice() []ssmtypes.Tag {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func normaliseTags(input []ssmtypes.Tag) map[string]string {
|
|
|
+ tags := make(map[string]string, len(input))
|
|
|
+ for _, tag := range input {
|
|
|
+ if tag.Key != nil && tag.Value != nil {
|
|
|
+ tags[*tag.Key] = *tag.Value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return tags
|
|
|
+}
|
|
|
+
|
|
|
func TestSecretExists(t *testing.T) {
|
|
|
parameterOutput := &ssm.GetParameterOutput{
|
|
|
Parameter: &ssmtypes.Parameter{
|
|
|
@@ -1038,3 +1084,248 @@ func TestSecretExists(t *testing.T) {
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func TestConstructMetadataWithDefaults(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ input *apiextensionsv1.JSON
|
|
|
+ expected *metadata.PushSecretMetadata[PushSecretMetadataSpec]
|
|
|
+ expectError bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "Valid metadata with multiple fields",
|
|
|
+ input: &apiextensionsv1.JSON{Raw: []byte(`{
|
|
|
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
|
|
|
+ "kind": "PushSecretMetadata",
|
|
|
+ "spec": {
|
|
|
+ "description": "test description",
|
|
|
+ "tier": {"type": "Advanced"},
|
|
|
+ "secretType":"SecureString",
|
|
|
+ "kmsKeyID": "custom-kms-key",
|
|
|
+ "tags": {
|
|
|
+ "customKey": "customValue"
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }`)},
|
|
|
+ expected: &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
|
|
|
+ APIVersion: "kubernetes.external-secrets.io/v1alpha1",
|
|
|
+ Kind: "PushSecretMetadata",
|
|
|
+ Spec: PushSecretMetadataSpec{
|
|
|
+ Description: "test description",
|
|
|
+ Tier: Tier{
|
|
|
+ Type: "Advanced",
|
|
|
+ },
|
|
|
+ SecretType: "SecureString",
|
|
|
+ KMSKeyID: "custom-kms-key",
|
|
|
+ Tags: map[string]string{
|
|
|
+ "customKey": "customValue",
|
|
|
+ "managed-by": "external-secrets",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty metadata, defaults applied",
|
|
|
+ input: nil,
|
|
|
+ expected: &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
|
|
|
+ Spec: PushSecretMetadataSpec{
|
|
|
+ Description: "secret 'managed-by:external-secrets'",
|
|
|
+ Tier: Tier{
|
|
|
+ Type: "Standard",
|
|
|
+ },
|
|
|
+ SecretType: "String",
|
|
|
+ KMSKeyID: "alias/aws/ssm",
|
|
|
+ Tags: map[string]string{
|
|
|
+ "managed-by": "external-secrets",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Added default metadata with 'managed-by' tag",
|
|
|
+ input: &apiextensionsv1.JSON{Raw: []byte(`{
|
|
|
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
|
|
|
+ "kind": "PushSecretMetadata",
|
|
|
+ "spec": {
|
|
|
+ "description": "adding managed-by tag explicitly",
|
|
|
+ "tags": {
|
|
|
+ "managed-by": "external-secrets",
|
|
|
+ "customKey": "customValue"
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }`)},
|
|
|
+ expectError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Invalid metadata format",
|
|
|
+ input: &apiextensionsv1.JSON{Raw: []byte(`invalid-json`)},
|
|
|
+ expected: nil,
|
|
|
+ expectError: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Metadata with 'managed-by' tag specified",
|
|
|
+ input: &apiextensionsv1.JSON{Raw: []byte(`{"tags":{"managed-by":"invalid"}}`)},
|
|
|
+ expected: nil,
|
|
|
+ expectError: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ result, err := (&ParameterStore{}).constructMetadataWithDefaults(tt.input)
|
|
|
+
|
|
|
+ if tt.expectError {
|
|
|
+ assert.Error(t, err)
|
|
|
+ } else {
|
|
|
+ assert.NoError(t, err)
|
|
|
+ assert.Equal(t, tt.expected, result)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestFindTagKeysToRemove(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ tags map[string]string
|
|
|
+ metaTags map[string]string
|
|
|
+ expected []string
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "No tags to remove",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ expected: []string{},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Some tags to remove",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ "key3": "value3",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ expected: []string{"key3"},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "All tags to remove",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{},
|
|
|
+ expected: []string{"key1", "key2"},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty tags and metaTags",
|
|
|
+ tags: map[string]string{},
|
|
|
+ metaTags: map[string]string{},
|
|
|
+ expected: []string{},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty metaTags with non-empty tags",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{},
|
|
|
+ expected: []string{"key1"},
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ result := findTagKeysToRemove(tt.tags, tt.metaTags)
|
|
|
+ assert.ElementsMatch(t, tt.expected, result)
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestComputeTagsToUpdate(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ name string
|
|
|
+ tags map[string]string
|
|
|
+ metaTags map[string]string
|
|
|
+ expected []ssmtypes.Tag
|
|
|
+ modified bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "No tags to update",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ expected: []ssmtypes.Tag{
|
|
|
+ {Key: ptr.To("key1"), Value: ptr.To("value1")},
|
|
|
+ {Key: ptr.To("key2"), Value: ptr.To("value2")},
|
|
|
+ },
|
|
|
+ modified: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Add new tag",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ "key2": "value2",
|
|
|
+ },
|
|
|
+ expected: []ssmtypes.Tag{
|
|
|
+ {Key: ptr.To("key1"), Value: ptr.To("value1")},
|
|
|
+ {Key: ptr.To("key2"), Value: ptr.To("value2")},
|
|
|
+ },
|
|
|
+ modified: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Update existing tag value",
|
|
|
+ tags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ },
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "newValue",
|
|
|
+ },
|
|
|
+ expected: []ssmtypes.Tag{
|
|
|
+ {Key: ptr.To("key1"), Value: ptr.To("newValue")},
|
|
|
+ },
|
|
|
+ modified: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty tags and metaTags",
|
|
|
+ tags: map[string]string{},
|
|
|
+ metaTags: map[string]string{},
|
|
|
+ expected: []ssmtypes.Tag{},
|
|
|
+ modified: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty tags with non-empty metaTags",
|
|
|
+ tags: map[string]string{},
|
|
|
+ metaTags: map[string]string{
|
|
|
+ "key1": "value1",
|
|
|
+ },
|
|
|
+ expected: []ssmtypes.Tag{
|
|
|
+ {Key: ptr.To("key1"), Value: ptr.To("value1")},
|
|
|
+ },
|
|
|
+ modified: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tt := range tests {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ result, modified := computeTagsToUpdate(tt.tags, tt.metaTags)
|
|
|
+ assert.ElementsMatch(t, tt.expected, result)
|
|
|
+ assert.Equal(t, tt.modified, modified)
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|