client_test.go 39 KB

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