parameterstore_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package parameterstore
  13. import (
  14. "context"
  15. "errors"
  16. "fmt"
  17. "strings"
  18. "testing"
  19. "github.com/aws/aws-sdk-go/aws"
  20. "github.com/aws/aws-sdk-go/aws/awserr"
  21. "github.com/aws/aws-sdk-go/service/ssm"
  22. "github.com/google/go-cmp/cmp"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  25. fakeps "github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore/fake"
  26. "github.com/external-secrets/external-secrets/pkg/provider/aws/util"
  27. )
  28. const (
  29. errInvalidProperty = "key INVALPROP does not exist in secret"
  30. invalidProp = "INVALPROP"
  31. )
  32. type parameterstoreTestCase struct {
  33. fakeClient *fakeps.Client
  34. apiInput *ssm.GetParameterInput
  35. apiOutput *ssm.GetParameterOutput
  36. remoteRef *esv1beta1.ExternalSecretDataRemoteRef
  37. apiErr error
  38. expectError string
  39. expectedSecret string
  40. expectedData map[string][]byte
  41. }
  42. type fakeRef struct {
  43. key string
  44. }
  45. func (f fakeRef) GetRemoteKey() string {
  46. return f.key
  47. }
  48. func (f fakeRef) GetProperty() string {
  49. return ""
  50. }
  51. func makeValidParameterStoreTestCase() *parameterstoreTestCase {
  52. return &parameterstoreTestCase{
  53. fakeClient: &fakeps.Client{},
  54. apiInput: makeValidAPIInput(),
  55. apiOutput: makeValidAPIOutput(),
  56. remoteRef: makeValidRemoteRef(),
  57. apiErr: nil,
  58. expectError: "",
  59. expectedSecret: "",
  60. expectedData: make(map[string][]byte),
  61. }
  62. }
  63. func makeValidAPIInput() *ssm.GetParameterInput {
  64. return &ssm.GetParameterInput{
  65. Name: aws.String("/baz"),
  66. WithDecryption: aws.Bool(true),
  67. }
  68. }
  69. func makeValidAPIOutput() *ssm.GetParameterOutput {
  70. return &ssm.GetParameterOutput{
  71. Parameter: &ssm.Parameter{
  72. Value: aws.String("RRRRR"),
  73. },
  74. }
  75. }
  76. func makeValidRemoteRef() *esv1beta1.ExternalSecretDataRemoteRef {
  77. return &esv1beta1.ExternalSecretDataRemoteRef{
  78. Key: "/baz",
  79. }
  80. }
  81. func makeValidParameterStoreTestCaseCustom(tweaks ...func(pstc *parameterstoreTestCase)) *parameterstoreTestCase {
  82. pstc := makeValidParameterStoreTestCase()
  83. for _, fn := range tweaks {
  84. fn(pstc)
  85. }
  86. pstc.fakeClient.WithValue(pstc.apiInput, pstc.apiOutput, pstc.apiErr)
  87. return pstc
  88. }
  89. func TestDeleteSecret(t *testing.T) {
  90. fakeClient := fakeps.Client{}
  91. parameterName := "parameter"
  92. managedBy := "managed-by"
  93. manager := "external-secrets"
  94. ssmTag := ssm.Tag{
  95. Key: &managedBy,
  96. Value: &manager,
  97. }
  98. type args struct {
  99. client fakeps.Client
  100. getParameterOutput *ssm.GetParameterOutput
  101. listTagsOutput *ssm.ListTagsForResourceOutput
  102. deleteParameterOutput *ssm.DeleteParameterOutput
  103. getParameterError error
  104. listTagsError error
  105. deleteParameterError error
  106. }
  107. type want struct {
  108. err error
  109. }
  110. type testCase struct {
  111. args args
  112. want want
  113. reason string
  114. }
  115. tests := map[string]testCase{
  116. "Deletes Successfully": {
  117. args: args{
  118. client: fakeClient,
  119. getParameterOutput: &ssm.GetParameterOutput{
  120. Parameter: &ssm.Parameter{
  121. Name: &parameterName,
  122. },
  123. },
  124. listTagsOutput: &ssm.ListTagsForResourceOutput{
  125. TagList: []*ssm.Tag{&ssmTag},
  126. },
  127. deleteParameterOutput: nil,
  128. getParameterError: nil,
  129. listTagsError: nil,
  130. deleteParameterError: nil,
  131. },
  132. want: want{
  133. err: nil,
  134. },
  135. reason: "",
  136. },
  137. "Secret Not Found": {
  138. args: args{
  139. client: fakeClient,
  140. getParameterOutput: nil,
  141. listTagsOutput: nil,
  142. deleteParameterOutput: nil,
  143. getParameterError: awserr.New(ssm.ErrCodeParameterNotFound, "not here, sorry dude", nil),
  144. listTagsError: nil,
  145. deleteParameterError: nil,
  146. },
  147. want: want{
  148. err: nil,
  149. },
  150. reason: "",
  151. },
  152. "No permissions to get secret": {
  153. args: args{
  154. client: fakeClient,
  155. getParameterOutput: nil,
  156. listTagsOutput: nil,
  157. deleteParameterOutput: nil,
  158. getParameterError: errors.New("no permissions"),
  159. listTagsError: nil,
  160. deleteParameterError: nil,
  161. },
  162. want: want{
  163. err: errors.New("no permissions"),
  164. },
  165. reason: "",
  166. },
  167. "No permissions to get tags": {
  168. args: args{
  169. client: fakeClient,
  170. getParameterOutput: &ssm.GetParameterOutput{
  171. Parameter: &ssm.Parameter{
  172. Name: &parameterName,
  173. },
  174. },
  175. listTagsOutput: nil,
  176. deleteParameterOutput: nil,
  177. getParameterError: nil,
  178. listTagsError: errors.New("no permissions"),
  179. deleteParameterError: nil,
  180. },
  181. want: want{
  182. err: errors.New("no permissions"),
  183. },
  184. reason: "",
  185. },
  186. "Secret Not Managed by External Secrets": {
  187. args: args{
  188. client: fakeClient,
  189. getParameterOutput: &ssm.GetParameterOutput{
  190. Parameter: &ssm.Parameter{
  191. Name: &parameterName,
  192. },
  193. },
  194. listTagsOutput: &ssm.ListTagsForResourceOutput{
  195. TagList: []*ssm.Tag{},
  196. },
  197. deleteParameterOutput: nil,
  198. getParameterError: nil,
  199. listTagsError: nil,
  200. deleteParameterError: nil,
  201. },
  202. want: want{
  203. err: nil,
  204. },
  205. reason: "",
  206. },
  207. "No permissions delete secret": {
  208. args: args{
  209. client: fakeClient,
  210. getParameterOutput: &ssm.GetParameterOutput{
  211. Parameter: &ssm.Parameter{
  212. Name: &parameterName,
  213. },
  214. },
  215. listTagsOutput: &ssm.ListTagsForResourceOutput{
  216. TagList: []*ssm.Tag{&ssmTag},
  217. },
  218. deleteParameterOutput: nil,
  219. getParameterError: nil,
  220. listTagsError: nil,
  221. deleteParameterError: errors.New("no permissions"),
  222. },
  223. want: want{
  224. err: errors.New("no permissions"),
  225. },
  226. reason: "",
  227. },
  228. }
  229. for name, tc := range tests {
  230. t.Run(name, func(t *testing.T) {
  231. ref := fakeRef{key: "fake-key"}
  232. ps := ParameterStore{
  233. client: &tc.args.client,
  234. }
  235. tc.args.client.GetParameterWithContextFn = fakeps.NewGetParameterWithContextFn(tc.args.getParameterOutput, tc.args.getParameterError)
  236. tc.args.client.ListTagsForResourceWithContextFn = fakeps.NewListTagsForResourceWithContextFn(tc.args.listTagsOutput, tc.args.listTagsError)
  237. tc.args.client.DeleteParameterWithContextFn = fakeps.NewDeleteParameterWithContextFn(tc.args.deleteParameterOutput, tc.args.deleteParameterError)
  238. err := ps.DeleteSecret(context.TODO(), ref)
  239. // Error nil XOR tc.want.err nil
  240. if ((err == nil) || (tc.want.err == nil)) && !((err == nil) && (tc.want.err == nil)) {
  241. t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error: %v", name, tc.reason, tc.want.err, err)
  242. }
  243. // if errors are the same type but their contents do not match
  244. if err != nil && tc.want.err != nil {
  245. if !strings.Contains(err.Error(), tc.want.err.Error()) {
  246. t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error got nil", name, tc.reason, tc.want.err)
  247. }
  248. }
  249. })
  250. }
  251. }
  252. func TestPushSecret(t *testing.T) {
  253. invalidParameters := errors.New(ssm.ErrCodeInvalidParameters)
  254. alreadyExistsError := errors.New(ssm.ErrCodeAlreadyExistsException)
  255. fakeValue := "fakeValue"
  256. managedByESO := ssm.Tag{
  257. Key: &managedBy,
  258. Value: &externalSecrets,
  259. }
  260. putParameterOutput := &ssm.PutParameterOutput{}
  261. getParameterOutput := &ssm.GetParameterOutput{}
  262. describeParameterOutput := &ssm.DescribeParametersOutput{}
  263. validListTagsForResourceOutput := &ssm.ListTagsForResourceOutput{
  264. TagList: []*ssm.Tag{&managedByESO},
  265. }
  266. noTagsResourceOutput := &ssm.ListTagsForResourceOutput{}
  267. validGetParameterOutput := &ssm.GetParameterOutput{
  268. Parameter: &ssm.Parameter{
  269. ARN: nil,
  270. DataType: nil,
  271. LastModifiedDate: nil,
  272. Name: nil,
  273. Selector: nil,
  274. SourceResult: nil,
  275. Type: nil,
  276. Value: nil,
  277. Version: nil,
  278. },
  279. }
  280. sameGetParameterOutput := &ssm.GetParameterOutput{
  281. Parameter: &ssm.Parameter{
  282. Value: &fakeValue,
  283. },
  284. }
  285. type args struct {
  286. store *esv1beta1.AWSProvider
  287. client fakeps.Client
  288. }
  289. type want struct {
  290. err error
  291. }
  292. tests := map[string]struct {
  293. reason string
  294. args args
  295. want want
  296. }{
  297. "PutParameterSucceeds": {
  298. reason: "a parameter can be successfully pushed to aws parameter store",
  299. args: args{
  300. store: makeValidParameterStore().Spec.Provider.AWS,
  301. client: fakeps.Client{
  302. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  303. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(getParameterOutput, nil),
  304. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  305. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
  306. },
  307. },
  308. want: want{
  309. err: nil,
  310. },
  311. },
  312. "SetParameterFailsWhenNoNameProvided": {
  313. reason: "test push secret with no name gives error",
  314. args: args{
  315. store: makeValidParameterStore().Spec.Provider.AWS,
  316. client: fakeps.Client{
  317. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  318. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(getParameterOutput, invalidParameters),
  319. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  320. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
  321. },
  322. },
  323. want: want{
  324. err: invalidParameters,
  325. },
  326. },
  327. "SetSecretWhenAlreadyExists": {
  328. reason: "test push secret with secret that already exists gives error",
  329. args: args{
  330. store: makeValidParameterStore().Spec.Provider.AWS,
  331. client: fakeps.Client{
  332. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, alreadyExistsError),
  333. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(getParameterOutput, nil),
  334. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  335. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
  336. },
  337. },
  338. want: want{
  339. err: alreadyExistsError,
  340. },
  341. },
  342. "GetSecretWithValidParameters": {
  343. reason: "Get secret with valid parameters",
  344. args: args{
  345. store: makeValidParameterStore().Spec.Provider.AWS,
  346. client: fakeps.Client{
  347. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  348. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(validGetParameterOutput, nil),
  349. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  350. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
  351. },
  352. },
  353. want: want{
  354. err: nil,
  355. },
  356. },
  357. "SetSecretNotManagedByESO": {
  358. reason: "SetSecret to the parameter store but tags are not managed by ESO",
  359. args: args{
  360. store: makeValidParameterStore().Spec.Provider.AWS,
  361. client: fakeps.Client{
  362. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  363. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(validGetParameterOutput, nil),
  364. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  365. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(noTagsResourceOutput, nil),
  366. },
  367. },
  368. want: want{
  369. err: fmt.Errorf("secret not managed by external-secrets"),
  370. },
  371. },
  372. "SetSecretGetTagsError": {
  373. reason: "SetSecret to the parameter store returns error while obtaining tags",
  374. args: args{
  375. store: makeValidParameterStore().Spec.Provider.AWS,
  376. client: fakeps.Client{
  377. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  378. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(validGetParameterOutput, nil),
  379. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  380. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(nil, fmt.Errorf("you shall not tag")),
  381. },
  382. },
  383. want: want{
  384. err: fmt.Errorf("you shall not tag"),
  385. },
  386. },
  387. "SetSecretContentMatches": {
  388. reason: "No ops",
  389. args: args{
  390. store: makeValidParameterStore().Spec.Provider.AWS,
  391. client: fakeps.Client{
  392. PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
  393. GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(sameGetParameterOutput, nil),
  394. DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
  395. ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
  396. },
  397. },
  398. want: want{
  399. err: nil,
  400. },
  401. },
  402. }
  403. for name, tc := range tests {
  404. t.Run(name, func(t *testing.T) {
  405. ref := fakeRef{key: "fake-key"}
  406. ps := ParameterStore{
  407. client: &tc.args.client,
  408. }
  409. err := ps.PushSecret(context.TODO(), []byte(fakeValue), ref)
  410. // Error nil XOR tc.want.err nil
  411. if ((err == nil) || (tc.want.err == nil)) && !((err == nil) && (tc.want.err == nil)) {
  412. t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error: %v", name, tc.reason, tc.want.err, err)
  413. }
  414. // if errors are the same type but their contents do not match
  415. if err != nil && tc.want.err != nil {
  416. if !strings.Contains(err.Error(), tc.want.err.Error()) {
  417. t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error got nil", name, tc.reason, tc.want.err)
  418. }
  419. }
  420. })
  421. }
  422. }
  423. // test the ssm<->aws interface
  424. // make sure correct values are passed and errors are handled accordingly.
  425. func TestGetSecret(t *testing.T) {
  426. // good case: key is passed in, output is sent back
  427. setSecretString := func(pstc *parameterstoreTestCase) {
  428. pstc.apiOutput.Parameter.Value = aws.String("RRRRR")
  429. pstc.expectedSecret = "RRRRR"
  430. }
  431. // good case: extract property
  432. setExtractProperty := func(pstc *parameterstoreTestCase) {
  433. pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo": "bang"}`)
  434. pstc.expectedSecret = "bang"
  435. pstc.remoteRef.Property = "/shmoo"
  436. }
  437. // good case: extract property with `.`
  438. setExtractPropertyWithDot := func(pstc *parameterstoreTestCase) {
  439. pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo.boom": "bang"}`)
  440. pstc.expectedSecret = "bang"
  441. pstc.remoteRef.Property = "/shmoo.boom"
  442. }
  443. // bad case: missing property
  444. setMissingProperty := func(pstc *parameterstoreTestCase) {
  445. pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo": "bang"}`)
  446. pstc.remoteRef.Property = "INVALPROP"
  447. pstc.expectError = "key INVALPROP does not exist in secret"
  448. }
  449. // bad case: parameter.Value not found
  450. setParameterValueNotFound := func(pstc *parameterstoreTestCase) {
  451. pstc.apiOutput.Parameter.Value = aws.String("NONEXISTENT")
  452. pstc.apiErr = esv1beta1.NoSecretErr
  453. pstc.expectError = "Secret does not exist"
  454. }
  455. // bad case: extract property failure due to invalid json
  456. setPropertyFail := func(pstc *parameterstoreTestCase) {
  457. pstc.apiOutput.Parameter.Value = aws.String(`------`)
  458. pstc.remoteRef.Property = invalidProp
  459. pstc.expectError = errInvalidProperty
  460. }
  461. // bad case: parameter.Value may be nil but binary is set
  462. setParameterValueNil := func(pstc *parameterstoreTestCase) {
  463. pstc.apiOutput.Parameter.Value = nil
  464. pstc.expectError = "parameter value is nil for key"
  465. }
  466. // base case: api output return error
  467. setAPIError := func(pstc *parameterstoreTestCase) {
  468. pstc.apiOutput = &ssm.GetParameterOutput{}
  469. pstc.apiErr = fmt.Errorf("oh no")
  470. pstc.expectError = "oh no"
  471. }
  472. // good case: metadata returned
  473. setMetadataString := func(pstc *parameterstoreTestCase) {
  474. pstc.remoteRef.MetadataPolicy = esv1beta1.ExternalSecretMetadataPolicyFetch
  475. output := ssm.ListTagsForResourceOutput{
  476. TagList: getTagSlice(),
  477. }
  478. pstc.fakeClient.ListTagsForResourceWithContextFn = fakeps.NewListTagsForResourceWithContextFn(&output, nil)
  479. pstc.expectedSecret, _ = util.ParameterTagsToJSONString(getTagSlice())
  480. }
  481. // good case: metadata property returned
  482. setMetadataProperty := func(pstc *parameterstoreTestCase) {
  483. pstc.remoteRef.MetadataPolicy = esv1beta1.ExternalSecretMetadataPolicyFetch
  484. output := ssm.ListTagsForResourceOutput{
  485. TagList: getTagSlice(),
  486. }
  487. pstc.fakeClient.ListTagsForResourceWithContextFn = fakeps.NewListTagsForResourceWithContextFn(&output, nil)
  488. pstc.remoteRef.Property = "tagname2"
  489. pstc.expectedSecret = "tagvalue2"
  490. }
  491. // bad case: metadata property not found
  492. setMetadataMissingProperty := func(pstc *parameterstoreTestCase) {
  493. pstc.remoteRef.MetadataPolicy = esv1beta1.ExternalSecretMetadataPolicyFetch
  494. output := ssm.ListTagsForResourceOutput{
  495. TagList: getTagSlice(),
  496. }
  497. pstc.fakeClient.ListTagsForResourceWithContextFn = fakeps.NewListTagsForResourceWithContextFn(&output, nil)
  498. pstc.remoteRef.Property = invalidProp
  499. pstc.expectError = errInvalidProperty
  500. }
  501. successCases := []*parameterstoreTestCase{
  502. makeValidParameterStoreTestCaseCustom(setSecretString),
  503. makeValidParameterStoreTestCaseCustom(setExtractProperty),
  504. makeValidParameterStoreTestCaseCustom(setMissingProperty),
  505. makeValidParameterStoreTestCaseCustom(setPropertyFail),
  506. makeValidParameterStoreTestCaseCustom(setParameterValueNil),
  507. makeValidParameterStoreTestCaseCustom(setAPIError),
  508. makeValidParameterStoreTestCaseCustom(setExtractPropertyWithDot),
  509. makeValidParameterStoreTestCaseCustom(setParameterValueNotFound),
  510. makeValidParameterStoreTestCaseCustom(setMetadataString),
  511. makeValidParameterStoreTestCaseCustom(setMetadataProperty),
  512. makeValidParameterStoreTestCaseCustom(setMetadataMissingProperty),
  513. }
  514. ps := ParameterStore{}
  515. for k, v := range successCases {
  516. ps.client = v.fakeClient
  517. out, err := ps.GetSecret(context.Background(), *v.remoteRef)
  518. if !ErrorContains(err, v.expectError) {
  519. t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
  520. }
  521. if cmp.Equal(out, v.expectedSecret) {
  522. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedSecret, out)
  523. }
  524. }
  525. }
  526. func TestGetSecretMap(t *testing.T) {
  527. // good case: default version & deserialization
  528. simpleJSON := func(pstc *parameterstoreTestCase) {
  529. pstc.apiOutput.Parameter.Value = aws.String(`{"foo":"bar"}`)
  530. pstc.expectedData["foo"] = []byte("bar")
  531. }
  532. // good case: default version & complex json
  533. complexJSON := func(pstc *parameterstoreTestCase) {
  534. pstc.apiOutput.Parameter.Value = aws.String(`{"int": 42, "str": "str", "nested": {"foo":"bar"}}`)
  535. pstc.expectedData["int"] = []byte("42")
  536. pstc.expectedData["str"] = []byte("str")
  537. pstc.expectedData["nested"] = []byte(`{"foo":"bar"}`)
  538. }
  539. // bad case: api error returned
  540. setAPIError := func(pstc *parameterstoreTestCase) {
  541. pstc.apiOutput.Parameter = &ssm.Parameter{}
  542. pstc.expectError = "some api err"
  543. pstc.apiErr = fmt.Errorf("some api err")
  544. }
  545. // bad case: invalid json
  546. setInvalidJSON := func(pstc *parameterstoreTestCase) {
  547. pstc.apiOutput.Parameter.Value = aws.String(`-----------------`)
  548. pstc.expectError = "unable to unmarshal secret"
  549. }
  550. successCases := []*parameterstoreTestCase{
  551. makeValidParameterStoreTestCaseCustom(simpleJSON),
  552. makeValidParameterStoreTestCaseCustom(complexJSON),
  553. makeValidParameterStoreTestCaseCustom(setAPIError),
  554. makeValidParameterStoreTestCaseCustom(setInvalidJSON),
  555. }
  556. ps := ParameterStore{}
  557. for k, v := range successCases {
  558. ps.client = v.fakeClient
  559. out, err := ps.GetSecretMap(context.Background(), *v.remoteRef)
  560. if !ErrorContains(err, v.expectError) {
  561. t.Errorf("[%d] unexpected error: %q, expected: %q", k, err.Error(), v.expectError)
  562. }
  563. if err == nil && !cmp.Equal(out, v.expectedData) {
  564. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
  565. }
  566. }
  567. }
  568. func makeValidParameterStore() *esv1beta1.SecretStore {
  569. return &esv1beta1.SecretStore{
  570. ObjectMeta: metav1.ObjectMeta{
  571. Name: "aws-parameterstore",
  572. Namespace: "default",
  573. },
  574. Spec: esv1beta1.SecretStoreSpec{
  575. Provider: &esv1beta1.SecretStoreProvider{
  576. AWS: &esv1beta1.AWSProvider{
  577. Service: esv1beta1.AWSServiceParameterStore,
  578. Region: "us-east-1",
  579. },
  580. },
  581. },
  582. }
  583. }
  584. func ErrorContains(out error, want string) bool {
  585. if out == nil {
  586. return want == ""
  587. }
  588. if want == "" {
  589. return false
  590. }
  591. return strings.Contains(out.Error(), want)
  592. }
  593. func getTagSlice() []*ssm.Tag {
  594. tagKey1 := "tagname1"
  595. tagValue1 := "tagvalue1"
  596. tagKey2 := "tagname2"
  597. tagValue2 := "tagvalue2"
  598. return []*ssm.Tag{
  599. {
  600. Key: &tagKey1,
  601. Value: &tagValue1,
  602. },
  603. {
  604. Key: &tagKey2,
  605. Value: &tagValue2,
  606. },
  607. }
  608. }