fake_test.go 14 KB

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