fake_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 fake
  13. import (
  14. "context"
  15. "errors"
  16. "fmt"
  17. "strings"
  18. "testing"
  19. "github.com/google/go-cmp/cmp"
  20. "github.com/onsi/gomega"
  21. corev1 "k8s.io/api/core/v1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/utils/ptr"
  24. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  25. testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  26. )
  27. func TestNewClient(t *testing.T) {
  28. p := &Provider{}
  29. gomega.RegisterTestingT(t)
  30. // nil store
  31. _, err := p.NewClient(context.Background(), nil, nil, "")
  32. gomega.Expect(err).To(gomega.HaveOccurred())
  33. // missing provider
  34. _, err = p.NewClient(context.Background(), &esv1beta1.SecretStore{}, nil, "")
  35. gomega.Expect(err).To(gomega.HaveOccurred())
  36. }
  37. func TestValidateStore(t *testing.T) {
  38. p := &Provider{}
  39. gomega.RegisterTestingT(t)
  40. store := &esv1beta1.SecretStore{
  41. Spec: esv1beta1.SecretStoreSpec{
  42. Provider: &esv1beta1.SecretStoreProvider{
  43. Fake: &esv1beta1.FakeProvider{
  44. Data: []esv1beta1.FakeProviderData{},
  45. },
  46. },
  47. },
  48. }
  49. // empty data must not error
  50. err := p.ValidateStore(store)
  51. gomega.Expect(err).To(gomega.BeNil())
  52. // missing key in data
  53. data := esv1beta1.FakeProviderData{}
  54. data.Version = "v1"
  55. store.Spec.Provider.Fake.Data = []esv1beta1.FakeProviderData{data}
  56. err = p.ValidateStore(store)
  57. gomega.Expect(err).To(gomega.BeEquivalentTo(fmt.Errorf(errMissingKeyField, 0)))
  58. // missing values in data
  59. data.Key = "/foo"
  60. store.Spec.Provider.Fake.Data = []esv1beta1.FakeProviderData{data}
  61. err = p.ValidateStore(store)
  62. gomega.Expect(err).To(gomega.BeEquivalentTo(fmt.Errorf(errMissingValueField, 0)))
  63. // spec ok
  64. data.Value = "bar"
  65. data.ValueMap = map[string]string{"foo": "bar"}
  66. store.Spec.Provider.Fake.Data = []esv1beta1.FakeProviderData{data}
  67. err = p.ValidateStore(store)
  68. gomega.Expect(err).To(gomega.BeNil())
  69. }
  70. func TestClose(t *testing.T) {
  71. p := &Provider{}
  72. gomega.RegisterTestingT(t)
  73. err := p.Close(context.TODO())
  74. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  75. }
  76. type testCase struct {
  77. name string
  78. input []esv1beta1.FakeProviderData
  79. request esv1beta1.ExternalSecretDataRemoteRef
  80. expValue string
  81. expErr string
  82. }
  83. func TestGetAllSecrets(t *testing.T) {
  84. cases := []struct {
  85. desc string
  86. data []esv1beta1.FakeProviderData
  87. ref esv1beta1.ExternalSecretFind
  88. expected map[string][]byte
  89. expectedErr string
  90. }{
  91. {
  92. desc: "no matches",
  93. data: []esv1beta1.FakeProviderData{},
  94. ref: esv1beta1.ExternalSecretFind{
  95. Name: &esv1beta1.FindName{
  96. RegExp: "some-key",
  97. },
  98. },
  99. expected: map[string][]byte{},
  100. },
  101. {
  102. desc: "matches",
  103. data: []esv1beta1.FakeProviderData{
  104. {
  105. Key: "some-key1",
  106. Value: "some-value1",
  107. },
  108. {
  109. Key: "some-key2",
  110. Value: "some-value2",
  111. },
  112. {
  113. Key: "another-key1",
  114. Value: "another-value1",
  115. },
  116. },
  117. ref: esv1beta1.ExternalSecretFind{
  118. Name: &esv1beta1.FindName{
  119. RegExp: "some-key.*",
  120. },
  121. },
  122. expected: map[string][]byte{
  123. "some-key1": []byte("some-value1"),
  124. "some-key2": []byte("some-value2"),
  125. },
  126. },
  127. {
  128. desc: "matches with version",
  129. data: []esv1beta1.FakeProviderData{
  130. {
  131. Key: "some-key1",
  132. Value: "some-value1-version1",
  133. Version: "1",
  134. },
  135. {
  136. Key: "some-key1",
  137. Value: "some-value1-version2",
  138. Version: "2",
  139. },
  140. {
  141. Key: "some-key2",
  142. Value: "some-value2-version1",
  143. Version: "1",
  144. },
  145. {
  146. Key: "some-key2",
  147. Value: "some-value2-version2",
  148. Version: "2",
  149. },
  150. {
  151. Key: "some-key2",
  152. Value: "some-value2-version3",
  153. Version: "3",
  154. },
  155. {
  156. Key: "another-key1",
  157. Value: "another-value1-version1",
  158. Version: "1",
  159. },
  160. {
  161. Key: "another-key1",
  162. Value: "another-value1-version2",
  163. Version: "2",
  164. },
  165. },
  166. ref: esv1beta1.ExternalSecretFind{
  167. Name: &esv1beta1.FindName{
  168. RegExp: "some-key.*",
  169. },
  170. },
  171. expected: map[string][]byte{
  172. "some-key1": []byte("some-value1-version2"),
  173. "some-key2": []byte("some-value2-version3"),
  174. },
  175. },
  176. {
  177. desc: "unsupported operator",
  178. data: []esv1beta1.FakeProviderData{},
  179. ref: esv1beta1.ExternalSecretFind{
  180. Path: ptr.To("some-path"),
  181. },
  182. expectedErr: "unsupported find operator",
  183. },
  184. }
  185. for i, tc := range cases {
  186. t.Run(tc.desc, func(t *testing.T) {
  187. ctx := context.Background()
  188. p := Provider{}
  189. client, err := p.NewClient(ctx, &esv1beta1.SecretStore{
  190. ObjectMeta: metav1.ObjectMeta{
  191. Name: fmt.Sprintf("secret-store-%v", i),
  192. },
  193. Spec: esv1beta1.SecretStoreSpec{
  194. Provider: &esv1beta1.SecretStoreProvider{
  195. Fake: &esv1beta1.FakeProvider{
  196. Data: tc.data,
  197. },
  198. },
  199. },
  200. }, nil, "")
  201. if err != nil {
  202. t.Fatalf("failed to create a client: %v", err)
  203. }
  204. got, err := client.GetAllSecrets(ctx, tc.ref)
  205. if err != nil {
  206. if tc.expectedErr == "" {
  207. t.Fatalf("failed to call GetAllSecrets: %v", err)
  208. }
  209. if !strings.Contains(err.Error(), tc.expectedErr) {
  210. t.Fatalf("%q expected to contain substring %q", err.Error(), tc.expectedErr)
  211. }
  212. return
  213. }
  214. if tc.expectedErr != "" {
  215. t.Fatal("expected to receive an error but got nil")
  216. }
  217. if diff := cmp.Diff(tc.expected, got); diff != "" {
  218. t.Fatalf("(-got, +want)\n%s", diff)
  219. }
  220. })
  221. }
  222. }
  223. func TestGetSecret(t *testing.T) {
  224. gomega.RegisterTestingT(t)
  225. p := &Provider{}
  226. tbl := []testCase{
  227. {
  228. name: "return err when not found",
  229. input: []esv1beta1.FakeProviderData{},
  230. request: esv1beta1.ExternalSecretDataRemoteRef{
  231. Key: "/foo",
  232. Version: "v2",
  233. },
  234. expErr: esv1beta1.NoSecretErr.Error(),
  235. },
  236. {
  237. name: "get correct value from multiple versions",
  238. input: []esv1beta1.FakeProviderData{
  239. {
  240. Key: "/foo",
  241. Value: "bar2",
  242. Version: "v2",
  243. },
  244. {
  245. Key: "junk",
  246. Value: "xxxxx",
  247. },
  248. {
  249. Key: "/foo",
  250. Value: "bar1",
  251. Version: "v1",
  252. },
  253. },
  254. request: esv1beta1.ExternalSecretDataRemoteRef{
  255. Key: "/foo",
  256. Version: "v2",
  257. },
  258. expValue: "bar2",
  259. },
  260. {
  261. name: "get correct value from multiple properties",
  262. input: []esv1beta1.FakeProviderData{
  263. {
  264. Key: "junk",
  265. Value: "xxxxx",
  266. },
  267. {
  268. Key: "/foo",
  269. Value: `{"p1":"bar","p2":"bar2"}`,
  270. },
  271. },
  272. request: esv1beta1.ExternalSecretDataRemoteRef{
  273. Key: "/foo",
  274. Property: "p2",
  275. },
  276. expValue: "bar2",
  277. },
  278. }
  279. for i, row := range tbl {
  280. t.Run(row.name, func(t *testing.T) {
  281. cl, err := p.NewClient(context.Background(), &esv1beta1.SecretStore{
  282. ObjectMeta: metav1.ObjectMeta{
  283. Name: fmt.Sprintf("secret-store-%v", i),
  284. },
  285. Spec: esv1beta1.SecretStoreSpec{
  286. Provider: &esv1beta1.SecretStoreProvider{
  287. Fake: &esv1beta1.FakeProvider{
  288. Data: row.input,
  289. },
  290. },
  291. },
  292. }, nil, "")
  293. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  294. out, err := cl.GetSecret(context.Background(), row.request)
  295. if row.expErr != "" {
  296. gomega.Expect(err).To(gomega.MatchError(row.expErr))
  297. } else {
  298. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  299. }
  300. gomega.Expect(string(out)).To(gomega.Equal(row.expValue))
  301. })
  302. }
  303. }
  304. type setSecretTestCase struct {
  305. name string
  306. input []esv1beta1.FakeProviderData
  307. requestKey string
  308. expValue string
  309. expErr string
  310. }
  311. func TestSetSecret(t *testing.T) {
  312. gomega.RegisterTestingT(t)
  313. p := &Provider{}
  314. secretKey := "secret-key"
  315. tbl := []setSecretTestCase{
  316. {
  317. name: "return nil if no existing secret",
  318. input: []esv1beta1.FakeProviderData{},
  319. requestKey: "/foo",
  320. expValue: "my-secret-value",
  321. },
  322. {
  323. name: "return err if existing secret",
  324. input: []esv1beta1.FakeProviderData{
  325. {
  326. Key: "/foo",
  327. Value: "bar2",
  328. },
  329. },
  330. requestKey: "/foo",
  331. expErr: errors.New("key already exists").Error(),
  332. },
  333. }
  334. for i, row := range tbl {
  335. t.Run(row.name, func(t *testing.T) {
  336. cl, err := p.NewClient(context.Background(), &esv1beta1.SecretStore{
  337. ObjectMeta: metav1.ObjectMeta{
  338. Name: fmt.Sprintf("secret-store-%v", i),
  339. },
  340. Spec: esv1beta1.SecretStoreSpec{
  341. Provider: &esv1beta1.SecretStoreProvider{
  342. Fake: &esv1beta1.FakeProvider{
  343. Data: row.input,
  344. },
  345. },
  346. },
  347. }, nil, "")
  348. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  349. secret := &corev1.Secret{
  350. Data: map[string][]byte{
  351. secretKey: []byte(row.expValue),
  352. },
  353. }
  354. err = cl.PushSecret(context.TODO(), secret, testingfake.PushSecretData{
  355. SecretKey: secretKey,
  356. RemoteKey: row.requestKey,
  357. })
  358. if row.expErr != "" {
  359. gomega.Expect(err).To(gomega.MatchError(row.expErr))
  360. } else {
  361. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  362. out, err := cl.GetSecret(context.Background(), esv1beta1.ExternalSecretDataRemoteRef{
  363. Key: row.requestKey,
  364. })
  365. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  366. gomega.Expect(string(out)).To(gomega.Equal(row.expValue))
  367. }
  368. })
  369. }
  370. }
  371. type testMapCase struct {
  372. name string
  373. input []esv1beta1.FakeProviderData
  374. request esv1beta1.ExternalSecretDataRemoteRef
  375. expValue map[string][]byte
  376. expErr string
  377. }
  378. func TestGetSecretMap(t *testing.T) {
  379. gomega.RegisterTestingT(t)
  380. p := &Provider{}
  381. tbl := []testMapCase{
  382. {
  383. name: "return err when not found",
  384. input: []esv1beta1.FakeProviderData{},
  385. request: esv1beta1.ExternalSecretDataRemoteRef{
  386. Key: "/foo",
  387. Version: "v2",
  388. },
  389. expErr: esv1beta1.NoSecretErr.Error(),
  390. },
  391. {
  392. name: "get correct map from multiple versions by using Value only",
  393. input: []esv1beta1.FakeProviderData{
  394. {
  395. Key: "/bar",
  396. Version: "v1",
  397. Value: `{"john":"doe"}`,
  398. },
  399. },
  400. request: esv1beta1.ExternalSecretDataRemoteRef{
  401. Key: "/bar",
  402. Version: "v1",
  403. },
  404. expValue: map[string][]byte{
  405. "john": []byte("doe"),
  406. },
  407. },
  408. {
  409. name: "get correct maps from multiple versions by using Value only",
  410. input: []esv1beta1.FakeProviderData{
  411. {
  412. Key: "/bar",
  413. Version: "v3",
  414. Value: `{"john":"doe", "foo": "bar"}`,
  415. },
  416. },
  417. request: esv1beta1.ExternalSecretDataRemoteRef{
  418. Key: "/bar",
  419. Version: "v3",
  420. },
  421. expValue: map[string][]byte{
  422. "john": []byte("doe"),
  423. "foo": []byte("bar"),
  424. },
  425. },
  426. {
  427. name: "invalid marshal",
  428. input: []esv1beta1.FakeProviderData{
  429. {
  430. Key: "/bar",
  431. Version: "v3",
  432. Value: `---------`,
  433. },
  434. },
  435. request: esv1beta1.ExternalSecretDataRemoteRef{
  436. Key: "/bar",
  437. Version: "v3",
  438. },
  439. expErr: "unable to unmarshal secret: invalid character '-' in numeric literal",
  440. },
  441. {
  442. name: "get correct value from ValueMap due to retrocompatibility",
  443. input: []esv1beta1.FakeProviderData{
  444. {
  445. Key: "/foo/bar",
  446. Version: "v3",
  447. ValueMap: map[string]string{
  448. "john": "doe",
  449. "baz": "bang",
  450. },
  451. },
  452. },
  453. request: esv1beta1.ExternalSecretDataRemoteRef{
  454. Key: "/foo/bar",
  455. Version: "v3",
  456. },
  457. expValue: map[string][]byte{
  458. "john": []byte("doe"),
  459. "baz": []byte("bang"),
  460. },
  461. },
  462. {
  463. name: "get correct value from multiple versions",
  464. input: []esv1beta1.FakeProviderData{
  465. {
  466. Key: "john",
  467. Value: "doe",
  468. Version: "v2",
  469. },
  470. {
  471. Key: "junk",
  472. ValueMap: map[string]string{
  473. "junk": "ok",
  474. },
  475. },
  476. {
  477. Key: "/foo",
  478. ValueMap: map[string]string{
  479. "foo": "bar",
  480. "baz": "bang",
  481. },
  482. Version: "v1",
  483. },
  484. {
  485. Key: "/foo",
  486. ValueMap: map[string]string{
  487. "foo": "bar",
  488. "baz": "bang",
  489. },
  490. Version: "v2",
  491. },
  492. },
  493. request: esv1beta1.ExternalSecretDataRemoteRef{
  494. Key: "/foo",
  495. Version: "v2",
  496. },
  497. expValue: map[string][]byte{
  498. "foo": []byte("bar"),
  499. "baz": []byte("bang"),
  500. },
  501. },
  502. }
  503. for i, row := range tbl {
  504. t.Run(row.name, func(t *testing.T) {
  505. cl, err := p.NewClient(context.Background(), &esv1beta1.SecretStore{
  506. ObjectMeta: metav1.ObjectMeta{
  507. Name: fmt.Sprintf("secret-store-%v", i),
  508. },
  509. Spec: esv1beta1.SecretStoreSpec{
  510. Provider: &esv1beta1.SecretStoreProvider{
  511. Fake: &esv1beta1.FakeProvider{
  512. Data: row.input,
  513. },
  514. },
  515. },
  516. }, nil, "")
  517. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  518. out, err := cl.GetSecretMap(context.Background(), row.request)
  519. if row.expErr != "" {
  520. gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring(row.expErr)))
  521. } else {
  522. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  523. }
  524. gomega.Expect(out).To(gomega.Equal(row.expValue))
  525. })
  526. }
  527. }