client_test.go 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  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 kubernetes
  13. import (
  14. "context"
  15. "errors"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. "github.com/google/go-cmp/cmp"
  20. "github.com/stretchr/testify/assert"
  21. v1 "k8s.io/api/core/v1"
  22. apierrors "k8s.io/apimachinery/pkg/api/errors"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/runtime/schema"
  25. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  26. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  27. testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  28. )
  29. const (
  30. errSomethingWentWrong = "Something went wrong"
  31. )
  32. type fakeClient struct {
  33. t *testing.T
  34. secretMap map[string]*v1.Secret
  35. expectedListOptions metav1.ListOptions
  36. err error
  37. }
  38. func (fk *fakeClient) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) {
  39. if fk.err != nil {
  40. return nil, fk.err
  41. }
  42. secret, ok := fk.secretMap[name]
  43. if !ok {
  44. return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret")
  45. }
  46. // return inmutable to simulate external system and avoid accidental side effects
  47. sCopy := secret.DeepCopy()
  48. // update operation requires to relate names
  49. sCopy.Name = name
  50. return sCopy, nil
  51. }
  52. func (fk *fakeClient) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
  53. assert.Equal(fk.t, fk.expectedListOptions, opts)
  54. list := &v1.SecretList{}
  55. for _, v := range fk.secretMap {
  56. list.Items = append(list.Items, *v)
  57. }
  58. return list, nil
  59. }
  60. func (fk *fakeClient) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
  61. if fk.err != nil {
  62. return fk.err
  63. }
  64. _, ok := fk.secretMap[name]
  65. if !ok {
  66. return apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret")
  67. }
  68. delete(fk.secretMap, name)
  69. return nil
  70. }
  71. func (fk *fakeClient) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
  72. s := &v1.Secret{
  73. Data: secret.Data,
  74. Type: secret.Type,
  75. }
  76. fk.secretMap[secret.Name] = s
  77. return s, nil
  78. }
  79. func (fk *fakeClient) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) {
  80. s, ok := fk.secretMap[secret.Name]
  81. if !ok {
  82. return nil, errors.New("error while updating secret")
  83. }
  84. s.Data = secret.Data
  85. return s, nil
  86. }
  87. var binaryTestData = []byte{0x00, 0xff, 0x00, 0xff, 0xac, 0xab, 0x28, 0x21}
  88. func TestGetSecret(t *testing.T) {
  89. tests := []struct {
  90. desc string
  91. secrets map[string]*v1.Secret
  92. clientErr error
  93. ref esv1beta1.ExternalSecretDataRemoteRef
  94. want []byte
  95. wantErr string
  96. }{
  97. {
  98. desc: "secret data with correct property",
  99. secrets: map[string]*v1.Secret{
  100. "mysec": {
  101. Data: map[string][]byte{
  102. "token": []byte(`foobar`),
  103. },
  104. },
  105. },
  106. ref: esv1beta1.ExternalSecretDataRemoteRef{
  107. Key: "mysec",
  108. Property: "token",
  109. },
  110. want: []byte(`foobar`),
  111. },
  112. {
  113. desc: "secret data with multi level property",
  114. secrets: map[string]*v1.Secret{
  115. "mysec": {
  116. Data: map[string][]byte{
  117. "foo": []byte(`{"huga":{"bar":"val"}}`),
  118. },
  119. },
  120. },
  121. ref: esv1beta1.ExternalSecretDataRemoteRef{
  122. Key: "mysec",
  123. Property: "foo.huga.bar",
  124. },
  125. want: []byte(`val`),
  126. },
  127. {
  128. desc: "secret data with property containing .",
  129. secrets: map[string]*v1.Secret{
  130. "mysec": {
  131. Data: map[string][]byte{
  132. "foo.png": []byte(`correct`),
  133. "foo": []byte(`{"png":"wrong"}`),
  134. },
  135. },
  136. },
  137. ref: esv1beta1.ExternalSecretDataRemoteRef{
  138. Key: "mysec",
  139. Property: "foo.png",
  140. },
  141. want: []byte(`correct`),
  142. },
  143. {
  144. desc: "secret data contains html characters",
  145. secrets: map[string]*v1.Secret{
  146. "mysec": {
  147. Data: map[string][]byte{
  148. "html": []byte(`<foobar>`),
  149. },
  150. },
  151. },
  152. ref: esv1beta1.ExternalSecretDataRemoteRef{
  153. Key: "mysec",
  154. },
  155. want: []byte(`{"html":"<foobar>"}`),
  156. },
  157. {
  158. desc: "secret metadata contains html characters",
  159. secrets: map[string]*v1.Secret{
  160. "mysec": {
  161. ObjectMeta: metav1.ObjectMeta{
  162. Annotations: map[string]string{"date": "today"},
  163. Labels: map[string]string{"dev": "<seb>"},
  164. },
  165. },
  166. },
  167. ref: esv1beta1.ExternalSecretDataRemoteRef{
  168. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  169. Key: "mysec",
  170. },
  171. want: []byte(`{"annotations":{"date":"today"},"labels":{"dev":"<seb>"}}`),
  172. },
  173. {
  174. desc: "secret data contains binary",
  175. secrets: map[string]*v1.Secret{
  176. "mysec": {
  177. Data: map[string][]byte{
  178. "bindata": binaryTestData,
  179. },
  180. },
  181. },
  182. ref: esv1beta1.ExternalSecretDataRemoteRef{
  183. Key: "mysec",
  184. Property: "bindata",
  185. },
  186. want: binaryTestData,
  187. },
  188. {
  189. desc: "secret data without property",
  190. secrets: map[string]*v1.Secret{
  191. "mysec": {
  192. Data: map[string][]byte{
  193. "token": []byte(`foobar`),
  194. },
  195. },
  196. },
  197. ref: esv1beta1.ExternalSecretDataRemoteRef{
  198. Key: "mysec",
  199. },
  200. want: []byte(`{"token":"foobar"}`),
  201. },
  202. {
  203. desc: "secret metadata without property",
  204. secrets: map[string]*v1.Secret{
  205. "mysec": {
  206. ObjectMeta: metav1.ObjectMeta{
  207. Annotations: map[string]string{"date": "today"},
  208. Labels: map[string]string{"dev": "seb"},
  209. },
  210. },
  211. },
  212. ref: esv1beta1.ExternalSecretDataRemoteRef{
  213. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  214. Key: "mysec",
  215. },
  216. want: []byte(`{"annotations":{"date":"today"},"labels":{"dev":"seb"}}`),
  217. },
  218. {
  219. desc: "secret metadata with single level property",
  220. secrets: map[string]*v1.Secret{
  221. "mysec": {
  222. ObjectMeta: metav1.ObjectMeta{
  223. Annotations: map[string]string{"date": "today"},
  224. Labels: map[string]string{"dev": "seb"},
  225. },
  226. },
  227. },
  228. ref: esv1beta1.ExternalSecretDataRemoteRef{
  229. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  230. Key: "mysec",
  231. Property: "labels",
  232. },
  233. want: []byte(`{"dev":"seb"}`),
  234. },
  235. {
  236. desc: "secret metadata with multiple level property",
  237. secrets: map[string]*v1.Secret{
  238. "mysec": {
  239. ObjectMeta: metav1.ObjectMeta{
  240. Annotations: map[string]string{"date": "today"},
  241. Labels: map[string]string{"dev": "seb"},
  242. },
  243. },
  244. },
  245. ref: esv1beta1.ExternalSecretDataRemoteRef{
  246. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  247. Key: "mysec",
  248. Property: "labels.dev",
  249. },
  250. want: []byte(`seb`),
  251. },
  252. {
  253. desc: "secret is not found",
  254. clientErr: apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret"),
  255. ref: esv1beta1.ExternalSecretDataRemoteRef{
  256. Key: "mysec",
  257. Property: "token",
  258. },
  259. wantErr: `Secret "secret" not found`,
  260. },
  261. {
  262. desc: "secret data with wrong property",
  263. secrets: map[string]*v1.Secret{
  264. "mysec": {
  265. Data: map[string][]byte{
  266. "token": []byte(`foobar`),
  267. },
  268. },
  269. },
  270. ref: esv1beta1.ExternalSecretDataRemoteRef{
  271. Key: "mysec",
  272. Property: "not-the-token",
  273. },
  274. wantErr: "property not-the-token does not exist in data of secret",
  275. },
  276. {
  277. desc: "secret metadata with wrong property",
  278. secrets: map[string]*v1.Secret{
  279. "mysec": {
  280. ObjectMeta: metav1.ObjectMeta{
  281. Annotations: map[string]string{"date": "today"},
  282. Labels: map[string]string{"dev": "seb"},
  283. },
  284. },
  285. },
  286. ref: esv1beta1.ExternalSecretDataRemoteRef{
  287. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  288. Key: "mysec",
  289. Property: "foo",
  290. },
  291. wantErr: "property foo does not exist in metadata of secret",
  292. },
  293. }
  294. for _, tt := range tests {
  295. t.Run(tt.desc, func(t *testing.T) {
  296. p := &Client{
  297. userSecretClient: &fakeClient{t: t, secretMap: tt.secrets, err: tt.clientErr},
  298. namespace: "default",
  299. }
  300. got, err := p.GetSecret(context.Background(), tt.ref)
  301. if err != nil {
  302. if tt.wantErr == "" {
  303. t.Fatalf("failed to call GetSecret: %v", err)
  304. }
  305. if !strings.Contains(err.Error(), tt.wantErr) {
  306. t.Fatalf("received an unexpected error: %q should have contained %q", err.Error(), tt.wantErr)
  307. }
  308. return
  309. }
  310. if tt.wantErr != "" {
  311. t.Fatalf("expected to receive an error but got nil")
  312. }
  313. if !reflect.DeepEqual(got, tt.want) {
  314. t.Fatalf("received an unexpected secret: got: %s, want %s", got, tt.want)
  315. }
  316. })
  317. }
  318. }
  319. func TestGetSecretMap(t *testing.T) {
  320. type fields struct {
  321. Client KClient
  322. ReviewClient RClient
  323. Namespace string
  324. }
  325. tests := []struct {
  326. name string
  327. fields fields
  328. ref esv1beta1.ExternalSecretDataRemoteRef
  329. want map[string][]byte
  330. wantErr bool
  331. }{
  332. {
  333. name: "successful case metadata without property",
  334. fields: fields{
  335. Client: &fakeClient{
  336. t: t,
  337. secretMap: map[string]*v1.Secret{
  338. "mysec": {
  339. ObjectMeta: metav1.ObjectMeta{
  340. Annotations: map[string]string{"date": "today"},
  341. Labels: map[string]string{"dev": "seb"},
  342. },
  343. },
  344. },
  345. },
  346. Namespace: "default",
  347. },
  348. ref: esv1beta1.ExternalSecretDataRemoteRef{
  349. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  350. Key: "mysec",
  351. },
  352. want: map[string][]byte{"annotations": []byte("{\"date\":\"today\"}"), "labels": []byte("{\"dev\":\"seb\"}")},
  353. },
  354. {
  355. name: "successful case metadata with single property",
  356. fields: fields{
  357. Client: &fakeClient{
  358. t: t,
  359. secretMap: map[string]*v1.Secret{
  360. "mysec": {
  361. ObjectMeta: metav1.ObjectMeta{
  362. Annotations: map[string]string{"date": "today"},
  363. Labels: map[string]string{"dev": "seb"},
  364. },
  365. },
  366. },
  367. },
  368. Namespace: "default",
  369. },
  370. ref: esv1beta1.ExternalSecretDataRemoteRef{
  371. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  372. Key: "mysec",
  373. Property: "labels",
  374. },
  375. want: map[string][]byte{"dev": []byte("\"seb\"")},
  376. },
  377. {
  378. name: "error case metadata with wrong property",
  379. fields: fields{
  380. Client: &fakeClient{
  381. t: t,
  382. secretMap: map[string]*v1.Secret{
  383. "mysec": {
  384. ObjectMeta: metav1.ObjectMeta{
  385. Annotations: map[string]string{"date": "today"},
  386. Labels: map[string]string{"dev": "seb"},
  387. },
  388. },
  389. },
  390. },
  391. Namespace: "default",
  392. },
  393. ref: esv1beta1.ExternalSecretDataRemoteRef{
  394. MetadataPolicy: esv1beta1.ExternalSecretMetadataPolicyFetch,
  395. Key: "mysec",
  396. Property: "foo",
  397. },
  398. wantErr: true,
  399. },
  400. }
  401. for _, tt := range tests {
  402. t.Run(tt.name, func(t *testing.T) {
  403. p := &Client{
  404. userSecretClient: tt.fields.Client,
  405. userReviewClient: tt.fields.ReviewClient,
  406. namespace: tt.fields.Namespace,
  407. }
  408. got, err := p.GetSecretMap(context.Background(), tt.ref)
  409. if (err != nil) != tt.wantErr {
  410. t.Errorf("ProviderKubernetes.GetSecretMap() error = %v, wantErr %v", err, tt.wantErr)
  411. return
  412. }
  413. if !reflect.DeepEqual(got, tt.want) {
  414. t.Errorf("ProviderKubernetes.GetSecretMap() = %v, want %v", got, tt.want)
  415. }
  416. })
  417. }
  418. }
  419. func TestGetAllSecrets(t *testing.T) {
  420. type fields struct {
  421. Client KClient
  422. ReviewClient RClient
  423. Namespace string
  424. }
  425. type args struct {
  426. ctx context.Context
  427. ref esv1beta1.ExternalSecretFind
  428. }
  429. tests := []struct {
  430. name string
  431. fields fields
  432. args args
  433. want map[string][]byte
  434. wantErr bool
  435. }{
  436. {
  437. name: "use regex",
  438. fields: fields{
  439. Client: &fakeClient{
  440. t: t,
  441. secretMap: map[string]*v1.Secret{
  442. "mysec": {
  443. ObjectMeta: metav1.ObjectMeta{
  444. Name: "mysec",
  445. },
  446. Data: map[string][]byte{
  447. "token": []byte(`foo`),
  448. },
  449. },
  450. "other": {
  451. ObjectMeta: metav1.ObjectMeta{
  452. Name: "other",
  453. },
  454. Data: map[string][]byte{
  455. "token": []byte(`bar`),
  456. },
  457. },
  458. },
  459. },
  460. },
  461. args: args{
  462. ref: esv1beta1.ExternalSecretFind{
  463. Name: &esv1beta1.FindName{
  464. RegExp: "other",
  465. },
  466. },
  467. },
  468. want: map[string][]byte{
  469. "other": []byte(`{"token":"bar"}`),
  470. },
  471. },
  472. {
  473. name: "use tags/labels",
  474. fields: fields{
  475. Client: &fakeClient{
  476. t: t,
  477. expectedListOptions: metav1.ListOptions{
  478. LabelSelector: "app=foobar",
  479. },
  480. secretMap: map[string]*v1.Secret{
  481. "mysec": {
  482. ObjectMeta: metav1.ObjectMeta{
  483. Name: "mysec",
  484. },
  485. Data: map[string][]byte{
  486. "token": []byte(`foo`),
  487. },
  488. },
  489. "other": {
  490. ObjectMeta: metav1.ObjectMeta{
  491. Name: "other",
  492. },
  493. Data: map[string][]byte{
  494. "token": []byte(`bar`),
  495. },
  496. },
  497. },
  498. },
  499. },
  500. args: args{
  501. ref: esv1beta1.ExternalSecretFind{
  502. Tags: map[string]string{
  503. "app": "foobar",
  504. },
  505. },
  506. },
  507. want: map[string][]byte{
  508. "mysec": []byte(`{"token":"foo"}`),
  509. "other": []byte(`{"token":"bar"}`),
  510. },
  511. },
  512. }
  513. for _, tt := range tests {
  514. t.Run(tt.name, func(t *testing.T) {
  515. p := &Client{
  516. userSecretClient: tt.fields.Client,
  517. userReviewClient: tt.fields.ReviewClient,
  518. namespace: tt.fields.Namespace,
  519. }
  520. got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
  521. if (err != nil) != tt.wantErr {
  522. t.Errorf("ProviderKubernetes.GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
  523. return
  524. }
  525. if !reflect.DeepEqual(got, tt.want) {
  526. t.Errorf("ProviderKubernetes.GetAllSecrets() = %v, want %v", got, tt.want)
  527. }
  528. })
  529. }
  530. }
  531. func TestDeleteSecret(t *testing.T) {
  532. type fields struct {
  533. Client KClient
  534. }
  535. tests := []struct {
  536. name string
  537. fields fields
  538. ref esv1beta1.PushSecretRemoteRef
  539. wantSecretMap map[string]*v1.Secret
  540. wantErr bool
  541. }{
  542. {
  543. name: "refuse to delete without property",
  544. fields: fields{
  545. Client: &fakeClient{
  546. t: t,
  547. secretMap: map[string]*v1.Secret{
  548. "mysec": {
  549. Data: map[string][]byte{
  550. "token": []byte(`foobar`),
  551. },
  552. },
  553. },
  554. },
  555. },
  556. ref: v1alpha1.PushSecretRemoteRef{
  557. RemoteKey: "mysec",
  558. },
  559. wantErr: true,
  560. wantSecretMap: map[string]*v1.Secret{
  561. "mysec": {
  562. Data: map[string][]byte{
  563. "token": []byte(`foobar`),
  564. },
  565. },
  566. },
  567. },
  568. {
  569. name: "gracefully ignore not found secret",
  570. fields: fields{
  571. Client: &fakeClient{
  572. t: t,
  573. secretMap: map[string]*v1.Secret{},
  574. },
  575. },
  576. ref: v1alpha1.PushSecretRemoteRef{
  577. RemoteKey: "mysec",
  578. Property: "token",
  579. },
  580. wantErr: false,
  581. wantSecretMap: map[string]*v1.Secret{},
  582. },
  583. {
  584. name: "gracefully ignore not found property",
  585. fields: fields{
  586. Client: &fakeClient{
  587. t: t,
  588. secretMap: map[string]*v1.Secret{
  589. "mysec": {
  590. Data: map[string][]byte{
  591. "token": []byte(`foobar`),
  592. },
  593. },
  594. },
  595. },
  596. },
  597. ref: v1alpha1.PushSecretRemoteRef{
  598. RemoteKey: "mysec",
  599. Property: "secret",
  600. },
  601. wantErr: false,
  602. wantSecretMap: map[string]*v1.Secret{
  603. "mysec": {
  604. Data: map[string][]byte{
  605. "token": []byte(`foobar`),
  606. },
  607. },
  608. },
  609. },
  610. {
  611. name: "unexpected lookup error",
  612. fields: fields{
  613. Client: &fakeClient{
  614. t: t,
  615. secretMap: map[string]*v1.Secret{
  616. "mysec": {
  617. Data: map[string][]byte{
  618. "token": []byte(`foobar`),
  619. },
  620. },
  621. },
  622. err: errors.New(errSomethingWentWrong),
  623. },
  624. },
  625. ref: v1alpha1.PushSecretRemoteRef{
  626. RemoteKey: "mysec",
  627. },
  628. wantErr: true,
  629. wantSecretMap: map[string]*v1.Secret{
  630. "mysec": {
  631. Data: map[string][]byte{
  632. "token": []byte(`foobar`),
  633. },
  634. },
  635. },
  636. },
  637. {
  638. name: "delete whole secret if only property should be removed",
  639. fields: fields{
  640. Client: &fakeClient{
  641. t: t,
  642. secretMap: map[string]*v1.Secret{
  643. "mysec": {
  644. Data: map[string][]byte{
  645. "token": []byte(`foobar`),
  646. },
  647. },
  648. },
  649. },
  650. },
  651. ref: v1alpha1.PushSecretRemoteRef{
  652. RemoteKey: "mysec",
  653. Property: "token",
  654. },
  655. wantErr: false,
  656. wantSecretMap: map[string]*v1.Secret{},
  657. },
  658. {
  659. name: "multiple properties, just remove that one",
  660. fields: fields{
  661. Client: &fakeClient{
  662. t: t,
  663. secretMap: map[string]*v1.Secret{
  664. "mysec": {
  665. Data: map[string][]byte{
  666. "token": []byte(`foo`),
  667. "secret": []byte(`bar`),
  668. },
  669. },
  670. },
  671. },
  672. },
  673. ref: v1alpha1.PushSecretRemoteRef{
  674. RemoteKey: "mysec",
  675. Property: "token",
  676. },
  677. wantErr: false,
  678. wantSecretMap: map[string]*v1.Secret{
  679. "mysec": {
  680. Data: map[string][]byte{
  681. "secret": []byte(`bar`),
  682. },
  683. },
  684. },
  685. },
  686. }
  687. for _, tt := range tests {
  688. t.Run(tt.name, func(t *testing.T) {
  689. p := &Client{
  690. userSecretClient: tt.fields.Client,
  691. }
  692. err := p.DeleteSecret(context.Background(), tt.ref)
  693. if (err != nil) != tt.wantErr {
  694. t.Errorf("ProviderKubernetes.DeleteSecret() error = %v, wantErr %v", err, tt.wantErr)
  695. return
  696. }
  697. fClient := tt.fields.Client.(*fakeClient)
  698. if diff := cmp.Diff(tt.wantSecretMap, fClient.secretMap); diff != "" {
  699. t.Errorf("Unexpected resulting secrets map: -want, +got :\n%s\n", diff)
  700. }
  701. })
  702. }
  703. }
  704. func TestPushSecret(t *testing.T) {
  705. secretKey := "secret-key"
  706. type fields struct {
  707. Client KClient
  708. }
  709. tests := []struct {
  710. name string
  711. fields fields
  712. data testingfake.PushSecretData
  713. secret *v1.Secret
  714. wantSecretMap map[string]*v1.Secret
  715. wantErr bool
  716. }{
  717. {
  718. name: "refuse to work without property if secret key is provided",
  719. fields: fields{
  720. Client: &fakeClient{
  721. t: t,
  722. secretMap: map[string]*v1.Secret{
  723. "mysec": {
  724. Data: map[string][]byte{
  725. "token": []byte(`foo`),
  726. },
  727. },
  728. },
  729. },
  730. },
  731. data: testingfake.PushSecretData{
  732. SecretKey: secretKey,
  733. RemoteKey: "mysec",
  734. },
  735. secret: &v1.Secret{
  736. Data: map[string][]byte{secretKey: []byte("bar")},
  737. },
  738. wantErr: true,
  739. wantSecretMap: map[string]*v1.Secret{
  740. "mysec": {
  741. Data: map[string][]byte{
  742. "token": []byte(`foo`),
  743. },
  744. },
  745. },
  746. },
  747. {
  748. name: "push the whole secret if neither remote property or secretKey is defined but keep existing keys",
  749. fields: fields{
  750. Client: &fakeClient{
  751. t: t,
  752. secretMap: map[string]*v1.Secret{
  753. "mysec": {
  754. Data: map[string][]byte{
  755. "token": []byte(`foo`),
  756. },
  757. },
  758. },
  759. },
  760. },
  761. data: testingfake.PushSecretData{
  762. RemoteKey: "mysec",
  763. },
  764. secret: &v1.Secret{
  765. Data: map[string][]byte{"token2": []byte("foo")},
  766. },
  767. wantSecretMap: map[string]*v1.Secret{
  768. "mysec": {
  769. Data: map[string][]byte{
  770. "token": []byte(`foo`),
  771. "token2": []byte(`foo`),
  772. },
  773. },
  774. },
  775. },
  776. {
  777. name: "push the whole secret while secret exists into a single property",
  778. fields: fields{
  779. Client: &fakeClient{
  780. t: t,
  781. secretMap: map[string]*v1.Secret{
  782. "mysec": {
  783. Data: map[string][]byte{
  784. "token": []byte(`foo`),
  785. },
  786. },
  787. },
  788. },
  789. },
  790. data: testingfake.PushSecretData{
  791. RemoteKey: "mysec",
  792. Property: "token",
  793. },
  794. secret: &v1.Secret{
  795. Data: map[string][]byte{"foo": []byte("bar")},
  796. },
  797. wantSecretMap: map[string]*v1.Secret{
  798. "mysec": {
  799. Data: map[string][]byte{
  800. "token": []byte(`{"foo":"bar"}`),
  801. },
  802. },
  803. },
  804. },
  805. {
  806. name: "push the whole secret while secret exists but new property is defined should update the secret and keep existing key",
  807. fields: fields{
  808. Client: &fakeClient{
  809. t: t,
  810. secretMap: map[string]*v1.Secret{
  811. "mysec": {
  812. Data: map[string][]byte{
  813. "token": []byte(`foo`),
  814. },
  815. },
  816. },
  817. },
  818. },
  819. data: testingfake.PushSecretData{
  820. RemoteKey: "mysec",
  821. Property: "token2",
  822. },
  823. secret: &v1.Secret{
  824. Data: map[string][]byte{"foo": []byte("bar")},
  825. },
  826. wantSecretMap: map[string]*v1.Secret{
  827. "mysec": {
  828. Data: map[string][]byte{
  829. "token": []byte(`foo`),
  830. "token2": []byte(`{"foo":"bar"}`),
  831. },
  832. },
  833. },
  834. },
  835. {
  836. name: "push the whole secret as json if remote property is defined but secret key is not given",
  837. fields: fields{
  838. Client: &fakeClient{
  839. t: t,
  840. secretMap: map[string]*v1.Secret{},
  841. },
  842. },
  843. data: testingfake.PushSecretData{
  844. RemoteKey: "mysec",
  845. Property: "marshaled",
  846. },
  847. secret: &v1.Secret{
  848. Data: map[string][]byte{
  849. "token": []byte("foo"),
  850. "token2": []byte("2"),
  851. },
  852. },
  853. wantSecretMap: map[string]*v1.Secret{
  854. "mysec": {
  855. Data: map[string][]byte{
  856. "marshaled": []byte(`{"token":"foo","token2":"2"}`),
  857. },
  858. Type: "Opaque",
  859. },
  860. },
  861. },
  862. {
  863. name: "add missing property to existing secret",
  864. fields: fields{
  865. Client: &fakeClient{
  866. t: t,
  867. secretMap: map[string]*v1.Secret{
  868. "mysec": {
  869. Data: map[string][]byte{
  870. "token": []byte(`foo`),
  871. },
  872. },
  873. },
  874. },
  875. },
  876. secret: &v1.Secret{
  877. Data: map[string][]byte{secretKey: []byte("bar")},
  878. },
  879. data: testingfake.PushSecretData{
  880. SecretKey: secretKey,
  881. RemoteKey: "mysec",
  882. Property: "secret",
  883. },
  884. wantErr: false,
  885. wantSecretMap: map[string]*v1.Secret{
  886. "mysec": {
  887. Data: map[string][]byte{
  888. "token": []byte(`foo`),
  889. "secret": []byte(`bar`),
  890. },
  891. },
  892. },
  893. },
  894. {
  895. name: "replace existing property in existing secret",
  896. fields: fields{
  897. Client: &fakeClient{
  898. t: t,
  899. secretMap: map[string]*v1.Secret{
  900. "mysec": {
  901. Data: map[string][]byte{
  902. "token": []byte(`foo`),
  903. },
  904. },
  905. },
  906. },
  907. },
  908. secret: &v1.Secret{
  909. Data: map[string][]byte{secretKey: []byte("bar")},
  910. },
  911. data: testingfake.PushSecretData{
  912. SecretKey: secretKey,
  913. RemoteKey: "mysec",
  914. Property: "token",
  915. },
  916. wantErr: false,
  917. wantSecretMap: map[string]*v1.Secret{
  918. "mysec": {
  919. Data: map[string][]byte{
  920. "token": []byte(`bar`),
  921. },
  922. },
  923. },
  924. },
  925. {
  926. name: "create new secret",
  927. fields: fields{
  928. Client: &fakeClient{
  929. t: t,
  930. secretMap: map[string]*v1.Secret{
  931. "yoursec": {
  932. Data: map[string][]byte{
  933. "token": []byte(`foo`),
  934. },
  935. },
  936. },
  937. },
  938. },
  939. secret: &v1.Secret{
  940. Data: map[string][]byte{secretKey: []byte("bar")},
  941. },
  942. data: testingfake.PushSecretData{
  943. SecretKey: secretKey,
  944. RemoteKey: "mysec",
  945. Property: "secret",
  946. },
  947. wantErr: false,
  948. wantSecretMap: map[string]*v1.Secret{
  949. "yoursec": {
  950. Data: map[string][]byte{
  951. "token": []byte(`foo`),
  952. },
  953. },
  954. "mysec": {
  955. Data: map[string][]byte{
  956. "secret": []byte(`bar`),
  957. },
  958. Type: v1.SecretTypeOpaque,
  959. },
  960. },
  961. },
  962. {
  963. name: "create new dockerconfigjson secret",
  964. fields: fields{
  965. Client: &fakeClient{
  966. t: t,
  967. secretMap: map[string]*v1.Secret{
  968. "yoursec": {
  969. Data: map[string][]byte{
  970. "token": []byte(`foo`),
  971. },
  972. },
  973. },
  974. },
  975. },
  976. secret: &v1.Secret{
  977. Type: v1.SecretTypeDockerConfigJson,
  978. Data: map[string][]byte{secretKey: []byte(`{"auths": {"myregistry.localhost": {"username": "{{ .username }}", "password": "{{ .password }}"}}}`)},
  979. },
  980. data: testingfake.PushSecretData{
  981. SecretKey: secretKey,
  982. RemoteKey: "mysec",
  983. Property: "config.json",
  984. },
  985. wantErr: false,
  986. wantSecretMap: map[string]*v1.Secret{
  987. "yoursec": {
  988. Data: map[string][]byte{
  989. "token": []byte(`foo`),
  990. },
  991. },
  992. "mysec": {
  993. Data: map[string][]byte{
  994. "config.json": []byte(`{"auths": {"myregistry.localhost": {"username": "{{ .username }}", "password": "{{ .password }}"}}}`),
  995. },
  996. Type: v1.SecretTypeDockerConfigJson,
  997. },
  998. },
  999. }}
  1000. for _, tt := range tests {
  1001. t.Run(tt.name, func(t *testing.T) {
  1002. p := &Client{
  1003. userSecretClient: tt.fields.Client,
  1004. store: &esv1beta1.KubernetesProvider{},
  1005. }
  1006. err := p.PushSecret(context.Background(), tt.secret, tt.data)
  1007. if (err != nil) != tt.wantErr {
  1008. t.Errorf("ProviderKubernetes error = %v, wantErr %v", err, tt.wantErr)
  1009. return
  1010. }
  1011. fClient := tt.fields.Client.(*fakeClient)
  1012. if diff := cmp.Diff(tt.wantSecretMap, fClient.secretMap); diff != "" {
  1013. t.Errorf("Unexpected resulting secrets map: -want, +got :\n%s\n", diff)
  1014. }
  1015. })
  1016. }
  1017. }