pushsecret_controller_test.go 92 KB

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