pushsecret_controller_test.go 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390
  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. v1 "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. "sigs.k8s.io/controller-runtime/pkg/client"
  24. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  25. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  26. ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
  27. "github.com/external-secrets/external-secrets/pkg/controllers/pushsecret/psmetrics"
  28. "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
  29. . "github.com/onsi/ginkgo/v2"
  30. . "github.com/onsi/gomega"
  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. managedStore1 v1beta1.GenericStore
  40. managedStore2 v1beta1.GenericStore
  41. unmanagedStore1 v1beta1.GenericStore
  42. unmanagedStore2 v1beta1.GenericStore
  43. pushsecret *v1alpha1.PushSecret
  44. secret *v1.Secret
  45. assert func(pushsecret *v1alpha1.PushSecret, secret *v1.Secret) bool
  46. }
  47. func init() {
  48. fakeProvider = fake.New()
  49. v1beta1.ForceRegister(fakeProvider, &v1beta1.SecretStoreProvider{
  50. Fake: &v1beta1.FakeProvider{},
  51. })
  52. psmetrics.SetUpMetrics()
  53. }
  54. func checkCondition(status v1alpha1.PushSecretStatus, cond v1alpha1.PushSecretStatusCondition) bool {
  55. fmt.Printf("status: %+v\ncond: %+v\n", status.Conditions, cond)
  56. for _, condition := range status.Conditions {
  57. if condition.Message == cond.Message &&
  58. condition.Reason == cond.Reason &&
  59. condition.Status == cond.Status &&
  60. condition.Type == cond.Type {
  61. return true
  62. }
  63. }
  64. return false
  65. }
  66. type testTweaks func(*testCase)
  67. var _ = Describe("PushSecret controller", func() {
  68. const (
  69. PushSecretName = "test-ps"
  70. PushSecretStore = "test-store"
  71. SecretName = "test-secret"
  72. )
  73. var PushSecretNamespace string
  74. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  75. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  76. if t, err := strconv.Atoi(customTimeout); err == nil {
  77. timeout = time.Second * time.Duration(t)
  78. }
  79. }
  80. BeforeEach(func() {
  81. var err error
  82. PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  83. Expect(err).ToNot(HaveOccurred())
  84. fakeProvider.Reset()
  85. })
  86. AfterEach(func() {
  87. k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
  88. ObjectMeta: metav1.ObjectMeta{
  89. Name: PushSecretName,
  90. Namespace: PushSecretNamespace,
  91. },
  92. })
  93. // give a time for reconciler to remove finalizers before removing SecretStores
  94. // TODO: Secret Stores should have finalizers bound to PushSecrets if DeletionPolicy == Delete
  95. time.Sleep(2 * time.Second)
  96. k8sClient.Delete(context.Background(), &v1beta1.SecretStore{
  97. ObjectMeta: metav1.ObjectMeta{
  98. Name: PushSecretStore,
  99. Namespace: PushSecretNamespace,
  100. },
  101. })
  102. k8sClient.Delete(context.Background(), &v1beta1.ClusterSecretStore{
  103. ObjectMeta: metav1.ObjectMeta{
  104. Name: PushSecretStore,
  105. },
  106. })
  107. k8sClient.Delete(context.Background(), &v1.Secret{
  108. ObjectMeta: metav1.ObjectMeta{
  109. Name: SecretName,
  110. Namespace: PushSecretNamespace,
  111. },
  112. })
  113. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  114. ObjectMeta: metav1.ObjectMeta{
  115. Name: PushSecretNamespace,
  116. },
  117. })).To(Succeed())
  118. })
  119. const (
  120. defaultKey = "key"
  121. defaultVal = "value"
  122. defaultPath = "path/to/key"
  123. otherKey = "other-key"
  124. otherVal = "other-value"
  125. otherPath = "path/to/other-key"
  126. newKey = "new-key"
  127. newVal = "new-value"
  128. storePrefixTemplate = "SecretStore/%v"
  129. )
  130. makeDefaultTestcase := func() *testCase {
  131. return &testCase{
  132. pushsecret: &v1alpha1.PushSecret{
  133. ObjectMeta: metav1.ObjectMeta{
  134. Name: PushSecretName,
  135. Namespace: PushSecretNamespace,
  136. },
  137. Spec: v1alpha1.PushSecretSpec{
  138. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  139. {
  140. Name: PushSecretStore,
  141. Kind: "SecretStore",
  142. },
  143. },
  144. Selector: v1alpha1.PushSecretSelector{
  145. Secret: v1alpha1.PushSecretSecret{
  146. Name: SecretName,
  147. },
  148. },
  149. Data: []v1alpha1.PushSecretData{
  150. {
  151. Match: v1alpha1.PushSecretMatch{
  152. SecretKey: defaultKey,
  153. RemoteRef: v1alpha1.PushSecretRemoteRef{
  154. RemoteKey: defaultPath,
  155. },
  156. },
  157. },
  158. },
  159. },
  160. },
  161. secret: &v1.Secret{
  162. ObjectMeta: metav1.ObjectMeta{
  163. Name: SecretName,
  164. Namespace: PushSecretNamespace,
  165. },
  166. Data: map[string][]byte{
  167. defaultKey: []byte(defaultVal),
  168. },
  169. },
  170. store: &v1beta1.SecretStore{
  171. ObjectMeta: metav1.ObjectMeta{
  172. Name: PushSecretStore,
  173. Namespace: PushSecretNamespace,
  174. },
  175. TypeMeta: metav1.TypeMeta{
  176. Kind: "SecretStore",
  177. },
  178. Spec: v1beta1.SecretStoreSpec{
  179. Provider: &v1beta1.SecretStoreProvider{
  180. Fake: &v1beta1.FakeProvider{
  181. Data: []v1beta1.FakeProviderData{},
  182. },
  183. },
  184. },
  185. },
  186. }
  187. }
  188. // if target Secret name is not specified it should use the ExternalSecret name.
  189. syncSuccessfully := func(tc *testCase) {
  190. fakeProvider.SetSecretFn = func() error {
  191. return nil
  192. }
  193. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  194. Eventually(func() bool {
  195. By("checking if Provider value got updated")
  196. secretValue := secret.Data[defaultKey]
  197. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  198. if !ok {
  199. return false
  200. }
  201. got := providerValue.Value
  202. return bytes.Equal(got, secretValue)
  203. }, time.Second*10, time.Second).Should(BeTrue())
  204. return true
  205. }
  206. }
  207. updateIfNotExists := func(tc *testCase) {
  208. fakeProvider.SetSecretFn = func() error {
  209. return nil
  210. }
  211. fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
  212. _, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
  213. return ok, nil
  214. }
  215. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  216. initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  217. tc.secret.Data[defaultKey] = []byte(newVal)
  218. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  219. Eventually(func() bool {
  220. By("checking if Provider value did not get updated")
  221. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  222. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  223. if !ok {
  224. return false
  225. }
  226. got := providerValue.Value
  227. return bytes.Equal(got, initialValue)
  228. }, time.Second*10, time.Second).Should(BeTrue())
  229. return true
  230. }
  231. }
  232. updateIfNotExistsPartialSecrets := func(tc *testCase) {
  233. fakeProvider.SetSecretFn = func() error {
  234. return nil
  235. }
  236. fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
  237. _, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
  238. return ok, nil
  239. }
  240. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  241. tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
  242. Match: v1alpha1.PushSecretMatch{
  243. SecretKey: otherKey,
  244. RemoteRef: v1alpha1.PushSecretRemoteRef{
  245. RemoteKey: otherPath,
  246. },
  247. },
  248. })
  249. initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  250. tc.secret.Data[defaultKey] = []byte(newVal) // change initial value in secret
  251. tc.secret.Data[otherKey] = []byte(otherVal)
  252. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  253. Eventually(func() bool {
  254. By("checking if only not existing Provider value got updated")
  255. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  256. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  257. if !ok {
  258. return false
  259. }
  260. got := providerValue.Value
  261. otherProviderValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[1].Match.RemoteRef.RemoteKey]
  262. if !ok {
  263. return false
  264. }
  265. gotOther := otherProviderValue.Value
  266. return bytes.Equal(gotOther, tc.secret.Data[otherKey]) && bytes.Equal(got, initialValue)
  267. }, time.Second*10, time.Second).Should(BeTrue())
  268. return true
  269. }
  270. }
  271. updateIfNotExistsSyncStatus := func(tc *testCase) {
  272. fakeProvider.SetSecretFn = func() error {
  273. return nil
  274. }
  275. fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
  276. _, ok := fakeProvider.SetSecretArgs[ref.GetRemoteKey()]
  277. return ok, nil
  278. }
  279. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  280. tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
  281. Match: v1alpha1.PushSecretMatch{
  282. SecretKey: otherKey,
  283. RemoteRef: v1alpha1.PushSecretRemoteRef{
  284. RemoteKey: otherPath,
  285. },
  286. },
  287. })
  288. tc.secret.Data[defaultKey] = []byte(newVal)
  289. tc.secret.Data[otherKey] = []byte(otherVal)
  290. updatedPS := &v1alpha1.PushSecret{}
  291. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  292. Eventually(func() bool {
  293. By("checking if PushSecret status gets updated correctly with UpdatePolicy=IfNotExists")
  294. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  295. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  296. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  297. if err != nil {
  298. return false
  299. }
  300. _, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
  301. if !ok {
  302. return false
  303. }
  304. _, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][otherPath]
  305. if !ok {
  306. return false
  307. }
  308. expected := v1alpha1.PushSecretStatusCondition{
  309. Type: v1alpha1.PushSecretReady,
  310. Status: v1.ConditionTrue,
  311. Reason: v1alpha1.ReasonSynced,
  312. Message: "PushSecret synced successfully. Existing secrets in providers unchanged.",
  313. }
  314. return checkCondition(ps.Status, expected)
  315. }, time.Second*10, time.Second).Should(BeTrue())
  316. return true
  317. }
  318. }
  319. updateIfNotExistsSyncFailed := func(tc *testCase) {
  320. fakeProvider.SetSecretFn = func() error {
  321. return nil
  322. }
  323. fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
  324. return false, fmt.Errorf("don't know")
  325. }
  326. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  327. initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  328. tc.secret.Data[defaultKey] = []byte(newVal)
  329. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  330. Eventually(func() bool {
  331. By("checking if sync failed if secret existence cannot be verified in Provider")
  332. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  333. if !ok {
  334. return false
  335. }
  336. got := providerValue.Value
  337. expected := v1alpha1.PushSecretStatusCondition{
  338. Type: v1alpha1.PushSecretReady,
  339. Status: v1.ConditionFalse,
  340. Reason: v1alpha1.ReasonErrored,
  341. Message: "set secret failed: could not verify if secret exists in store: don't know",
  342. }
  343. return checkCondition(ps.Status, expected) && bytes.Equal(got, initialValue)
  344. }, time.Second*10, time.Second).Should(BeTrue())
  345. return true
  346. }
  347. }
  348. // if target Secret name is not specified it should use the ExternalSecret name.
  349. syncSuccessfullyWithTemplate := func(tc *testCase) {
  350. fakeProvider.SetSecretFn = func() error {
  351. return nil
  352. }
  353. tc.pushsecret = &v1alpha1.PushSecret{
  354. ObjectMeta: metav1.ObjectMeta{
  355. Name: PushSecretName,
  356. Namespace: PushSecretNamespace,
  357. },
  358. Spec: v1alpha1.PushSecretSpec{
  359. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  360. {
  361. Name: PushSecretStore,
  362. Kind: "SecretStore",
  363. },
  364. },
  365. Selector: v1alpha1.PushSecretSelector{
  366. Secret: v1alpha1.PushSecretSecret{
  367. Name: SecretName,
  368. },
  369. },
  370. Data: []v1alpha1.PushSecretData{
  371. {
  372. Match: v1alpha1.PushSecretMatch{
  373. SecretKey: defaultKey,
  374. RemoteRef: v1alpha1.PushSecretRemoteRef{
  375. RemoteKey: defaultPath,
  376. },
  377. },
  378. },
  379. },
  380. Template: &v1beta1.ExternalSecretTemplate{
  381. Metadata: v1beta1.ExternalSecretTemplateMetadata{
  382. Labels: map[string]string{
  383. "foos": "ball",
  384. },
  385. Annotations: map[string]string{
  386. "hihi": "ga",
  387. },
  388. },
  389. Type: v1.SecretTypeOpaque,
  390. EngineVersion: v1beta1.TemplateEngineV2,
  391. Data: map[string]string{
  392. defaultKey: "{{ .key | toString | upper }} was templated",
  393. },
  394. },
  395. },
  396. }
  397. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  398. Eventually(func() bool {
  399. By("checking if Provider value got updated")
  400. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  401. if !ok {
  402. return false
  403. }
  404. got := providerValue.Value
  405. return bytes.Equal(got, []byte("VALUE was templated"))
  406. }, time.Second*10, time.Second).Should(BeTrue())
  407. return true
  408. }
  409. }
  410. // if target Secret name is not specified it should use the ExternalSecret name.
  411. syncAndDeleteSuccessfully := func(tc *testCase) {
  412. fakeProvider.SetSecretFn = func() error {
  413. return nil
  414. }
  415. tc.pushsecret = &v1alpha1.PushSecret{
  416. ObjectMeta: metav1.ObjectMeta{
  417. Name: PushSecretName,
  418. Namespace: PushSecretNamespace,
  419. },
  420. Spec: v1alpha1.PushSecretSpec{
  421. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  422. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  423. {
  424. Name: PushSecretStore,
  425. Kind: "SecretStore",
  426. },
  427. },
  428. Selector: v1alpha1.PushSecretSelector{
  429. Secret: v1alpha1.PushSecretSecret{
  430. Name: SecretName,
  431. },
  432. },
  433. Data: []v1alpha1.PushSecretData{
  434. {
  435. Match: v1alpha1.PushSecretMatch{
  436. SecretKey: defaultKey,
  437. RemoteRef: v1alpha1.PushSecretRemoteRef{
  438. RemoteKey: defaultPath,
  439. },
  440. },
  441. },
  442. },
  443. },
  444. }
  445. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  446. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
  447. updatedPS := &v1alpha1.PushSecret{}
  448. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  449. Eventually(func() bool {
  450. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  451. By("checking if Provider value got updated")
  452. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  453. if err != nil {
  454. return false
  455. }
  456. key, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
  457. if !ok {
  458. return false
  459. }
  460. return key.Match.SecretKey == defaultKey
  461. }, time.Second*10, time.Second).Should(BeTrue())
  462. return true
  463. }
  464. }
  465. // if PushSecret's DeletionPolicy is cleared, it should delete successfully
  466. syncChangePolicyAndDeleteSuccessfully := func(tc *testCase) {
  467. fakeProvider.SetSecretFn = func() error {
  468. return nil
  469. }
  470. tc.pushsecret = &v1alpha1.PushSecret{
  471. ObjectMeta: metav1.ObjectMeta{
  472. Name: PushSecretName,
  473. Namespace: PushSecretNamespace,
  474. },
  475. Spec: v1alpha1.PushSecretSpec{
  476. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  477. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  478. {
  479. Name: PushSecretStore,
  480. Kind: "SecretStore",
  481. },
  482. },
  483. Selector: v1alpha1.PushSecretSelector{
  484. Secret: v1alpha1.PushSecretSecret{
  485. Name: SecretName,
  486. },
  487. },
  488. Data: []v1alpha1.PushSecretData{
  489. {
  490. Match: v1alpha1.PushSecretMatch{
  491. SecretKey: defaultKey,
  492. RemoteRef: v1alpha1.PushSecretRemoteRef{
  493. RemoteKey: defaultPath,
  494. },
  495. },
  496. },
  497. },
  498. },
  499. }
  500. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  501. ps.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyNone
  502. updatedPS := &v1alpha1.PushSecret{}
  503. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  504. Expect(k8sClient.Delete(context.Background(), ps, &client.DeleteOptions{})).Should(Succeed())
  505. Eventually(func() bool {
  506. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  507. By("checking if Get PushSecret returns not found")
  508. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  509. if err != nil && client.IgnoreNotFound(err) == nil {
  510. return true
  511. }
  512. return false
  513. }, time.Second*10, time.Second).Should(BeTrue())
  514. return true
  515. }
  516. }
  517. failDelete := func(tc *testCase) {
  518. fakeProvider.SetSecretFn = func() error {
  519. return nil
  520. }
  521. fakeProvider.DeleteSecretFn = func() error {
  522. return fmt.Errorf("Nope")
  523. }
  524. tc.pushsecret = &v1alpha1.PushSecret{
  525. ObjectMeta: metav1.ObjectMeta{
  526. Name: PushSecretName,
  527. Namespace: PushSecretNamespace,
  528. },
  529. Spec: v1alpha1.PushSecretSpec{
  530. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  531. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  532. {
  533. Name: PushSecretStore,
  534. Kind: "SecretStore",
  535. },
  536. },
  537. Selector: v1alpha1.PushSecretSelector{
  538. Secret: v1alpha1.PushSecretSecret{
  539. Name: SecretName,
  540. },
  541. },
  542. Data: []v1alpha1.PushSecretData{
  543. {
  544. Match: v1alpha1.PushSecretMatch{
  545. SecretKey: defaultKey,
  546. RemoteRef: v1alpha1.PushSecretRemoteRef{
  547. RemoteKey: defaultPath,
  548. },
  549. },
  550. },
  551. },
  552. },
  553. }
  554. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  555. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
  556. updatedPS := &v1alpha1.PushSecret{}
  557. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  558. Eventually(func() bool {
  559. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  560. By("checking if synced secrets correspond to both keys")
  561. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  562. if err != nil {
  563. return false
  564. }
  565. _, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
  566. if !ok {
  567. return false
  568. }
  569. _, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
  570. return ok
  571. }, time.Second*10, time.Second).Should(BeTrue())
  572. return true
  573. }
  574. }
  575. failDeleteStore := func(tc *testCase) {
  576. fakeProvider.SetSecretFn = func() error {
  577. return nil
  578. }
  579. fakeProvider.DeleteSecretFn = func() error {
  580. return fmt.Errorf("boom")
  581. }
  582. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  583. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  584. secondStore := &v1beta1.SecretStore{
  585. ObjectMeta: metav1.ObjectMeta{
  586. Name: "new-store",
  587. Namespace: PushSecretNamespace,
  588. },
  589. TypeMeta: metav1.TypeMeta{
  590. Kind: "SecretStore",
  591. },
  592. Spec: v1beta1.SecretStoreSpec{
  593. Provider: &v1beta1.SecretStoreProvider{
  594. Fake: &v1beta1.FakeProvider{
  595. Data: []v1beta1.FakeProviderData{},
  596. },
  597. },
  598. },
  599. }
  600. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  601. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  602. updatedPS := &v1alpha1.PushSecret{}
  603. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  604. Eventually(func() bool {
  605. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  606. By("checking if Provider value got updated")
  607. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  608. if err != nil {
  609. return false
  610. }
  611. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  612. return syncedLen == 2
  613. }, time.Second*10, time.Second).Should(BeTrue())
  614. return true
  615. }
  616. }
  617. deleteWholeStore := func(tc *testCase) {
  618. fakeProvider.SetSecretFn = func() error {
  619. return nil
  620. }
  621. fakeProvider.DeleteSecretFn = func() error {
  622. return nil
  623. }
  624. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  625. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  626. secondStore := &v1beta1.SecretStore{
  627. ObjectMeta: metav1.ObjectMeta{
  628. Name: "new-store",
  629. Namespace: PushSecretNamespace,
  630. },
  631. TypeMeta: metav1.TypeMeta{
  632. Kind: "SecretStore",
  633. },
  634. Spec: v1beta1.SecretStoreSpec{
  635. Provider: &v1beta1.SecretStoreProvider{
  636. Fake: &v1beta1.FakeProvider{
  637. Data: []v1beta1.FakeProviderData{},
  638. },
  639. },
  640. },
  641. }
  642. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  643. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  644. updatedPS := &v1alpha1.PushSecret{}
  645. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  646. Eventually(func() bool {
  647. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  648. By("checking if Provider value got updated")
  649. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  650. if err != nil {
  651. return false
  652. }
  653. key, ok := updatedPS.Status.SyncedPushSecrets["SecretStore/new-store"][defaultPath]
  654. if !ok {
  655. return false
  656. }
  657. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  658. if syncedLen != 1 {
  659. return false
  660. }
  661. return key.Match.SecretKey == defaultKey
  662. }, time.Second*10, time.Second).Should(BeTrue())
  663. return true
  664. }
  665. }
  666. // if conversion strategy is defined, revert the keys based on the strategy.
  667. syncSuccessfullyWithConversionStrategy := func(tc *testCase) {
  668. fakeProvider.SetSecretFn = func() error {
  669. return nil
  670. }
  671. tc.pushsecret = &v1alpha1.PushSecret{
  672. ObjectMeta: metav1.ObjectMeta{
  673. Name: PushSecretName,
  674. Namespace: PushSecretNamespace,
  675. },
  676. Spec: v1alpha1.PushSecretSpec{
  677. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  678. {
  679. Name: PushSecretStore,
  680. Kind: "SecretStore",
  681. },
  682. },
  683. Selector: v1alpha1.PushSecretSelector{
  684. Secret: v1alpha1.PushSecretSecret{
  685. Name: SecretName,
  686. },
  687. },
  688. Data: []v1alpha1.PushSecretData{
  689. {
  690. ConversionStrategy: v1alpha1.PushSecretConversionReverseUnicode,
  691. Match: v1alpha1.PushSecretMatch{
  692. SecretKey: "some-array[0].entity",
  693. RemoteRef: v1alpha1.PushSecretRemoteRef{
  694. RemoteKey: "path/to/key",
  695. },
  696. },
  697. },
  698. },
  699. },
  700. }
  701. tc.secret = &v1.Secret{
  702. ObjectMeta: metav1.ObjectMeta{
  703. Name: SecretName,
  704. Namespace: PushSecretNamespace,
  705. },
  706. Data: map[string][]byte{
  707. "some-array_U005b_0_U005d_.entity": []byte("value"),
  708. },
  709. }
  710. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  711. Eventually(func() bool {
  712. By("checking if Provider value got updated")
  713. secretValue := secret.Data["some-array_U005b_0_U005d_.entity"]
  714. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  715. if !ok {
  716. return false
  717. }
  718. got := providerValue.Value
  719. return bytes.Equal(got, secretValue)
  720. }, time.Second*10, time.Second).Should(BeTrue())
  721. return true
  722. }
  723. }
  724. // if target Secret name is not specified it should use the ExternalSecret name.
  725. syncMatchingLabels := func(tc *testCase) {
  726. fakeProvider.SetSecretFn = func() error {
  727. return nil
  728. }
  729. fakeProvider.DeleteSecretFn = func() error {
  730. return nil
  731. }
  732. tc.pushsecret = &v1alpha1.PushSecret{
  733. ObjectMeta: metav1.ObjectMeta{
  734. Name: PushSecretName,
  735. Namespace: PushSecretNamespace,
  736. },
  737. Spec: v1alpha1.PushSecretSpec{
  738. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  739. {
  740. LabelSelector: &metav1.LabelSelector{
  741. MatchLabels: map[string]string{
  742. "foo": "bar",
  743. },
  744. },
  745. Kind: "SecretStore",
  746. Name: PushSecretStore,
  747. },
  748. },
  749. Selector: v1alpha1.PushSecretSelector{
  750. Secret: v1alpha1.PushSecretSecret{
  751. Name: SecretName,
  752. },
  753. },
  754. Data: []v1alpha1.PushSecretData{
  755. {
  756. Match: v1alpha1.PushSecretMatch{
  757. SecretKey: defaultKey,
  758. RemoteRef: v1alpha1.PushSecretRemoteRef{
  759. RemoteKey: defaultPath,
  760. },
  761. },
  762. },
  763. },
  764. },
  765. }
  766. tc.store = &v1beta1.SecretStore{
  767. TypeMeta: metav1.TypeMeta{
  768. Kind: "SecretStore",
  769. },
  770. ObjectMeta: metav1.ObjectMeta{
  771. Name: PushSecretStore,
  772. Namespace: PushSecretNamespace,
  773. Labels: map[string]string{
  774. "foo": "bar",
  775. },
  776. },
  777. Spec: v1beta1.SecretStoreSpec{
  778. Provider: &v1beta1.SecretStoreProvider{
  779. Fake: &v1beta1.FakeProvider{
  780. Data: []v1beta1.FakeProviderData{},
  781. },
  782. },
  783. },
  784. }
  785. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  786. secretValue := secret.Data[defaultKey]
  787. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  788. expected := v1alpha1.PushSecretStatusCondition{
  789. Type: v1alpha1.PushSecretReady,
  790. Status: v1.ConditionTrue,
  791. Reason: v1alpha1.ReasonSynced,
  792. Message: "PushSecret synced successfully",
  793. }
  794. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  795. }
  796. }
  797. syncWithClusterStore := func(tc *testCase) {
  798. fakeProvider.SetSecretFn = func() error {
  799. return nil
  800. }
  801. tc.store = &v1beta1.ClusterSecretStore{
  802. TypeMeta: metav1.TypeMeta{
  803. Kind: "ClusterSecretStore",
  804. },
  805. ObjectMeta: metav1.ObjectMeta{
  806. Name: PushSecretStore,
  807. },
  808. Spec: v1beta1.SecretStoreSpec{
  809. Provider: &v1beta1.SecretStoreProvider{
  810. Fake: &v1beta1.FakeProvider{
  811. Data: []v1beta1.FakeProviderData{},
  812. },
  813. },
  814. },
  815. }
  816. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  817. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  818. secretValue := secret.Data[defaultKey]
  819. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  820. expected := v1alpha1.PushSecretStatusCondition{
  821. Type: v1alpha1.PushSecretReady,
  822. Status: v1.ConditionTrue,
  823. Reason: v1alpha1.ReasonSynced,
  824. Message: "PushSecret synced successfully",
  825. }
  826. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  827. }
  828. }
  829. // if target Secret name is not specified it should use the ExternalSecret name.
  830. syncWithClusterStoreMatchingLabels := func(tc *testCase) {
  831. fakeProvider.SetSecretFn = func() error {
  832. return nil
  833. }
  834. tc.pushsecret = &v1alpha1.PushSecret{
  835. ObjectMeta: metav1.ObjectMeta{
  836. Name: PushSecretName,
  837. Namespace: PushSecretNamespace,
  838. },
  839. Spec: v1alpha1.PushSecretSpec{
  840. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  841. {
  842. LabelSelector: &metav1.LabelSelector{
  843. MatchLabels: map[string]string{
  844. "foo": "bar",
  845. },
  846. },
  847. Kind: "ClusterSecretStore",
  848. Name: PushSecretStore,
  849. },
  850. },
  851. Selector: v1alpha1.PushSecretSelector{
  852. Secret: v1alpha1.PushSecretSecret{
  853. Name: SecretName,
  854. },
  855. },
  856. Data: []v1alpha1.PushSecretData{
  857. {
  858. Match: v1alpha1.PushSecretMatch{
  859. SecretKey: defaultKey,
  860. RemoteRef: v1alpha1.PushSecretRemoteRef{
  861. RemoteKey: defaultPath,
  862. },
  863. },
  864. },
  865. },
  866. },
  867. }
  868. tc.store = &v1beta1.ClusterSecretStore{
  869. ObjectMeta: metav1.ObjectMeta{
  870. Name: PushSecretStore,
  871. Labels: map[string]string{
  872. "foo": "bar",
  873. },
  874. },
  875. Spec: v1beta1.SecretStoreSpec{
  876. Provider: &v1beta1.SecretStoreProvider{
  877. Fake: &v1beta1.FakeProvider{
  878. Data: []v1beta1.FakeProviderData{},
  879. },
  880. },
  881. },
  882. }
  883. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  884. secretValue := secret.Data[defaultKey]
  885. providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  886. expected := v1alpha1.PushSecretStatusCondition{
  887. Type: v1alpha1.PushSecretReady,
  888. Status: v1.ConditionTrue,
  889. Reason: v1alpha1.ReasonSynced,
  890. Message: "PushSecret synced successfully",
  891. }
  892. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  893. }
  894. }
  895. // if target Secret name is not specified it should use the ExternalSecret name.
  896. failNoSecret := func(tc *testCase) {
  897. fakeProvider.SetSecretFn = func() error {
  898. return nil
  899. }
  900. tc.secret = nil
  901. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  902. expected := v1alpha1.PushSecretStatusCondition{
  903. Type: v1alpha1.PushSecretReady,
  904. Status: v1.ConditionFalse,
  905. Reason: v1alpha1.ReasonErrored,
  906. Message: "could not get source secret",
  907. }
  908. return checkCondition(ps.Status, expected)
  909. }
  910. }
  911. // if target Secret name is not specified it should use the ExternalSecret name.
  912. failNoSecretKey := func(tc *testCase) {
  913. fakeProvider.SetSecretFn = func() error {
  914. return nil
  915. }
  916. tc.pushsecret.Spec.Data[0].Match.SecretKey = "unexisting"
  917. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  918. expected := v1alpha1.PushSecretStatusCondition{
  919. Type: v1alpha1.PushSecretReady,
  920. Status: v1.ConditionFalse,
  921. Reason: v1alpha1.ReasonErrored,
  922. Message: "set secret failed: secret key unexisting does not exist",
  923. }
  924. return checkCondition(ps.Status, expected)
  925. }
  926. }
  927. // if target Secret name is not specified it should use the ExternalSecret name.
  928. failNoSecretStore := func(tc *testCase) {
  929. fakeProvider.SetSecretFn = func() error {
  930. return nil
  931. }
  932. tc.store = nil
  933. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  934. expected := v1alpha1.PushSecretStatusCondition{
  935. Type: v1alpha1.PushSecretReady,
  936. Status: v1.ConditionFalse,
  937. Reason: v1alpha1.ReasonErrored,
  938. Message: "could not get SecretStore \"test-store\", secretstores.external-secrets.io \"test-store\" not found",
  939. }
  940. return checkCondition(ps.Status, expected)
  941. }
  942. }
  943. // if target Secret name is not specified it should use the ExternalSecret name.
  944. failNoClusterStore := func(tc *testCase) {
  945. fakeProvider.SetSecretFn = func() error {
  946. return nil
  947. }
  948. tc.store = nil
  949. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  950. tc.pushsecret.Spec.SecretStoreRefs[0].Name = "unexisting"
  951. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  952. expected := v1alpha1.PushSecretStatusCondition{
  953. Type: v1alpha1.PushSecretReady,
  954. Status: v1.ConditionFalse,
  955. Reason: v1alpha1.ReasonErrored,
  956. Message: "could not get ClusterSecretStore \"unexisting\", clustersecretstores.external-secrets.io \"unexisting\" not found",
  957. }
  958. return checkCondition(ps.Status, expected)
  959. }
  960. }
  961. // if target Secret name is not specified it should use the ExternalSecret name.
  962. setSecretFail := func(tc *testCase) {
  963. fakeProvider.SetSecretFn = func() error {
  964. return fmt.Errorf("boom")
  965. }
  966. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  967. expected := v1alpha1.PushSecretStatusCondition{
  968. Type: v1alpha1.PushSecretReady,
  969. Status: v1.ConditionFalse,
  970. Reason: v1alpha1.ReasonErrored,
  971. Message: "set secret failed: could not write remote ref key to target secretstore test-store: boom",
  972. }
  973. return checkCondition(ps.Status, expected)
  974. }
  975. }
  976. // if target Secret name is not specified it should use the ExternalSecret name.
  977. newClientFail := func(tc *testCase) {
  978. fakeProvider.NewFn = func(context.Context, v1beta1.GenericStore, client.Client, string) (v1beta1.SecretsClient, error) {
  979. return nil, fmt.Errorf("boom")
  980. }
  981. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  982. expected := v1alpha1.PushSecretStatusCondition{
  983. Type: v1alpha1.PushSecretReady,
  984. Status: v1.ConditionFalse,
  985. Reason: v1alpha1.ReasonErrored,
  986. Message: "set secret failed: could not get secrets client for store test-store: boom",
  987. }
  988. return checkCondition(ps.Status, expected)
  989. }
  990. }
  991. DescribeTable("When reconciling a PushSecret",
  992. func(tweaks ...testTweaks) {
  993. tc := makeDefaultTestcase()
  994. for _, tweak := range tweaks {
  995. tweak(tc)
  996. }
  997. ctx := context.Background()
  998. By("creating a secret store, secret and pushsecret")
  999. if tc.store != nil {
  1000. Expect(k8sClient.Create(ctx, tc.store)).To(Succeed())
  1001. }
  1002. if tc.secret != nil {
  1003. Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
  1004. }
  1005. if tc.pushsecret != nil {
  1006. Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
  1007. }
  1008. time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
  1009. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  1010. createdPS := &v1alpha1.PushSecret{}
  1011. By("checking the pushSecret condition")
  1012. Eventually(func() bool {
  1013. err := k8sClient.Get(ctx, psKey, createdPS)
  1014. if err != nil {
  1015. return false
  1016. }
  1017. return tc.assert(createdPS, tc.secret)
  1018. }, timeout, interval).Should(BeTrue())
  1019. // this must be optional so we can test faulty es configuration
  1020. },
  1021. Entry("should sync", syncSuccessfully),
  1022. Entry("should not update existing secret if UpdatePolicy=IfNotExists", updateIfNotExists),
  1023. Entry("should only update parts of secret that don't already exist if UpdatePolicy=IfNotExists", updateIfNotExistsPartialSecrets),
  1024. Entry("should update the PushSecret status correctly if UpdatePolicy=IfNotExists", updateIfNotExistsSyncStatus),
  1025. Entry("should fail if secret existence cannot be verified if UpdatePolicy=IfNotExists", updateIfNotExistsSyncFailed),
  1026. Entry("should sync with template", syncSuccessfullyWithTemplate),
  1027. Entry("should sync with conversion strategy", syncSuccessfullyWithConversionStrategy),
  1028. Entry("should delete if DeletionPolicy=Delete", syncAndDeleteSuccessfully),
  1029. Entry("should delete after DeletionPolicy changed from Delete to None", syncChangePolicyAndDeleteSuccessfully),
  1030. Entry("should track deletion tasks if Delete fails", failDelete),
  1031. Entry("should track deleted stores if Delete fails", failDeleteStore),
  1032. Entry("should delete all secrets if SecretStore changes", deleteWholeStore),
  1033. Entry("should sync to stores matching labels", syncMatchingLabels),
  1034. Entry("should sync with ClusterStore", syncWithClusterStore),
  1035. Entry("should sync with ClusterStore matching labels", syncWithClusterStoreMatchingLabels),
  1036. Entry("should fail if Secret is not created", failNoSecret),
  1037. Entry("should fail if Secret Key does not exist", failNoSecretKey),
  1038. Entry("should fail if SetSecret fails", setSecretFail),
  1039. Entry("should fail if no valid SecretStore", failNoSecretStore),
  1040. Entry("should fail if no valid ClusterSecretStore", failNoClusterStore),
  1041. Entry("should fail if NewClient fails", newClientFail),
  1042. )
  1043. })
  1044. var _ = Describe("PushSecret Controller Un/Managed Stores", func() {
  1045. const (
  1046. PushSecretName = "test-ps"
  1047. ManagedPushSecretStore1 = "test-managed-store-1"
  1048. ManagedPushSecretStore2 = "test-managed-store-2"
  1049. UnmanagedPushSecretStore1 = "test-unmanaged-store-1"
  1050. UnmanagedPushSecretStore2 = "test-unmanaged-store-2"
  1051. SecretName = "test-secret"
  1052. )
  1053. var PushSecretNamespace string
  1054. PushSecretStores := []string{ManagedPushSecretStore1, ManagedPushSecretStore2, UnmanagedPushSecretStore1, UnmanagedPushSecretStore2}
  1055. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  1056. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  1057. if t, err := strconv.Atoi(customTimeout); err == nil {
  1058. timeout = time.Second * time.Duration(t)
  1059. }
  1060. }
  1061. BeforeEach(func() {
  1062. var err error
  1063. PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  1064. Expect(err).ToNot(HaveOccurred())
  1065. fakeProvider.Reset()
  1066. })
  1067. AfterEach(func() {
  1068. k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
  1069. ObjectMeta: metav1.ObjectMeta{
  1070. Name: PushSecretName,
  1071. Namespace: PushSecretNamespace,
  1072. },
  1073. })
  1074. // give a time for reconciler to remove finalizers before removing SecretStores
  1075. // TODO: Secret Stores should have finalizers bound to PushSecrets if DeletionPolicy == Delete
  1076. time.Sleep(2 * time.Second)
  1077. for _, psstore := range PushSecretStores {
  1078. k8sClient.Delete(context.Background(), &v1beta1.SecretStore{
  1079. ObjectMeta: metav1.ObjectMeta{
  1080. Name: psstore,
  1081. Namespace: PushSecretNamespace,
  1082. },
  1083. })
  1084. k8sClient.Delete(context.Background(), &v1beta1.ClusterSecretStore{
  1085. ObjectMeta: metav1.ObjectMeta{
  1086. Name: psstore,
  1087. },
  1088. })
  1089. }
  1090. k8sClient.Delete(context.Background(), &v1.Secret{
  1091. ObjectMeta: metav1.ObjectMeta{
  1092. Name: SecretName,
  1093. Namespace: PushSecretNamespace,
  1094. },
  1095. })
  1096. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  1097. ObjectMeta: metav1.ObjectMeta{
  1098. Name: PushSecretNamespace,
  1099. },
  1100. })).To(Succeed())
  1101. })
  1102. const (
  1103. defaultKey = "key"
  1104. defaultVal = "value"
  1105. defaultPath = "path/to/key"
  1106. otherKey = "other-key"
  1107. otherVal = "other-value"
  1108. otherPath = "path/to/other-key"
  1109. newKey = "new-key"
  1110. newVal = "new-value"
  1111. storePrefixTemplate = "SecretStore/%v"
  1112. )
  1113. makeDefaultTestcase := func() *testCase {
  1114. return &testCase{
  1115. pushsecret: &v1alpha1.PushSecret{
  1116. ObjectMeta: metav1.ObjectMeta{
  1117. Name: PushSecretName,
  1118. Namespace: PushSecretNamespace,
  1119. },
  1120. Spec: v1alpha1.PushSecretSpec{
  1121. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  1122. {
  1123. Name: ManagedPushSecretStore1,
  1124. Kind: "SecretStore",
  1125. },
  1126. },
  1127. Selector: v1alpha1.PushSecretSelector{
  1128. Secret: v1alpha1.PushSecretSecret{
  1129. Name: SecretName,
  1130. },
  1131. },
  1132. Data: []v1alpha1.PushSecretData{
  1133. {
  1134. Match: v1alpha1.PushSecretMatch{
  1135. SecretKey: defaultKey,
  1136. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1137. RemoteKey: defaultPath,
  1138. },
  1139. },
  1140. },
  1141. },
  1142. },
  1143. },
  1144. secret: &v1.Secret{
  1145. ObjectMeta: metav1.ObjectMeta{
  1146. Name: SecretName,
  1147. Namespace: PushSecretNamespace,
  1148. },
  1149. Data: map[string][]byte{
  1150. defaultKey: []byte(defaultVal),
  1151. },
  1152. },
  1153. managedStore1: &v1beta1.SecretStore{
  1154. ObjectMeta: metav1.ObjectMeta{
  1155. Name: ManagedPushSecretStore1,
  1156. Namespace: PushSecretNamespace,
  1157. },
  1158. TypeMeta: metav1.TypeMeta{
  1159. Kind: "SecretStore",
  1160. },
  1161. Spec: v1beta1.SecretStoreSpec{
  1162. Provider: &v1beta1.SecretStoreProvider{
  1163. Fake: &v1beta1.FakeProvider{
  1164. Data: []v1beta1.FakeProviderData{},
  1165. },
  1166. },
  1167. },
  1168. },
  1169. managedStore2: &v1beta1.SecretStore{
  1170. ObjectMeta: metav1.ObjectMeta{
  1171. Name: ManagedPushSecretStore2,
  1172. Namespace: PushSecretNamespace,
  1173. },
  1174. TypeMeta: metav1.TypeMeta{
  1175. Kind: "SecretStore",
  1176. },
  1177. Spec: v1beta1.SecretStoreSpec{
  1178. Provider: &v1beta1.SecretStoreProvider{
  1179. Fake: &v1beta1.FakeProvider{
  1180. Data: []v1beta1.FakeProviderData{},
  1181. },
  1182. },
  1183. },
  1184. },
  1185. unmanagedStore1: &v1beta1.SecretStore{
  1186. ObjectMeta: metav1.ObjectMeta{
  1187. Name: UnmanagedPushSecretStore1,
  1188. Namespace: PushSecretNamespace,
  1189. },
  1190. TypeMeta: metav1.TypeMeta{
  1191. Kind: "SecretStore",
  1192. },
  1193. Spec: v1beta1.SecretStoreSpec{
  1194. Provider: &v1beta1.SecretStoreProvider{
  1195. Fake: &v1beta1.FakeProvider{
  1196. Data: []v1beta1.FakeProviderData{},
  1197. },
  1198. },
  1199. Controller: "not-managed",
  1200. },
  1201. },
  1202. unmanagedStore2: &v1beta1.SecretStore{
  1203. ObjectMeta: metav1.ObjectMeta{
  1204. Name: UnmanagedPushSecretStore2,
  1205. Namespace: PushSecretNamespace,
  1206. },
  1207. TypeMeta: metav1.TypeMeta{
  1208. Kind: "SecretStore",
  1209. },
  1210. Spec: v1beta1.SecretStoreSpec{
  1211. Provider: &v1beta1.SecretStoreProvider{
  1212. Fake: &v1beta1.FakeProvider{
  1213. Data: []v1beta1.FakeProviderData{},
  1214. },
  1215. },
  1216. Controller: "not-managed",
  1217. },
  1218. },
  1219. }
  1220. }
  1221. multipleManagedStoresSyncsSuccessfully := func(tc *testCase) {
  1222. fakeProvider.SetSecretFn = func() error {
  1223. return nil
  1224. }
  1225. tc.pushsecret.Spec.SecretStoreRefs = append(tc.pushsecret.Spec.SecretStoreRefs,
  1226. v1alpha1.PushSecretStoreRef{
  1227. Name: ManagedPushSecretStore2,
  1228. Kind: "SecretStore",
  1229. },
  1230. )
  1231. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1232. Eventually(func() bool {
  1233. By("checking if Provider value got updated")
  1234. secretValue := secret.Data[defaultKey]
  1235. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  1236. if !ok {
  1237. return false
  1238. }
  1239. got := providerValue.Value
  1240. return bytes.Equal(got, secretValue)
  1241. }, time.Second*10, time.Second).Should(BeTrue())
  1242. return true
  1243. }
  1244. }
  1245. skipUnmanagedStores := func(tc *testCase) {
  1246. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  1247. {
  1248. Name: UnmanagedPushSecretStore1,
  1249. Kind: "SecretStore",
  1250. },
  1251. {
  1252. Name: UnmanagedPushSecretStore2,
  1253. Kind: "SecretStore",
  1254. },
  1255. }
  1256. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1257. return len(ps.Status.Conditions) == 0
  1258. }
  1259. }
  1260. warnUnmanagedStoresAndSyncManagedStores := func(tc *testCase) {
  1261. fakeProvider.SetSecretFn = func() error {
  1262. return nil
  1263. }
  1264. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  1265. {
  1266. Name: ManagedPushSecretStore1,
  1267. Kind: "SecretStore",
  1268. },
  1269. {
  1270. Name: ManagedPushSecretStore2,
  1271. Kind: "SecretStore",
  1272. },
  1273. {
  1274. Name: UnmanagedPushSecretStore1,
  1275. Kind: "SecretStore",
  1276. },
  1277. {
  1278. Name: UnmanagedPushSecretStore2,
  1279. Kind: "SecretStore",
  1280. },
  1281. }
  1282. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1283. Eventually(func() bool {
  1284. By("checking if Provider value got updated")
  1285. secretValue := secret.Data[defaultKey]
  1286. providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  1287. if !ok {
  1288. return false
  1289. }
  1290. got := providerValue.Value
  1291. return bytes.Equal(got, secretValue)
  1292. }, time.Second*10, time.Second).Should(BeTrue())
  1293. return true
  1294. }
  1295. }
  1296. DescribeTable("When reconciling a PushSecret with multiple secret stores",
  1297. func(tweaks ...testTweaks) {
  1298. tc := makeDefaultTestcase()
  1299. for _, tweak := range tweaks {
  1300. tweak(tc)
  1301. }
  1302. ctx := context.Background()
  1303. By("creating secret stores, a secret and a pushsecret")
  1304. if tc.managedStore1 != nil {
  1305. Expect(k8sClient.Create(ctx, tc.managedStore1)).To(Succeed())
  1306. }
  1307. if tc.managedStore2 != nil {
  1308. Expect(k8sClient.Create(ctx, tc.managedStore2)).To(Succeed())
  1309. }
  1310. if tc.unmanagedStore1 != nil {
  1311. Expect(k8sClient.Create(ctx, tc.unmanagedStore1)).To(Succeed())
  1312. }
  1313. if tc.unmanagedStore2 != nil {
  1314. Expect(k8sClient.Create(ctx, tc.unmanagedStore2)).To(Succeed())
  1315. }
  1316. if tc.secret != nil {
  1317. Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
  1318. }
  1319. if tc.pushsecret != nil {
  1320. Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
  1321. }
  1322. time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
  1323. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  1324. createdPS := &v1alpha1.PushSecret{}
  1325. By("checking the pushSecret condition")
  1326. Eventually(func() bool {
  1327. err := k8sClient.Get(ctx, psKey, createdPS)
  1328. if err != nil {
  1329. return false
  1330. }
  1331. return tc.assert(createdPS, tc.secret)
  1332. }, timeout, interval).Should(BeTrue())
  1333. // this must be optional so we can test faulty es configuration
  1334. },
  1335. Entry("should sync successfully if there are multiple managed stores", multipleManagedStoresSyncsSuccessfully),
  1336. Entry("should skip unmanaged stores", skipUnmanagedStores),
  1337. Entry("should skip unmanaged stores and sync managed stores", warnUnmanagedStoresAndSyncManagedStores),
  1338. )
  1339. })