pushsecret_controller_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  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 pushsecret
  13. import (
  14. "bytes"
  15. "context"
  16. "fmt"
  17. "os"
  18. "strconv"
  19. "time"
  20. . "github.com/onsi/ginkgo/v2"
  21. . "github.com/onsi/gomega"
  22. v1 "k8s.io/api/core/v1"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/types"
  25. "sigs.k8s.io/controller-runtime/pkg/client"
  26. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  27. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  28. ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
  29. "github.com/external-secrets/external-secrets/pkg/controllers/pushsecret/psmetrics"
  30. "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  31. )
  32. var (
  33. fakeProvider *fake.Client
  34. timeout = time.Second * 10
  35. interval = time.Millisecond * 250
  36. )
  37. type testCase struct {
  38. store v1beta1.GenericStore
  39. pushsecret *v1alpha1.PushSecret
  40. secret *v1.Secret
  41. assert func(pushsecret *v1alpha1.PushSecret, secret *v1.Secret) bool
  42. }
  43. func init() {
  44. fakeProvider = fake.New()
  45. v1beta1.ForceRegister(fakeProvider, &v1beta1.SecretStoreProvider{
  46. Fake: &v1beta1.FakeProvider{},
  47. })
  48. psmetrics.SetUpMetrics()
  49. }
  50. func checkCondition(status v1alpha1.PushSecretStatus, cond v1alpha1.PushSecretStatusCondition) bool {
  51. for _, condition := range status.Conditions {
  52. if condition.Message == cond.Message &&
  53. condition.Reason == cond.Reason &&
  54. condition.Status == cond.Status &&
  55. condition.Type == cond.Type {
  56. return true
  57. }
  58. }
  59. return false
  60. }
  61. type testTweaks func(*testCase)
  62. var _ = Describe("ExternalSecret controller", func() {
  63. const (
  64. PushSecretName = "test-es"
  65. PushSecretStore = "test-store"
  66. SecretName = "test-secret"
  67. )
  68. var PushSecretNamespace string
  69. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  70. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  71. if t, err := strconv.Atoi(customTimeout); err == nil {
  72. timeout = time.Second * time.Duration(t)
  73. }
  74. }
  75. BeforeEach(func() {
  76. var err error
  77. PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  78. Expect(err).ToNot(HaveOccurred())
  79. fakeProvider.Reset()
  80. })
  81. AfterEach(func() {
  82. k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
  83. ObjectMeta: metav1.ObjectMeta{
  84. Name: PushSecretName,
  85. Namespace: PushSecretNamespace,
  86. },
  87. })
  88. // give a time for reconciler to remove finalizers before removing SecretStores
  89. // TODO: Secret Stores should have finalizers bound to PushSecrets if DeletionPolicy == Delete
  90. time.Sleep(2 * time.Second)
  91. k8sClient.Delete(context.Background(), &v1beta1.SecretStore{
  92. ObjectMeta: metav1.ObjectMeta{
  93. Name: PushSecretStore,
  94. Namespace: PushSecretNamespace,
  95. },
  96. })
  97. k8sClient.Delete(context.Background(), &v1beta1.ClusterSecretStore{
  98. ObjectMeta: metav1.ObjectMeta{
  99. Name: PushSecretStore,
  100. },
  101. })
  102. k8sClient.Delete(context.Background(), &v1.Secret{
  103. ObjectMeta: metav1.ObjectMeta{
  104. Name: SecretName,
  105. Namespace: PushSecretNamespace,
  106. },
  107. })
  108. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  109. ObjectMeta: metav1.ObjectMeta{
  110. Name: PushSecretNamespace,
  111. },
  112. })).To(Succeed())
  113. })
  114. makeDefaultTestcase := func() *testCase {
  115. return &testCase{
  116. pushsecret: &v1alpha1.PushSecret{
  117. ObjectMeta: metav1.ObjectMeta{
  118. Name: PushSecretName,
  119. Namespace: PushSecretNamespace,
  120. },
  121. Spec: v1alpha1.PushSecretSpec{
  122. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  123. {
  124. Name: PushSecretStore,
  125. Kind: "SecretStore",
  126. },
  127. },
  128. Selector: v1alpha1.PushSecretSelector{
  129. Secret: v1alpha1.PushSecretSecret{
  130. Name: SecretName,
  131. },
  132. },
  133. Data: []v1alpha1.PushSecretData{
  134. {
  135. Match: v1alpha1.PushSecretMatch{
  136. SecretKey: "key",
  137. RemoteRef: v1alpha1.PushSecretRemoteRef{
  138. RemoteKey: "path/to/key",
  139. },
  140. },
  141. },
  142. },
  143. },
  144. },
  145. secret: &v1.Secret{
  146. ObjectMeta: metav1.ObjectMeta{
  147. Name: SecretName,
  148. Namespace: PushSecretNamespace,
  149. },
  150. Data: map[string][]byte{
  151. "key": []byte("value"),
  152. },
  153. },
  154. store: &v1beta1.SecretStore{
  155. ObjectMeta: metav1.ObjectMeta{
  156. Name: PushSecretStore,
  157. Namespace: PushSecretNamespace,
  158. },
  159. TypeMeta: metav1.TypeMeta{
  160. Kind: "SecretStore",
  161. },
  162. Spec: v1beta1.SecretStoreSpec{
  163. Provider: &v1beta1.SecretStoreProvider{
  164. Fake: &v1beta1.FakeProvider{
  165. Data: []v1beta1.FakeProviderData{},
  166. },
  167. },
  168. },
  169. },
  170. }
  171. }
  172. // if target Secret name is not specified it should use the ExternalSecret name.
  173. syncSuccessfully := func(tc *testCase) {
  174. fakeProvider.SetSecretFn = func() error {
  175. return nil
  176. }
  177. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  178. Eventually(func() bool {
  179. By("checking if Provider value got updated")
  180. secretValue := secret.Data["key"]
  181. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  182. if !ok {
  183. return false
  184. }
  185. got := providerValue.Value
  186. return bytes.Equal(got, secretValue)
  187. }, time.Second*10, time.Second).Should(BeTrue())
  188. return true
  189. }
  190. }
  191. // if target Secret name is not specified it should use the ExternalSecret name.
  192. syncSuccessfullyWithTemplate := func(tc *testCase) {
  193. fakeProvider.SetSecretFn = func() error {
  194. return nil
  195. }
  196. tc.pushsecret = &v1alpha1.PushSecret{
  197. ObjectMeta: metav1.ObjectMeta{
  198. Name: PushSecretName,
  199. Namespace: PushSecretNamespace,
  200. },
  201. Spec: v1alpha1.PushSecretSpec{
  202. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  203. {
  204. Name: PushSecretStore,
  205. Kind: "SecretStore",
  206. },
  207. },
  208. Selector: v1alpha1.PushSecretSelector{
  209. Secret: v1alpha1.PushSecretSecret{
  210. Name: SecretName,
  211. },
  212. },
  213. Data: []v1alpha1.PushSecretData{
  214. {
  215. Match: v1alpha1.PushSecretMatch{
  216. SecretKey: "key",
  217. RemoteRef: v1alpha1.PushSecretRemoteRef{
  218. RemoteKey: "path/to/key",
  219. },
  220. },
  221. },
  222. },
  223. Template: &v1beta1.ExternalSecretTemplate{
  224. Metadata: v1beta1.ExternalSecretTemplateMetadata{
  225. Labels: map[string]string{
  226. "foos": "ball",
  227. },
  228. Annotations: map[string]string{
  229. "hihi": "ga",
  230. },
  231. },
  232. Type: v1.SecretTypeOpaque,
  233. EngineVersion: v1beta1.TemplateEngineV2,
  234. Data: map[string]string{
  235. "key": "{{ .key | toString | upper }} was templated",
  236. },
  237. },
  238. },
  239. }
  240. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  241. Eventually(func() bool {
  242. By("checking if Provider value got updated")
  243. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  244. if !ok {
  245. return false
  246. }
  247. got := providerValue.Value
  248. return bytes.Equal(got, []byte("VALUE was templated"))
  249. }, time.Second*10, time.Second).Should(BeTrue())
  250. return true
  251. }
  252. }
  253. // if target Secret name is not specified it should use the ExternalSecret name.
  254. syncAndDeleteSuccessfully := func(tc *testCase) {
  255. fakeProvider.SetSecretFn = func() error {
  256. return nil
  257. }
  258. tc.pushsecret = &v1alpha1.PushSecret{
  259. ObjectMeta: metav1.ObjectMeta{
  260. Name: PushSecretName,
  261. Namespace: PushSecretNamespace,
  262. },
  263. Spec: v1alpha1.PushSecretSpec{
  264. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  265. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  266. {
  267. Name: PushSecretStore,
  268. Kind: "SecretStore",
  269. },
  270. },
  271. Selector: v1alpha1.PushSecretSelector{
  272. Secret: v1alpha1.PushSecretSecret{
  273. Name: SecretName,
  274. },
  275. },
  276. Data: []v1alpha1.PushSecretData{
  277. {
  278. Match: v1alpha1.PushSecretMatch{
  279. SecretKey: "key",
  280. RemoteRef: v1alpha1.PushSecretRemoteRef{
  281. RemoteKey: "path/to/key",
  282. },
  283. },
  284. },
  285. },
  286. },
  287. }
  288. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  289. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = "different-key"
  290. updatedPS := &v1alpha1.PushSecret{}
  291. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  292. Eventually(func() bool {
  293. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  294. By("checking if Provider value got updated")
  295. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  296. if err != nil {
  297. return false
  298. }
  299. key, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["different-key"]
  300. if !ok {
  301. return false
  302. }
  303. return key.Match.SecretKey == "key"
  304. }, time.Second*10, time.Second).Should(BeTrue())
  305. return true
  306. }
  307. }
  308. failDelete := func(tc *testCase) {
  309. fakeProvider.SetSecretFn = func() error {
  310. return nil
  311. }
  312. fakeProvider.DeleteSecretFn = func() error {
  313. return fmt.Errorf("Nope")
  314. }
  315. tc.pushsecret = &v1alpha1.PushSecret{
  316. ObjectMeta: metav1.ObjectMeta{
  317. Name: PushSecretName,
  318. Namespace: PushSecretNamespace,
  319. },
  320. Spec: v1alpha1.PushSecretSpec{
  321. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  322. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  323. {
  324. Name: PushSecretStore,
  325. Kind: "SecretStore",
  326. },
  327. },
  328. Selector: v1alpha1.PushSecretSelector{
  329. Secret: v1alpha1.PushSecretSecret{
  330. Name: SecretName,
  331. },
  332. },
  333. Data: []v1alpha1.PushSecretData{
  334. {
  335. Match: v1alpha1.PushSecretMatch{
  336. SecretKey: "key",
  337. RemoteRef: v1alpha1.PushSecretRemoteRef{
  338. RemoteKey: "path/to/key",
  339. },
  340. },
  341. },
  342. },
  343. },
  344. }
  345. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  346. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = "different-key"
  347. updatedPS := &v1alpha1.PushSecret{}
  348. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  349. Eventually(func() bool {
  350. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  351. By("checking if synced secrets correspond to both keys")
  352. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  353. if err != nil {
  354. return false
  355. }
  356. _, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["different-key"]
  357. if !ok {
  358. return false
  359. }
  360. _, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf("SecretStore/%v", PushSecretStore)]["path/to/key"]
  361. return ok
  362. }, time.Second*10, time.Second).Should(BeTrue())
  363. return true
  364. }
  365. }
  366. failDeleteStore := func(tc *testCase) {
  367. fakeProvider.SetSecretFn = func() error {
  368. return nil
  369. }
  370. fakeProvider.DeleteSecretFn = func() error {
  371. return fmt.Errorf("boom")
  372. }
  373. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  374. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  375. secondStore := &v1beta1.SecretStore{
  376. ObjectMeta: metav1.ObjectMeta{
  377. Name: "new-store",
  378. Namespace: PushSecretNamespace,
  379. },
  380. TypeMeta: metav1.TypeMeta{
  381. Kind: "SecretStore",
  382. },
  383. Spec: v1beta1.SecretStoreSpec{
  384. Provider: &v1beta1.SecretStoreProvider{
  385. Fake: &v1beta1.FakeProvider{
  386. Data: []v1beta1.FakeProviderData{},
  387. },
  388. },
  389. },
  390. }
  391. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  392. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  393. updatedPS := &v1alpha1.PushSecret{}
  394. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  395. Eventually(func() bool {
  396. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  397. By("checking if Provider value got updated")
  398. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  399. if err != nil {
  400. return false
  401. }
  402. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  403. return syncedLen == 2
  404. }, time.Second*10, time.Second).Should(BeTrue())
  405. return true
  406. }
  407. }
  408. deleteWholeStore := func(tc *testCase) {
  409. fakeProvider.SetSecretFn = func() error {
  410. return nil
  411. }
  412. fakeProvider.DeleteSecretFn = func() error {
  413. return nil
  414. }
  415. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  416. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  417. secondStore := &v1beta1.SecretStore{
  418. ObjectMeta: metav1.ObjectMeta{
  419. Name: "new-store",
  420. Namespace: PushSecretNamespace,
  421. },
  422. TypeMeta: metav1.TypeMeta{
  423. Kind: "SecretStore",
  424. },
  425. Spec: v1beta1.SecretStoreSpec{
  426. Provider: &v1beta1.SecretStoreProvider{
  427. Fake: &v1beta1.FakeProvider{
  428. Data: []v1beta1.FakeProviderData{},
  429. },
  430. },
  431. },
  432. }
  433. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  434. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  435. updatedPS := &v1alpha1.PushSecret{}
  436. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  437. Eventually(func() bool {
  438. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  439. By("checking if Provider value got updated")
  440. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  441. if err != nil {
  442. return false
  443. }
  444. key, ok := updatedPS.Status.SyncedPushSecrets["SecretStore/new-store"]["path/to/key"]
  445. if !ok {
  446. return false
  447. }
  448. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  449. if syncedLen != 1 {
  450. return false
  451. }
  452. return key.Match.SecretKey == "key"
  453. }, time.Second*10, time.Second).Should(BeTrue())
  454. return true
  455. }
  456. }
  457. // if target Secret name is not specified it should use the ExternalSecret name.
  458. syncMatchingLabels := func(tc *testCase) {
  459. fakeProvider.SetSecretFn = func() error {
  460. return nil
  461. }
  462. fakeProvider.DeleteSecretFn = func() error {
  463. return nil
  464. }
  465. tc.pushsecret = &v1alpha1.PushSecret{
  466. ObjectMeta: metav1.ObjectMeta{
  467. Name: PushSecretName,
  468. Namespace: PushSecretNamespace,
  469. },
  470. Spec: v1alpha1.PushSecretSpec{
  471. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  472. {
  473. LabelSelector: &metav1.LabelSelector{
  474. MatchLabels: map[string]string{
  475. "foo": "bar",
  476. },
  477. },
  478. Kind: "SecretStore",
  479. },
  480. },
  481. Selector: v1alpha1.PushSecretSelector{
  482. Secret: v1alpha1.PushSecretSecret{
  483. Name: SecretName,
  484. },
  485. },
  486. Data: []v1alpha1.PushSecretData{
  487. {
  488. Match: v1alpha1.PushSecretMatch{
  489. SecretKey: "key",
  490. RemoteRef: v1alpha1.PushSecretRemoteRef{
  491. RemoteKey: "path/to/key",
  492. },
  493. },
  494. },
  495. },
  496. },
  497. }
  498. tc.store = &v1beta1.SecretStore{
  499. TypeMeta: metav1.TypeMeta{
  500. Kind: "SecretStore",
  501. },
  502. ObjectMeta: metav1.ObjectMeta{
  503. Name: PushSecretStore,
  504. Namespace: PushSecretNamespace,
  505. Labels: map[string]string{
  506. "foo": "bar",
  507. },
  508. },
  509. Spec: v1beta1.SecretStoreSpec{
  510. Provider: &v1beta1.SecretStoreProvider{
  511. Fake: &v1beta1.FakeProvider{
  512. Data: []v1beta1.FakeProviderData{},
  513. },
  514. },
  515. },
  516. }
  517. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  518. secretValue := secret.Data["key"]
  519. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  520. expected := v1alpha1.PushSecretStatusCondition{
  521. Type: v1alpha1.PushSecretReady,
  522. Status: v1.ConditionTrue,
  523. Reason: v1alpha1.ReasonSynced,
  524. Message: "PushSecret synced successfully",
  525. }
  526. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  527. }
  528. }
  529. syncWithClusterStore := func(tc *testCase) {
  530. fakeProvider.SetSecretFn = func() error {
  531. return nil
  532. }
  533. tc.store = &v1beta1.ClusterSecretStore{
  534. TypeMeta: metav1.TypeMeta{
  535. Kind: "ClusterSecretStore",
  536. },
  537. ObjectMeta: metav1.ObjectMeta{
  538. Name: PushSecretStore,
  539. },
  540. Spec: v1beta1.SecretStoreSpec{
  541. Provider: &v1beta1.SecretStoreProvider{
  542. Fake: &v1beta1.FakeProvider{
  543. Data: []v1beta1.FakeProviderData{},
  544. },
  545. },
  546. },
  547. }
  548. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  549. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  550. secretValue := secret.Data["key"]
  551. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  552. expected := v1alpha1.PushSecretStatusCondition{
  553. Type: v1alpha1.PushSecretReady,
  554. Status: v1.ConditionTrue,
  555. Reason: v1alpha1.ReasonSynced,
  556. Message: "PushSecret synced successfully",
  557. }
  558. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  559. }
  560. }
  561. // if target Secret name is not specified it should use the ExternalSecret name.
  562. syncWithClusterStoreMatchingLabels := func(tc *testCase) {
  563. fakeProvider.SetSecretFn = func() error {
  564. return nil
  565. }
  566. tc.pushsecret = &v1alpha1.PushSecret{
  567. ObjectMeta: metav1.ObjectMeta{
  568. Name: PushSecretName,
  569. Namespace: PushSecretNamespace,
  570. },
  571. Spec: v1alpha1.PushSecretSpec{
  572. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  573. {
  574. LabelSelector: &metav1.LabelSelector{
  575. MatchLabels: map[string]string{
  576. "foo": "bar",
  577. },
  578. },
  579. Kind: "ClusterSecretStore",
  580. },
  581. },
  582. Selector: v1alpha1.PushSecretSelector{
  583. Secret: v1alpha1.PushSecretSecret{
  584. Name: SecretName,
  585. },
  586. },
  587. Data: []v1alpha1.PushSecretData{
  588. {
  589. Match: v1alpha1.PushSecretMatch{
  590. SecretKey: "key",
  591. RemoteRef: v1alpha1.PushSecretRemoteRef{
  592. RemoteKey: "path/to/key",
  593. },
  594. },
  595. },
  596. },
  597. },
  598. }
  599. tc.store = &v1beta1.ClusterSecretStore{
  600. ObjectMeta: metav1.ObjectMeta{
  601. Name: PushSecretStore,
  602. Labels: map[string]string{
  603. "foo": "bar",
  604. },
  605. },
  606. Spec: v1beta1.SecretStoreSpec{
  607. Provider: &v1beta1.SecretStoreProvider{
  608. Fake: &v1beta1.FakeProvider{
  609. Data: []v1beta1.FakeProviderData{},
  610. },
  611. },
  612. },
  613. }
  614. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  615. secretValue := secret.Data["key"]
  616. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  617. expected := v1alpha1.PushSecretStatusCondition{
  618. Type: v1alpha1.PushSecretReady,
  619. Status: v1.ConditionTrue,
  620. Reason: v1alpha1.ReasonSynced,
  621. Message: "PushSecret synced successfully",
  622. }
  623. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  624. }
  625. }
  626. // if target Secret name is not specified it should use the ExternalSecret name.
  627. failNoSecret := func(tc *testCase) {
  628. fakeProvider.SetSecretFn = func() error {
  629. return nil
  630. }
  631. tc.secret = nil
  632. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  633. expected := v1alpha1.PushSecretStatusCondition{
  634. Type: v1alpha1.PushSecretReady,
  635. Status: v1.ConditionFalse,
  636. Reason: v1alpha1.ReasonErrored,
  637. Message: "could not get source secret",
  638. }
  639. return checkCondition(ps.Status, expected)
  640. }
  641. }
  642. // if target Secret name is not specified it should use the ExternalSecret name.
  643. failNoSecretKey := func(tc *testCase) {
  644. fakeProvider.SetSecretFn = func() error {
  645. return nil
  646. }
  647. tc.pushsecret.Spec.Data[0].Match.SecretKey = "unexisting"
  648. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  649. expected := v1alpha1.PushSecretStatusCondition{
  650. Type: v1alpha1.PushSecretReady,
  651. Status: v1.ConditionFalse,
  652. Reason: v1alpha1.ReasonErrored,
  653. Message: "set secret failed: secret key unexisting does not exist",
  654. }
  655. return checkCondition(ps.Status, expected)
  656. }
  657. }
  658. // if target Secret name is not specified it should use the ExternalSecret name.
  659. failNoSecretStore := func(tc *testCase) {
  660. fakeProvider.SetSecretFn = func() error {
  661. return nil
  662. }
  663. tc.store = nil
  664. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  665. expected := v1alpha1.PushSecretStatusCondition{
  666. Type: v1alpha1.PushSecretReady,
  667. Status: v1.ConditionFalse,
  668. Reason: v1alpha1.ReasonErrored,
  669. Message: "could not get SecretStore \"test-store\", secretstores.external-secrets.io \"test-store\" not found",
  670. }
  671. return checkCondition(ps.Status, expected)
  672. }
  673. }
  674. // if target Secret name is not specified it should use the ExternalSecret name.
  675. failNoClusterStore := func(tc *testCase) {
  676. fakeProvider.SetSecretFn = func() error {
  677. return nil
  678. }
  679. tc.store = nil
  680. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  681. tc.pushsecret.Spec.SecretStoreRefs[0].Name = "unexisting"
  682. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  683. expected := v1alpha1.PushSecretStatusCondition{
  684. Type: v1alpha1.PushSecretReady,
  685. Status: v1.ConditionFalse,
  686. Reason: v1alpha1.ReasonErrored,
  687. Message: "could not get ClusterSecretStore \"unexisting\", clustersecretstores.external-secrets.io \"unexisting\" not found",
  688. }
  689. return checkCondition(ps.Status, expected)
  690. }
  691. } // if target Secret name is not specified it should use the ExternalSecret name.
  692. setSecretFail := func(tc *testCase) {
  693. fakeProvider.SetSecretFn = func() error {
  694. return fmt.Errorf("boom")
  695. }
  696. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  697. expected := v1alpha1.PushSecretStatusCondition{
  698. Type: v1alpha1.PushSecretReady,
  699. Status: v1.ConditionFalse,
  700. Reason: v1alpha1.ReasonErrored,
  701. Message: "set secret failed: could not write remote ref key to target secretstore test-store: boom",
  702. }
  703. return checkCondition(ps.Status, expected)
  704. }
  705. }
  706. // if target Secret name is not specified it should use the ExternalSecret name.
  707. newClientFail := func(tc *testCase) {
  708. fakeProvider.NewFn = func(context.Context, v1beta1.GenericStore, client.Client, string) (v1beta1.SecretsClient, error) {
  709. return nil, fmt.Errorf("boom")
  710. }
  711. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  712. expected := v1alpha1.PushSecretStatusCondition{
  713. Type: v1alpha1.PushSecretReady,
  714. Status: v1.ConditionFalse,
  715. Reason: v1alpha1.ReasonErrored,
  716. Message: "set secret failed: could not get secrets client for store test-store: boom",
  717. }
  718. return checkCondition(ps.Status, expected)
  719. }
  720. }
  721. DescribeTable("When reconciling a PushSecret",
  722. func(tweaks ...testTweaks) {
  723. tc := makeDefaultTestcase()
  724. for _, tweak := range tweaks {
  725. tweak(tc)
  726. }
  727. ctx := context.Background()
  728. By("creating a secret store, secret and pushsecret")
  729. if tc.store != nil {
  730. Expect(k8sClient.Create(ctx, tc.store)).To(Succeed())
  731. }
  732. if tc.secret != nil {
  733. Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
  734. }
  735. if tc.pushsecret != nil {
  736. Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
  737. }
  738. time.Sleep(2 * time.Second)
  739. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  740. createdPS := &v1alpha1.PushSecret{}
  741. By("checking the pushSecret condition")
  742. Eventually(func() bool {
  743. err := k8sClient.Get(ctx, psKey, createdPS)
  744. if err != nil {
  745. return false
  746. }
  747. return tc.assert(createdPS, tc.secret)
  748. }, timeout, interval).Should(BeTrue())
  749. // this must be optional so we can test faulty es configuration
  750. },
  751. Entry("should sync", syncSuccessfully),
  752. Entry("should sync with template", syncSuccessfullyWithTemplate),
  753. Entry("should delete if DeletionPolicy=Delete", syncAndDeleteSuccessfully),
  754. Entry("should track deletion tasks if Delete fails", failDelete),
  755. Entry("should track deleted stores if Delete fails", failDeleteStore),
  756. Entry("should delete all secrets if SecretStore changes", deleteWholeStore),
  757. Entry("should sync to stores matching labels", syncMatchingLabels),
  758. Entry("should sync with ClusterStore", syncWithClusterStore),
  759. Entry("should sync with ClusterStore matching labels", syncWithClusterStoreMatchingLabels),
  760. Entry("should fail if Secret is not created", failNoSecret),
  761. Entry("should fail if Secret Key does not exist", failNoSecretKey),
  762. Entry("should fail if SetSecret fails", setSecretFail),
  763. Entry("should fail if no valid SecretStore", failNoSecretStore),
  764. Entry("should fail if no valid ClusterSecretStore", failNoClusterStore),
  765. Entry("should fail if NewClient fails", newClientFail),
  766. )
  767. })