fake_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  25. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  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(), &esv1.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 := &esv1.SecretStore{
  42. Spec: esv1.SecretStoreSpec{
  43. Provider: &esv1.SecretStoreProvider{
  44. Fake: &esv1.FakeProvider{
  45. Data: []esv1.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 := esv1.FakeProviderData{}
  55. data.Version = "v1"
  56. store.Spec.Provider.Fake.Data = []esv1.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 = []esv1.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. store.Spec.Provider.Fake.Data = []esv1.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 []esv1.FakeProviderData
  79. request esv1.ExternalSecretDataRemoteRef
  80. expValue string
  81. expErr string
  82. }
  83. func TestGetAllSecrets(t *testing.T) {
  84. cases := []struct {
  85. desc string
  86. data []esv1.FakeProviderData
  87. ref esv1.ExternalSecretFind
  88. expected map[string][]byte
  89. expectedErr string
  90. }{
  91. {
  92. desc: "no matches",
  93. data: []esv1.FakeProviderData{},
  94. ref: esv1.ExternalSecretFind{
  95. Name: &esv1.FindName{
  96. RegExp: "some-key",
  97. },
  98. },
  99. expected: map[string][]byte{},
  100. },
  101. {
  102. desc: "matches",
  103. data: []esv1.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: esv1.ExternalSecretFind{
  118. Name: &esv1.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: []esv1.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: esv1.ExternalSecretFind{
  167. Name: &esv1.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: []esv1.FakeProviderData{},
  179. ref: esv1.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, &esv1.SecretStore{
  190. ObjectMeta: metav1.ObjectMeta{
  191. Name: fmt.Sprintf("secret-store-%v", i),
  192. },
  193. Spec: esv1.SecretStoreSpec{
  194. Provider: &esv1.SecretStoreProvider{
  195. Fake: &esv1.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: []esv1.FakeProviderData{},
  230. request: esv1.ExternalSecretDataRemoteRef{
  231. Key: "/foo",
  232. Version: "v2",
  233. },
  234. expErr: esv1.NoSecretErr.Error(),
  235. },
  236. {
  237. name: "get correct value from multiple versions",
  238. input: []esv1.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: esv1.ExternalSecretDataRemoteRef{
  255. Key: "/foo",
  256. Version: "v2",
  257. },
  258. expValue: "bar2",
  259. },
  260. {
  261. name: "get correct value from multiple properties",
  262. input: []esv1.FakeProviderData{
  263. {
  264. Key: "junk",
  265. Value: "xxxxx",
  266. },
  267. {
  268. Key: "/foo",
  269. Value: `{"p1":"bar","p2":"bar2"}`,
  270. },
  271. },
  272. request: esv1.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(), &esv1.SecretStore{
  282. ObjectMeta: metav1.ObjectMeta{
  283. Name: fmt.Sprintf("secret-store-%v", i),
  284. },
  285. Spec: esv1.SecretStoreSpec{
  286. Provider: &esv1.SecretStoreProvider{
  287. Fake: &esv1.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 []esv1.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: []esv1.FakeProviderData{},
  319. requestKey: "/foo",
  320. expValue: "my-secret-value",
  321. },
  322. {
  323. name: "return err if existing secret",
  324. input: []esv1.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(), &esv1.SecretStore{
  337. ObjectMeta: metav1.ObjectMeta{
  338. Name: fmt.Sprintf("secret-store-%v", i),
  339. },
  340. Spec: esv1.SecretStoreSpec{
  341. Provider: &esv1.SecretStoreProvider{
  342. Fake: &esv1.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(), esv1.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 secretExistsTestCase struct {
  372. name string
  373. input []esv1.FakeProviderData
  374. request esv1alpha1.PushSecretRemoteRef
  375. expExists bool
  376. }
  377. func TestSecretExists(t *testing.T) {
  378. gomega.RegisterTestingT(t)
  379. p := &Provider{}
  380. tbl := []secretExistsTestCase{
  381. {
  382. name: "return false, nil if no existing secret",
  383. input: []esv1.FakeProviderData{},
  384. request: esv1alpha1.PushSecretRemoteRef{
  385. RemoteKey: "/foo",
  386. },
  387. expExists: false,
  388. },
  389. {
  390. name: "return true, nil if existing secret",
  391. input: []esv1.FakeProviderData{
  392. {
  393. Key: "/foo",
  394. Value: "bar",
  395. },
  396. },
  397. request: esv1alpha1.PushSecretRemoteRef{
  398. RemoteKey: "/foo",
  399. },
  400. expExists: true,
  401. },
  402. }
  403. for i, row := range tbl {
  404. t.Run(row.name, func(t *testing.T) {
  405. cl, err := p.NewClient(context.Background(), &esv1.SecretStore{
  406. ObjectMeta: metav1.ObjectMeta{
  407. Name: fmt.Sprintf("secret-store-%v", i),
  408. },
  409. Spec: esv1.SecretStoreSpec{
  410. Provider: &esv1.SecretStoreProvider{
  411. Fake: &esv1.FakeProvider{
  412. Data: row.input,
  413. },
  414. },
  415. },
  416. }, nil, "")
  417. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  418. exists, err := cl.SecretExists(context.TODO(), row.request)
  419. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  420. gomega.Expect(exists).To(gomega.Equal(row.expExists))
  421. })
  422. }
  423. }
  424. type testMapCase struct {
  425. name string
  426. input []esv1.FakeProviderData
  427. request esv1.ExternalSecretDataRemoteRef
  428. expValue map[string][]byte
  429. expErr string
  430. }
  431. func TestGetSecretMap(t *testing.T) {
  432. gomega.RegisterTestingT(t)
  433. p := &Provider{}
  434. tbl := []testMapCase{
  435. {
  436. name: "return err when not found",
  437. input: []esv1.FakeProviderData{},
  438. request: esv1.ExternalSecretDataRemoteRef{
  439. Key: "/foo",
  440. Version: "v2",
  441. },
  442. expErr: esv1.NoSecretErr.Error(),
  443. },
  444. {
  445. name: "get correct map from multiple versions by using Value only",
  446. input: []esv1.FakeProviderData{
  447. {
  448. Key: "/bar",
  449. Version: "v1",
  450. Value: `{"john":"doe"}`,
  451. },
  452. },
  453. request: esv1.ExternalSecretDataRemoteRef{
  454. Key: "/bar",
  455. Version: "v1",
  456. },
  457. expValue: map[string][]byte{
  458. "john": []byte("doe"),
  459. },
  460. },
  461. {
  462. name: "get correct maps from multiple versions by using Value only",
  463. input: []esv1.FakeProviderData{
  464. {
  465. Key: "/bar",
  466. Version: "v3",
  467. Value: `{"john":"doe", "foo": "bar"}`,
  468. },
  469. },
  470. request: esv1.ExternalSecretDataRemoteRef{
  471. Key: "/bar",
  472. Version: "v3",
  473. },
  474. expValue: map[string][]byte{
  475. "john": []byte("doe"),
  476. "foo": []byte("bar"),
  477. },
  478. },
  479. {
  480. name: "invalid marshal",
  481. input: []esv1.FakeProviderData{
  482. {
  483. Key: "/bar",
  484. Version: "v3",
  485. Value: `---------`,
  486. },
  487. },
  488. request: esv1.ExternalSecretDataRemoteRef{
  489. Key: "/bar",
  490. Version: "v3",
  491. },
  492. expErr: "unable to unmarshal secret: invalid character '-' in numeric literal",
  493. },
  494. {
  495. name: "get correct value from multiple versions",
  496. input: []esv1.FakeProviderData{
  497. {
  498. Key: "john",
  499. Value: "doe",
  500. Version: "v2",
  501. },
  502. {
  503. Key: "junk",
  504. Value: `{"junk":"ok"}`,
  505. },
  506. {
  507. Key: "/foo",
  508. Value: `{"foo":"bar","baz":"bang"}`,
  509. Version: "v1",
  510. },
  511. {
  512. Key: "/foo",
  513. Value: `{"foo":"bar","baz":"bang"}`,
  514. Version: "v2",
  515. },
  516. },
  517. request: esv1.ExternalSecretDataRemoteRef{
  518. Key: "/foo",
  519. Version: "v2",
  520. },
  521. expValue: map[string][]byte{
  522. "foo": []byte("bar"),
  523. "baz": []byte("bang"),
  524. },
  525. },
  526. }
  527. for i, row := range tbl {
  528. t.Run(row.name, func(t *testing.T) {
  529. cl, err := p.NewClient(context.Background(), &esv1.SecretStore{
  530. ObjectMeta: metav1.ObjectMeta{
  531. Name: fmt.Sprintf("secret-store-%v", i),
  532. },
  533. Spec: esv1.SecretStoreSpec{
  534. Provider: &esv1.SecretStoreProvider{
  535. Fake: &esv1.FakeProvider{
  536. Data: row.input,
  537. },
  538. },
  539. },
  540. }, nil, "")
  541. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  542. out, err := cl.GetSecretMap(context.Background(), row.request)
  543. if row.expErr != "" {
  544. gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring(row.expErr)))
  545. } else {
  546. gomega.Expect(err).ToNot(gomega.HaveOccurred())
  547. }
  548. gomega.Expect(out).To(gomega.Equal(row.expValue))
  549. })
  550. }
  551. }