client_test.go 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414
  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. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  23. apierrors "k8s.io/apimachinery/pkg/api/errors"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/runtime/schema"
  26. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  27. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  28. testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  29. )
  30. const (
  31. errSomethingWentWrong = "Something went wrong"
  32. )
  33. type fakeClient struct {
  34. t *testing.T
  35. secretMap map[string]*v1.Secret
  36. expectedListOptions metav1.ListOptions
  37. err error
  38. }
  39. func (fk *fakeClient) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) {
  40. if fk.err != nil {
  41. return nil, fk.err
  42. }
  43. secret, ok := fk.secretMap[name]
  44. if !ok {
  45. return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret")
  46. }
  47. // return inmutable to simulate external system and avoid accidental side effects
  48. sCopy := secret.DeepCopy()
  49. // update operation requires to relate names
  50. sCopy.Name = name
  51. return sCopy, nil
  52. }
  53. func (fk *fakeClient) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
  54. assert.Equal(fk.t, fk.expectedListOptions, opts)
  55. list := &v1.SecretList{}
  56. for _, v := range fk.secretMap {
  57. list.Items = append(list.Items, *v)
  58. }
  59. return list, nil
  60. }
  61. func (fk *fakeClient) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
  62. if fk.err != nil {
  63. return fk.err
  64. }
  65. _, ok := fk.secretMap[name]
  66. if !ok {
  67. return apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret")
  68. }
  69. delete(fk.secretMap, name)
  70. return nil
  71. }
  72. func (fk *fakeClient) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
  73. s := &v1.Secret{
  74. Data: secret.Data,
  75. ObjectMeta: secret.ObjectMeta,
  76. Type: secret.Type,
  77. }
  78. fk.secretMap[secret.Name] = s
  79. return s, nil
  80. }
  81. func (fk *fakeClient) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) {
  82. s, ok := fk.secretMap[secret.Name]
  83. if !ok {
  84. return nil, errors.New("error while updating secret")
  85. }
  86. s.ObjectMeta = secret.ObjectMeta
  87. s.Data = secret.Data
  88. return s, nil
  89. }
  90. var binaryTestData = []byte{0x00, 0xff, 0x00, 0xff, 0xac, 0xab, 0x28, 0x21}
  91. func TestGetSecret(t *testing.T) {
  92. tests := []struct {
  93. desc string
  94. secrets map[string]*v1.Secret
  95. clientErr error
  96. ref esv1.ExternalSecretDataRemoteRef
  97. want []byte
  98. wantErr string
  99. }{
  100. {
  101. desc: "secret data with correct property",
  102. secrets: map[string]*v1.Secret{
  103. "mysec": {
  104. Data: map[string][]byte{
  105. "token": []byte(`foobar`),
  106. },
  107. },
  108. },
  109. ref: esv1.ExternalSecretDataRemoteRef{
  110. Key: "mysec",
  111. Property: "token",
  112. },
  113. want: []byte(`foobar`),
  114. },
  115. {
  116. desc: "secret data with multi level property",
  117. secrets: map[string]*v1.Secret{
  118. "mysec": {
  119. Data: map[string][]byte{
  120. "foo": []byte(`{"huga":{"bar":"val"}}`),
  121. },
  122. },
  123. },
  124. ref: esv1.ExternalSecretDataRemoteRef{
  125. Key: "mysec",
  126. Property: "foo.huga.bar",
  127. },
  128. want: []byte(`val`),
  129. },
  130. {
  131. desc: "secret data with property containing .",
  132. secrets: map[string]*v1.Secret{
  133. "mysec": {
  134. Data: map[string][]byte{
  135. "foo.png": []byte(`correct`),
  136. "foo": []byte(`{"png":"wrong"}`),
  137. },
  138. },
  139. },
  140. ref: esv1.ExternalSecretDataRemoteRef{
  141. Key: "mysec",
  142. Property: "foo.png",
  143. },
  144. want: []byte(`correct`),
  145. },
  146. {
  147. desc: "secret data contains html characters",
  148. secrets: map[string]*v1.Secret{
  149. "mysec": {
  150. Data: map[string][]byte{
  151. "html": []byte(`<foobar>`),
  152. },
  153. },
  154. },
  155. ref: esv1.ExternalSecretDataRemoteRef{
  156. Key: "mysec",
  157. },
  158. want: []byte(`{"html":"<foobar>"}`),
  159. },
  160. {
  161. desc: "secret metadata contains html characters",
  162. secrets: map[string]*v1.Secret{
  163. "mysec": {
  164. ObjectMeta: metav1.ObjectMeta{
  165. Annotations: map[string]string{"date": "today"},
  166. Labels: map[string]string{"dev": "<seb>"},
  167. },
  168. },
  169. },
  170. ref: esv1.ExternalSecretDataRemoteRef{
  171. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  172. Key: "mysec",
  173. },
  174. want: []byte(`{"annotations":{"date":"today"},"labels":{"dev":"<seb>"}}`),
  175. },
  176. {
  177. desc: "secret data contains binary",
  178. secrets: map[string]*v1.Secret{
  179. "mysec": {
  180. Data: map[string][]byte{
  181. "bindata": binaryTestData,
  182. },
  183. },
  184. },
  185. ref: esv1.ExternalSecretDataRemoteRef{
  186. Key: "mysec",
  187. Property: "bindata",
  188. },
  189. want: binaryTestData,
  190. },
  191. {
  192. desc: "secret data without property",
  193. secrets: map[string]*v1.Secret{
  194. "mysec": {
  195. Data: map[string][]byte{
  196. "token": []byte(`foobar`),
  197. },
  198. },
  199. },
  200. ref: esv1.ExternalSecretDataRemoteRef{
  201. Key: "mysec",
  202. },
  203. want: []byte(`{"token":"foobar"}`),
  204. },
  205. {
  206. desc: "secret metadata without property",
  207. secrets: map[string]*v1.Secret{
  208. "mysec": {
  209. ObjectMeta: metav1.ObjectMeta{
  210. Annotations: map[string]string{"date": "today"},
  211. Labels: map[string]string{"dev": "seb"},
  212. },
  213. },
  214. },
  215. ref: esv1.ExternalSecretDataRemoteRef{
  216. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  217. Key: "mysec",
  218. },
  219. want: []byte(`{"annotations":{"date":"today"},"labels":{"dev":"seb"}}`),
  220. },
  221. {
  222. desc: "secret metadata with single level property",
  223. secrets: map[string]*v1.Secret{
  224. "mysec": {
  225. ObjectMeta: metav1.ObjectMeta{
  226. Annotations: map[string]string{"date": "today"},
  227. Labels: map[string]string{"dev": "seb"},
  228. },
  229. },
  230. },
  231. ref: esv1.ExternalSecretDataRemoteRef{
  232. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  233. Key: "mysec",
  234. Property: "labels",
  235. },
  236. want: []byte(`{"dev":"seb"}`),
  237. },
  238. {
  239. desc: "secret metadata with multiple level property",
  240. secrets: map[string]*v1.Secret{
  241. "mysec": {
  242. ObjectMeta: metav1.ObjectMeta{
  243. Annotations: map[string]string{"date": "today"},
  244. Labels: map[string]string{"dev": "seb"},
  245. },
  246. },
  247. },
  248. ref: esv1.ExternalSecretDataRemoteRef{
  249. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  250. Key: "mysec",
  251. Property: "labels.dev",
  252. },
  253. want: []byte(`seb`),
  254. },
  255. {
  256. desc: "secret is not found",
  257. clientErr: apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, "secret"),
  258. ref: esv1.ExternalSecretDataRemoteRef{
  259. Key: "mysec",
  260. Property: "token",
  261. },
  262. wantErr: `Secret "secret" not found`,
  263. },
  264. {
  265. desc: "secret data with wrong property",
  266. secrets: map[string]*v1.Secret{
  267. "mysec": {
  268. Data: map[string][]byte{
  269. "token": []byte(`foobar`),
  270. },
  271. },
  272. },
  273. ref: esv1.ExternalSecretDataRemoteRef{
  274. Key: "mysec",
  275. Property: "not-the-token",
  276. },
  277. wantErr: "property not-the-token does not exist in data of secret",
  278. },
  279. {
  280. desc: "secret metadata with wrong property",
  281. secrets: map[string]*v1.Secret{
  282. "mysec": {
  283. ObjectMeta: metav1.ObjectMeta{
  284. Annotations: map[string]string{"date": "today"},
  285. Labels: map[string]string{"dev": "seb"},
  286. },
  287. },
  288. },
  289. ref: esv1.ExternalSecretDataRemoteRef{
  290. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  291. Key: "mysec",
  292. Property: "foo",
  293. },
  294. wantErr: "property foo does not exist in metadata of secret",
  295. },
  296. }
  297. for _, tt := range tests {
  298. t.Run(tt.desc, func(t *testing.T) {
  299. p := &Client{
  300. userSecretClient: &fakeClient{t: t, secretMap: tt.secrets, err: tt.clientErr},
  301. namespace: "default",
  302. }
  303. got, err := p.GetSecret(context.Background(), tt.ref)
  304. if err != nil {
  305. if tt.wantErr == "" {
  306. t.Fatalf("failed to call GetSecret: %v", err)
  307. }
  308. if !strings.Contains(err.Error(), tt.wantErr) {
  309. t.Fatalf("received an unexpected error: %q should have contained %q", err.Error(), tt.wantErr)
  310. }
  311. return
  312. }
  313. if tt.wantErr != "" {
  314. t.Fatalf("expected to receive an error but got nil")
  315. }
  316. if !reflect.DeepEqual(got, tt.want) {
  317. t.Fatalf("received an unexpected secret: got: %s, want %s", got, tt.want)
  318. }
  319. })
  320. }
  321. }
  322. func TestGetSecretMap(t *testing.T) {
  323. type fields struct {
  324. Client KClient
  325. ReviewClient RClient
  326. Namespace string
  327. }
  328. tests := []struct {
  329. name string
  330. fields fields
  331. ref esv1.ExternalSecretDataRemoteRef
  332. want map[string][]byte
  333. wantErr bool
  334. }{
  335. {
  336. name: "successful case metadata without property",
  337. fields: fields{
  338. Client: &fakeClient{
  339. t: t,
  340. secretMap: map[string]*v1.Secret{
  341. "mysec": {
  342. ObjectMeta: metav1.ObjectMeta{
  343. Annotations: map[string]string{"date": "today"},
  344. Labels: map[string]string{"dev": "seb"},
  345. },
  346. },
  347. },
  348. },
  349. Namespace: "default",
  350. },
  351. ref: esv1.ExternalSecretDataRemoteRef{
  352. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  353. Key: "mysec",
  354. },
  355. want: map[string][]byte{"annotations": []byte("{\"date\":\"today\"}"), "labels": []byte("{\"dev\":\"seb\"}")},
  356. },
  357. {
  358. name: "successful case metadata with single property",
  359. fields: fields{
  360. Client: &fakeClient{
  361. t: t,
  362. secretMap: map[string]*v1.Secret{
  363. "mysec": {
  364. ObjectMeta: metav1.ObjectMeta{
  365. Annotations: map[string]string{"date": "today"},
  366. Labels: map[string]string{"dev": "seb"},
  367. },
  368. },
  369. },
  370. },
  371. Namespace: "default",
  372. },
  373. ref: esv1.ExternalSecretDataRemoteRef{
  374. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  375. Key: "mysec",
  376. Property: "labels",
  377. },
  378. want: map[string][]byte{"dev": []byte("\"seb\"")},
  379. },
  380. {
  381. name: "error case metadata with wrong property",
  382. fields: fields{
  383. Client: &fakeClient{
  384. t: t,
  385. secretMap: map[string]*v1.Secret{
  386. "mysec": {
  387. ObjectMeta: metav1.ObjectMeta{
  388. Annotations: map[string]string{"date": "today"},
  389. Labels: map[string]string{"dev": "seb"},
  390. },
  391. },
  392. },
  393. },
  394. Namespace: "default",
  395. },
  396. ref: esv1.ExternalSecretDataRemoteRef{
  397. MetadataPolicy: esv1.ExternalSecretMetadataPolicyFetch,
  398. Key: "mysec",
  399. Property: "foo",
  400. },
  401. wantErr: true,
  402. },
  403. }
  404. for _, tt := range tests {
  405. t.Run(tt.name, func(t *testing.T) {
  406. p := &Client{
  407. userSecretClient: tt.fields.Client,
  408. userReviewClient: tt.fields.ReviewClient,
  409. namespace: tt.fields.Namespace,
  410. }
  411. got, err := p.GetSecretMap(context.Background(), tt.ref)
  412. if (err != nil) != tt.wantErr {
  413. t.Errorf("ProviderKubernetes.GetSecretMap() error = %v, wantErr %v", err, tt.wantErr)
  414. return
  415. }
  416. if !reflect.DeepEqual(got, tt.want) {
  417. t.Errorf("ProviderKubernetes.GetSecretMap() = %v, want %v", got, tt.want)
  418. }
  419. })
  420. }
  421. }
  422. func TestGetAllSecrets(t *testing.T) {
  423. type fields struct {
  424. Client KClient
  425. ReviewClient RClient
  426. Namespace string
  427. }
  428. type args struct {
  429. ctx context.Context
  430. ref esv1.ExternalSecretFind
  431. }
  432. tests := []struct {
  433. name string
  434. fields fields
  435. args args
  436. want map[string][]byte
  437. wantErr bool
  438. }{
  439. {
  440. name: "use regex",
  441. fields: fields{
  442. Client: &fakeClient{
  443. t: t,
  444. secretMap: map[string]*v1.Secret{
  445. "mysec": {
  446. ObjectMeta: metav1.ObjectMeta{
  447. Name: "mysec",
  448. },
  449. Data: map[string][]byte{
  450. "token": []byte(`foo`),
  451. },
  452. },
  453. "other": {
  454. ObjectMeta: metav1.ObjectMeta{
  455. Name: "other",
  456. },
  457. Data: map[string][]byte{
  458. "token": []byte(`bar`),
  459. },
  460. },
  461. },
  462. },
  463. },
  464. args: args{
  465. ref: esv1.ExternalSecretFind{
  466. Name: &esv1.FindName{
  467. RegExp: "other",
  468. },
  469. },
  470. },
  471. want: map[string][]byte{
  472. "other": []byte(`{"token":"bar"}`),
  473. },
  474. },
  475. {
  476. name: "use tags/labels",
  477. fields: fields{
  478. Client: &fakeClient{
  479. t: t,
  480. expectedListOptions: metav1.ListOptions{
  481. LabelSelector: "app=foobar",
  482. },
  483. secretMap: map[string]*v1.Secret{
  484. "mysec": {
  485. ObjectMeta: metav1.ObjectMeta{
  486. Name: "mysec",
  487. },
  488. Data: map[string][]byte{
  489. "token": []byte(`foo`),
  490. },
  491. },
  492. "other": {
  493. ObjectMeta: metav1.ObjectMeta{
  494. Name: "other",
  495. },
  496. Data: map[string][]byte{
  497. "token": []byte(`bar`),
  498. },
  499. },
  500. },
  501. },
  502. },
  503. args: args{
  504. ref: esv1.ExternalSecretFind{
  505. Tags: map[string]string{
  506. "app": "foobar",
  507. },
  508. },
  509. },
  510. want: map[string][]byte{
  511. "mysec": []byte(`{"token":"foo"}`),
  512. "other": []byte(`{"token":"bar"}`),
  513. },
  514. },
  515. }
  516. for _, tt := range tests {
  517. t.Run(tt.name, func(t *testing.T) {
  518. p := &Client{
  519. userSecretClient: tt.fields.Client,
  520. userReviewClient: tt.fields.ReviewClient,
  521. namespace: tt.fields.Namespace,
  522. }
  523. got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
  524. if (err != nil) != tt.wantErr {
  525. t.Errorf("ProviderKubernetes.GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
  526. return
  527. }
  528. if !reflect.DeepEqual(got, tt.want) {
  529. t.Errorf("ProviderKubernetes.GetAllSecrets() = %v, want %v", got, tt.want)
  530. }
  531. })
  532. }
  533. }
  534. func TestDeleteSecret(t *testing.T) {
  535. type fields struct {
  536. Client KClient
  537. }
  538. tests := []struct {
  539. name string
  540. fields fields
  541. ref esv1.PushSecretRemoteRef
  542. wantSecretMap map[string]*v1.Secret
  543. wantErr bool
  544. }{
  545. {
  546. name: "refuse to delete without property",
  547. fields: fields{
  548. Client: &fakeClient{
  549. t: t,
  550. secretMap: map[string]*v1.Secret{
  551. "mysec": {
  552. Data: map[string][]byte{
  553. "token": []byte(`foobar`),
  554. },
  555. },
  556. },
  557. },
  558. },
  559. ref: v1alpha1.PushSecretRemoteRef{
  560. RemoteKey: "mysec",
  561. },
  562. wantErr: true,
  563. wantSecretMap: map[string]*v1.Secret{
  564. "mysec": {
  565. Data: map[string][]byte{
  566. "token": []byte(`foobar`),
  567. },
  568. },
  569. },
  570. },
  571. {
  572. name: "gracefully ignore not found secret",
  573. fields: fields{
  574. Client: &fakeClient{
  575. t: t,
  576. secretMap: map[string]*v1.Secret{},
  577. },
  578. },
  579. ref: v1alpha1.PushSecretRemoteRef{
  580. RemoteKey: "mysec",
  581. Property: "token",
  582. },
  583. wantErr: false,
  584. wantSecretMap: map[string]*v1.Secret{},
  585. },
  586. {
  587. name: "gracefully ignore not found property",
  588. fields: fields{
  589. Client: &fakeClient{
  590. t: t,
  591. secretMap: map[string]*v1.Secret{
  592. "mysec": {
  593. Data: map[string][]byte{
  594. "token": []byte(`foobar`),
  595. },
  596. },
  597. },
  598. },
  599. },
  600. ref: v1alpha1.PushSecretRemoteRef{
  601. RemoteKey: "mysec",
  602. Property: "secret",
  603. },
  604. wantErr: false,
  605. wantSecretMap: map[string]*v1.Secret{
  606. "mysec": {
  607. Data: map[string][]byte{
  608. "token": []byte(`foobar`),
  609. },
  610. },
  611. },
  612. },
  613. {
  614. name: "unexpected lookup error",
  615. fields: fields{
  616. Client: &fakeClient{
  617. t: t,
  618. secretMap: map[string]*v1.Secret{
  619. "mysec": {
  620. Data: map[string][]byte{
  621. "token": []byte(`foobar`),
  622. },
  623. },
  624. },
  625. err: errors.New(errSomethingWentWrong),
  626. },
  627. },
  628. ref: v1alpha1.PushSecretRemoteRef{
  629. RemoteKey: "mysec",
  630. },
  631. wantErr: true,
  632. wantSecretMap: map[string]*v1.Secret{
  633. "mysec": {
  634. Data: map[string][]byte{
  635. "token": []byte(`foobar`),
  636. },
  637. },
  638. },
  639. },
  640. {
  641. name: "delete whole secret if only property should be removed",
  642. fields: fields{
  643. Client: &fakeClient{
  644. t: t,
  645. secretMap: map[string]*v1.Secret{
  646. "mysec": {
  647. Data: map[string][]byte{
  648. "token": []byte(`foobar`),
  649. },
  650. },
  651. },
  652. },
  653. },
  654. ref: v1alpha1.PushSecretRemoteRef{
  655. RemoteKey: "mysec",
  656. Property: "token",
  657. },
  658. wantErr: false,
  659. wantSecretMap: map[string]*v1.Secret{},
  660. },
  661. {
  662. name: "multiple properties, just remove that one",
  663. fields: fields{
  664. Client: &fakeClient{
  665. t: t,
  666. secretMap: map[string]*v1.Secret{
  667. "mysec": {
  668. Data: map[string][]byte{
  669. "token": []byte(`foo`),
  670. "secret": []byte(`bar`),
  671. },
  672. },
  673. },
  674. },
  675. },
  676. ref: v1alpha1.PushSecretRemoteRef{
  677. RemoteKey: "mysec",
  678. Property: "token",
  679. },
  680. wantErr: false,
  681. wantSecretMap: map[string]*v1.Secret{
  682. "mysec": {
  683. ObjectMeta: metav1.ObjectMeta{
  684. Name: "mysec",
  685. },
  686. Data: map[string][]byte{
  687. "secret": []byte(`bar`),
  688. },
  689. },
  690. },
  691. },
  692. }
  693. for _, tt := range tests {
  694. t.Run(tt.name, func(t *testing.T) {
  695. p := &Client{
  696. userSecretClient: tt.fields.Client,
  697. }
  698. err := p.DeleteSecret(context.Background(), tt.ref)
  699. if (err != nil) != tt.wantErr {
  700. t.Errorf("ProviderKubernetes.DeleteSecret() error = %v, wantErr %v", err, tt.wantErr)
  701. return
  702. }
  703. fClient := tt.fields.Client.(*fakeClient)
  704. if diff := cmp.Diff(tt.wantSecretMap, fClient.secretMap); diff != "" {
  705. t.Errorf("Unexpected resulting secrets map: -want, +got :\n%s\n", diff)
  706. }
  707. })
  708. }
  709. }
  710. func TestPushSecret(t *testing.T) {
  711. secretKey := "secret-key"
  712. type fields struct {
  713. Client KClient
  714. }
  715. tests := []struct {
  716. name string
  717. fields fields
  718. data testingfake.PushSecretData
  719. secret *v1.Secret
  720. wantSecretMap map[string]*v1.Secret
  721. wantErr bool
  722. }{
  723. {
  724. name: "refuse to work without property if secret key is provided",
  725. fields: fields{
  726. Client: &fakeClient{
  727. t: t,
  728. secretMap: map[string]*v1.Secret{
  729. "mysec": {
  730. Data: map[string][]byte{
  731. "token": []byte(`foo`),
  732. },
  733. },
  734. },
  735. },
  736. },
  737. data: testingfake.PushSecretData{
  738. SecretKey: secretKey,
  739. RemoteKey: "mysec",
  740. },
  741. secret: &v1.Secret{
  742. Data: map[string][]byte{secretKey: []byte("bar")},
  743. },
  744. wantErr: true,
  745. wantSecretMap: map[string]*v1.Secret{
  746. "mysec": {
  747. Data: map[string][]byte{
  748. "token": []byte(`foo`),
  749. },
  750. },
  751. },
  752. },
  753. {
  754. name: "push the whole secret if neither remote property or secretKey is defined but keep existing keys",
  755. fields: fields{
  756. Client: &fakeClient{
  757. t: t,
  758. secretMap: map[string]*v1.Secret{
  759. "mysec": {
  760. Data: map[string][]byte{
  761. "token": []byte(`foo`),
  762. },
  763. },
  764. },
  765. },
  766. },
  767. data: testingfake.PushSecretData{
  768. RemoteKey: "mysec",
  769. },
  770. secret: &v1.Secret{
  771. Data: map[string][]byte{"token2": []byte("foo")},
  772. },
  773. wantSecretMap: map[string]*v1.Secret{
  774. "mysec": {
  775. ObjectMeta: metav1.ObjectMeta{
  776. Name: "mysec",
  777. Labels: map[string]string{},
  778. Annotations: map[string]string{},
  779. },
  780. Data: map[string][]byte{
  781. "token": []byte(`foo`),
  782. "token2": []byte(`foo`),
  783. },
  784. },
  785. },
  786. },
  787. {
  788. name: "push the whole secret while secret exists into a single property",
  789. fields: fields{
  790. Client: &fakeClient{
  791. t: t,
  792. secretMap: map[string]*v1.Secret{
  793. "mysec": {
  794. Data: map[string][]byte{
  795. "token": []byte(`foo`),
  796. },
  797. },
  798. },
  799. },
  800. },
  801. data: testingfake.PushSecretData{
  802. RemoteKey: "mysec",
  803. Property: "token",
  804. },
  805. secret: &v1.Secret{
  806. Data: map[string][]byte{"foo": []byte("bar")},
  807. },
  808. wantSecretMap: map[string]*v1.Secret{
  809. "mysec": {
  810. ObjectMeta: metav1.ObjectMeta{
  811. Name: "mysec",
  812. Labels: map[string]string{},
  813. Annotations: map[string]string{},
  814. },
  815. Data: map[string][]byte{
  816. "token": []byte(`{"foo":"bar"}`),
  817. },
  818. },
  819. },
  820. },
  821. {
  822. name: "push the whole secret while secret exists but new property is defined should update the secret and keep existing key",
  823. fields: fields{
  824. Client: &fakeClient{
  825. t: t,
  826. secretMap: map[string]*v1.Secret{
  827. "mysec": {
  828. Data: map[string][]byte{
  829. "token": []byte(`foo`),
  830. },
  831. },
  832. },
  833. },
  834. },
  835. data: testingfake.PushSecretData{
  836. RemoteKey: "mysec",
  837. Property: "token2",
  838. },
  839. secret: &v1.Secret{
  840. Data: map[string][]byte{"foo": []byte("bar")},
  841. },
  842. wantSecretMap: map[string]*v1.Secret{
  843. "mysec": {
  844. ObjectMeta: metav1.ObjectMeta{
  845. Name: "mysec",
  846. Labels: map[string]string{},
  847. Annotations: map[string]string{},
  848. },
  849. Data: map[string][]byte{
  850. "token": []byte(`foo`),
  851. "token2": []byte(`{"foo":"bar"}`),
  852. },
  853. },
  854. },
  855. },
  856. {
  857. name: "push the whole secret as json if remote property is defined but secret key is not given",
  858. fields: fields{
  859. Client: &fakeClient{
  860. t: t,
  861. secretMap: map[string]*v1.Secret{},
  862. },
  863. },
  864. data: testingfake.PushSecretData{
  865. RemoteKey: "mysec",
  866. Property: "marshaled",
  867. },
  868. secret: &v1.Secret{
  869. Data: map[string][]byte{
  870. "token": []byte("foo"),
  871. "token2": []byte("2"),
  872. },
  873. },
  874. wantSecretMap: map[string]*v1.Secret{
  875. "mysec": {
  876. ObjectMeta: metav1.ObjectMeta{
  877. Name: "mysec",
  878. Labels: map[string]string{},
  879. Annotations: map[string]string{},
  880. },
  881. Data: map[string][]byte{
  882. "marshaled": []byte(`{"token":"foo","token2":"2"}`),
  883. },
  884. Type: "Opaque",
  885. },
  886. },
  887. },
  888. {
  889. name: "add missing property to existing secret",
  890. fields: fields{
  891. Client: &fakeClient{
  892. t: t,
  893. secretMap: map[string]*v1.Secret{
  894. "mysec": {
  895. Data: map[string][]byte{
  896. "token": []byte(`foo`),
  897. },
  898. },
  899. },
  900. },
  901. },
  902. secret: &v1.Secret{
  903. Data: map[string][]byte{secretKey: []byte("bar")},
  904. },
  905. data: testingfake.PushSecretData{
  906. SecretKey: secretKey,
  907. RemoteKey: "mysec",
  908. Property: "secret",
  909. },
  910. wantErr: false,
  911. wantSecretMap: map[string]*v1.Secret{
  912. "mysec": {
  913. ObjectMeta: metav1.ObjectMeta{
  914. Name: "mysec",
  915. Labels: map[string]string{},
  916. Annotations: map[string]string{},
  917. },
  918. Data: map[string][]byte{
  919. "token": []byte(`foo`),
  920. "secret": []byte(`bar`),
  921. },
  922. },
  923. },
  924. },
  925. {
  926. name: "replace existing property in existing secret",
  927. fields: fields{
  928. Client: &fakeClient{
  929. t: t,
  930. secretMap: map[string]*v1.Secret{
  931. "mysec": {
  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: "token",
  946. },
  947. wantErr: false,
  948. wantSecretMap: map[string]*v1.Secret{
  949. "mysec": {
  950. ObjectMeta: metav1.ObjectMeta{
  951. Name: "mysec",
  952. Labels: map[string]string{},
  953. Annotations: map[string]string{},
  954. },
  955. Data: map[string][]byte{
  956. "token": []byte(`bar`),
  957. },
  958. },
  959. },
  960. },
  961. {
  962. name: "replace existing property in existing secret with targetMergePolicy set to Ignore",
  963. fields: fields{
  964. Client: &fakeClient{
  965. t: t,
  966. secretMap: map[string]*v1.Secret{
  967. "mysec": {
  968. Data: map[string][]byte{
  969. "token": []byte(`foo`),
  970. },
  971. },
  972. },
  973. },
  974. },
  975. secret: &v1.Secret{
  976. ObjectMeta: metav1.ObjectMeta{
  977. Name: "mysec",
  978. // these should be ignored as the targetMergePolicy is set to Ignore
  979. Labels: map[string]string{"dev": "seb"},
  980. Annotations: map[string]string{"date": "today"},
  981. },
  982. Data: map[string][]byte{secretKey: []byte("bar")},
  983. },
  984. data: testingfake.PushSecretData{
  985. SecretKey: secretKey,
  986. RemoteKey: "mysec",
  987. Property: "token",
  988. Metadata: &apiextensionsv1.JSON{
  989. Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"targetMergePolicy": "Ignore"}}`),
  990. },
  991. },
  992. wantErr: false,
  993. wantSecretMap: map[string]*v1.Secret{
  994. "mysec": {
  995. ObjectMeta: metav1.ObjectMeta{
  996. Name: "mysec",
  997. Labels: map[string]string{},
  998. Annotations: map[string]string{},
  999. },
  1000. Data: map[string][]byte{
  1001. "token": []byte(`bar`),
  1002. },
  1003. },
  1004. },
  1005. },
  1006. {
  1007. name: "replace existing property in existing secret with targetMergePolicy set to Replace",
  1008. fields: fields{
  1009. Client: &fakeClient{
  1010. t: t,
  1011. secretMap: map[string]*v1.Secret{
  1012. "mysec": {
  1013. ObjectMeta: metav1.ObjectMeta{
  1014. Name: "mysec",
  1015. Labels: map[string]string{
  1016. "already": "existing",
  1017. },
  1018. Annotations: map[string]string{
  1019. "already": "existing",
  1020. },
  1021. },
  1022. Data: map[string][]byte{
  1023. "token": []byte(`foo`),
  1024. },
  1025. },
  1026. },
  1027. },
  1028. },
  1029. secret: &v1.Secret{
  1030. ObjectMeta: metav1.ObjectMeta{
  1031. Name: "mysec",
  1032. // these should replace existing metadata as the targetMergePolicy is set to Replace
  1033. Labels: map[string]string{"dev": "seb"},
  1034. Annotations: map[string]string{"date": "today"},
  1035. },
  1036. Data: map[string][]byte{secretKey: []byte("bar")},
  1037. },
  1038. data: testingfake.PushSecretData{
  1039. SecretKey: secretKey,
  1040. RemoteKey: "mysec",
  1041. Property: "token",
  1042. Metadata: &apiextensionsv1.JSON{
  1043. Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"targetMergePolicy": "Replace"}}`),
  1044. },
  1045. },
  1046. wantErr: false,
  1047. wantSecretMap: map[string]*v1.Secret{
  1048. "mysec": {
  1049. ObjectMeta: metav1.ObjectMeta{
  1050. Name: "mysec",
  1051. Labels: map[string]string{
  1052. "dev": "seb",
  1053. },
  1054. Annotations: map[string]string{
  1055. "date": "today",
  1056. },
  1057. },
  1058. Data: map[string][]byte{
  1059. "token": []byte(`bar`),
  1060. },
  1061. },
  1062. },
  1063. },
  1064. {
  1065. name: "create new secret, merging existing metadata",
  1066. fields: fields{
  1067. Client: &fakeClient{
  1068. t: t,
  1069. secretMap: map[string]*v1.Secret{
  1070. "yoursec": {
  1071. Data: map[string][]byte{
  1072. "token": []byte(`foo`),
  1073. },
  1074. },
  1075. },
  1076. },
  1077. },
  1078. secret: &v1.Secret{
  1079. ObjectMeta: metav1.ObjectMeta{
  1080. Annotations: map[string]string{
  1081. "this-annotation": "should be present on the targey secret",
  1082. },
  1083. },
  1084. Data: map[string][]byte{secretKey: []byte("bar")},
  1085. },
  1086. data: testingfake.PushSecretData{
  1087. SecretKey: secretKey,
  1088. RemoteKey: "mysec",
  1089. Property: "secret",
  1090. Metadata: &apiextensionsv1.JSON{
  1091. Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"annotations": {"date": "today"}, "labels": {"dev": "seb"}}}`),
  1092. },
  1093. },
  1094. wantErr: false,
  1095. wantSecretMap: map[string]*v1.Secret{
  1096. "yoursec": {
  1097. Data: map[string][]byte{
  1098. "token": []byte(`foo`),
  1099. },
  1100. },
  1101. "mysec": {
  1102. ObjectMeta: metav1.ObjectMeta{
  1103. Name: "mysec",
  1104. Annotations: map[string]string{
  1105. "date": "today",
  1106. "this-annotation": "should be present on the targey secret",
  1107. },
  1108. Labels: map[string]string{"dev": "seb"},
  1109. },
  1110. Data: map[string][]byte{
  1111. "secret": []byte(`bar`),
  1112. },
  1113. Type: v1.SecretTypeOpaque,
  1114. },
  1115. },
  1116. },
  1117. {
  1118. name: "create new secret with metadata from secret metadata and remoteRef.metadata",
  1119. fields: fields{
  1120. Client: &fakeClient{
  1121. t: t,
  1122. secretMap: map[string]*v1.Secret{
  1123. "yoursec": {
  1124. Data: map[string][]byte{
  1125. "token": []byte(`foo`),
  1126. },
  1127. },
  1128. },
  1129. },
  1130. },
  1131. secret: &v1.Secret{
  1132. ObjectMeta: metav1.ObjectMeta{
  1133. Annotations: map[string]string{"date": "today"},
  1134. Labels: map[string]string{"dev": "seb"},
  1135. },
  1136. Data: map[string][]byte{secretKey: []byte("bar")},
  1137. },
  1138. data: testingfake.PushSecretData{
  1139. SecretKey: secretKey,
  1140. RemoteKey: "mysec",
  1141. Property: "secret",
  1142. Metadata: &apiextensionsv1.JSON{
  1143. Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: { "sourceMergePolicy": "Replace", "annotations": {"another-field": "from-remote-ref"}, "labels": {"other-label": "from-remote-ref"}}}`),
  1144. },
  1145. },
  1146. wantErr: false,
  1147. wantSecretMap: map[string]*v1.Secret{
  1148. "yoursec": {
  1149. Data: map[string][]byte{
  1150. "token": []byte(`foo`),
  1151. },
  1152. },
  1153. "mysec": {
  1154. ObjectMeta: metav1.ObjectMeta{
  1155. Name: "mysec",
  1156. Annotations: map[string]string{
  1157. "another-field": "from-remote-ref",
  1158. },
  1159. Labels: map[string]string{
  1160. "other-label": "from-remote-ref",
  1161. },
  1162. },
  1163. Data: map[string][]byte{
  1164. "secret": []byte(`bar`),
  1165. },
  1166. Type: v1.SecretTypeOpaque,
  1167. },
  1168. },
  1169. },
  1170. {
  1171. name: "invalid secret metadata structure results in error",
  1172. fields: fields{
  1173. Client: &fakeClient{
  1174. t: t,
  1175. secretMap: map[string]*v1.Secret{
  1176. "yoursec": {
  1177. Data: map[string][]byte{
  1178. "token": []byte(`foo`),
  1179. },
  1180. },
  1181. },
  1182. },
  1183. },
  1184. secret: &v1.Secret{
  1185. Data: map[string][]byte{secretKey: []byte("bar")},
  1186. },
  1187. data: testingfake.PushSecretData{
  1188. SecretKey: secretKey,
  1189. RemoteKey: "mysec",
  1190. Property: "secret",
  1191. Metadata: &apiextensionsv1.JSON{
  1192. Raw: []byte(`{}`),
  1193. },
  1194. },
  1195. wantErr: true,
  1196. wantSecretMap: map[string]*v1.Secret{
  1197. "yoursec": {
  1198. Data: map[string][]byte{
  1199. "token": []byte(`foo`),
  1200. },
  1201. },
  1202. },
  1203. },
  1204. {
  1205. name: "non-json secret metadata results in error",
  1206. fields: fields{
  1207. Client: &fakeClient{
  1208. t: t,
  1209. secretMap: map[string]*v1.Secret{
  1210. "yoursec": {
  1211. Data: map[string][]byte{
  1212. "token": []byte(`foo`),
  1213. },
  1214. },
  1215. },
  1216. },
  1217. },
  1218. secret: &v1.Secret{
  1219. Data: map[string][]byte{secretKey: []byte("bar")},
  1220. },
  1221. data: testingfake.PushSecretData{
  1222. SecretKey: secretKey,
  1223. RemoteKey: "mysec",
  1224. Property: "secret",
  1225. Metadata: &apiextensionsv1.JSON{
  1226. Raw: []byte(`--- not json ---`),
  1227. },
  1228. },
  1229. wantErr: true,
  1230. wantSecretMap: map[string]*v1.Secret{
  1231. "yoursec": {
  1232. Data: map[string][]byte{
  1233. "token": []byte(`foo`),
  1234. },
  1235. },
  1236. },
  1237. },
  1238. {
  1239. name: "create new secret with whole secret",
  1240. fields: fields{
  1241. Client: &fakeClient{
  1242. t: t,
  1243. secretMap: map[string]*v1.Secret{
  1244. "yoursec": {
  1245. Data: map[string][]byte{
  1246. "token": []byte(`foo`),
  1247. },
  1248. },
  1249. },
  1250. },
  1251. },
  1252. secret: &v1.Secret{
  1253. Data: map[string][]byte{
  1254. "foo": []byte("bar"),
  1255. "baz": []byte("bang"),
  1256. },
  1257. },
  1258. data: testingfake.PushSecretData{
  1259. RemoteKey: "mysec",
  1260. },
  1261. wantErr: false,
  1262. wantSecretMap: map[string]*v1.Secret{
  1263. "yoursec": {
  1264. Data: map[string][]byte{
  1265. "token": []byte(`foo`),
  1266. },
  1267. },
  1268. "mysec": {
  1269. ObjectMeta: metav1.ObjectMeta{
  1270. Name: "mysec",
  1271. Labels: map[string]string{},
  1272. Annotations: map[string]string{},
  1273. },
  1274. Data: map[string][]byte{
  1275. "foo": []byte("bar"),
  1276. "baz": []byte("bang"),
  1277. },
  1278. Type: v1.SecretTypeOpaque,
  1279. },
  1280. },
  1281. },
  1282. {
  1283. name: "create new dockerconfigjson secret",
  1284. fields: fields{
  1285. Client: &fakeClient{
  1286. t: t,
  1287. secretMap: map[string]*v1.Secret{
  1288. "yoursec": {
  1289. Data: map[string][]byte{
  1290. "token": []byte(`foo`),
  1291. },
  1292. },
  1293. },
  1294. },
  1295. },
  1296. secret: &v1.Secret{
  1297. Type: v1.SecretTypeDockerConfigJson,
  1298. Data: map[string][]byte{secretKey: []byte(`{"auths": {"myregistry.localhost": {"username": "{{ .username }}", "password": "{{ .password }}"}}}`)},
  1299. },
  1300. data: testingfake.PushSecretData{
  1301. SecretKey: secretKey,
  1302. RemoteKey: "mysec",
  1303. Property: "config.json",
  1304. },
  1305. wantErr: false,
  1306. wantSecretMap: map[string]*v1.Secret{
  1307. "yoursec": {
  1308. Data: map[string][]byte{
  1309. "token": []byte(`foo`),
  1310. },
  1311. },
  1312. "mysec": {
  1313. ObjectMeta: metav1.ObjectMeta{
  1314. Name: "mysec",
  1315. Labels: map[string]string{},
  1316. Annotations: map[string]string{},
  1317. },
  1318. Data: map[string][]byte{
  1319. "config.json": []byte(`{"auths": {"myregistry.localhost": {"username": "{{ .username }}", "password": "{{ .password }}"}}}`),
  1320. },
  1321. Type: v1.SecretTypeDockerConfigJson,
  1322. },
  1323. },
  1324. },
  1325. {
  1326. name: "create new secret with remote namespace",
  1327. fields: fields{
  1328. Client: &fakeClient{
  1329. t: t,
  1330. secretMap: map[string]*v1.Secret{},
  1331. },
  1332. },
  1333. secret: &v1.Secret{
  1334. ObjectMeta: metav1.ObjectMeta{
  1335. Name: "mysec",
  1336. Namespace: "source-namespace",
  1337. },
  1338. Data: map[string][]byte{secretKey: []byte("bar")},
  1339. },
  1340. data: testingfake.PushSecretData{
  1341. SecretKey: secretKey,
  1342. RemoteKey: "mysec",
  1343. Property: "secret",
  1344. Metadata: &apiextensionsv1.JSON{
  1345. Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", "spec": {"remoteNamespace": "target-namespace"}}`),
  1346. },
  1347. },
  1348. wantErr: false,
  1349. wantSecretMap: map[string]*v1.Secret{
  1350. "mysec": {
  1351. ObjectMeta: metav1.ObjectMeta{
  1352. Name: "mysec",
  1353. Namespace: "target-namespace",
  1354. Labels: map[string]string{},
  1355. Annotations: map[string]string{},
  1356. },
  1357. Data: map[string][]byte{
  1358. "secret": []byte(`bar`),
  1359. },
  1360. Type: v1.SecretTypeOpaque,
  1361. },
  1362. },
  1363. },
  1364. }
  1365. for _, tt := range tests {
  1366. t.Run(tt.name, func(t *testing.T) {
  1367. p := &Client{
  1368. userSecretClient: tt.fields.Client,
  1369. store: &esv1.KubernetesProvider{},
  1370. }
  1371. err := p.PushSecret(context.Background(), tt.secret, tt.data)
  1372. if (err != nil) != tt.wantErr {
  1373. t.Errorf("ProviderKubernetes error = %v, wantErr %v", err, tt.wantErr)
  1374. return
  1375. }
  1376. fClient := tt.fields.Client.(*fakeClient)
  1377. if diff := cmp.Diff(tt.wantSecretMap, fClient.secretMap); diff != "" {
  1378. t.Errorf("Unexpected resulting secrets map: -want, +got :\n%s\n", diff)
  1379. }
  1380. })
  1381. }
  1382. }