doppler_test.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 doppler
  13. import (
  14. "context"
  15. "fmt"
  16. "strings"
  17. "testing"
  18. "github.com/google/go-cmp/cmp"
  19. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  20. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  21. "github.com/external-secrets/external-secrets/pkg/provider/doppler/client"
  22. "github.com/external-secrets/external-secrets/pkg/provider/doppler/fake"
  23. )
  24. const (
  25. validSecretName = "API_KEY"
  26. validSecretValue = "3a3ea4f5"
  27. dopplerProject = "DOPPLER_PROJECT"
  28. dopplerProjectVal = "auth-api"
  29. missingSecret = "INVALID_NAME"
  30. invalidSecret = "doppler_project"
  31. missingSecretErr = "could not get secret"
  32. )
  33. type dopplerTestCase struct {
  34. label string
  35. fakeClient *fake.DopplerClient
  36. request client.SecretRequest
  37. response *client.SecretResponse
  38. remoteRef *esv1beta1.ExternalSecretDataRemoteRef
  39. apiErr error
  40. expectError string
  41. expectedSecret string
  42. expectedData map[string][]byte
  43. }
  44. func makeValidAPIRequest() client.SecretRequest {
  45. return client.SecretRequest{
  46. Name: validSecretName,
  47. }
  48. }
  49. func makeValidAPIOutput() *client.SecretResponse {
  50. return &client.SecretResponse{
  51. Name: validSecretName,
  52. Value: validSecretValue,
  53. }
  54. }
  55. func makeValidRemoteRef() *esv1beta1.ExternalSecretDataRemoteRef {
  56. return &esv1beta1.ExternalSecretDataRemoteRef{
  57. Key: validSecretName,
  58. }
  59. }
  60. func makeValidDopplerTestCase() *dopplerTestCase {
  61. return &dopplerTestCase{
  62. fakeClient: &fake.DopplerClient{},
  63. request: makeValidAPIRequest(),
  64. response: makeValidAPIOutput(),
  65. remoteRef: makeValidRemoteRef(),
  66. apiErr: nil,
  67. expectError: "",
  68. expectedSecret: "",
  69. expectedData: make(map[string][]byte),
  70. }
  71. }
  72. func makeValidDopplerTestCaseCustom(tweaks ...func(pstc *dopplerTestCase)) *dopplerTestCase {
  73. pstc := makeValidDopplerTestCase()
  74. for _, fn := range tweaks {
  75. fn(pstc)
  76. }
  77. pstc.fakeClient.WithValue(pstc.request, pstc.response, pstc.apiErr)
  78. return pstc
  79. }
  80. func TestGetSecret(t *testing.T) {
  81. setSecret := func(pstc *dopplerTestCase) {
  82. pstc.label = "set secret"
  83. pstc.request.Name = dopplerProject
  84. pstc.response.Name = dopplerProject
  85. pstc.response.Value = dopplerProjectVal
  86. pstc.expectedSecret = dopplerProjectVal
  87. pstc.remoteRef.Key = dopplerProject
  88. }
  89. setMissingSecret := func(pstc *dopplerTestCase) {
  90. pstc.label = "invalid missing secret"
  91. pstc.remoteRef.Key = missingSecret
  92. pstc.request.Name = missingSecret
  93. pstc.response = nil
  94. pstc.expectError = missingSecretErr
  95. pstc.apiErr = fmt.Errorf("")
  96. }
  97. setInvalidSecret := func(pstc *dopplerTestCase) {
  98. pstc.label = "invalid secret name format"
  99. pstc.remoteRef.Key = invalidSecret
  100. pstc.request.Name = invalidSecret
  101. pstc.response = nil
  102. pstc.expectError = missingSecretErr
  103. pstc.apiErr = fmt.Errorf("")
  104. }
  105. setClientError := func(pstc *dopplerTestCase) {
  106. pstc.label = "invalid client error"
  107. pstc.response = &client.SecretResponse{}
  108. pstc.expectError = missingSecretErr
  109. pstc.apiErr = fmt.Errorf("")
  110. }
  111. testCases := []*dopplerTestCase{
  112. makeValidDopplerTestCaseCustom(setSecret),
  113. makeValidDopplerTestCaseCustom(setMissingSecret),
  114. makeValidDopplerTestCaseCustom(setInvalidSecret),
  115. makeValidDopplerTestCaseCustom(setClientError),
  116. }
  117. c := Client{}
  118. for k, tc := range testCases {
  119. c.doppler = tc.fakeClient
  120. out, err := c.GetSecret(context.Background(), *tc.remoteRef)
  121. if !ErrorContains(err, tc.expectError) {
  122. t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), tc.expectError)
  123. }
  124. if err == nil && !cmp.Equal(string(out), tc.expectedSecret) {
  125. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, tc.expectedSecret, string(out))
  126. }
  127. }
  128. }
  129. func TestGetSecretMap(t *testing.T) {
  130. simpleJSON := func(pstc *dopplerTestCase) {
  131. pstc.label = "valid unmarshalling"
  132. pstc.response.Value = `{"API_KEY":"3a3ea4f5"}`
  133. pstc.expectedData["API_KEY"] = []byte("3a3ea4f5")
  134. }
  135. complexJSON := func(pstc *dopplerTestCase) {
  136. pstc.label = "valid unmarshalling for nested json"
  137. pstc.response.Value = `{"API_KEY": "3a3ea4f5", "AUTH_SA": {"appID": "a1ea-48bd-8749-b6f5ec3c5a1f"}}`
  138. pstc.expectedData["API_KEY"] = []byte("3a3ea4f5")
  139. pstc.expectedData["AUTH_SA"] = []byte(`{"appID": "a1ea-48bd-8749-b6f5ec3c5a1f"}`)
  140. }
  141. setInvalidJSON := func(pstc *dopplerTestCase) {
  142. pstc.label = "invalid json"
  143. pstc.response.Value = `{"API_KEY": "3a3ea4f`
  144. pstc.expectError = "unable to unmarshal secret"
  145. }
  146. setAPIError := func(pstc *dopplerTestCase) {
  147. pstc.label = "client error"
  148. pstc.response = &client.SecretResponse{}
  149. pstc.expectError = missingSecretErr
  150. pstc.apiErr = fmt.Errorf("")
  151. }
  152. testCases := []*dopplerTestCase{
  153. makeValidDopplerTestCaseCustom(simpleJSON),
  154. makeValidDopplerTestCaseCustom(complexJSON),
  155. makeValidDopplerTestCaseCustom(setInvalidJSON),
  156. makeValidDopplerTestCaseCustom(setAPIError),
  157. }
  158. d := Client{}
  159. for k, tc := range testCases {
  160. t.Run(tc.label, func(t *testing.T) {
  161. d.doppler = tc.fakeClient
  162. out, err := d.GetSecretMap(context.Background(), *tc.remoteRef)
  163. if !ErrorContains(err, tc.expectError) {
  164. t.Errorf("[%d] unexpected error: %q, expected: %q", k, err.Error(), tc.expectError)
  165. }
  166. if err == nil && !cmp.Equal(out, tc.expectedData) {
  167. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, tc.expectedData, out)
  168. }
  169. })
  170. }
  171. }
  172. func ErrorContains(out error, want string) bool {
  173. if out == nil {
  174. return want == ""
  175. }
  176. if want == "" {
  177. return false
  178. }
  179. return strings.Contains(out.Error(), want)
  180. }
  181. type storeModifier func(*esv1beta1.SecretStore) *esv1beta1.SecretStore
  182. func makeSecretStore(fn ...storeModifier) *esv1beta1.SecretStore {
  183. store := &esv1beta1.SecretStore{
  184. Spec: esv1beta1.SecretStoreSpec{
  185. Provider: &esv1beta1.SecretStoreProvider{
  186. Doppler: &esv1beta1.DopplerProvider{
  187. Auth: &esv1beta1.DopplerAuth{},
  188. },
  189. },
  190. },
  191. }
  192. for _, f := range fn {
  193. store = f(store)
  194. }
  195. return store
  196. }
  197. func withAuth(name, key string, namespace *string) storeModifier {
  198. return func(store *esv1beta1.SecretStore) *esv1beta1.SecretStore {
  199. store.Spec.Provider.Doppler.Auth.SecretRef.DopplerToken = v1.SecretKeySelector{
  200. Name: name,
  201. Key: key,
  202. Namespace: namespace,
  203. }
  204. return store
  205. }
  206. }
  207. type ValidateStoreTestCase struct {
  208. label string
  209. store *esv1beta1.SecretStore
  210. err error
  211. }
  212. func TestValidateStore(t *testing.T) {
  213. namespace := "ns"
  214. secretName := "doppler-token-secret"
  215. testCases := []ValidateStoreTestCase{
  216. {
  217. label: "invalid store missing dopplerToken.name",
  218. store: makeSecretStore(withAuth("", "", nil)),
  219. err: fmt.Errorf("invalid store: dopplerToken.name cannot be empty"),
  220. },
  221. {
  222. label: "invalid store namespace not allowed",
  223. store: makeSecretStore(withAuth(secretName, "", &namespace)),
  224. err: fmt.Errorf("invalid store: namespace not allowed with namespaced SecretStore"),
  225. },
  226. {
  227. label: "valid provide optional dopplerToken.key",
  228. store: makeSecretStore(withAuth(secretName, "customSecretKey", nil)),
  229. err: nil,
  230. },
  231. {
  232. label: "valid namespace not set",
  233. store: makeSecretStore(withAuth(secretName, "", nil)),
  234. err: nil,
  235. },
  236. }
  237. p := Provider{}
  238. for _, tc := range testCases {
  239. t.Run(tc.label, func(t *testing.T) {
  240. err := p.ValidateStore(tc.store)
  241. if tc.err != nil && err != nil && err.Error() != tc.err.Error() {
  242. t.Errorf("test failed! want %v, got %v", tc.err, err)
  243. } else if tc.err == nil && err != nil {
  244. t.Errorf("want nil got err %v", err)
  245. } else if tc.err != nil && err == nil {
  246. t.Errorf("want err %v got nil", tc.err)
  247. }
  248. })
  249. }
  250. }