pushsecret_controller_test.go 43 KB

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