webhook_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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 webhook
  14. import (
  15. "bytes"
  16. "context"
  17. "errors"
  18. "io"
  19. "net/http"
  20. "net/http/httptest"
  21. "strings"
  22. "testing"
  23. "time"
  24. "gopkg.in/yaml.v3"
  25. corev1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  28. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  29. )
  30. type testCase struct {
  31. Case string `json:"case,omitempty"`
  32. Args args `json:"args"`
  33. Want want `json:"want"`
  34. }
  35. type secret struct {
  36. Name string `json:"name"`
  37. Data map[string]string `json:"data"`
  38. }
  39. type args struct {
  40. URL string `json:"url,omitempty"`
  41. Body string `json:"body,omitempty"`
  42. Timeout string `json:"timeout,omitempty"`
  43. Key string `json:"key,omitempty"`
  44. SecretKey string `json:"secretkey,omitempty"`
  45. Property string `json:"property,omitempty"`
  46. Version string `json:"version,omitempty"`
  47. JSONPath string `json:"jsonpath,omitempty"`
  48. Response string `json:"response,omitempty"`
  49. StatusCode int `json:"statuscode,omitempty"`
  50. PushSecret bool `json:"pushsecret,omitempty"`
  51. Secret secret `json:"secret,omitempty"`
  52. }
  53. type want struct {
  54. Path string `json:"path,omitempty"`
  55. Body *string `json:"body,omitempty"`
  56. Err string `json:"err,omitempty"`
  57. Result string `json:"result,omitempty"`
  58. ResultMap map[string]string `json:"resultmap,omitempty"`
  59. }
  60. var testCases = `
  61. case: error url
  62. args:
  63. url: /api/getsecret?id={{ .unclosed.template
  64. want:
  65. err: failed to parse url
  66. ---
  67. case: error body
  68. args:
  69. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  70. body: Body error {{ .unclosed.template
  71. want:
  72. err: failed to parse body
  73. ---
  74. case: error connection
  75. args:
  76. url: 1/api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  77. want:
  78. err: failed to call endpoint
  79. ---
  80. case: error no secret err
  81. args:
  82. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  83. key: testkey
  84. version: 1
  85. statuscode: 404
  86. response: not found
  87. want:
  88. path: /api/getsecret?id=testkey&version=1
  89. err: ` + esv1.NoSecretErr.Error() + `
  90. ---
  91. case: error server error
  92. args:
  93. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  94. key: testkey
  95. version: 1
  96. statuscode: 500
  97. response: server error
  98. want:
  99. path: /api/getsecret?id=testkey&version=1
  100. err: endpoint gave error 500
  101. ---
  102. case: error bad json
  103. args:
  104. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  105. key: testkey
  106. version: 1
  107. jsonpath: $.result.thesecret
  108. response: '{"result":{"thesecret":"secret-value"}'
  109. want:
  110. path: /api/getsecret?id=testkey&version=1
  111. err: failed to parse response json
  112. ---
  113. case: error bad jsonpath
  114. args:
  115. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  116. key: testkey
  117. version: 1
  118. jsonpath: $.result.thesecret
  119. response: '{"result":{"nosecret":"secret-value"}}'
  120. want:
  121. path: /api/getsecret?id=testkey&version=1
  122. err: failed to get response path
  123. ---
  124. case: pull data out of map
  125. args:
  126. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  127. key: testkey
  128. version: 1
  129. jsonpath: $.result.thesecret
  130. response: '{"result":{"thesecret":{"one":"secret-value"}}}'
  131. want:
  132. path: /api/getsecret?id=testkey&version=1
  133. err: ''
  134. result: '{"one":"secret-value"}'
  135. ---
  136. case: error timeout
  137. args:
  138. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  139. key: testkey
  140. version: 1
  141. response: secret-value
  142. timeout: 0.01ms
  143. want:
  144. path: /api/getsecret?id=testkey&version=1
  145. err: context deadline exceeded
  146. ---
  147. case: good plaintext
  148. args:
  149. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  150. key: testkey
  151. version: 1
  152. response: secret-value
  153. want:
  154. path: /api/getsecret?id=testkey&version=1
  155. err: ''
  156. result: secret-value
  157. ---
  158. case: good json
  159. args:
  160. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  161. key: testkey
  162. version: 1
  163. jsonpath: $.result.thesecret
  164. response: '{"result":{"thesecret":"secret-value"}}'
  165. want:
  166. path: /api/getsecret?id=testkey&version=1
  167. err: ''
  168. result: secret-value
  169. ---
  170. case: good json map
  171. args:
  172. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  173. key: testkey
  174. version: 1
  175. jsonpath: $.result
  176. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
  177. want:
  178. path: /api/getsecret?id=testkey&version=1
  179. err: ''
  180. resultmap:
  181. thesecret: secret-value
  182. alsosecret: another-value
  183. ---
  184. case: templated jsonpath good json map
  185. args:
  186. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  187. key: testkey
  188. version: 1
  189. jsonpath: $.{{printf "result" }}
  190. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
  191. want:
  192. path: /api/getsecret?id=testkey&version=1
  193. err: ''
  194. resultmap:
  195. thesecret: secret-value
  196. alsosecret: another-value
  197. ---
  198. case: templated jsonpath invalid template
  199. args:
  200. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  201. key: testkey
  202. version: 1
  203. jsonpath: $.{{printf 'result' }}
  204. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
  205. want:
  206. path: /api/getsecret?id=testkey&version=1
  207. err: "cannot get templated json path"
  208. resultmap:
  209. thesecret: secret-value
  210. alsosecret: another-value
  211. ---
  212. case: good json map string
  213. args:
  214. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  215. key: testkey
  216. version: 1
  217. response: '{"thesecret":"secret-value","alsosecret":"another-value"}'
  218. want:
  219. path: /api/getsecret?id=testkey&version=1
  220. err: ''
  221. resultmap:
  222. thesecret: secret-value
  223. alsosecret: another-value
  224. ---
  225. case: error json map string
  226. args:
  227. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  228. key: testkey
  229. version: 1
  230. response: 'some simple string'
  231. want:
  232. path: /api/getsecret?id=testkey&version=1
  233. err: "failed to parse response json: invalid character"
  234. resultmap: {}
  235. ---
  236. case: error json map
  237. args:
  238. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  239. key: testkey
  240. version: 1
  241. jsonpath: $.result.thesecret
  242. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
  243. want:
  244. path: /api/getsecret?id=testkey&version=1
  245. err: "failed to parse response json from jsonpath"
  246. resultmap: {}
  247. ---
  248. case: good json with good templated jsonpath
  249. args:
  250. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  251. key: testkey
  252. property: thesecret
  253. version: 1
  254. jsonpath: $.result.{{ .remoteRef.property }}
  255. response: '{"result":{"thesecret":"secret-value"}}'
  256. want:
  257. path: /api/getsecret?id=testkey&version=1
  258. err: ''
  259. result: secret-value
  260. ---
  261. case: good json with jsonpath filter
  262. args:
  263. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  264. key: testkey
  265. version: 1
  266. jsonpath: $.secrets[?@.name=="thesecret"].value
  267. response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
  268. want:
  269. path: /api/getsecret?id=testkey&version=1
  270. err: ''
  271. result: secret-value
  272. ---
  273. case: good json with bad templated jsonpath
  274. args:
  275. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  276. key: testkey
  277. property: thesecret
  278. version: 1
  279. jsonpath: $.result.{{ .remoteRef.property }
  280. response: '{"result":{"thesecret":"secret-value"}}'
  281. want:
  282. path: /api/getsecret?id=testkey&version=1
  283. err: 'template: webhooktemplate:1: unexpected "}" in operand'
  284. ---
  285. case: error with jsonpath filter empty results
  286. args:
  287. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  288. key: testkey
  289. version: 1
  290. jsonpath: $.secrets[?@.name=="thebadsecret"].value
  291. response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
  292. want:
  293. path: /api/getsecret?id=testkey&version=1
  294. err: "filter worked but didn't get any result"
  295. ---
  296. case: success with jsonpath filter and result array
  297. args:
  298. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  299. key: testkey
  300. version: 1
  301. jsonpath: $..name
  302. response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
  303. want:
  304. path: /api/getsecret?id=testkey&version=1
  305. err: ''
  306. result: 'thesecret'
  307. ---
  308. case: success with jsonpath filter and result array of ints
  309. args:
  310. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  311. key: testkey
  312. version: 1
  313. jsonpath: $..name
  314. response: '{"secrets": [{"name": 123, "value": "secret-value"}, {"name": 456, "value": "another-value"}]}'
  315. want:
  316. path: /api/getsecret?id=testkey&version=1
  317. err: ''
  318. result: 123
  319. ---
  320. case: support backslash
  321. args:
  322. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  323. key: testkey
  324. version: 1
  325. jsonpath: $.refresh_token
  326. response: '{"access_token":"REDACTED","refresh_token":"RE\/DACTED=="}'
  327. want:
  328. path: /api/getsecret?id=testkey&version=1
  329. err: ''
  330. result: "RE/DACTED=="
  331. ---
  332. case: good json with mixed fields and jsonpath filter
  333. args:
  334. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  335. key: testkey
  336. version: 1
  337. jsonpath: $.result.thesecret
  338. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
  339. want:
  340. path: /api/getsecret?id=testkey&version=1
  341. err: ''
  342. result: secret-value
  343. ---
  344. case: good json with mixed fields to map
  345. args:
  346. url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  347. key: testkey
  348. version: 1
  349. jsonpath: $.result
  350. response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
  351. want:
  352. path: /api/getsecret?id=testkey&version=1
  353. err: ''
  354. resultmap:
  355. thesecret: secret-value
  356. alsosecret: another-value
  357. id: 1234
  358. weight: 1.5
  359. ---
  360. case: only url encoding for url templates
  361. args:
  362. url: /api/getsecrets?folder={{ .remoteRef.key }}
  363. body: '{"folder": "{{ .remoteRef.key }}"}'
  364. key: /myapp/secrets
  365. want:
  366. path: /api/getsecrets?folder=%2Fmyapp%2Fsecrets
  367. body: '{"folder": "/myapp/secrets"}'
  368. ---
  369. case: namespace template in headers
  370. args:
  371. url: /api/getsecret?id={{ .remoteRef.key }}
  372. key: testkey
  373. response: secret-value
  374. want:
  375. path: /api/getsecret?id=testkey
  376. err: ''
  377. result: secret-value
  378. ---
  379. case: namespace template in url
  380. args:
  381. url: /api/getsecret?id={{ .remoteRef.key }}&namespace={{ .remoteRef.namespace }}
  382. key: testkey
  383. response: secret-value
  384. want:
  385. path: /api/getsecret?id=testkey&namespace=testnamespace
  386. err: ''
  387. result: secret-value
  388. `
  389. func TestWebhookGetSecret(t *testing.T) {
  390. ydec := yaml.NewDecoder(bytes.NewReader([]byte(testCases)))
  391. for {
  392. var tc testCase
  393. if err := ydec.Decode(&tc); err != nil {
  394. if !errors.Is(err, io.EOF) {
  395. t.Errorf("testcase decode error %v", err)
  396. }
  397. break
  398. }
  399. runTestCase(tc, t)
  400. }
  401. }
  402. var testCasesPushSecret = `
  403. ---
  404. case: secret key not found
  405. args:
  406. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  407. key: testkey
  408. secretkey: not-found
  409. pushsecret: true
  410. secret:
  411. name: test-secret
  412. data:
  413. secretkey: value
  414. want:
  415. path: /api/pushsecret?id=testkey&secret=not-found
  416. err: 'failed to find secret key in secret with key: not-found'
  417. ---
  418. case: default body good json
  419. args:
  420. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  421. key: testkey
  422. secretkey: secretkey
  423. pushsecret: true
  424. secret:
  425. name: test-secret
  426. data:
  427. secretkey: value
  428. want:
  429. path: /api/pushsecret?id=testkey&secret=secretkey
  430. body: 'value'
  431. err: ''
  432. ---
  433. case: good json
  434. args:
  435. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  436. key: testkey
  437. body: 'pre {{ .remoteRef.remoteKey }} {{ .remoteRef.testkey }} post'
  438. secretkey: secretkey
  439. pushsecret: true
  440. secret:
  441. name: test-secret
  442. data:
  443. secretkey: value
  444. want:
  445. path: /api/pushsecret?id=testkey&secret=secretkey
  446. body: 'pre testkey value post'
  447. err: ''
  448. ---
  449. case: empty body
  450. args:
  451. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  452. key: testkey
  453. body: '{{ "" }}'
  454. pushsecret: true
  455. secret:
  456. name: test-secret
  457. data:
  458. secretkey: value
  459. want:
  460. path: /api/pushsecret?id=testkey
  461. body: ''
  462. err: ''
  463. ---
  464. case: default body pushing without secret key
  465. args:
  466. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  467. key: testkey
  468. pushsecret: true
  469. secret:
  470. name: test-secret
  471. data:
  472. secretkey: value
  473. want:
  474. path: /api/pushsecret?id=testkey
  475. body: '{"secretkey":"value"}'
  476. err: ''
  477. ---
  478. case: pushing without secret key
  479. args:
  480. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  481. key: testkey
  482. body: 'pre {{ .remoteRef.remoteKey }} {{ index (.remoteRef.testkey | fromJson) "secretkey" }} post'
  483. pushsecret: true
  484. secret:
  485. name: test-secret
  486. data:
  487. secretkey: value
  488. want:
  489. path: /api/pushsecret?id=testkey
  490. body: 'pre testkey value post'
  491. err: ''
  492. ---
  493. case: pushing without secret key with dynamic resolution
  494. args:
  495. url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  496. key: testkey
  497. body: 'pre {{ .remoteRef.remoteKey }} {{ index (index .remoteRef .remoteRef.remoteKey | fromJson) "secretkey" }} post'
  498. pushsecret: true
  499. secret:
  500. name: test-secret
  501. data:
  502. secretkey: value
  503. want:
  504. path: /api/pushsecret?id=testkey
  505. body: 'pre testkey value post'
  506. err: ''
  507. `
  508. func TestWebhookPushSecret(t *testing.T) {
  509. ydec := yaml.NewDecoder(bytes.NewReader([]byte(testCasesPushSecret)))
  510. for {
  511. var tc testCase
  512. if err := ydec.Decode(&tc); err != nil {
  513. if !errors.Is(err, io.EOF) {
  514. t.Errorf("testcase decode error %v", err)
  515. }
  516. break
  517. }
  518. runTestCase(tc, t)
  519. }
  520. }
  521. func testCaseServer(tc testCase, t *testing.T) *httptest.Server {
  522. // Start a new server for every test case because the server wants to check the expected api path
  523. return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
  524. if tc.Want.Path != "" && req.URL.String() != tc.Want.Path {
  525. t.Errorf("%s: unexpected api path: %s, expected %s", tc.Case, req.URL.String(), tc.Want.Path)
  526. }
  527. if tc.Want.Body != nil {
  528. b, _ := io.ReadAll(req.Body)
  529. if tc.Want.Body != nil && string(b) != *tc.Want.Body {
  530. t.Errorf("%s: unexpected body: %s, expected %s", tc.Case, string(b), *tc.Want.Body)
  531. }
  532. }
  533. if tc.Args.StatusCode != 0 {
  534. rw.WriteHeader(tc.Args.StatusCode)
  535. }
  536. rw.Write([]byte(tc.Args.Response))
  537. }))
  538. }
  539. func parseTimeout(timeout string) (*metav1.Duration, error) {
  540. if timeout == "" {
  541. return nil, nil
  542. }
  543. dur, err := time.ParseDuration(timeout)
  544. if err != nil {
  545. return nil, err
  546. }
  547. return &metav1.Duration{Duration: dur}, nil
  548. }
  549. func runTestCase(tc testCase, t *testing.T) {
  550. t.Run(tc.Case, func(t *testing.T) {
  551. ts := testCaseServer(tc, t)
  552. defer ts.Close()
  553. testStore := makeClusterSecretStore(ts.URL, tc.Args)
  554. var err error
  555. timeout, err := parseTimeout(tc.Args.Timeout)
  556. if err != nil {
  557. t.Errorf("%s: error parsing timeout '%s': %s", tc.Case, tc.Args.Timeout, err.Error())
  558. return
  559. }
  560. testStore.Spec.Provider.Webhook.Timeout = timeout
  561. testProv := &Provider{}
  562. client, err := testProv.NewClient(context.Background(), testStore, nil, "testnamespace")
  563. if err != nil {
  564. t.Errorf("%s: error creating client: %s", tc.Case, err.Error())
  565. return
  566. }
  567. if tc.Want.ResultMap != nil && !tc.Args.PushSecret {
  568. testGetSecretMap(tc, t, client)
  569. } else if !tc.Args.PushSecret {
  570. testGetSecret(tc, t, client)
  571. } else {
  572. testPushSecret(tc, t, client)
  573. }
  574. })
  575. }
  576. func testGetSecretMap(tc testCase, t *testing.T, client esv1.SecretsClient) {
  577. testRef := esv1.ExternalSecretDataRemoteRef{
  578. Key: tc.Args.Key,
  579. Version: tc.Args.Version,
  580. }
  581. secretmap, err := client.GetSecretMap(context.Background(), testRef)
  582. errStr := ""
  583. if err != nil {
  584. errStr = err.Error()
  585. }
  586. if (tc.Want.Err == "") != (errStr == "") || !strings.Contains(errStr, tc.Want.Err) {
  587. t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
  588. }
  589. if err == nil {
  590. for wantkey, wantval := range tc.Want.ResultMap {
  591. gotval, ok := secretmap[wantkey]
  592. if !ok {
  593. t.Errorf("%s: unexpected response: wanted key '%s' not found", tc.Case, wantkey)
  594. } else if string(gotval) != wantval {
  595. t.Errorf("%s: unexpected response: key '%s' = '%s' (expected '%s')", tc.Case, wantkey, wantval, gotval)
  596. }
  597. }
  598. }
  599. }
  600. func testGetSecret(tc testCase, t *testing.T, client esv1.SecretsClient) {
  601. testRef := esv1.ExternalSecretDataRemoteRef{
  602. Key: tc.Args.Key,
  603. Property: tc.Args.Property,
  604. Version: tc.Args.Version,
  605. }
  606. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  607. defer cancel()
  608. secret, err := client.GetSecret(ctx, testRef)
  609. errStr := ""
  610. if err != nil {
  611. errStr = err.Error()
  612. }
  613. if !strings.Contains(errStr, tc.Want.Err) {
  614. t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
  615. }
  616. if err == nil && string(secret) != tc.Want.Result {
  617. t.Errorf("%s: unexpected response: '%s' (expected '%s')", tc.Case, secret, tc.Want.Result)
  618. }
  619. }
  620. func testPushSecret(tc testCase, t *testing.T, client esv1.SecretsClient) {
  621. testRef := v1alpha1.PushSecretData{
  622. Match: v1alpha1.PushSecretMatch{
  623. SecretKey: tc.Args.SecretKey,
  624. RemoteRef: v1alpha1.PushSecretRemoteRef{
  625. RemoteKey: tc.Args.Key,
  626. },
  627. },
  628. }
  629. data := map[string][]byte{}
  630. for k, v := range tc.Args.Secret.Data {
  631. data[k] = []byte(v)
  632. }
  633. sec := &corev1.Secret{
  634. ObjectMeta: metav1.ObjectMeta{
  635. Name: tc.Args.Secret.Name,
  636. },
  637. Data: data,
  638. }
  639. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  640. defer cancel()
  641. err := client.PushSecret(ctx, sec, testRef)
  642. errStr := ""
  643. if err != nil {
  644. errStr = err.Error()
  645. }
  646. if tc.Want.Err == "" && errStr != "" {
  647. t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
  648. }
  649. if !strings.Contains(errStr, tc.Want.Err) {
  650. t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
  651. }
  652. }
  653. func makeClusterSecretStore(url string, args args) *esv1.ClusterSecretStore {
  654. store := &esv1.ClusterSecretStore{
  655. TypeMeta: metav1.TypeMeta{
  656. Kind: "ClusterSecretStore",
  657. },
  658. ObjectMeta: metav1.ObjectMeta{
  659. Name: "wehbook-store",
  660. Namespace: "default",
  661. },
  662. Spec: esv1.SecretStoreSpec{
  663. Provider: &esv1.SecretStoreProvider{
  664. Webhook: &esv1.WebhookProvider{
  665. URL: url + args.URL,
  666. Body: args.Body,
  667. Headers: map[string]string{
  668. "Content-Type": "application.json",
  669. "X-SecretKey": "{{ .remoteRef.key }}",
  670. "X-Kubernetes-Namespace": "{{ .remoteRef.namespace }}",
  671. },
  672. Result: esv1.WebhookResult{
  673. JSONPath: args.JSONPath,
  674. },
  675. },
  676. },
  677. },
  678. }
  679. return store
  680. }