parameterstore_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. "fmt"
  16. "strings"
  17. "testing"
  18. "github.com/aws/aws-sdk-go/aws"
  19. "github.com/aws/aws-sdk-go/service/ssm"
  20. "github.com/google/go-cmp/cmp"
  21. "github.com/stretchr/testify/assert"
  22. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  23. fake "github.com/external-secrets/external-secrets/pkg/provider/aws/parameterstore/fake"
  24. sess "github.com/external-secrets/external-secrets/pkg/provider/aws/session"
  25. )
  26. func TestConstructor(t *testing.T) {
  27. s, err := sess.New("1111", "2222", "foo", "", nil)
  28. assert.Nil(t, err)
  29. c, err := New(s)
  30. assert.Nil(t, err)
  31. assert.NotNil(t, c.client)
  32. }
  33. // test the ssm<->aws interface
  34. // make sure correct values are passed and errors are handled accordingly.
  35. func TestGetSecret(t *testing.T) {
  36. f := &fake.Client{}
  37. p := &ParameterStore{
  38. client: f,
  39. }
  40. for i, row := range []struct {
  41. apiInput *ssm.GetParameterInput
  42. apiOutput *ssm.GetParameterOutput
  43. rr esv1alpha1.ExternalSecretDataRemoteRef
  44. apiErr error
  45. expectError string
  46. expectedSecret string
  47. }{
  48. {
  49. // good case: key is passed in, output is sent back
  50. apiInput: &ssm.GetParameterInput{
  51. Name: aws.String("/baz"),
  52. WithDecryption: aws.Bool(true),
  53. },
  54. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  55. Key: "/baz",
  56. },
  57. apiOutput: &ssm.GetParameterOutput{
  58. Parameter: &ssm.Parameter{
  59. Value: aws.String("RRRRR"),
  60. },
  61. },
  62. apiErr: nil,
  63. expectError: "",
  64. expectedSecret: "RRRRR",
  65. },
  66. {
  67. // good case: extract property
  68. apiInput: &ssm.GetParameterInput{
  69. Name: aws.String("/baz"),
  70. WithDecryption: aws.Bool(true),
  71. },
  72. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  73. Key: "/baz",
  74. Property: "/shmoo",
  75. },
  76. apiOutput: &ssm.GetParameterOutput{
  77. Parameter: &ssm.Parameter{
  78. Value: aws.String(`{"/shmoo": "bang"}`),
  79. },
  80. },
  81. apiErr: nil,
  82. expectError: "",
  83. expectedSecret: "bang",
  84. },
  85. {
  86. // bad case: missing property
  87. apiInput: &ssm.GetParameterInput{
  88. Name: aws.String("/baz"),
  89. WithDecryption: aws.Bool(true),
  90. },
  91. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  92. Key: "/baz",
  93. Property: "INVALPROP",
  94. },
  95. apiOutput: &ssm.GetParameterOutput{
  96. Parameter: &ssm.Parameter{
  97. Value: aws.String(`{"/shmoo": "bang"}`),
  98. },
  99. },
  100. apiErr: nil,
  101. expectError: "key INVALPROP does not exist in secret",
  102. expectedSecret: "",
  103. },
  104. {
  105. // bad case: extract property failure due to invalid json
  106. apiInput: &ssm.GetParameterInput{
  107. Name: aws.String("/baz"),
  108. WithDecryption: aws.Bool(true),
  109. },
  110. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  111. Key: "/baz",
  112. Property: "INVALPROP",
  113. },
  114. apiOutput: &ssm.GetParameterOutput{
  115. Parameter: &ssm.Parameter{
  116. Value: aws.String(`------`),
  117. },
  118. },
  119. apiErr: nil,
  120. expectError: "key INVALPROP does not exist in secret",
  121. expectedSecret: "",
  122. },
  123. {
  124. // case: parameter.Value may be nil but binary is set
  125. apiInput: &ssm.GetParameterInput{
  126. Name: aws.String("/baz"),
  127. WithDecryption: aws.Bool(true),
  128. },
  129. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  130. Key: "/baz",
  131. },
  132. apiOutput: &ssm.GetParameterOutput{
  133. Parameter: &ssm.Parameter{
  134. Value: nil,
  135. },
  136. },
  137. apiErr: nil,
  138. expectError: "parameter value is nil for key",
  139. expectedSecret: "",
  140. },
  141. {
  142. // should return err
  143. apiInput: &ssm.GetParameterInput{
  144. Name: aws.String("/foo/bar"),
  145. WithDecryption: aws.Bool(true),
  146. },
  147. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  148. Key: "/foo/bar",
  149. },
  150. apiOutput: &ssm.GetParameterOutput{},
  151. apiErr: fmt.Errorf("oh no"),
  152. expectError: "oh no",
  153. },
  154. } {
  155. f.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  156. out, err := p.GetSecret(context.Background(), row.rr)
  157. if !ErrorContains(err, row.expectError) {
  158. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  159. }
  160. if string(out) != row.expectedSecret {
  161. t.Errorf("[%d] unexpected secret: expected %s, got %s", i, row.expectedSecret, string(out))
  162. }
  163. }
  164. }
  165. func TestGetSecretMap(t *testing.T) {
  166. f := &fake.Client{}
  167. p := &ParameterStore{
  168. client: f,
  169. }
  170. for i, row := range []struct {
  171. apiInput *ssm.GetParameterInput
  172. apiOutput *ssm.GetParameterOutput
  173. rr esv1alpha1.ExternalSecretDataRemoteRef
  174. expectedData map[string]string
  175. apiErr error
  176. expectError string
  177. }{
  178. {
  179. // good case: default version & deserialization
  180. apiInput: &ssm.GetParameterInput{
  181. Name: aws.String("/baz"),
  182. WithDecryption: aws.Bool(true),
  183. },
  184. apiOutput: &ssm.GetParameterOutput{
  185. Parameter: &ssm.Parameter{
  186. Value: aws.String(`{"foo":"bar"}`),
  187. },
  188. },
  189. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  190. Key: "/baz",
  191. },
  192. expectedData: map[string]string{
  193. "foo": "bar",
  194. },
  195. apiErr: nil,
  196. expectError: "",
  197. },
  198. {
  199. // bad case: api error returned
  200. apiInput: &ssm.GetParameterInput{
  201. Name: aws.String("/baz"),
  202. WithDecryption: aws.Bool(true),
  203. },
  204. apiOutput: &ssm.GetParameterOutput{
  205. Parameter: &ssm.Parameter{},
  206. },
  207. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  208. Key: "/baz",
  209. },
  210. expectedData: map[string]string{
  211. "foo": "bar",
  212. },
  213. apiErr: fmt.Errorf("some api err"),
  214. expectError: "some api err",
  215. },
  216. {
  217. // bad case: invalid json
  218. apiInput: &ssm.GetParameterInput{
  219. Name: aws.String("/baz"),
  220. WithDecryption: aws.Bool(true),
  221. },
  222. apiOutput: &ssm.GetParameterOutput{
  223. Parameter: &ssm.Parameter{
  224. Value: aws.String(`-----------------`),
  225. },
  226. },
  227. rr: esv1alpha1.ExternalSecretDataRemoteRef{
  228. Key: "/baz",
  229. },
  230. expectedData: map[string]string{},
  231. apiErr: nil,
  232. expectError: "unable to unmarshal secret",
  233. },
  234. } {
  235. f.WithValue(row.apiInput, row.apiOutput, row.apiErr)
  236. out, err := p.GetSecretMap(context.Background(), row.rr)
  237. if !ErrorContains(err, row.expectError) {
  238. t.Errorf("[%d] unexpected error: %s, expected: '%s'", i, err.Error(), row.expectError)
  239. }
  240. if cmp.Equal(out, row.expectedData) {
  241. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", i, row.expectedData, out)
  242. }
  243. }
  244. }
  245. func ErrorContains(out error, want string) bool {
  246. if out == nil {
  247. return want == ""
  248. }
  249. if want == "" {
  250. return false
  251. }
  252. return strings.Contains(out.Error(), want)
  253. }