pushsecret_controller_test.go 97 KB

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