pushsecret_controller_test.go 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package pushsecret
  14. import (
  15. "bytes"
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "os"
  21. "strconv"
  22. "sync/atomic"
  23. "time"
  24. v1 "k8s.io/api/core/v1"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. "sigs.k8s.io/controller-runtime/pkg/client"
  28. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  29. "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  30. genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
  31. ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
  32. "github.com/external-secrets/external-secrets/pkg/controllers/pushsecret/psmetrics"
  33. "github.com/external-secrets/external-secrets/runtime/testing/fake"
  34. . "github.com/onsi/ginkgo/v2"
  35. . "github.com/onsi/gomega"
  36. )
  37. const (
  38. testAdminUser = "admin"
  39. testLocalhost = "localhost"
  40. testDBHost = "db-host"
  41. testDBPort = "db-port"
  42. testAppName = "app-name"
  43. testDBRegexp = "^db-.*"
  44. testAPIKey = "api-key"
  45. testDuplicateRemoteKeyErr = "duplicate remote key"
  46. )
  47. var (
  48. fakeProvider *fake.Client
  49. timeout = time.Second * 10
  50. interval = time.Millisecond * 250
  51. )
  52. type testCase struct {
  53. store esv1.GenericStore
  54. managedStore1 esv1.GenericStore
  55. managedStore2 esv1.GenericStore
  56. unmanagedStore1 esv1.GenericStore
  57. unmanagedStore2 esv1.GenericStore
  58. pushsecret *v1alpha1.PushSecret
  59. secret *v1.Secret
  60. assert func(pushsecret *v1alpha1.PushSecret, secret *v1.Secret) bool
  61. }
  62. func init() {
  63. fakeProvider = fake.New()
  64. esv1.ForceRegister(fakeProvider, &esv1.SecretStoreProvider{
  65. Fake: &esv1.FakeProvider{},
  66. }, esv1.MaintenanceStatusMaintained)
  67. psmetrics.SetUpMetrics()
  68. }
  69. func checkCondition(status v1alpha1.PushSecretStatus, cond v1alpha1.PushSecretStatusCondition) bool {
  70. for _, condition := range status.Conditions {
  71. if condition.Message == cond.Message &&
  72. condition.Reason == cond.Reason &&
  73. condition.Status == cond.Status &&
  74. condition.Type == cond.Type {
  75. return true
  76. }
  77. }
  78. return false
  79. }
  80. type testTweaks func(*testCase)
  81. var _ = Describe("PushSecret controller", func() {
  82. const (
  83. PushSecretName = "test-ps"
  84. PushSecretStore = "test-store"
  85. SecretName = "test-secret"
  86. )
  87. var PushSecretNamespace, OtherNamespace string
  88. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  89. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  90. if t, err := strconv.Atoi(customTimeout); err == nil {
  91. timeout = time.Second * time.Duration(t)
  92. }
  93. }
  94. BeforeEach(func() {
  95. var err error
  96. PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  97. Expect(err).ToNot(HaveOccurred())
  98. OtherNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  99. Expect(err).ToNot(HaveOccurred())
  100. fakeProvider.Reset()
  101. Expect(k8sClient.Create(context.Background(), &genv1alpha1.Fake{
  102. TypeMeta: metav1.TypeMeta{
  103. Kind: "Fake",
  104. APIVersion: "generators.external-secrets.io/v1alpha1",
  105. },
  106. ObjectMeta: metav1.ObjectMeta{
  107. Name: "test",
  108. Namespace: PushSecretNamespace,
  109. },
  110. Spec: genv1alpha1.FakeSpec{
  111. Data: map[string]string{
  112. "key": "foo-bar-from-generator",
  113. },
  114. }})).ToNot(HaveOccurred())
  115. })
  116. AfterEach(func() {
  117. k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
  118. ObjectMeta: metav1.ObjectMeta{
  119. Name: PushSecretName,
  120. Namespace: PushSecretNamespace,
  121. },
  122. })
  123. // give a time for reconciler to remove finalizers before removing SecretStores
  124. time.Sleep(2 * time.Second)
  125. k8sClient.Delete(context.Background(), &esv1.SecretStore{
  126. ObjectMeta: metav1.ObjectMeta{
  127. Name: PushSecretStore,
  128. Namespace: PushSecretNamespace,
  129. },
  130. })
  131. k8sClient.Delete(context.Background(), &esv1.ClusterSecretStore{
  132. ObjectMeta: metav1.ObjectMeta{
  133. Name: PushSecretStore,
  134. },
  135. })
  136. k8sClient.Delete(context.Background(), &v1.Secret{
  137. ObjectMeta: metav1.ObjectMeta{
  138. Name: SecretName,
  139. Namespace: PushSecretNamespace,
  140. },
  141. })
  142. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  143. ObjectMeta: metav1.ObjectMeta{
  144. Name: PushSecretNamespace,
  145. },
  146. })).To(Succeed())
  147. })
  148. const (
  149. defaultKey = "key"
  150. defaultVal = "value"
  151. defaultPath = "path/to/key"
  152. otherKey = "other-key"
  153. otherVal = "other-value"
  154. otherPath = "path/to/other-key"
  155. newKey = "new-key"
  156. newVal = "new-value"
  157. storePrefixTemplate = "SecretStore/%v"
  158. )
  159. It("keeps managed stores when secretStoreRef kind is omitted", func() {
  160. store := &esv1.SecretStore{
  161. ObjectMeta: metav1.ObjectMeta{
  162. Name: PushSecretStore,
  163. Namespace: PushSecretNamespace,
  164. },
  165. Spec: esv1.SecretStoreSpec{
  166. Provider: &esv1.SecretStoreProvider{
  167. Fake: &esv1.FakeProvider{
  168. Data: []esv1.FakeProviderData{
  169. {Key: "key", Value: "value"},
  170. },
  171. },
  172. },
  173. },
  174. }
  175. Expect(k8sClient.Create(context.Background(), store)).To(Succeed())
  176. stores, err := removeUnmanagedStores(context.Background(), PushSecretNamespace, &Reconciler{
  177. Client: k8sClient,
  178. }, map[v1alpha1.PushSecretStoreRef]esv1.GenericStore{
  179. {
  180. Name: PushSecretStore,
  181. APIVersion: esv1.SchemeGroupVersion.String(),
  182. }: store,
  183. })
  184. Expect(err).ToNot(HaveOccurred())
  185. Expect(stores).To(HaveLen(1))
  186. })
  187. makeDefaultTestcase := func() *testCase {
  188. return &testCase{
  189. pushsecret: &v1alpha1.PushSecret{
  190. ObjectMeta: metav1.ObjectMeta{
  191. Name: PushSecretName,
  192. Namespace: PushSecretNamespace,
  193. },
  194. Spec: v1alpha1.PushSecretSpec{
  195. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  196. {
  197. Name: PushSecretStore,
  198. Kind: "SecretStore",
  199. },
  200. },
  201. Selector: v1alpha1.PushSecretSelector{
  202. Secret: &v1alpha1.PushSecretSecret{
  203. Name: SecretName,
  204. },
  205. },
  206. Data: []v1alpha1.PushSecretData{
  207. {
  208. Match: v1alpha1.PushSecretMatch{
  209. SecretKey: defaultKey,
  210. RemoteRef: v1alpha1.PushSecretRemoteRef{
  211. RemoteKey: defaultPath,
  212. },
  213. },
  214. },
  215. },
  216. },
  217. },
  218. secret: &v1.Secret{
  219. ObjectMeta: metav1.ObjectMeta{
  220. Name: SecretName,
  221. Namespace: PushSecretNamespace,
  222. },
  223. Data: map[string][]byte{
  224. defaultKey: []byte(defaultVal),
  225. },
  226. },
  227. store: &esv1.SecretStore{
  228. ObjectMeta: metav1.ObjectMeta{
  229. Name: PushSecretStore,
  230. Namespace: PushSecretNamespace,
  231. },
  232. TypeMeta: metav1.TypeMeta{
  233. Kind: "SecretStore",
  234. },
  235. Spec: esv1.SecretStoreSpec{
  236. Provider: &esv1.SecretStoreProvider{
  237. Fake: &esv1.FakeProvider{
  238. Data: []esv1.FakeProviderData{},
  239. },
  240. },
  241. },
  242. },
  243. }
  244. }
  245. // if target Secret name is not specified it should use the ExternalSecret name.
  246. syncSuccessfully := func(tc *testCase) {
  247. fakeProvider.SetSecretFn = func() error {
  248. return nil
  249. }
  250. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  251. Eventually(func() bool {
  252. By("checking if Provider value got updated")
  253. secretValue := secret.Data[defaultKey]
  254. setSecretArgs := fakeProvider.GetPushSecretData()
  255. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  256. if !ok {
  257. return false
  258. }
  259. got := providerValue.Value
  260. return bytes.Equal(got, secretValue)
  261. }, time.Second*10, time.Second).Should(BeTrue())
  262. return true
  263. }
  264. }
  265. updateIfNotExists := func(tc *testCase) {
  266. fakeProvider.SetSecretFn = func() error {
  267. return nil
  268. }
  269. fakeProvider.SecretExistsFn = func(_ context.Context, ref esv1.PushSecretRemoteRef) (bool, error) {
  270. setSecretArgs := fakeProvider.GetPushSecretData()
  271. _, ok := setSecretArgs[ref.GetRemoteKey()]
  272. return ok, nil
  273. }
  274. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  275. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  276. Consistently(func() bool {
  277. By("updating the secret value")
  278. tc.secret.Data[defaultKey] = []byte(newVal)
  279. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  280. By("checking if Provider value does not get updated")
  281. setSecretArgs := fakeProvider.GetPushSecretData()
  282. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  283. if !ok {
  284. return false
  285. }
  286. got := providerValue.Value
  287. return bytes.Equal(got, []byte(defaultVal))
  288. }, time.Second*10, time.Second).Should(BeTrue())
  289. return true
  290. }
  291. }
  292. updateIfNotExistsPartialSecrets := func(tc *testCase) {
  293. fakeProvider.SetSecretFn = func() error {
  294. return nil
  295. }
  296. fakeProvider.SecretExistsFn = func(_ context.Context, ref esv1.PushSecretRemoteRef) (bool, error) {
  297. setSecretArgs := fakeProvider.GetPushSecretData()
  298. _, ok := setSecretArgs[ref.GetRemoteKey()]
  299. return ok, nil
  300. }
  301. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  302. tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
  303. Match: v1alpha1.PushSecretMatch{
  304. SecretKey: otherKey,
  305. RemoteRef: v1alpha1.PushSecretRemoteRef{
  306. RemoteKey: otherPath,
  307. },
  308. },
  309. })
  310. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  311. Eventually(func() bool {
  312. tc.secret.Data[defaultKey] = []byte(newVal) // change initial value in secret
  313. tc.secret.Data[otherKey] = []byte(otherVal)
  314. By("checking if only not existing Provider value got updated")
  315. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  316. setSecretArgs := fakeProvider.GetPushSecretData()
  317. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  318. if !ok {
  319. return false
  320. }
  321. got := providerValue.Value
  322. otherProviderValue, ok := setSecretArgs[ps.Spec.Data[1].Match.RemoteRef.RemoteKey]
  323. if !ok {
  324. return false
  325. }
  326. gotOther := otherProviderValue.Value
  327. return bytes.Equal(gotOther, tc.secret.Data[otherKey]) && bytes.Equal(got, []byte(defaultVal))
  328. }, time.Second*10, time.Second).Should(BeTrue())
  329. return true
  330. }
  331. }
  332. updateIfNotExistsSyncStatus := func(tc *testCase) {
  333. fakeProvider.SetSecretFn = func() error {
  334. return nil
  335. }
  336. fakeProvider.SecretExistsFn = func(_ context.Context, ref esv1.PushSecretRemoteRef) (bool, error) {
  337. setSecretArgs := fakeProvider.GetPushSecretData()
  338. _, ok := setSecretArgs[ref.GetRemoteKey()]
  339. return ok, nil
  340. }
  341. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  342. tc.pushsecret.Spec.Data = append(tc.pushsecret.Spec.Data, v1alpha1.PushSecretData{
  343. Match: v1alpha1.PushSecretMatch{
  344. SecretKey: otherKey,
  345. RemoteRef: v1alpha1.PushSecretRemoteRef{
  346. RemoteKey: otherPath,
  347. },
  348. },
  349. })
  350. tc.secret.Data[defaultKey] = []byte(newVal)
  351. tc.secret.Data[otherKey] = []byte(otherVal)
  352. updatedPS := &v1alpha1.PushSecret{}
  353. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  354. Eventually(func() bool {
  355. By("checking if PushSecret status gets updated correctly with UpdatePolicy=IfNotExists")
  356. Expect(k8sClient.Update(context.Background(), secret, &client.UpdateOptions{})).Should(Succeed())
  357. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  358. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  359. if err != nil {
  360. return false
  361. }
  362. _, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
  363. if !ok {
  364. return false
  365. }
  366. _, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][otherPath]
  367. if !ok {
  368. return false
  369. }
  370. expected := v1alpha1.PushSecretStatusCondition{
  371. Type: v1alpha1.PushSecretReady,
  372. Status: v1.ConditionTrue,
  373. Reason: v1alpha1.ReasonSynced,
  374. Message: "PushSecret synced successfully. Existing secrets in providers unchanged.",
  375. }
  376. return checkCondition(ps.Status, expected)
  377. }, time.Second*10, time.Second).Should(BeTrue())
  378. return true
  379. }
  380. }
  381. updateIfNotExistsSyncFailed := func(tc *testCase) {
  382. fakeProvider.SetSecretFn = func() error {
  383. return nil
  384. }
  385. fakeProvider.SecretExistsFn = func(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  386. return false, errors.New("don't know")
  387. }
  388. tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
  389. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  390. Eventually(func() bool {
  391. By("checking if sync failed if secret existence cannot be verified in Provider")
  392. expected := v1alpha1.PushSecretStatusCondition{
  393. Type: v1alpha1.PushSecretReady,
  394. Status: v1.ConditionFalse,
  395. Reason: v1alpha1.ReasonErrored,
  396. Message: "set secret failed: could not verify if secret exists in store: don't know",
  397. }
  398. return checkCondition(ps.Status, expected)
  399. }, time.Second*10, time.Second).Should(BeTrue())
  400. return true
  401. }
  402. }
  403. syncSuccessfullyReusingKeys := func(tc *testCase) {
  404. fakeProvider.SetSecretFn = func() error {
  405. return nil
  406. }
  407. tc.pushsecret = &v1alpha1.PushSecret{
  408. ObjectMeta: metav1.ObjectMeta{
  409. Name: PushSecretName,
  410. Namespace: PushSecretNamespace,
  411. },
  412. Spec: v1alpha1.PushSecretSpec{
  413. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  414. {
  415. Name: PushSecretStore,
  416. APIVersion: esv1.SchemeGroupVersion.String(),
  417. },
  418. },
  419. Selector: v1alpha1.PushSecretSelector{
  420. Secret: &v1alpha1.PushSecretSecret{
  421. Name: SecretName,
  422. },
  423. },
  424. Data: []v1alpha1.PushSecretData{
  425. {
  426. Match: v1alpha1.PushSecretMatch{
  427. SecretKey: "otherKey",
  428. RemoteRef: v1alpha1.PushSecretRemoteRef{
  429. RemoteKey: defaultPath,
  430. },
  431. },
  432. },
  433. },
  434. Template: &esv1.ExternalSecretTemplate{
  435. Metadata: esv1.ExternalSecretTemplateMetadata{
  436. Labels: map[string]string{
  437. "foos": "ball",
  438. },
  439. Annotations: map[string]string{
  440. "hihi": "ga",
  441. },
  442. Finalizers: []string{"example.com/finalizer"},
  443. },
  444. Type: v1.SecretTypeOpaque,
  445. EngineVersion: esv1.TemplateEngineV2,
  446. Data: map[string]string{
  447. defaultKey: "{{ .key | toString | upper }} was templated",
  448. "otherKey": "{{ .key | toString | upper }} was also templated",
  449. },
  450. },
  451. },
  452. }
  453. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  454. Eventually(func() bool {
  455. By("checking if Provider value got updated")
  456. setSecretArgs := fakeProvider.GetPushSecretData()
  457. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  458. if !ok {
  459. return false
  460. }
  461. got := providerValue.Value
  462. return bytes.Equal(got, []byte("VALUE was also templated"))
  463. }, time.Second*10, time.Second).Should(BeTrue())
  464. return true
  465. }
  466. }
  467. // if target Secret name is not specified it should use the ExternalSecret name.
  468. syncSuccessfullyWithTemplate := func(tc *testCase) {
  469. fakeProvider.SetSecretFn = func() error {
  470. return nil
  471. }
  472. tc.pushsecret = &v1alpha1.PushSecret{
  473. ObjectMeta: metav1.ObjectMeta{
  474. Name: PushSecretName,
  475. Namespace: PushSecretNamespace,
  476. },
  477. Spec: v1alpha1.PushSecretSpec{
  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. Template: &esv1.ExternalSecretTemplate{
  500. Metadata: esv1.ExternalSecretTemplateMetadata{
  501. Labels: map[string]string{
  502. "foos": "ball",
  503. },
  504. Annotations: map[string]string{
  505. "hihi": "ga",
  506. },
  507. Finalizers: []string{"example.com/finalizer"},
  508. },
  509. Type: v1.SecretTypeOpaque,
  510. EngineVersion: esv1.TemplateEngineV2,
  511. Data: map[string]string{
  512. defaultKey: "{{ .key | toString | upper }} was templated",
  513. },
  514. },
  515. },
  516. }
  517. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  518. Eventually(func() bool {
  519. By("checking if Provider value got updated")
  520. setSecretArgs := fakeProvider.GetPushSecretData()
  521. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  522. if !ok {
  523. return false
  524. }
  525. got := providerValue.Value
  526. return bytes.Equal(got, []byte("VALUE was templated"))
  527. }, time.Second*10, time.Second).Should(BeTrue())
  528. return true
  529. }
  530. }
  531. // if target Secret name is not specified it should use the ExternalSecret name.
  532. syncAndDeleteSuccessfully := func(tc *testCase) {
  533. fakeProvider.SetSecretFn = func() error {
  534. return nil
  535. }
  536. tc.pushsecret = &v1alpha1.PushSecret{
  537. ObjectMeta: metav1.ObjectMeta{
  538. Name: PushSecretName,
  539. Namespace: PushSecretNamespace,
  540. },
  541. Spec: v1alpha1.PushSecretSpec{
  542. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  543. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  544. {
  545. Name: PushSecretStore,
  546. Kind: "SecretStore",
  547. },
  548. },
  549. Selector: v1alpha1.PushSecretSelector{
  550. Secret: &v1alpha1.PushSecretSecret{
  551. Name: SecretName,
  552. },
  553. },
  554. Data: []v1alpha1.PushSecretData{
  555. {
  556. Match: v1alpha1.PushSecretMatch{
  557. SecretKey: defaultKey,
  558. RemoteRef: v1alpha1.PushSecretRemoteRef{
  559. RemoteKey: defaultPath,
  560. },
  561. },
  562. },
  563. },
  564. },
  565. }
  566. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  567. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
  568. updatedPS := &v1alpha1.PushSecret{}
  569. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  570. Eventually(func() bool {
  571. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  572. By("checking if Provider value got updated")
  573. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  574. if err != nil {
  575. return false
  576. }
  577. key, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
  578. if !ok {
  579. return false
  580. }
  581. return key.Match.SecretKey == defaultKey
  582. }, time.Second*10, time.Second).Should(BeTrue())
  583. return true
  584. }
  585. }
  586. // if PushSecret deletes a secret with properties, the status map should be cleaned up correctly
  587. syncAndDeleteWithProperties := func(tc *testCase) {
  588. fakeProvider.SetSecretFn = func() error {
  589. return nil
  590. }
  591. fakeProvider.DeleteSecretFn = func() error {
  592. return nil
  593. }
  594. tc.pushsecret = &v1alpha1.PushSecret{
  595. ObjectMeta: metav1.ObjectMeta{
  596. Name: PushSecretName,
  597. Namespace: PushSecretNamespace,
  598. },
  599. Spec: v1alpha1.PushSecretSpec{
  600. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  601. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  602. {
  603. Name: PushSecretStore,
  604. Kind: "SecretStore",
  605. },
  606. },
  607. Selector: v1alpha1.PushSecretSelector{
  608. Secret: &v1alpha1.PushSecretSecret{
  609. Name: SecretName,
  610. },
  611. },
  612. Data: []v1alpha1.PushSecretData{
  613. {
  614. Match: v1alpha1.PushSecretMatch{
  615. SecretKey: defaultKey,
  616. RemoteRef: v1alpha1.PushSecretRemoteRef{
  617. RemoteKey: defaultPath,
  618. Property: "field1",
  619. },
  620. },
  621. },
  622. {
  623. Match: v1alpha1.PushSecretMatch{
  624. SecretKey: otherKey,
  625. RemoteRef: v1alpha1.PushSecretRemoteRef{
  626. RemoteKey: defaultPath,
  627. Property: "field2",
  628. },
  629. },
  630. },
  631. },
  632. },
  633. }
  634. tc.secret.Data[otherKey] = []byte(otherVal)
  635. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  636. updatedPS := &v1alpha1.PushSecret{}
  637. // Wait for initial sync
  638. Eventually(func() bool {
  639. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  640. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  641. if err != nil {
  642. return false
  643. }
  644. // Check both properties are in status
  645. _, ok1 := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath+"/field1"]
  646. _, ok2 := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath+"/field2"]
  647. return ok1 && ok2
  648. }, time.Second*10, time.Second).Should(BeTrue())
  649. // Remove one property
  650. updatedPS.Spec.Data = []v1alpha1.PushSecretData{
  651. {
  652. Match: v1alpha1.PushSecretMatch{
  653. SecretKey: defaultKey,
  654. RemoteRef: v1alpha1.PushSecretRemoteRef{
  655. RemoteKey: defaultPath,
  656. Property: "field1",
  657. },
  658. },
  659. },
  660. }
  661. Expect(k8sClient.Update(context.Background(), updatedPS, &client.UpdateOptions{})).Should(Succeed())
  662. // Verify the removed property is deleted from status
  663. Eventually(func() bool {
  664. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  665. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  666. if err != nil {
  667. return false
  668. }
  669. // field1 should still exist
  670. _, ok1 := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath+"/field1"]
  671. // field2 should be removed
  672. _, ok2 := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath+"/field2"]
  673. return ok1 && !ok2
  674. }, time.Second*10, time.Second).Should(BeTrue())
  675. return true
  676. }
  677. }
  678. // if PushSecret's DeletionPolicy is cleared, it should delete successfully
  679. syncChangePolicyAndDeleteSuccessfully := func(tc *testCase) {
  680. fakeProvider.SetSecretFn = func() error {
  681. return nil
  682. }
  683. tc.pushsecret = &v1alpha1.PushSecret{
  684. ObjectMeta: metav1.ObjectMeta{
  685. Name: PushSecretName,
  686. Namespace: PushSecretNamespace,
  687. },
  688. Spec: v1alpha1.PushSecretSpec{
  689. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  690. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  691. {
  692. Name: PushSecretStore,
  693. Kind: "SecretStore",
  694. },
  695. },
  696. Selector: v1alpha1.PushSecretSelector{
  697. Secret: &v1alpha1.PushSecretSecret{
  698. Name: SecretName,
  699. },
  700. },
  701. Data: []v1alpha1.PushSecretData{
  702. {
  703. Match: v1alpha1.PushSecretMatch{
  704. SecretKey: defaultKey,
  705. RemoteRef: v1alpha1.PushSecretRemoteRef{
  706. RemoteKey: defaultPath,
  707. },
  708. },
  709. },
  710. },
  711. },
  712. }
  713. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  714. ps.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyNone
  715. updatedPS := &v1alpha1.PushSecret{}
  716. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  717. Expect(k8sClient.Delete(context.Background(), ps, &client.DeleteOptions{})).Should(Succeed())
  718. Eventually(func() bool {
  719. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  720. By("checking if Get PushSecret returns not found")
  721. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  722. if err != nil && client.IgnoreNotFound(err) == nil {
  723. return true
  724. }
  725. return false
  726. }, time.Second*10, time.Second).Should(BeTrue())
  727. return true
  728. }
  729. }
  730. // When source Secret is deleted and DeletionPolicy=Delete, provider secrets should be cleaned up
  731. deleteProviderSecretsOnSourceSecretDeleted := func(tc *testCase) {
  732. var deleteCallCount int32
  733. fakeProvider.SetSecretFn = func() error {
  734. return nil
  735. }
  736. fakeProvider.DeleteSecretFn = func() error {
  737. atomic.AddInt32(&deleteCallCount, 1)
  738. return nil
  739. }
  740. tc.pushsecret = &v1alpha1.PushSecret{
  741. ObjectMeta: metav1.ObjectMeta{
  742. Name: PushSecretName,
  743. Namespace: PushSecretNamespace,
  744. },
  745. Spec: v1alpha1.PushSecretSpec{
  746. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  747. // Short refresh interval so reconciler detects deleted secret quickly
  748. RefreshInterval: &metav1.Duration{Duration: time.Second},
  749. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  750. {
  751. Name: PushSecretStore,
  752. Kind: "SecretStore",
  753. },
  754. },
  755. Selector: v1alpha1.PushSecretSelector{
  756. Secret: &v1alpha1.PushSecretSecret{
  757. Name: SecretName,
  758. },
  759. },
  760. Data: []v1alpha1.PushSecretData{
  761. {
  762. Match: v1alpha1.PushSecretMatch{
  763. SecretKey: defaultKey,
  764. RemoteRef: v1alpha1.PushSecretRemoteRef{
  765. RemoteKey: defaultPath,
  766. },
  767. },
  768. },
  769. },
  770. },
  771. }
  772. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  773. updatedPS := &v1alpha1.PushSecret{}
  774. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  775. // Wait for initial sync
  776. Eventually(func() bool {
  777. By("waiting for initial sync to complete")
  778. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  779. if err != nil {
  780. return false
  781. }
  782. storeKey := fmt.Sprintf(storePrefixTemplate, PushSecretStore)
  783. _, ok := updatedPS.Status.SyncedPushSecrets[storeKey][defaultPath]
  784. return ok
  785. }, time.Second*10, time.Second).Should(BeTrue())
  786. // Delete the source Secret
  787. By("deleting source Secret")
  788. Expect(k8sClient.Delete(context.Background(), secret, &client.DeleteOptions{})).Should(Succeed())
  789. // Verify provider secrets are cleaned up
  790. Eventually(func() bool {
  791. By("checking if provider secrets were deleted")
  792. return atomic.LoadInt32(&deleteCallCount) > 0
  793. }, time.Second*10, time.Second).Should(BeTrue())
  794. // Verify status shows empty synced secrets
  795. Eventually(func() bool {
  796. By("checking if SyncedPushSecrets is empty")
  797. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  798. if err != nil {
  799. return false
  800. }
  801. storeKey := fmt.Sprintf(storePrefixTemplate, PushSecretStore)
  802. secrets, exists := updatedPS.Status.SyncedPushSecrets[storeKey]
  803. return !exists || len(secrets) == 0
  804. }, time.Second*10, time.Second).Should(BeTrue())
  805. return true
  806. }
  807. }
  808. failDelete := func(tc *testCase) {
  809. fakeProvider.SetSecretFn = func() error {
  810. return nil
  811. }
  812. fakeProvider.DeleteSecretFn = func() error {
  813. return errors.New("Nope")
  814. }
  815. tc.pushsecret = &v1alpha1.PushSecret{
  816. ObjectMeta: metav1.ObjectMeta{
  817. Name: PushSecretName,
  818. Namespace: PushSecretNamespace,
  819. },
  820. Spec: v1alpha1.PushSecretSpec{
  821. DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
  822. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  823. {
  824. Name: PushSecretStore,
  825. Kind: "SecretStore",
  826. },
  827. },
  828. Selector: v1alpha1.PushSecretSelector{
  829. Secret: &v1alpha1.PushSecretSecret{
  830. Name: SecretName,
  831. },
  832. },
  833. Data: []v1alpha1.PushSecretData{
  834. {
  835. Match: v1alpha1.PushSecretMatch{
  836. SecretKey: defaultKey,
  837. RemoteRef: v1alpha1.PushSecretRemoteRef{
  838. RemoteKey: defaultPath,
  839. },
  840. },
  841. },
  842. },
  843. },
  844. }
  845. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  846. ps.Spec.Data[0].Match.RemoteRef.RemoteKey = newKey
  847. updatedPS := &v1alpha1.PushSecret{}
  848. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  849. Eventually(func() bool {
  850. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  851. By("checking if synced secrets correspond to both keys")
  852. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  853. if err != nil {
  854. return false
  855. }
  856. _, ok := updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][newKey]
  857. if !ok {
  858. return false
  859. }
  860. _, ok = updatedPS.Status.SyncedPushSecrets[fmt.Sprintf(storePrefixTemplate, PushSecretStore)][defaultPath]
  861. return ok
  862. }, time.Second*10, time.Second).Should(BeTrue())
  863. return true
  864. }
  865. }
  866. failDeleteStore := func(tc *testCase) {
  867. fakeProvider.SetSecretFn = func() error {
  868. return nil
  869. }
  870. fakeProvider.DeleteSecretFn = func() error {
  871. return errors.New("boom")
  872. }
  873. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  874. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  875. secondStore := &esv1.SecretStore{
  876. ObjectMeta: metav1.ObjectMeta{
  877. Name: "new-store",
  878. Namespace: PushSecretNamespace,
  879. },
  880. TypeMeta: metav1.TypeMeta{
  881. Kind: "SecretStore",
  882. },
  883. Spec: esv1.SecretStoreSpec{
  884. Provider: &esv1.SecretStoreProvider{
  885. Fake: &esv1.FakeProvider{
  886. Data: []esv1.FakeProviderData{},
  887. },
  888. },
  889. },
  890. }
  891. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  892. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  893. updatedPS := &v1alpha1.PushSecret{}
  894. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  895. Eventually(func() bool {
  896. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  897. By("checking if Provider value got updated")
  898. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  899. if err != nil {
  900. return false
  901. }
  902. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  903. return syncedLen == 2
  904. }, time.Second*10, time.Second).Should(BeTrue())
  905. return true
  906. }
  907. }
  908. deleteWholeStore := func(tc *testCase) {
  909. fakeProvider.SetSecretFn = func() error {
  910. return nil
  911. }
  912. fakeProvider.DeleteSecretFn = func() error {
  913. return nil
  914. }
  915. tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
  916. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  917. secondStore := &esv1.SecretStore{
  918. ObjectMeta: metav1.ObjectMeta{
  919. Name: "new-store",
  920. Namespace: PushSecretNamespace,
  921. },
  922. TypeMeta: metav1.TypeMeta{
  923. Kind: "SecretStore",
  924. },
  925. Spec: esv1.SecretStoreSpec{
  926. Provider: &esv1.SecretStoreProvider{
  927. Fake: &esv1.FakeProvider{
  928. Data: []esv1.FakeProviderData{},
  929. },
  930. },
  931. },
  932. }
  933. Expect(k8sClient.Create(context.Background(), secondStore, &client.CreateOptions{})).Should(Succeed())
  934. ps.Spec.SecretStoreRefs[0].Name = "new-store"
  935. updatedPS := &v1alpha1.PushSecret{}
  936. Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
  937. Eventually(func() bool {
  938. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  939. By("checking if Provider value got updated")
  940. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  941. if err != nil {
  942. return false
  943. }
  944. key, ok := updatedPS.Status.SyncedPushSecrets["SecretStore/new-store"][defaultPath]
  945. if !ok {
  946. return false
  947. }
  948. syncedLen := len(updatedPS.Status.SyncedPushSecrets)
  949. if syncedLen != 1 {
  950. return false
  951. }
  952. return key.Match.SecretKey == defaultKey
  953. }, time.Second*10, time.Second).Should(BeTrue())
  954. return true
  955. }
  956. }
  957. // if conversion strategy is defined, revert the keys based on the strategy.
  958. syncSuccessfullyWithConversionStrategy := func(tc *testCase) {
  959. fakeProvider.SetSecretFn = func() error {
  960. return nil
  961. }
  962. tc.pushsecret = &v1alpha1.PushSecret{
  963. ObjectMeta: metav1.ObjectMeta{
  964. Name: PushSecretName,
  965. Namespace: PushSecretNamespace,
  966. },
  967. Spec: v1alpha1.PushSecretSpec{
  968. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  969. {
  970. Name: PushSecretStore,
  971. Kind: "SecretStore",
  972. },
  973. },
  974. Selector: v1alpha1.PushSecretSelector{
  975. Secret: &v1alpha1.PushSecretSecret{
  976. Name: SecretName,
  977. },
  978. },
  979. Data: []v1alpha1.PushSecretData{
  980. {
  981. ConversionStrategy: v1alpha1.PushSecretConversionReverseUnicode,
  982. Match: v1alpha1.PushSecretMatch{
  983. SecretKey: "some-array[0].entity",
  984. RemoteRef: v1alpha1.PushSecretRemoteRef{
  985. RemoteKey: defaultPath,
  986. },
  987. },
  988. },
  989. },
  990. },
  991. }
  992. tc.secret = &v1.Secret{
  993. ObjectMeta: metav1.ObjectMeta{
  994. Name: SecretName,
  995. Namespace: PushSecretNamespace,
  996. },
  997. Data: map[string][]byte{
  998. "some-array_U005b_0_U005d_.entity": []byte("value"),
  999. },
  1000. }
  1001. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1002. Eventually(func() bool {
  1003. By("checking if Provider value got updated")
  1004. secretValue := secret.Data["some-array_U005b_0_U005d_.entity"]
  1005. setSecretArgs := fakeProvider.GetPushSecretData()
  1006. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  1007. if !ok {
  1008. return false
  1009. }
  1010. got := providerValue.Value
  1011. return bytes.Equal(got, secretValue)
  1012. }, time.Second*10, time.Second).Should(BeTrue())
  1013. return true
  1014. }
  1015. }
  1016. // if target Secret name is not specified it should use the ExternalSecret name.
  1017. syncMatchingLabels := func(tc *testCase) {
  1018. fakeProvider.SetSecretFn = func() error {
  1019. return nil
  1020. }
  1021. fakeProvider.DeleteSecretFn = func() error {
  1022. return nil
  1023. }
  1024. tc.pushsecret = &v1alpha1.PushSecret{
  1025. ObjectMeta: metav1.ObjectMeta{
  1026. Name: PushSecretName,
  1027. Namespace: PushSecretNamespace,
  1028. },
  1029. Spec: v1alpha1.PushSecretSpec{
  1030. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  1031. {
  1032. LabelSelector: &metav1.LabelSelector{
  1033. MatchLabels: map[string]string{
  1034. "foo": "bar",
  1035. },
  1036. },
  1037. Kind: "SecretStore",
  1038. Name: PushSecretStore,
  1039. },
  1040. },
  1041. Selector: v1alpha1.PushSecretSelector{
  1042. Secret: &v1alpha1.PushSecretSecret{
  1043. Name: SecretName,
  1044. },
  1045. },
  1046. Data: []v1alpha1.PushSecretData{
  1047. {
  1048. Match: v1alpha1.PushSecretMatch{
  1049. SecretKey: defaultKey,
  1050. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1051. RemoteKey: defaultPath,
  1052. },
  1053. },
  1054. },
  1055. },
  1056. },
  1057. }
  1058. tc.store = &esv1.SecretStore{
  1059. TypeMeta: metav1.TypeMeta{
  1060. Kind: "SecretStore",
  1061. },
  1062. ObjectMeta: metav1.ObjectMeta{
  1063. Name: PushSecretStore,
  1064. Namespace: PushSecretNamespace,
  1065. Labels: map[string]string{
  1066. "foo": "bar",
  1067. },
  1068. },
  1069. Spec: esv1.SecretStoreSpec{
  1070. Provider: &esv1.SecretStoreProvider{
  1071. Fake: &esv1.FakeProvider{
  1072. Data: []esv1.FakeProviderData{},
  1073. },
  1074. },
  1075. },
  1076. }
  1077. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1078. secretValue := secret.Data[defaultKey]
  1079. setSecretArgs := fakeProvider.GetPushSecretData()
  1080. providerValue := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  1081. expected := v1alpha1.PushSecretStatusCondition{
  1082. Type: v1alpha1.PushSecretReady,
  1083. Status: v1.ConditionTrue,
  1084. Reason: v1alpha1.ReasonSynced,
  1085. Message: "PushSecret synced successfully",
  1086. }
  1087. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  1088. }
  1089. }
  1090. syncWithClusterStore := func(tc *testCase) {
  1091. fakeProvider.SetSecretFn = func() error {
  1092. return nil
  1093. }
  1094. tc.store = &esv1.ClusterSecretStore{
  1095. TypeMeta: metav1.TypeMeta{
  1096. Kind: "ClusterSecretStore",
  1097. },
  1098. ObjectMeta: metav1.ObjectMeta{
  1099. Name: PushSecretStore,
  1100. },
  1101. Spec: esv1.SecretStoreSpec{
  1102. Provider: &esv1.SecretStoreProvider{
  1103. Fake: &esv1.FakeProvider{
  1104. Data: []esv1.FakeProviderData{},
  1105. },
  1106. },
  1107. },
  1108. }
  1109. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  1110. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1111. secretValue := secret.Data[defaultKey]
  1112. setSecretArgs := fakeProvider.GetPushSecretData()
  1113. providerValue := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  1114. expected := v1alpha1.PushSecretStatusCondition{
  1115. Type: v1alpha1.PushSecretReady,
  1116. Status: v1.ConditionTrue,
  1117. Reason: v1alpha1.ReasonSynced,
  1118. Message: "PushSecret synced successfully",
  1119. }
  1120. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  1121. }
  1122. }
  1123. syncWithGenerator := func(tc *testCase) {
  1124. fakeProvider.SetSecretFn = func() error {
  1125. return nil
  1126. }
  1127. tc.pushsecret.Spec.Selector.Secret = nil
  1128. tc.pushsecret.Spec.Selector.GeneratorRef = &esv1.GeneratorRef{
  1129. APIVersion: "generators.external-secrets.io/v1alpha1",
  1130. Kind: "Fake",
  1131. Name: "test",
  1132. }
  1133. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1134. setSecretArgs := fakeProvider.GetPushSecretData()
  1135. providerValue := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  1136. expected := v1alpha1.PushSecretStatusCondition{
  1137. Type: v1alpha1.PushSecretReady,
  1138. Status: v1.ConditionTrue,
  1139. Reason: v1alpha1.ReasonSynced,
  1140. Message: "PushSecret synced successfully",
  1141. }
  1142. return bytes.Equal([]byte("foo-bar-from-generator"), providerValue) && checkCondition(ps.Status, expected)
  1143. }
  1144. }
  1145. // if target Secret name is not specified it should use the ExternalSecret name.
  1146. syncWithClusterStoreMatchingLabels := func(tc *testCase) {
  1147. fakeProvider.SetSecretFn = func() error {
  1148. return nil
  1149. }
  1150. tc.pushsecret = &v1alpha1.PushSecret{
  1151. ObjectMeta: metav1.ObjectMeta{
  1152. Name: PushSecretName,
  1153. Namespace: PushSecretNamespace,
  1154. },
  1155. Spec: v1alpha1.PushSecretSpec{
  1156. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  1157. {
  1158. LabelSelector: &metav1.LabelSelector{
  1159. MatchLabels: map[string]string{
  1160. "foo": "bar",
  1161. },
  1162. },
  1163. Kind: "ClusterSecretStore",
  1164. Name: PushSecretStore,
  1165. },
  1166. },
  1167. Selector: v1alpha1.PushSecretSelector{
  1168. Secret: &v1alpha1.PushSecretSecret{
  1169. Name: SecretName,
  1170. },
  1171. },
  1172. Data: []v1alpha1.PushSecretData{
  1173. {
  1174. Match: v1alpha1.PushSecretMatch{
  1175. SecretKey: defaultKey,
  1176. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1177. RemoteKey: defaultPath,
  1178. },
  1179. },
  1180. },
  1181. },
  1182. },
  1183. }
  1184. tc.store = &esv1.ClusterSecretStore{
  1185. ObjectMeta: metav1.ObjectMeta{
  1186. Name: PushSecretStore,
  1187. Labels: map[string]string{
  1188. "foo": "bar",
  1189. },
  1190. },
  1191. Spec: esv1.SecretStoreSpec{
  1192. Provider: &esv1.SecretStoreProvider{
  1193. Fake: &esv1.FakeProvider{
  1194. Data: []esv1.FakeProviderData{},
  1195. },
  1196. },
  1197. },
  1198. }
  1199. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1200. secretValue := secret.Data[defaultKey]
  1201. setSecretArgs := fakeProvider.GetPushSecretData()
  1202. providerValue := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
  1203. expected := v1alpha1.PushSecretStatusCondition{
  1204. Type: v1alpha1.PushSecretReady,
  1205. Status: v1.ConditionTrue,
  1206. Reason: v1alpha1.ReasonSynced,
  1207. Message: "PushSecret synced successfully",
  1208. }
  1209. return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
  1210. }
  1211. }
  1212. // if target Secret name is not specified it should use the ExternalSecret name.
  1213. failNoSecret := func(tc *testCase) {
  1214. fakeProvider.SetSecretFn = func() error {
  1215. return nil
  1216. }
  1217. tc.secret = nil
  1218. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1219. expected := v1alpha1.PushSecretStatusCondition{
  1220. Type: v1alpha1.PushSecretReady,
  1221. Status: v1.ConditionFalse,
  1222. Reason: v1alpha1.ReasonErrored,
  1223. Message: "could not get source secret",
  1224. }
  1225. return checkCondition(ps.Status, expected)
  1226. }
  1227. }
  1228. // if target Secret name is not specified it should use the ExternalSecret name.
  1229. failNoSecretKey := func(tc *testCase) {
  1230. fakeProvider.SetSecretFn = func() error {
  1231. return nil
  1232. }
  1233. tc.pushsecret.Spec.Data[0].Match.SecretKey = "unexisting"
  1234. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1235. expected := v1alpha1.PushSecretStatusCondition{
  1236. Type: v1alpha1.PushSecretReady,
  1237. Status: v1.ConditionFalse,
  1238. Reason: v1alpha1.ReasonErrored,
  1239. Message: "set secret failed: secret key unexisting does not exist",
  1240. }
  1241. return checkCondition(ps.Status, expected)
  1242. }
  1243. }
  1244. // if target Secret name is not specified it should use the ExternalSecret name.
  1245. failNoSecretStore := func(tc *testCase) {
  1246. fakeProvider.SetSecretFn = func() error {
  1247. return nil
  1248. }
  1249. tc.store = nil
  1250. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1251. expected := v1alpha1.PushSecretStatusCondition{
  1252. Type: v1alpha1.PushSecretReady,
  1253. Status: v1.ConditionFalse,
  1254. Reason: v1alpha1.ReasonErrored,
  1255. Message: "could not get SecretStore \"test-store\", secretstores.external-secrets.io \"test-store\" not found",
  1256. }
  1257. return checkCondition(ps.Status, expected)
  1258. }
  1259. }
  1260. // if target Secret name is not specified it should use the ExternalSecret name.
  1261. failNoClusterStore := func(tc *testCase) {
  1262. fakeProvider.SetSecretFn = func() error {
  1263. return nil
  1264. }
  1265. tc.store = nil
  1266. tc.pushsecret.Spec.SecretStoreRefs[0].Kind = "ClusterSecretStore"
  1267. tc.pushsecret.Spec.SecretStoreRefs[0].Name = "unexisting"
  1268. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1269. expected := v1alpha1.PushSecretStatusCondition{
  1270. Type: v1alpha1.PushSecretReady,
  1271. Status: v1.ConditionFalse,
  1272. Reason: v1alpha1.ReasonErrored,
  1273. Message: "could not get ClusterSecretStore \"unexisting\", clustersecretstores.external-secrets.io \"unexisting\" not found",
  1274. }
  1275. return checkCondition(ps.Status, expected)
  1276. }
  1277. }
  1278. // if target Secret name is not specified it should use the ExternalSecret name.
  1279. setSecretFail := func(tc *testCase) {
  1280. fakeProvider.SetSecretFn = func() error {
  1281. return errors.New("boom")
  1282. }
  1283. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1284. expected := v1alpha1.PushSecretStatusCondition{
  1285. Type: v1alpha1.PushSecretReady,
  1286. Status: v1.ConditionFalse,
  1287. Reason: v1alpha1.ReasonErrored,
  1288. Message: "set secret failed: could not write remote ref key to target secretstore test-store: boom",
  1289. }
  1290. return checkCondition(ps.Status, expected)
  1291. }
  1292. }
  1293. // if target Secret name is not specified it should use the ExternalSecret name.
  1294. newClientFail := func(tc *testCase) {
  1295. fakeProvider.NewFn = func(_ context.Context, _ esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  1296. return nil, errors.New("boom")
  1297. }
  1298. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1299. expected := v1alpha1.PushSecretStatusCondition{
  1300. Type: v1alpha1.PushSecretReady,
  1301. Status: v1.ConditionFalse,
  1302. Reason: v1alpha1.ReasonErrored,
  1303. Message: "set secret failed: could not get secrets client for store test-store: boom",
  1304. }
  1305. return checkCondition(ps.Status, expected)
  1306. }
  1307. }
  1308. // SecretStores in different namespace than PushSecret should not be selected.
  1309. secretStoreDifferentNamespace := func(tc *testCase) {
  1310. fakeProvider.SetSecretFn = func() error {
  1311. return nil
  1312. }
  1313. // Create the SecretStore in a different namespace
  1314. tc.store = &esv1.SecretStore{
  1315. ObjectMeta: metav1.ObjectMeta{
  1316. Name: "other-ns-store",
  1317. Namespace: OtherNamespace,
  1318. Labels: map[string]string{
  1319. "foo": "bar",
  1320. },
  1321. },
  1322. TypeMeta: metav1.TypeMeta{
  1323. Kind: "SecretStore",
  1324. },
  1325. Spec: esv1.SecretStoreSpec{
  1326. Provider: &esv1.SecretStoreProvider{
  1327. Fake: &esv1.FakeProvider{
  1328. Data: []esv1.FakeProviderData{},
  1329. },
  1330. },
  1331. },
  1332. }
  1333. // Use label selector to select SecretStores
  1334. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  1335. {
  1336. Kind: "SecretStore",
  1337. LabelSelector: &metav1.LabelSelector{
  1338. MatchLabels: map[string]string{
  1339. "foo": "bar",
  1340. },
  1341. },
  1342. },
  1343. }
  1344. // Should not select the SecretStore in a different namespace
  1345. // (if so, it would fail to find it in the same namespace and be reflected in the status)
  1346. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1347. // Assert that the status is never updated (no SecretStores found)
  1348. Consistently(func() bool {
  1349. err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(ps), ps)
  1350. if err != nil {
  1351. return false
  1352. }
  1353. return len(ps.Status.Conditions) == 0
  1354. }, timeout, interval).Should(BeTrue())
  1355. return true
  1356. }
  1357. }
  1358. // Secrets in different namespace than PushSecret should not be selected.
  1359. secretDifferentNamespace := func(tc *testCase) {
  1360. fakeProvider.SetSecretFn = func() error {
  1361. return nil
  1362. }
  1363. // Create the Secret in a different namespace
  1364. tc.secret = &v1.Secret{
  1365. ObjectMeta: metav1.ObjectMeta{
  1366. Name: SecretName,
  1367. Namespace: OtherNamespace,
  1368. Labels: map[string]string{
  1369. "foo": "bar",
  1370. },
  1371. },
  1372. Data: map[string][]byte{
  1373. defaultKey: []byte(defaultVal),
  1374. },
  1375. }
  1376. // Use label selector to select Secrets
  1377. tc.pushsecret.Spec.Selector.Secret = &v1alpha1.PushSecretSecret{
  1378. Selector: &metav1.LabelSelector{
  1379. MatchLabels: map[string]string{
  1380. "foo": "bar",
  1381. },
  1382. },
  1383. }
  1384. tc.assert = func(_ *v1alpha1.PushSecret, _ *v1.Secret) bool {
  1385. Eventually(func() bool {
  1386. // We should not be able to reference a secret across namespaces,
  1387. // the map should be empty.
  1388. Expect(fakeProvider.GetPushSecretData()).To(BeEmpty())
  1389. return true
  1390. }, time.Second*10, time.Second).Should(BeTrue())
  1391. return true
  1392. }
  1393. }
  1394. // dataTo tests
  1395. syncWithDataToMatchAll := func(tc *testCase) {
  1396. fakeProvider.SetSecretFn = func() error {
  1397. return nil
  1398. }
  1399. // Set up secret with multiple keys
  1400. tc.secret.Data = map[string][]byte{
  1401. testDBHost: []byte(testLocalhost),
  1402. testDBPort: []byte("5432"),
  1403. "db-username": []byte(testAdminUser),
  1404. }
  1405. // Replace data with dataTo that matches all keys
  1406. tc.pushsecret.Spec.Data = nil
  1407. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1408. {
  1409. StoreRef: &v1alpha1.PushSecretStoreRef{
  1410. Name: PushSecretStore,
  1411. },
  1412. // No match pattern means match all
  1413. },
  1414. }
  1415. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1416. Eventually(func() bool {
  1417. By("checking if all keys were pushed to provider")
  1418. setSecretArgs := fakeProvider.GetPushSecretData()
  1419. // All three keys should be pushed
  1420. if len(setSecretArgs) != 3 {
  1421. return false
  1422. }
  1423. // Check each key was pushed with same name
  1424. for key, expectedValue := range secret.Data {
  1425. providerValue, ok := setSecretArgs[key]
  1426. if !ok {
  1427. return false
  1428. }
  1429. if !bytes.Equal(providerValue.Value, expectedValue) {
  1430. return false
  1431. }
  1432. }
  1433. return true
  1434. }, time.Second*10, time.Second).Should(BeTrue())
  1435. return true
  1436. }
  1437. }
  1438. syncWithDataToRegex := func(tc *testCase) {
  1439. fakeProvider.SetSecretFn = func() error {
  1440. return nil
  1441. }
  1442. // Set up secret with multiple keys
  1443. tc.secret.Data = map[string][]byte{
  1444. testDBHost: []byte(testLocalhost),
  1445. testDBPort: []byte("5432"),
  1446. testAppName: []byte("myapp"),
  1447. "app-version": []byte("1.0"),
  1448. }
  1449. // Use dataTo with regex to match only db-* keys
  1450. tc.pushsecret.Spec.Data = nil
  1451. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1452. {
  1453. StoreRef: &v1alpha1.PushSecretStoreRef{
  1454. Name: PushSecretStore,
  1455. },
  1456. Match: &v1alpha1.PushSecretDataToMatch{
  1457. RegExp: testDBRegexp,
  1458. },
  1459. },
  1460. }
  1461. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1462. Eventually(func() bool {
  1463. By("checking if only db-* keys were pushed")
  1464. setSecretArgs := fakeProvider.GetPushSecretData()
  1465. // Only two db-* keys should be pushed
  1466. if len(setSecretArgs) != 2 {
  1467. return false
  1468. }
  1469. // Check db-* keys were pushed
  1470. for key := range setSecretArgs {
  1471. if key != testDBHost && key != testDBPort {
  1472. return false
  1473. }
  1474. }
  1475. return true
  1476. }, time.Second*10, time.Second).Should(BeTrue())
  1477. return true
  1478. }
  1479. }
  1480. syncWithDataToRegexpRewrite := func(tc *testCase) {
  1481. fakeProvider.SetSecretFn = func() error {
  1482. return nil
  1483. }
  1484. // Set up secret with multiple keys
  1485. tc.secret.Data = map[string][]byte{
  1486. testDBHost: []byte(testLocalhost),
  1487. testDBPort: []byte("5432"),
  1488. }
  1489. // Use dataTo with regex rewrite to add prefix
  1490. tc.pushsecret.Spec.Data = nil
  1491. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1492. {
  1493. StoreRef: &v1alpha1.PushSecretStoreRef{
  1494. Name: PushSecretStore,
  1495. },
  1496. Match: &v1alpha1.PushSecretDataToMatch{
  1497. RegExp: testDBRegexp,
  1498. },
  1499. Rewrite: []v1alpha1.PushSecretRewrite{
  1500. {
  1501. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1502. Source: "^db-",
  1503. Target: "app/database/",
  1504. },
  1505. },
  1506. },
  1507. },
  1508. }
  1509. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1510. Eventually(func() bool {
  1511. By("checking if keys were rewritten with prefix")
  1512. setSecretArgs := fakeProvider.GetPushSecretData()
  1513. if len(setSecretArgs) != 2 {
  1514. return false
  1515. }
  1516. // Check keys were rewritten
  1517. _, hasHost := setSecretArgs["app/database/host"]
  1518. _, hasPort := setSecretArgs["app/database/port"]
  1519. return hasHost && hasPort
  1520. }, time.Second*10, time.Second).Should(BeTrue())
  1521. return true
  1522. }
  1523. }
  1524. syncWithDataToTransformRewrite := func(tc *testCase) {
  1525. fakeProvider.SetSecretFn = func() error {
  1526. return nil
  1527. }
  1528. tc.secret.Data = map[string][]byte{
  1529. "username": []byte(testAdminUser),
  1530. }
  1531. // Use dataTo with template transformation
  1532. tc.pushsecret.Spec.Data = nil
  1533. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1534. {
  1535. StoreRef: &v1alpha1.PushSecretStoreRef{
  1536. Name: PushSecretStore,
  1537. },
  1538. Rewrite: []v1alpha1.PushSecretRewrite{
  1539. {
  1540. Transform: &esv1.ExternalSecretRewriteTransform{
  1541. Template: "app/{{ .value | upper }}",
  1542. },
  1543. },
  1544. },
  1545. },
  1546. }
  1547. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1548. Eventually(func() bool {
  1549. By("checking if key was transformed using template")
  1550. setSecretArgs := fakeProvider.GetPushSecretData()
  1551. if len(setSecretArgs) != 1 {
  1552. return false
  1553. }
  1554. // Check key was transformed to uppercase with prefix
  1555. providerValue, ok := setSecretArgs["app/USERNAME"]
  1556. if !ok {
  1557. return false
  1558. }
  1559. return bytes.Equal(providerValue.Value, []byte(testAdminUser))
  1560. }, time.Second*10, time.Second).Should(BeTrue())
  1561. return true
  1562. }
  1563. }
  1564. syncDataToWithDataOverride := func(tc *testCase) {
  1565. fakeProvider.SetSecretFn = func() error {
  1566. return nil
  1567. }
  1568. tc.secret.Data = map[string][]byte{
  1569. "key1": []byte("value1"),
  1570. "key2": []byte("value2"),
  1571. }
  1572. // Use both dataTo and explicit data
  1573. // Explicit data should override dataTo for key1
  1574. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1575. {
  1576. StoreRef: &v1alpha1.PushSecretStoreRef{
  1577. Name: PushSecretStore,
  1578. },
  1579. // Match all keys, no rewrite
  1580. },
  1581. }
  1582. tc.pushsecret.Spec.Data = []v1alpha1.PushSecretData{
  1583. {
  1584. Match: v1alpha1.PushSecretMatch{
  1585. SecretKey: "key1",
  1586. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1587. RemoteKey: "override-key1", // Different remote key
  1588. },
  1589. },
  1590. },
  1591. }
  1592. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1593. Eventually(func() bool {
  1594. By("checking if explicit data overrode dataTo")
  1595. setSecretArgs := fakeProvider.GetPushSecretData()
  1596. // Should have 2 keys: override-key1 and key2
  1597. if len(setSecretArgs) != 2 {
  1598. return false
  1599. }
  1600. // key1 should be pushed as override-key1 (from explicit data)
  1601. _, hasOverride := setSecretArgs["override-key1"]
  1602. // key2 should be pushed as key2 (from dataTo)
  1603. _, hasKey2 := setSecretArgs["key2"]
  1604. return hasOverride && hasKey2
  1605. }, time.Second*10, time.Second).Should(BeTrue())
  1606. return true
  1607. }
  1608. }
  1609. failDataToInvalidRegex := func(tc *testCase) {
  1610. tc.secret.Data = map[string][]byte{
  1611. "key1": []byte("value1"),
  1612. }
  1613. // Use invalid regex pattern
  1614. tc.pushsecret.Spec.Data = nil
  1615. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1616. {
  1617. StoreRef: &v1alpha1.PushSecretStoreRef{
  1618. Name: PushSecretStore,
  1619. },
  1620. Match: &v1alpha1.PushSecretDataToMatch{
  1621. RegExp: "[invalid(regex",
  1622. },
  1623. },
  1624. }
  1625. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1626. Eventually(func() bool {
  1627. By("checking if PushSecret has error condition")
  1628. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1629. if cond == nil {
  1630. return false
  1631. }
  1632. // Should have error status
  1633. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  1634. }, time.Second*10, time.Second).Should(BeTrue())
  1635. return true
  1636. }
  1637. }
  1638. syncWithDataToConversionStrategy := func(tc *testCase) {
  1639. fakeProvider.SetSecretFn = func() error {
  1640. return nil
  1641. }
  1642. // Set up secret with unicode data
  1643. tc.secret.Data = map[string][]byte{
  1644. "unicode-key": []byte("unicode-value-αβγ"),
  1645. "normal-key": []byte("normal-value"),
  1646. }
  1647. // Use dataTo with ConversionStrategy
  1648. tc.pushsecret.Spec.Data = nil
  1649. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1650. {
  1651. StoreRef: &v1alpha1.PushSecretStoreRef{
  1652. Name: PushSecretStore,
  1653. },
  1654. ConversionStrategy: v1alpha1.PushSecretConversionReverseUnicode,
  1655. },
  1656. }
  1657. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1658. Eventually(func() bool {
  1659. By("checking if all keys were pushed with unicode conversion")
  1660. setSecretArgs := fakeProvider.GetPushSecretData()
  1661. // Both keys should be pushed
  1662. if len(setSecretArgs) != 2 {
  1663. return false
  1664. }
  1665. // Verify keys exist (actual unicode encoding is tested in provider tests)
  1666. _, hasUnicode := setSecretArgs["unicode-key"]
  1667. _, hasNormal := setSecretArgs["normal-key"]
  1668. return hasUnicode && hasNormal
  1669. }, timeout, time.Second).Should(BeTrue())
  1670. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1671. return cond != nil && cond.Status == v1.ConditionTrue && cond.Reason == v1alpha1.ReasonSynced
  1672. }
  1673. }
  1674. // Test dataTo with storeRef targeting specific store
  1675. syncWithDataToStoreRef := func(tc *testCase) {
  1676. fakeProvider.SetSecretFn = func() error {
  1677. return nil
  1678. }
  1679. // Create a second store
  1680. secondStoreName := "second-store"
  1681. secondStore := &esv1.SecretStore{
  1682. ObjectMeta: metav1.ObjectMeta{
  1683. Name: secondStoreName,
  1684. Namespace: PushSecretNamespace,
  1685. },
  1686. TypeMeta: metav1.TypeMeta{
  1687. Kind: "SecretStore",
  1688. },
  1689. Spec: esv1.SecretStoreSpec{
  1690. Provider: &esv1.SecretStoreProvider{
  1691. Fake: &esv1.FakeProvider{
  1692. Data: []esv1.FakeProviderData{},
  1693. },
  1694. },
  1695. },
  1696. }
  1697. tc.secret.Data = map[string][]byte{
  1698. testDBHost: []byte(testLocalhost),
  1699. testAPIKey: []byte("secret123"),
  1700. testAppName: []byte("myapp"),
  1701. }
  1702. // Configure multiple stores
  1703. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  1704. {Name: PushSecretStore, Kind: "SecretStore"},
  1705. {Name: secondStoreName, Kind: "SecretStore"},
  1706. }
  1707. tc.pushsecret.Spec.Data = nil
  1708. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1709. {
  1710. // Entry targeting first store only
  1711. StoreRef: &v1alpha1.PushSecretStoreRef{
  1712. Name: PushSecretStore,
  1713. Kind: "SecretStore",
  1714. },
  1715. Match: &v1alpha1.PushSecretDataToMatch{
  1716. RegExp: testDBRegexp,
  1717. },
  1718. },
  1719. {
  1720. // Entry targeting second store only
  1721. StoreRef: &v1alpha1.PushSecretStoreRef{
  1722. Name: secondStoreName,
  1723. Kind: "SecretStore",
  1724. },
  1725. Match: &v1alpha1.PushSecretDataToMatch{
  1726. RegExp: "^api-.*",
  1727. },
  1728. },
  1729. {
  1730. // Entry targeting first store (app-name)
  1731. StoreRef: &v1alpha1.PushSecretStoreRef{
  1732. Name: PushSecretStore,
  1733. Kind: "SecretStore",
  1734. },
  1735. Match: &v1alpha1.PushSecretDataToMatch{
  1736. RegExp: "^app-.*",
  1737. },
  1738. },
  1739. {
  1740. // Entry targeting second store (app-name)
  1741. StoreRef: &v1alpha1.PushSecretStoreRef{
  1742. Name: secondStoreName,
  1743. Kind: "SecretStore",
  1744. },
  1745. Match: &v1alpha1.PushSecretDataToMatch{
  1746. RegExp: "^app-.*",
  1747. },
  1748. },
  1749. }
  1750. // Second store is created by test harness via tc.managedStore2 so it exists before PushSecret
  1751. tc.managedStore2 = secondStore
  1752. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1753. updatedPS := &v1alpha1.PushSecret{}
  1754. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  1755. Eventually(func() bool {
  1756. By("checking if secrets are synced to correct stores")
  1757. err := k8sClient.Get(context.Background(), psKey, updatedPS)
  1758. if err != nil {
  1759. return false
  1760. }
  1761. firstStoreKey := fmt.Sprintf(storePrefixTemplate, PushSecretStore)
  1762. secondStoreKey := "SecretStore/" + secondStoreName
  1763. firstStoreSynced := updatedPS.Status.SyncedPushSecrets[firstStoreKey]
  1764. secondStoreSynced := updatedPS.Status.SyncedPushSecrets[secondStoreKey]
  1765. // First store should have: db-host and app-name (both from storeRef entries)
  1766. _, hasDbHost := firstStoreSynced[testDBHost]
  1767. _, hasAppNameFirst := firstStoreSynced[testAppName]
  1768. // First store should NOT have api-key (targeted to second store)
  1769. _, hasApiKeyFirst := firstStoreSynced[testAPIKey]
  1770. // Second store should have: api-key and app-name (both from storeRef entries)
  1771. _, hasApiKey := secondStoreSynced[testAPIKey]
  1772. _, hasAppNameSecond := secondStoreSynced[testAppName]
  1773. // Second store should NOT have db-host (targeted to first store)
  1774. _, hasDbHostSecond := secondStoreSynced[testDBHost]
  1775. return hasDbHost && hasAppNameFirst && !hasApiKeyFirst &&
  1776. hasApiKey && hasAppNameSecond && !hasDbHostSecond
  1777. }, time.Second*10, time.Second).Should(BeTrue())
  1778. return true
  1779. }
  1780. }
  1781. // Test: Template creates new keys, dataTo matches them
  1782. templateCreatesKeysThenDataToMatches := func(tc *testCase) {
  1783. fakeProvider.SetSecretFn = func() error {
  1784. return nil
  1785. }
  1786. // Source secret has individual components
  1787. tc.secret.Data = map[string][]byte{
  1788. "db_host": []byte(testLocalhost),
  1789. "db_port": []byte("3306"),
  1790. }
  1791. // Template creates connection string from components
  1792. tc.pushsecret.Spec.Template = &esv1.ExternalSecretTemplate{
  1793. Data: map[string]string{
  1794. "mysql-connection": "mysql://{{ .db_host }}:{{ .db_port }}/mydb",
  1795. },
  1796. }
  1797. // dataTo only matches keys ending in -connection (created by template)
  1798. tc.pushsecret.Spec.Data = nil
  1799. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1800. {
  1801. StoreRef: &v1alpha1.PushSecretStoreRef{
  1802. Name: PushSecretStore,
  1803. },
  1804. Match: &v1alpha1.PushSecretDataToMatch{
  1805. RegExp: ".*-connection$",
  1806. },
  1807. },
  1808. }
  1809. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1810. Eventually(func() bool {
  1811. By("checking template key was pushed, not originals")
  1812. setSecretArgs := fakeProvider.GetPushSecretData()
  1813. // Only mysql-connection should be pushed
  1814. _, hasConnection := setSecretArgs["mysql-connection"]
  1815. _, hasDbHost := setSecretArgs["db_host"]
  1816. _, hasDbPort := setSecretArgs["db_port"]
  1817. return hasConnection && !hasDbHost && !hasDbPort
  1818. }, timeout, time.Second).Should(BeTrue())
  1819. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1820. return cond != nil && cond.Status == v1.ConditionTrue && cond.Reason == v1alpha1.ReasonSynced
  1821. }
  1822. }
  1823. // Test: Template + dataTo + explicit data combined
  1824. templateWithDataToAndExplicitData := func(tc *testCase) {
  1825. fakeProvider.SetSecretFn = func() error {
  1826. return nil
  1827. }
  1828. tc.secret.Data = map[string][]byte{
  1829. "token": []byte("abc123"),
  1830. "config-timeout": []byte("30s"),
  1831. "config-retries": []byte("3"),
  1832. }
  1833. // Template creates api-key from token
  1834. tc.pushsecret.Spec.Template = &esv1.ExternalSecretTemplate{
  1835. Data: map[string]string{
  1836. testAPIKey: "Bearer {{ .token }}",
  1837. },
  1838. }
  1839. // dataTo matches config-* keys
  1840. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1841. {
  1842. StoreRef: &v1alpha1.PushSecretStoreRef{
  1843. Name: PushSecretStore,
  1844. },
  1845. Match: &v1alpha1.PushSecretDataToMatch{
  1846. RegExp: "^config-.*",
  1847. },
  1848. },
  1849. }
  1850. // Explicit data for api-key with custom remote path
  1851. tc.pushsecret.Spec.Data = []v1alpha1.PushSecretData{
  1852. {
  1853. Match: v1alpha1.PushSecretMatch{
  1854. SecretKey: testAPIKey,
  1855. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1856. RemoteKey: "credentials/api-key",
  1857. },
  1858. },
  1859. },
  1860. }
  1861. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1862. Eventually(func() bool {
  1863. By("checking all expected keys were pushed")
  1864. setSecretArgs := fakeProvider.GetPushSecretData()
  1865. // api-key should be at credentials/api-key (explicit), config-* from dataTo
  1866. _, hasApiKey := setSecretArgs["credentials/api-key"]
  1867. _, hasTimeout := setSecretArgs["config-timeout"]
  1868. _, hasRetries := setSecretArgs["config-retries"]
  1869. // Original token should NOT be pushed
  1870. _, hasToken := setSecretArgs["token"]
  1871. return hasApiKey && hasTimeout && hasRetries && !hasToken
  1872. }, timeout, time.Second).Should(BeTrue())
  1873. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1874. return cond != nil && cond.Status == v1.ConditionTrue && cond.Reason == v1alpha1.ReasonSynced
  1875. }
  1876. }
  1877. failDataToDuplicateAcrossEntries := func(tc *testCase) {
  1878. tc.secret.Data = map[string][]byte{
  1879. testDBHost: []byte(testLocalhost),
  1880. testDBPort: []byte("5432"),
  1881. }
  1882. // Create two dataTo entries that both produce the same remote key "app/config"
  1883. tc.pushsecret.Spec.Data = nil
  1884. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1885. {
  1886. StoreRef: &v1alpha1.PushSecretStoreRef{
  1887. Name: PushSecretStore,
  1888. },
  1889. Match: &v1alpha1.PushSecretDataToMatch{
  1890. RegExp: "^db-host$",
  1891. },
  1892. Rewrite: []v1alpha1.PushSecretRewrite{
  1893. {
  1894. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1895. Source: ".*",
  1896. Target: "app/config",
  1897. },
  1898. },
  1899. },
  1900. },
  1901. {
  1902. StoreRef: &v1alpha1.PushSecretStoreRef{
  1903. Name: PushSecretStore,
  1904. },
  1905. Match: &v1alpha1.PushSecretDataToMatch{
  1906. RegExp: "^db-port$",
  1907. },
  1908. Rewrite: []v1alpha1.PushSecretRewrite{
  1909. {
  1910. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1911. Source: ".*",
  1912. Target: "app/config",
  1913. },
  1914. },
  1915. },
  1916. },
  1917. }
  1918. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1919. Eventually(func() bool {
  1920. By("checking if PushSecret has error condition for duplicate remote keys")
  1921. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1922. if cond == nil {
  1923. return false
  1924. }
  1925. // Should have error status
  1926. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  1927. }, time.Second*10, time.Second).Should(BeTrue())
  1928. return true
  1929. }
  1930. }
  1931. failDataToAndDataDuplicateRemoteKey := func(tc *testCase) {
  1932. tc.secret.Data = map[string][]byte{
  1933. testDBHost: []byte(testLocalhost),
  1934. testAPIKey: []byte("secret123"),
  1935. }
  1936. // Create dataTo entry and explicit data that map to the same remote key
  1937. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1938. {
  1939. StoreRef: &v1alpha1.PushSecretStoreRef{
  1940. Name: PushSecretStore,
  1941. },
  1942. Match: &v1alpha1.PushSecretDataToMatch{
  1943. RegExp: "^db-host$",
  1944. },
  1945. Rewrite: []v1alpha1.PushSecretRewrite{
  1946. {
  1947. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1948. Source: ".*",
  1949. Target: "myapp/config",
  1950. },
  1951. },
  1952. },
  1953. },
  1954. }
  1955. tc.pushsecret.Spec.Data = []v1alpha1.PushSecretData{
  1956. {
  1957. Match: v1alpha1.PushSecretMatch{
  1958. SecretKey: testAPIKey,
  1959. RemoteRef: v1alpha1.PushSecretRemoteRef{
  1960. RemoteKey: "myapp/config", // Same remote key as dataTo produces
  1961. },
  1962. },
  1963. },
  1964. }
  1965. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1966. Eventually(func() bool {
  1967. By("checking if PushSecret has error condition for duplicate remote keys")
  1968. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1969. if cond == nil {
  1970. return false
  1971. }
  1972. // Should have error status
  1973. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  1974. }, time.Second*10, time.Second).Should(BeTrue())
  1975. return true
  1976. }
  1977. }
  1978. // Note: failDataToMissingStoreRef test removed - missing storeRef is now
  1979. // blocked by CEL validation at admission time
  1980. failDataToStoreRefNotInList := func(tc *testCase) {
  1981. tc.secret.Data = map[string][]byte{
  1982. "key1": []byte("value1"),
  1983. }
  1984. // dataTo with storeRef that doesn't exist in secretStoreRefs
  1985. tc.pushsecret.Spec.Data = nil
  1986. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  1987. {
  1988. StoreRef: &v1alpha1.PushSecretStoreRef{
  1989. Name: "non-existent-store", // Not in secretStoreRefs
  1990. },
  1991. },
  1992. }
  1993. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  1994. Eventually(func() bool {
  1995. By("checking PushSecret has error for invalid storeRef")
  1996. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  1997. if cond == nil {
  1998. return false
  1999. }
  2000. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  2001. }, time.Second*10, time.Second).Should(BeTrue())
  2002. return true
  2003. }
  2004. }
  2005. failDataToNamedStoreRefWithLabelSelectorRefs := func(tc *testCase) {
  2006. tc.secret.Data = map[string][]byte{
  2007. "key1": []byte("value1"),
  2008. }
  2009. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  2010. {
  2011. LabelSelector: &metav1.LabelSelector{
  2012. MatchLabels: map[string]string{
  2013. "env": "prod",
  2014. },
  2015. },
  2016. },
  2017. }
  2018. tc.pushsecret.Spec.Data = nil
  2019. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2020. {
  2021. StoreRef: &v1alpha1.PushSecretStoreRef{
  2022. Name: "totally-nonexistent-store",
  2023. },
  2024. },
  2025. }
  2026. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2027. Eventually(func() bool {
  2028. By("checking PushSecret rejects named storeRef not in secretStoreRefs")
  2029. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  2030. if cond == nil {
  2031. return false
  2032. }
  2033. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  2034. }, time.Second*10, time.Second).Should(BeTrue())
  2035. return true
  2036. }
  2037. }
  2038. failDataToLabelSelectorMatchesNoStore := func(tc *testCase) {
  2039. tc.store = &esv1.SecretStore{
  2040. ObjectMeta: metav1.ObjectMeta{
  2041. Name: PushSecretStore,
  2042. Namespace: PushSecretNamespace,
  2043. Labels: map[string]string{
  2044. "env": "staging",
  2045. },
  2046. },
  2047. TypeMeta: metav1.TypeMeta{
  2048. Kind: "SecretStore",
  2049. },
  2050. Spec: esv1.SecretStoreSpec{
  2051. Provider: &esv1.SecretStoreProvider{
  2052. Fake: &esv1.FakeProvider{
  2053. Data: []esv1.FakeProviderData{},
  2054. },
  2055. },
  2056. },
  2057. }
  2058. tc.secret.Data = map[string][]byte{
  2059. "key1": []byte("value1"),
  2060. }
  2061. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  2062. {
  2063. LabelSelector: &metav1.LabelSelector{
  2064. MatchLabels: map[string]string{
  2065. "env": "staging",
  2066. },
  2067. },
  2068. },
  2069. }
  2070. tc.pushsecret.Spec.Data = nil
  2071. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2072. {
  2073. StoreRef: &v1alpha1.PushSecretStoreRef{
  2074. LabelSelector: &metav1.LabelSelector{
  2075. MatchLabels: map[string]string{
  2076. "env": "production",
  2077. },
  2078. },
  2079. },
  2080. },
  2081. }
  2082. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2083. Eventually(func() bool {
  2084. cond := GetPushSecretCondition(ps.Status.Conditions, v1alpha1.PushSecretReady)
  2085. if cond == nil {
  2086. return false
  2087. }
  2088. return cond.Status == v1.ConditionFalse && cond.Reason == v1alpha1.ReasonErrored
  2089. }, time.Second*10, time.Second).Should(BeTrue())
  2090. return true
  2091. }
  2092. }
  2093. syncWithDataToLabelSelector := func(tc *testCase) {
  2094. fakeProvider.SetSecretFn = func() error {
  2095. return nil
  2096. }
  2097. // Add labels to the store
  2098. tc.store = &esv1.SecretStore{
  2099. ObjectMeta: metav1.ObjectMeta{
  2100. Name: PushSecretStore,
  2101. Namespace: PushSecretNamespace,
  2102. Labels: map[string]string{
  2103. "env": "test",
  2104. },
  2105. },
  2106. TypeMeta: metav1.TypeMeta{
  2107. Kind: "SecretStore",
  2108. },
  2109. Spec: esv1.SecretStoreSpec{
  2110. Provider: &esv1.SecretStoreProvider{
  2111. Fake: &esv1.FakeProvider{
  2112. Data: []esv1.FakeProviderData{},
  2113. },
  2114. },
  2115. },
  2116. }
  2117. tc.secret.Data = map[string][]byte{
  2118. "key1": []byte("value1"),
  2119. }
  2120. // Use labelSelector in secretStoreRefs to select stores by labels
  2121. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  2122. {
  2123. LabelSelector: &metav1.LabelSelector{
  2124. MatchLabels: map[string]string{
  2125. "env": "test",
  2126. },
  2127. },
  2128. },
  2129. }
  2130. // Use labelSelector in dataTo to target stores with matching labels
  2131. tc.pushsecret.Spec.Data = nil
  2132. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2133. {
  2134. StoreRef: &v1alpha1.PushSecretStoreRef{
  2135. LabelSelector: &metav1.LabelSelector{
  2136. MatchLabels: map[string]string{
  2137. "env": "test",
  2138. },
  2139. },
  2140. },
  2141. },
  2142. }
  2143. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2144. Eventually(func() bool {
  2145. By("checking key was pushed via labelSelector")
  2146. setSecretArgs := fakeProvider.GetPushSecretData()
  2147. return len(setSecretArgs) == 1
  2148. }, time.Second*10, time.Second).Should(BeTrue())
  2149. return true
  2150. }
  2151. }
  2152. syncWithDataToDuplicateValues := func(tc *testCase) {
  2153. fakeProvider.SetSecretFn = func() error {
  2154. return nil
  2155. }
  2156. // Keys with same value - tests deterministic key mapping
  2157. tc.secret.Data = map[string][]byte{
  2158. testDBHost: []byte("same-value"),
  2159. "cache-host": []byte("same-value"),
  2160. }
  2161. tc.pushsecret.Spec.Data = nil
  2162. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2163. {
  2164. StoreRef: &v1alpha1.PushSecretStoreRef{
  2165. Name: PushSecretStore,
  2166. },
  2167. Rewrite: []v1alpha1.PushSecretRewrite{
  2168. {
  2169. Regexp: &esv1.ExternalSecretRewriteRegexp{
  2170. Source: "-host$",
  2171. Target: "/endpoint",
  2172. },
  2173. },
  2174. },
  2175. },
  2176. }
  2177. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2178. Eventually(func() bool {
  2179. By("checking both keys are rewritten despite same value")
  2180. setSecretArgs := fakeProvider.GetPushSecretData()
  2181. if len(setSecretArgs) != 2 {
  2182. return false
  2183. }
  2184. _, hasDb := setSecretArgs["db/endpoint"]
  2185. _, hasCache := setSecretArgs["cache/endpoint"]
  2186. return hasDb && hasCache
  2187. }, time.Second*10, time.Second).Should(BeTrue())
  2188. return true
  2189. }
  2190. }
  2191. syncWithDataToMultipleRewrites := func(tc *testCase) {
  2192. fakeProvider.SetSecretFn = func() error {
  2193. return nil
  2194. }
  2195. tc.secret.Data = map[string][]byte{
  2196. "db-username": []byte(testAdminUser),
  2197. }
  2198. // Chain multiple rewrites
  2199. tc.pushsecret.Spec.Data = nil
  2200. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2201. {
  2202. StoreRef: &v1alpha1.PushSecretStoreRef{
  2203. Name: PushSecretStore,
  2204. },
  2205. Rewrite: []v1alpha1.PushSecretRewrite{
  2206. {
  2207. // First rewrite: remove db- prefix
  2208. Regexp: &esv1.ExternalSecretRewriteRegexp{
  2209. Source: "^db-",
  2210. Target: "",
  2211. },
  2212. },
  2213. {
  2214. // Second rewrite: add app/ prefix
  2215. Regexp: &esv1.ExternalSecretRewriteRegexp{
  2216. Source: "^",
  2217. Target: "app/",
  2218. },
  2219. },
  2220. },
  2221. },
  2222. }
  2223. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2224. Eventually(func() bool {
  2225. By("checking if multiple rewrites were applied")
  2226. setSecretArgs := fakeProvider.GetPushSecretData()
  2227. if len(setSecretArgs) != 1 {
  2228. return false
  2229. }
  2230. // db-username -> username -> app/username
  2231. _, ok := setSecretArgs["app/username"]
  2232. return ok
  2233. }, time.Second*10, time.Second).Should(BeTrue())
  2234. return true
  2235. }
  2236. }
  2237. // dataTo bundle mode tests (remoteKey set → all matched keys bundled as JSON)
  2238. syncWithDataToBundleAllKeys := func(tc *testCase) {
  2239. fakeProvider.SetSecretFn = func() error {
  2240. return nil
  2241. }
  2242. tc.secret.Data = map[string][]byte{
  2243. "DB_HOST": []byte(testLocalhost),
  2244. "DB_USER": []byte(testAdminUser),
  2245. }
  2246. tc.pushsecret.Spec.Data = nil
  2247. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2248. {
  2249. StoreRef: &v1alpha1.PushSecretStoreRef{
  2250. Name: PushSecretStore,
  2251. },
  2252. RemoteKey: "secrets-sync-target",
  2253. },
  2254. }
  2255. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2256. Eventually(func() bool {
  2257. By("checking that all keys were bundled into a single provider secret")
  2258. setSecretArgs := fakeProvider.GetPushSecretData()
  2259. // Only one provider secret should be created
  2260. if len(setSecretArgs) != 1 {
  2261. return false
  2262. }
  2263. if bundled, ok := setSecretArgs["secrets-sync-target"]; ok {
  2264. // Value should be a JSON object containing both keys
  2265. var decoded map[string]string
  2266. if json.Unmarshal(bundled.Value, &decoded) != nil {
  2267. return false
  2268. }
  2269. return decoded["DB_HOST"] == testLocalhost && decoded["DB_USER"] == testAdminUser
  2270. }
  2271. return false
  2272. }, time.Second*10, time.Second).Should(BeTrue())
  2273. return true
  2274. }
  2275. }
  2276. syncWithDataToBundleWithRegexFilter := func(tc *testCase) {
  2277. fakeProvider.SetSecretFn = func() error {
  2278. return nil
  2279. }
  2280. tc.secret.Data = map[string][]byte{
  2281. "DB_HOST": []byte(testLocalhost),
  2282. "DB_USER": []byte(testAdminUser),
  2283. "APP_NAME": []byte("myapp"),
  2284. }
  2285. tc.pushsecret.Spec.Data = nil
  2286. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2287. {
  2288. StoreRef: &v1alpha1.PushSecretStoreRef{
  2289. Name: PushSecretStore,
  2290. },
  2291. RemoteKey: "db-bundle",
  2292. Match: &v1alpha1.PushSecretDataToMatch{
  2293. RegExp: "^DB_",
  2294. },
  2295. },
  2296. }
  2297. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2298. Eventually(func() bool {
  2299. By("checking that only matched keys were bundled")
  2300. setSecretArgs := fakeProvider.GetPushSecretData()
  2301. if len(setSecretArgs) != 1 {
  2302. return false
  2303. }
  2304. if bundled, ok := setSecretArgs["db-bundle"]; ok {
  2305. var decoded map[string]string
  2306. if json.Unmarshal(bundled.Value, &decoded) != nil {
  2307. return false
  2308. }
  2309. // Only DB_* keys should be in the bundle, not APP_NAME
  2310. _, hasApp := decoded["APP_NAME"]
  2311. return decoded["DB_HOST"] == testLocalhost &&
  2312. decoded["DB_USER"] == testAdminUser &&
  2313. !hasApp
  2314. }
  2315. return false
  2316. }, time.Second*10, time.Second).Should(BeTrue())
  2317. return true
  2318. }
  2319. }
  2320. syncWithDataToBundleAndPerKeyMixed := func(tc *testCase) {
  2321. fakeProvider.SetSecretFn = func() error {
  2322. return nil
  2323. }
  2324. tc.secret.Data = map[string][]byte{
  2325. "DB_HOST": []byte(testLocalhost),
  2326. "DB_USER": []byte(testAdminUser),
  2327. "API_KEY": []byte("secret-key"),
  2328. }
  2329. tc.pushsecret.Spec.Data = nil
  2330. tc.pushsecret.Spec.DataTo = []v1alpha1.PushSecretDataTo{
  2331. {
  2332. // Bundle mode: DB_* keys → single JSON secret
  2333. StoreRef: &v1alpha1.PushSecretStoreRef{
  2334. Name: PushSecretStore,
  2335. },
  2336. RemoteKey: "db-config",
  2337. Match: &v1alpha1.PushSecretDataToMatch{
  2338. RegExp: "^DB_",
  2339. },
  2340. },
  2341. {
  2342. // Per-key mode: API_KEY → individual secret
  2343. StoreRef: &v1alpha1.PushSecretStoreRef{
  2344. Name: PushSecretStore,
  2345. },
  2346. Match: &v1alpha1.PushSecretDataToMatch{
  2347. RegExp: "^API_",
  2348. },
  2349. },
  2350. }
  2351. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2352. Eventually(func() bool {
  2353. By("checking bundle and per-key entries coexist")
  2354. setSecretArgs := fakeProvider.GetPushSecretData()
  2355. // db-config (bundle) + API_KEY (per-key) = 2 provider secrets
  2356. if len(setSecretArgs) != 2 {
  2357. return false
  2358. }
  2359. if bundled, ok := setSecretArgs["db-config"]; ok {
  2360. var decoded map[string]string
  2361. if json.Unmarshal(bundled.Value, &decoded) != nil {
  2362. return false
  2363. }
  2364. if decoded["DB_HOST"] != testLocalhost || decoded["DB_USER"] != testAdminUser {
  2365. return false
  2366. }
  2367. _, hasAPIKey := setSecretArgs["API_KEY"]
  2368. return hasAPIKey
  2369. }
  2370. return false
  2371. }, time.Second*10, time.Second).Should(BeTrue())
  2372. return true
  2373. }
  2374. }
  2375. DescribeTable("When reconciling a PushSecret",
  2376. func(tweaks ...testTweaks) {
  2377. tc := makeDefaultTestcase()
  2378. for _, tweak := range tweaks {
  2379. tweak(tc)
  2380. }
  2381. ctx := context.Background()
  2382. By("creating a secret store, secret and pushsecret")
  2383. if tc.store != nil {
  2384. Expect(k8sClient.Create(ctx, tc.store)).To(Succeed())
  2385. }
  2386. if tc.managedStore2 != nil {
  2387. Expect(k8sClient.Create(ctx, tc.managedStore2)).To(Succeed())
  2388. }
  2389. if tc.secret != nil {
  2390. Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
  2391. }
  2392. if tc.pushsecret != nil {
  2393. Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
  2394. }
  2395. time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
  2396. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  2397. createdPS := &v1alpha1.PushSecret{}
  2398. By("checking the pushSecret condition")
  2399. Eventually(func() bool {
  2400. err := k8sClient.Get(ctx, psKey, createdPS)
  2401. if err != nil {
  2402. return false
  2403. }
  2404. return tc.assert(createdPS, tc.secret)
  2405. }, timeout, interval).Should(BeTrue())
  2406. // this must be optional so we can test faulty es configuration
  2407. },
  2408. Entry("should sync", syncSuccessfully),
  2409. Entry("should not update existing secret if UpdatePolicy=IfNotExists", updateIfNotExists),
  2410. Entry("should only update parts of secret that don't already exist if UpdatePolicy=IfNotExists", updateIfNotExistsPartialSecrets),
  2411. Entry("should update the PushSecret status correctly if UpdatePolicy=IfNotExists", updateIfNotExistsSyncStatus),
  2412. Entry("should fail if secret existence cannot be verified if UpdatePolicy=IfNotExists", updateIfNotExistsSyncFailed),
  2413. Entry("should sync with template", syncSuccessfullyWithTemplate),
  2414. Entry("should sync with template reusing keys", syncSuccessfullyReusingKeys),
  2415. Entry("should sync with conversion strategy", syncSuccessfullyWithConversionStrategy),
  2416. Entry("should delete if DeletionPolicy=Delete", syncAndDeleteSuccessfully),
  2417. Entry("should delete secrets with properties and update status correctly", syncAndDeleteWithProperties),
  2418. Entry("should delete after DeletionPolicy changed from Delete to None", syncChangePolicyAndDeleteSuccessfully),
  2419. Entry("should cleanup provider secrets when source Secret is deleted", deleteProviderSecretsOnSourceSecretDeleted),
  2420. Entry("should track deletion tasks if Delete fails", failDelete),
  2421. Entry("should track deleted stores if Delete fails", failDeleteStore),
  2422. Entry("should delete all secrets if SecretStore changes", deleteWholeStore),
  2423. Entry("should sync to stores matching labels", syncMatchingLabels),
  2424. Entry("should sync with ClusterStore", syncWithClusterStore),
  2425. Entry("should sync with ClusterStore matching labels", syncWithClusterStoreMatchingLabels),
  2426. Entry("should sync with Generator", syncWithGenerator),
  2427. Entry("should fail if Secret is not created", failNoSecret),
  2428. Entry("should fail if Secret Key does not exist", failNoSecretKey),
  2429. Entry("should fail if SetSecret fails", setSecretFail),
  2430. Entry("should fail if no valid SecretStore", failNoSecretStore),
  2431. Entry("should fail if no valid ClusterSecretStore", failNoClusterStore),
  2432. Entry("should fail if NewClient fails", newClientFail),
  2433. Entry("should not sync to SecretStore in different namespace", secretStoreDifferentNamespace),
  2434. Entry("should not reference secret in different namespace", secretDifferentNamespace),
  2435. Entry("should sync with dataTo matching all keys", syncWithDataToMatchAll),
  2436. Entry("should sync with dataTo using regex pattern", syncWithDataToRegex),
  2437. Entry("should sync with dataTo and regexp rewrite", syncWithDataToRegexpRewrite),
  2438. Entry("should sync with dataTo and transform rewrite", syncWithDataToTransformRewrite),
  2439. Entry("should override dataTo with explicit data", syncDataToWithDataOverride),
  2440. Entry("should sync with dataTo and multiple chained rewrites", syncWithDataToMultipleRewrites),
  2441. Entry("should fail with invalid regex in dataTo", failDataToInvalidRegex),
  2442. Entry("should sync with dataTo and conversion strategy", syncWithDataToConversionStrategy),
  2443. Entry("should sync with dataTo storeRef targeting specific stores", syncWithDataToStoreRef),
  2444. Entry("should match dataTo against template-created keys", templateCreatesKeysThenDataToMatches),
  2445. Entry("should combine template, dataTo and explicit data", templateWithDataToAndExplicitData),
  2446. Entry("should fail with duplicate remote keys across dataTo entries", failDataToDuplicateAcrossEntries),
  2447. Entry("should fail with duplicate remote keys between dataTo and explicit data", failDataToAndDataDuplicateRemoteKey),
  2448. Entry("should fail with dataTo storeRef not in secretStoreRefs", failDataToStoreRefNotInList),
  2449. Entry("should fail with named dataTo storeRef when secretStoreRefs only has labelSelector", failDataToNamedStoreRefWithLabelSelectorRefs),
  2450. Entry("should fail with dataTo labelSelector matching no resolved store", failDataToLabelSelectorMatchesNoStore),
  2451. Entry("should sync with dataTo using labelSelector", syncWithDataToLabelSelector),
  2452. Entry("should sync with dataTo when keys have duplicate values", syncWithDataToDuplicateValues),
  2453. Entry("should bundle all keys into single provider secret with dataTo remoteKey", syncWithDataToBundleAllKeys),
  2454. Entry("should bundle only regex-matched keys with dataTo remoteKey and match filter", syncWithDataToBundleWithRegexFilter),
  2455. Entry("should mix bundle mode and per-key mode in the same dataTo list", syncWithDataToBundleAndPerKeyMixed),
  2456. )
  2457. It("should reject dataTo with both remoteKey and rewrite at admission", func() {
  2458. ns, err := ctest.CreateNamespace("test-ns", k8sClient)
  2459. Expect(err).ToNot(HaveOccurred())
  2460. ps := &v1alpha1.PushSecret{
  2461. ObjectMeta: metav1.ObjectMeta{
  2462. Name: PushSecretName,
  2463. Namespace: ns,
  2464. },
  2465. Spec: v1alpha1.PushSecretSpec{
  2466. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  2467. {Name: PushSecretStore},
  2468. },
  2469. Selector: v1alpha1.PushSecretSelector{
  2470. Secret: &v1alpha1.PushSecretSecret{Name: SecretName},
  2471. },
  2472. DataTo: []v1alpha1.PushSecretDataTo{
  2473. {
  2474. StoreRef: &v1alpha1.PushSecretStoreRef{
  2475. Name: PushSecretStore,
  2476. },
  2477. RemoteKey: "my-bundle",
  2478. Rewrite: []v1alpha1.PushSecretRewrite{
  2479. {
  2480. Regexp: &esv1.ExternalSecretRewriteRegexp{
  2481. Source: "^",
  2482. Target: "prefix/",
  2483. },
  2484. },
  2485. },
  2486. },
  2487. },
  2488. },
  2489. }
  2490. err = k8sClient.Create(context.Background(), ps)
  2491. Expect(err).To(HaveOccurred())
  2492. Expect(err.Error()).To(ContainSubstring("remoteKey and rewrite are mutually exclusive"))
  2493. })
  2494. })
  2495. var _ = Describe("PushSecret Controller Un/Managed Stores", func() {
  2496. const (
  2497. PushSecretName = "test-ps"
  2498. ManagedPushSecretStore1 = "test-managed-store-1"
  2499. ManagedPushSecretStore2 = "test-managed-store-2"
  2500. UnmanagedPushSecretStore1 = "test-unmanaged-store-1"
  2501. UnmanagedPushSecretStore2 = "test-unmanaged-store-2"
  2502. SecretName = "test-secret"
  2503. )
  2504. var PushSecretNamespace string
  2505. PushSecretStores := []string{ManagedPushSecretStore1, ManagedPushSecretStore2, UnmanagedPushSecretStore1, UnmanagedPushSecretStore2}
  2506. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  2507. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  2508. if t, err := strconv.Atoi(customTimeout); err == nil {
  2509. timeout = time.Second * time.Duration(t)
  2510. }
  2511. }
  2512. BeforeEach(func() {
  2513. var err error
  2514. PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
  2515. Expect(err).ToNot(HaveOccurred())
  2516. fakeProvider.Reset()
  2517. })
  2518. AfterEach(func() {
  2519. k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
  2520. ObjectMeta: metav1.ObjectMeta{
  2521. Name: PushSecretName,
  2522. Namespace: PushSecretNamespace,
  2523. },
  2524. })
  2525. // give a time for reconciler to remove finalizers before removing SecretStores
  2526. time.Sleep(2 * time.Second)
  2527. for _, psstore := range PushSecretStores {
  2528. k8sClient.Delete(context.Background(), &esv1.SecretStore{
  2529. ObjectMeta: metav1.ObjectMeta{
  2530. Name: psstore,
  2531. Namespace: PushSecretNamespace,
  2532. },
  2533. })
  2534. k8sClient.Delete(context.Background(), &esv1.ClusterSecretStore{
  2535. ObjectMeta: metav1.ObjectMeta{
  2536. Name: psstore,
  2537. },
  2538. })
  2539. }
  2540. k8sClient.Delete(context.Background(), &v1.Secret{
  2541. ObjectMeta: metav1.ObjectMeta{
  2542. Name: SecretName,
  2543. Namespace: PushSecretNamespace,
  2544. },
  2545. })
  2546. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  2547. ObjectMeta: metav1.ObjectMeta{
  2548. Name: PushSecretNamespace,
  2549. },
  2550. })).To(Succeed())
  2551. })
  2552. const (
  2553. defaultKey = "key"
  2554. defaultVal = "value"
  2555. defaultPath = "path/to/key"
  2556. )
  2557. makeDefaultTestcase := func() *testCase {
  2558. return &testCase{
  2559. pushsecret: &v1alpha1.PushSecret{
  2560. ObjectMeta: metav1.ObjectMeta{
  2561. Name: PushSecretName,
  2562. Namespace: PushSecretNamespace,
  2563. },
  2564. Spec: v1alpha1.PushSecretSpec{
  2565. SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
  2566. {
  2567. Name: ManagedPushSecretStore1,
  2568. Kind: "SecretStore",
  2569. },
  2570. },
  2571. Selector: v1alpha1.PushSecretSelector{
  2572. Secret: &v1alpha1.PushSecretSecret{
  2573. Name: SecretName,
  2574. },
  2575. },
  2576. Data: []v1alpha1.PushSecretData{
  2577. {
  2578. Match: v1alpha1.PushSecretMatch{
  2579. SecretKey: defaultKey,
  2580. RemoteRef: v1alpha1.PushSecretRemoteRef{
  2581. RemoteKey: defaultPath,
  2582. },
  2583. },
  2584. },
  2585. },
  2586. },
  2587. },
  2588. secret: &v1.Secret{
  2589. ObjectMeta: metav1.ObjectMeta{
  2590. Name: SecretName,
  2591. Namespace: PushSecretNamespace,
  2592. },
  2593. Data: map[string][]byte{
  2594. defaultKey: []byte(defaultVal),
  2595. },
  2596. },
  2597. managedStore1: &esv1.SecretStore{
  2598. ObjectMeta: metav1.ObjectMeta{
  2599. Name: ManagedPushSecretStore1,
  2600. Namespace: PushSecretNamespace,
  2601. },
  2602. TypeMeta: metav1.TypeMeta{
  2603. Kind: "SecretStore",
  2604. },
  2605. Spec: esv1.SecretStoreSpec{
  2606. Provider: &esv1.SecretStoreProvider{
  2607. Fake: &esv1.FakeProvider{
  2608. Data: []esv1.FakeProviderData{},
  2609. },
  2610. },
  2611. },
  2612. },
  2613. managedStore2: &esv1.SecretStore{
  2614. ObjectMeta: metav1.ObjectMeta{
  2615. Name: ManagedPushSecretStore2,
  2616. Namespace: PushSecretNamespace,
  2617. },
  2618. TypeMeta: metav1.TypeMeta{
  2619. Kind: "SecretStore",
  2620. },
  2621. Spec: esv1.SecretStoreSpec{
  2622. Provider: &esv1.SecretStoreProvider{
  2623. Fake: &esv1.FakeProvider{
  2624. Data: []esv1.FakeProviderData{},
  2625. },
  2626. },
  2627. },
  2628. },
  2629. unmanagedStore1: &esv1.SecretStore{
  2630. ObjectMeta: metav1.ObjectMeta{
  2631. Name: UnmanagedPushSecretStore1,
  2632. Namespace: PushSecretNamespace,
  2633. },
  2634. TypeMeta: metav1.TypeMeta{
  2635. Kind: "SecretStore",
  2636. },
  2637. Spec: esv1.SecretStoreSpec{
  2638. Provider: &esv1.SecretStoreProvider{
  2639. Fake: &esv1.FakeProvider{
  2640. Data: []esv1.FakeProviderData{},
  2641. },
  2642. },
  2643. Controller: "not-managed",
  2644. },
  2645. },
  2646. unmanagedStore2: &esv1.SecretStore{
  2647. ObjectMeta: metav1.ObjectMeta{
  2648. Name: UnmanagedPushSecretStore2,
  2649. Namespace: PushSecretNamespace,
  2650. },
  2651. TypeMeta: metav1.TypeMeta{
  2652. Kind: "SecretStore",
  2653. },
  2654. Spec: esv1.SecretStoreSpec{
  2655. Provider: &esv1.SecretStoreProvider{
  2656. Fake: &esv1.FakeProvider{
  2657. Data: []esv1.FakeProviderData{},
  2658. },
  2659. },
  2660. Controller: "not-managed",
  2661. },
  2662. },
  2663. }
  2664. }
  2665. multipleManagedStoresSyncsSuccessfully := func(tc *testCase) {
  2666. fakeProvider.SetSecretFn = func() error {
  2667. return nil
  2668. }
  2669. tc.pushsecret.Spec.SecretStoreRefs = append(tc.pushsecret.Spec.SecretStoreRefs,
  2670. v1alpha1.PushSecretStoreRef{
  2671. Name: ManagedPushSecretStore2,
  2672. Kind: "SecretStore",
  2673. },
  2674. )
  2675. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2676. Eventually(func() bool {
  2677. By("checking if Provider value got updated")
  2678. secretValue := secret.Data[defaultKey]
  2679. setSecretArgs := fakeProvider.GetPushSecretData()
  2680. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  2681. if !ok {
  2682. return false
  2683. }
  2684. got := providerValue.Value
  2685. return bytes.Equal(got, secretValue)
  2686. }, time.Second*10, time.Second).Should(BeTrue())
  2687. return true
  2688. }
  2689. }
  2690. skipUnmanagedStores := func(tc *testCase) {
  2691. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  2692. {
  2693. Name: UnmanagedPushSecretStore1,
  2694. Kind: "SecretStore",
  2695. },
  2696. {
  2697. Name: UnmanagedPushSecretStore2,
  2698. Kind: "SecretStore",
  2699. },
  2700. }
  2701. tc.assert = func(ps *v1alpha1.PushSecret, _ *v1.Secret) bool {
  2702. return len(ps.Status.Conditions) == 0
  2703. }
  2704. }
  2705. warnUnmanagedStoresAndSyncManagedStores := func(tc *testCase) {
  2706. fakeProvider.SetSecretFn = func() error {
  2707. return nil
  2708. }
  2709. tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
  2710. {
  2711. Name: ManagedPushSecretStore1,
  2712. Kind: "SecretStore",
  2713. },
  2714. {
  2715. Name: ManagedPushSecretStore2,
  2716. Kind: "SecretStore",
  2717. },
  2718. {
  2719. Name: UnmanagedPushSecretStore1,
  2720. Kind: "SecretStore",
  2721. },
  2722. {
  2723. Name: UnmanagedPushSecretStore2,
  2724. Kind: "SecretStore",
  2725. },
  2726. }
  2727. tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
  2728. Eventually(func() bool {
  2729. By("checking if Provider value got updated")
  2730. secretValue := secret.Data[defaultKey]
  2731. setSecretArgs := fakeProvider.GetPushSecretData()
  2732. providerValue, ok := setSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
  2733. if !ok {
  2734. return false
  2735. }
  2736. got := providerValue.Value
  2737. return bytes.Equal(got, secretValue)
  2738. }, time.Second*10, time.Second).Should(BeTrue())
  2739. return true
  2740. }
  2741. }
  2742. DescribeTable("When reconciling a PushSecret with multiple secret stores",
  2743. func(tweaks ...testTweaks) {
  2744. tc := makeDefaultTestcase()
  2745. for _, tweak := range tweaks {
  2746. tweak(tc)
  2747. }
  2748. ctx := context.Background()
  2749. By("creating secret stores, a secret and a pushsecret")
  2750. if tc.managedStore1 != nil {
  2751. Expect(k8sClient.Create(ctx, tc.managedStore1)).To(Succeed())
  2752. }
  2753. if tc.managedStore2 != nil {
  2754. Expect(k8sClient.Create(ctx, tc.managedStore2)).To(Succeed())
  2755. }
  2756. if tc.unmanagedStore1 != nil {
  2757. Expect(k8sClient.Create(ctx, tc.unmanagedStore1)).To(Succeed())
  2758. }
  2759. if tc.unmanagedStore2 != nil {
  2760. Expect(k8sClient.Create(ctx, tc.unmanagedStore2)).To(Succeed())
  2761. }
  2762. if tc.secret != nil {
  2763. Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
  2764. }
  2765. if tc.pushsecret != nil {
  2766. Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
  2767. }
  2768. time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
  2769. psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
  2770. createdPS := &v1alpha1.PushSecret{}
  2771. By("checking the pushSecret condition")
  2772. Eventually(func() bool {
  2773. err := k8sClient.Get(ctx, psKey, createdPS)
  2774. if err != nil {
  2775. return false
  2776. }
  2777. return tc.assert(createdPS, tc.secret)
  2778. }, timeout, interval).Should(BeTrue())
  2779. // this must be optional so we can test faulty es configuration
  2780. },
  2781. Entry("should sync successfully if there are multiple managed stores", multipleManagedStoresSyncsSuccessfully),
  2782. Entry("should skip unmanaged stores", skipUnmanagedStores),
  2783. Entry("should skip unmanaged stores and sync managed stores", warnUnmanagedStoresAndSyncManagedStores),
  2784. )
  2785. })
  2786. var _ = Describe("mergeDataEntries unit tests", func() {
  2787. Describe("resolveSourceKeyConflicts", func() {
  2788. It("should let explicit data override dataTo for same source key", func() {
  2789. secret := &v1.Secret{Data: map[string][]byte{"foo": []byte("v1"), "bar": []byte("v2")}}
  2790. dataTo := []v1alpha1.PushSecretData{
  2791. {Match: v1alpha1.PushSecretMatch{SecretKey: "foo", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "dataTo/foo"}}},
  2792. {Match: v1alpha1.PushSecretMatch{SecretKey: "bar", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "dataTo/bar"}}},
  2793. }
  2794. explicit := []v1alpha1.PushSecretData{
  2795. {Match: v1alpha1.PushSecretMatch{SecretKey: "foo", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "explicit/foo"}}},
  2796. }
  2797. result := resolveSourceKeyConflicts(dataTo, explicit, secret)
  2798. Expect(result).To(HaveLen(2))
  2799. keys := make(map[string]string)
  2800. for _, d := range result {
  2801. keys[d.GetSecretKey()] = d.GetRemoteKey()
  2802. }
  2803. Expect(keys["bar"]).To(Equal("dataTo/bar"))
  2804. Expect(keys["foo"]).To(Equal("explicit/foo"))
  2805. })
  2806. It("should keep all entries when no conflicts", func() {
  2807. secret := &v1.Secret{Data: map[string][]byte{"a": []byte("v1"), "b": []byte("v2")}}
  2808. dataTo := []v1alpha1.PushSecretData{
  2809. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "a"}}},
  2810. }
  2811. explicit := []v1alpha1.PushSecretData{
  2812. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "b"}}},
  2813. }
  2814. result := resolveSourceKeyConflicts(dataTo, explicit, secret)
  2815. Expect(result).To(HaveLen(2))
  2816. })
  2817. It("should handle empty dataTo", func() {
  2818. secret := &v1.Secret{Data: map[string][]byte{"x": []byte("v1")}}
  2819. explicit := []v1alpha1.PushSecretData{
  2820. {Match: v1alpha1.PushSecretMatch{SecretKey: "x", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "x"}}},
  2821. }
  2822. result := resolveSourceKeyConflicts(nil, explicit, secret)
  2823. Expect(result).To(HaveLen(1))
  2824. Expect(result[0].GetSecretKey()).To(Equal("x"))
  2825. })
  2826. It("should handle empty explicit", func() {
  2827. secret := &v1.Secret{Data: map[string][]byte{"y": []byte("v1")}}
  2828. dataTo := []v1alpha1.PushSecretData{
  2829. {Match: v1alpha1.PushSecretMatch{SecretKey: "y", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "y"}}},
  2830. }
  2831. result := resolveSourceKeyConflicts(dataTo, nil, secret)
  2832. Expect(result).To(HaveLen(1))
  2833. Expect(result[0].GetSecretKey()).To(Equal("y"))
  2834. })
  2835. It("should resolve conflicts when explicit data uses ConversionStrategy", func() {
  2836. secret := &v1.Secret{Data: map[string][]byte{
  2837. "some_U002D_key": []byte("value"),
  2838. "other": []byte("other-value"),
  2839. }}
  2840. dataTo := []v1alpha1.PushSecretData{
  2841. {Match: v1alpha1.PushSecretMatch{SecretKey: "some_U002D_key", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "dataTo/some-key"}}},
  2842. {Match: v1alpha1.PushSecretMatch{SecretKey: "other", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "dataTo/other"}}},
  2843. }
  2844. explicit := []v1alpha1.PushSecretData{
  2845. {
  2846. ConversionStrategy: v1alpha1.PushSecretConversionReverseUnicode,
  2847. Match: v1alpha1.PushSecretMatch{SecretKey: "some-key", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "explicit/some-key"}},
  2848. },
  2849. }
  2850. result := resolveSourceKeyConflicts(dataTo, explicit, secret)
  2851. Expect(result).To(HaveLen(2))
  2852. keys := make(map[string]string)
  2853. for _, d := range result {
  2854. keys[d.GetSecretKey()] = d.GetRemoteKey()
  2855. }
  2856. Expect(keys["other"]).To(Equal("dataTo/other"))
  2857. Expect(keys["some-key"]).To(Equal("explicit/some-key"))
  2858. })
  2859. })
  2860. Describe("validateRemoteKeyUniqueness", func() {
  2861. It("should pass for unique remote keys", func() {
  2862. entries := []v1alpha1.PushSecretData{
  2863. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "remote-a"}}},
  2864. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "remote-b"}}},
  2865. }
  2866. err := validateRemoteKeyUniqueness(entries)
  2867. Expect(err).ToNot(HaveOccurred())
  2868. })
  2869. It("should fail for duplicate remote keys", func() {
  2870. entries := []v1alpha1.PushSecretData{
  2871. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared"}}},
  2872. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared"}}},
  2873. }
  2874. err := validateRemoteKeyUniqueness(entries)
  2875. Expect(err).To(HaveOccurred())
  2876. Expect(err.Error()).To(ContainSubstring(testDuplicateRemoteKeyErr))
  2877. })
  2878. It("should pass for same remote key with different properties", func() {
  2879. entries := []v1alpha1.PushSecretData{
  2880. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared", Property: "field1"}}},
  2881. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared", Property: "field2"}}},
  2882. }
  2883. err := validateRemoteKeyUniqueness(entries)
  2884. Expect(err).ToNot(HaveOccurred())
  2885. })
  2886. It("should fail for same remote key and same property", func() {
  2887. entries := []v1alpha1.PushSecretData{
  2888. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared", Property: "field"}}},
  2889. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared", Property: "field"}}},
  2890. }
  2891. err := validateRemoteKeyUniqueness(entries)
  2892. Expect(err).To(HaveOccurred())
  2893. Expect(err.Error()).To(ContainSubstring(testDuplicateRemoteKeyErr))
  2894. })
  2895. })
  2896. Describe("mergeDataEntries", func() {
  2897. It("should merge valid entries", func() {
  2898. secret := &v1.Secret{Data: map[string][]byte{"a": []byte("v1"), "b": []byte("v2")}}
  2899. dataTo := []v1alpha1.PushSecretData{
  2900. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "a"}}},
  2901. }
  2902. explicit := []v1alpha1.PushSecretData{
  2903. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "b"}}},
  2904. }
  2905. result, err := mergeDataEntries(dataTo, explicit, secret)
  2906. Expect(err).ToNot(HaveOccurred())
  2907. Expect(result).To(HaveLen(2))
  2908. })
  2909. It("should override dataTo with explicit for same source key", func() {
  2910. secret := &v1.Secret{Data: map[string][]byte{"key": []byte("v1")}}
  2911. dataTo := []v1alpha1.PushSecretData{
  2912. {Match: v1alpha1.PushSecretMatch{SecretKey: "key", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "dataTo-path"}}},
  2913. }
  2914. explicit := []v1alpha1.PushSecretData{
  2915. {Match: v1alpha1.PushSecretMatch{SecretKey: "key", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "explicit-path"}}},
  2916. }
  2917. result, err := mergeDataEntries(dataTo, explicit, secret)
  2918. Expect(err).ToNot(HaveOccurred())
  2919. Expect(result).To(HaveLen(1))
  2920. Expect(result[0].GetRemoteKey()).To(Equal("explicit-path"))
  2921. })
  2922. It("should fail for remote key conflict after merge", func() {
  2923. secret := &v1.Secret{Data: map[string][]byte{"a": []byte("v1"), "b": []byte("v2")}}
  2924. dataTo := []v1alpha1.PushSecretData{
  2925. {Match: v1alpha1.PushSecretMatch{SecretKey: "a", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared"}}},
  2926. }
  2927. explicit := []v1alpha1.PushSecretData{
  2928. {Match: v1alpha1.PushSecretMatch{SecretKey: "b", RemoteRef: v1alpha1.PushSecretRemoteRef{RemoteKey: "shared"}}},
  2929. }
  2930. _, err := mergeDataEntries(dataTo, explicit, secret)
  2931. Expect(err).To(HaveOccurred())
  2932. Expect(err.Error()).To(ContainSubstring(testDuplicateRemoteKeyErr))
  2933. })
  2934. })
  2935. })