externalsecret_controller_test.go 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073
  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 externalsecret
  14. import (
  15. "bytes"
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "os"
  21. "strconv"
  22. "time"
  23. "github.com/google/go-cmp/cmp"
  24. "github.com/google/go-cmp/cmp/cmpopts"
  25. "github.com/onsi/gomega/format"
  26. "github.com/prometheus/client_golang/prometheus"
  27. dto "github.com/prometheus/client_model/go"
  28. v1 "k8s.io/api/core/v1"
  29. apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  30. apierrors "k8s.io/apimachinery/pkg/api/errors"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apimachinery/pkg/types"
  33. "sigs.k8s.io/controller-runtime/pkg/client"
  34. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  35. genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
  36. ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
  37. "github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
  38. ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
  39. ctrlutil "github.com/external-secrets/external-secrets/pkg/controllers/util"
  40. "github.com/external-secrets/external-secrets/runtime/esutils"
  41. "github.com/external-secrets/external-secrets/runtime/testing/fake"
  42. . "github.com/onsi/ginkgo/v2"
  43. . "github.com/onsi/gomega"
  44. )
  45. const (
  46. labelKey = "label-key"
  47. labelValue = "label-value"
  48. annotationKey = "annotation-key"
  49. annotationValue = "annotation-value"
  50. existingLabelKey = "existing-label-key"
  51. existingLabelValue = "existing-label-value"
  52. )
  53. var (
  54. fakeProvider *fake.Client
  55. metric dto.Metric
  56. metricDuration dto.Metric
  57. timeout = time.Second * 10
  58. interval = time.Millisecond * 250
  59. )
  60. var (
  61. testSyncCallsTotal *prometheus.CounterVec
  62. testSyncCallsError *prometheus.CounterVec
  63. testExternalSecretCondition *prometheus.GaugeVec
  64. testExternalSecretReconcileDuration *prometheus.GaugeVec
  65. )
  66. type testCase struct {
  67. secretStore esv1.GenericStore
  68. externalSecret *esv1.ExternalSecret
  69. targetSecretName string
  70. // checkCondition should return true if the externalSecret
  71. // has the expected condition
  72. checkCondition func(*esv1.ExternalSecret) bool
  73. // checkExternalSecret is called after the condition has been verified
  74. // use this to verify the externalSecret
  75. checkExternalSecret func(*esv1.ExternalSecret)
  76. // optional. use this to test the secret value
  77. checkSecret func(*esv1.ExternalSecret, *v1.Secret)
  78. }
  79. func makeExternalSecret(policy esv1.ExternalSecretCreationPolicy) *esv1.ExternalSecret {
  80. return &esv1.ExternalSecret{
  81. Spec: esv1.ExternalSecretSpec{
  82. Target: esv1.ExternalSecretTarget{
  83. CreationPolicy: policy,
  84. },
  85. },
  86. }
  87. }
  88. type testTweaks func(*testCase)
  89. var _ = Describe("Kind=secret existence logic", func() {
  90. validData := map[string][]byte{
  91. "foo": []byte("value1"),
  92. "bar": []byte("value2"),
  93. }
  94. type testCase struct {
  95. Name string
  96. Input *v1.Secret
  97. ExternalSecret *esv1.ExternalSecret
  98. ExpectedOutput bool
  99. }
  100. tests := []testCase{
  101. {
  102. Name: "Should not be valid in case of missing uid",
  103. Input: &v1.Secret{},
  104. ExternalSecret: &esv1.ExternalSecret{},
  105. ExpectedOutput: false,
  106. },
  107. {
  108. Name: "A nil annotation should not be valid",
  109. Input: &v1.Secret{
  110. ObjectMeta: metav1.ObjectMeta{
  111. UID: "xxx",
  112. Labels: map[string]string{
  113. esv1.LabelManaged: esv1.LabelManagedValue,
  114. },
  115. Annotations: map[string]string{},
  116. },
  117. },
  118. ExternalSecret: makeExternalSecret(esv1.CreatePolicyOwner),
  119. ExpectedOutput: false,
  120. },
  121. {
  122. Name: "An invalid annotation hash should not be valid",
  123. Input: &v1.Secret{
  124. ObjectMeta: metav1.ObjectMeta{
  125. UID: "xxx",
  126. Labels: map[string]string{
  127. esv1.LabelManaged: esv1.LabelManagedValue,
  128. },
  129. Annotations: map[string]string{
  130. esv1.AnnotationDataHash: "xxxxxx",
  131. },
  132. },
  133. },
  134. ExternalSecret: makeExternalSecret(esv1.CreatePolicyOwner),
  135. ExpectedOutput: false,
  136. },
  137. {
  138. Name: "A valid secret should return true",
  139. Input: &v1.Secret{
  140. ObjectMeta: metav1.ObjectMeta{
  141. UID: "xxx",
  142. Labels: map[string]string{
  143. esv1.LabelManaged: esv1.LabelManagedValue,
  144. },
  145. Annotations: map[string]string{
  146. esv1.AnnotationDataHash: esutils.ObjectHash(validData),
  147. },
  148. },
  149. Data: validData,
  150. },
  151. ExternalSecret: makeExternalSecret(esv1.CreatePolicyOwner),
  152. ExpectedOutput: true,
  153. },
  154. {
  155. Name: "Ignore Annotations if creation policy is Orphan",
  156. Input: &v1.Secret{
  157. ObjectMeta: metav1.ObjectMeta{
  158. UID: "xxx",
  159. Labels: map[string]string{
  160. esv1.LabelManaged: esv1.LabelManagedValue,
  161. },
  162. Annotations: map[string]string{
  163. esv1.AnnotationDataHash: "xxxxxx",
  164. },
  165. },
  166. },
  167. ExternalSecret: makeExternalSecret(esv1.CreatePolicyOrphan),
  168. ExpectedOutput: true,
  169. },
  170. {
  171. Name: "Ignore missing UID Secret if creation policy is Orphan",
  172. Input: &v1.Secret{},
  173. ExternalSecret: makeExternalSecret(esv1.CreatePolicyOrphan),
  174. ExpectedOutput: true,
  175. },
  176. }
  177. for _, tt := range tests {
  178. It(tt.Name, func() {
  179. Expect(isSecretValid(tt.Input, tt.ExternalSecret)).To(BeEquivalentTo(tt.ExpectedOutput))
  180. })
  181. }
  182. })
  183. var _ = Describe("ExternalSecret controller", Serial, func() {
  184. const (
  185. ExternalSecretName = "test-es"
  186. ExternalSecretFQDN = "externalsecrets.external-secrets.io/test-es"
  187. ExternalSecretStore = "test-store"
  188. ExternalSecretTargetSecretName = "test-secret"
  189. FakeManager = "fake.manager"
  190. expectedSecretVal = "SOMEVALUE was templated"
  191. targetPropObj = "{{ .targetProperty | toString | upper }} was templated"
  192. FooValue = "map-foo-value"
  193. BarValue = "map-bar-value"
  194. NamespaceLabelKey = "css-test-label-key"
  195. NamespaceLabelValue = "css-test-label-value"
  196. )
  197. var ExternalSecretNamespace string
  198. // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
  199. if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
  200. if t, err := strconv.Atoi(customTimeout); err == nil {
  201. timeout = time.Second * time.Duration(t)
  202. }
  203. }
  204. BeforeEach(func() {
  205. var err error
  206. ExternalSecretNamespace, err = ctest.CreateNamespaceWithLabels("test-ns", k8sClient, map[string]string{NamespaceLabelKey: NamespaceLabelValue})
  207. Expect(err).ToNot(HaveOccurred())
  208. metric.Reset()
  209. testSyncCallsTotal.Reset()
  210. testSyncCallsError.Reset()
  211. testExternalSecretCondition.Reset()
  212. testExternalSecretReconcileDuration.Reset()
  213. fakeProvider.Reset()
  214. })
  215. AfterEach(
  216. func() {
  217. secretStore := &esv1.SecretStore{}
  218. secretStoreLookupKey := types.NamespacedName{
  219. Name: ExternalSecretStore,
  220. Namespace: ExternalSecretNamespace,
  221. }
  222. if err := k8sClient.Get(context.Background(), secretStoreLookupKey, secretStore); err == nil {
  223. Expect(k8sClient.Delete(context.Background(), secretStore)).To(Succeed())
  224. }
  225. clusterSecretStore := &esv1.ClusterSecretStore{}
  226. clusterSecretStoreLookupKey := types.NamespacedName{
  227. Name: ExternalSecretStore,
  228. }
  229. if err := k8sClient.Get(context.Background(), clusterSecretStoreLookupKey, clusterSecretStore); err == nil {
  230. Expect(k8sClient.Delete(context.Background(), clusterSecretStore)).To(Succeed())
  231. }
  232. Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
  233. ObjectMeta: metav1.ObjectMeta{
  234. Name: ExternalSecretNamespace,
  235. },
  236. })).To(Succeed())
  237. },
  238. )
  239. const (
  240. secretVal = "some-value"
  241. targetProp = "targetProperty"
  242. remoteKey = "barz"
  243. remoteProperty = "bang"
  244. existingKey = "pre-existing-key"
  245. existingVal = "pre-existing-value"
  246. )
  247. makeDefaultTestcase := func() *testCase {
  248. return &testCase{
  249. // default condition: es should be ready
  250. targetSecretName: ExternalSecretTargetSecretName,
  251. checkCondition: func(es *esv1.ExternalSecret) bool {
  252. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  253. if cond == nil || cond.Status != v1.ConditionTrue {
  254. return false
  255. }
  256. return true
  257. },
  258. checkExternalSecret: func(_ *esv1.ExternalSecret) {
  259. // noop by default
  260. },
  261. secretStore: &esv1.SecretStore{
  262. ObjectMeta: metav1.ObjectMeta{
  263. Name: ExternalSecretStore,
  264. Namespace: ExternalSecretNamespace,
  265. },
  266. Spec: esv1.SecretStoreSpec{
  267. Provider: &esv1.SecretStoreProvider{
  268. AWS: &esv1.AWSProvider{
  269. Service: esv1.AWSServiceSecretsManager,
  270. },
  271. },
  272. },
  273. },
  274. externalSecret: &esv1.ExternalSecret{
  275. ObjectMeta: metav1.ObjectMeta{
  276. Name: ExternalSecretName,
  277. Namespace: ExternalSecretNamespace,
  278. },
  279. Spec: esv1.ExternalSecretSpec{
  280. SecretStoreRef: esv1.SecretStoreRef{
  281. Name: ExternalSecretStore,
  282. },
  283. Target: esv1.ExternalSecretTarget{
  284. Name: ExternalSecretTargetSecretName,
  285. },
  286. Data: []esv1.ExternalSecretData{
  287. {
  288. SecretKey: targetProp,
  289. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  290. Key: remoteKey,
  291. Property: remoteProperty,
  292. },
  293. },
  294. },
  295. },
  296. },
  297. }
  298. }
  299. // if target Secret name is not specified it should use the ExternalSecret name.
  300. syncWithoutTargetName := func(tc *testCase) {
  301. tc.externalSecret.Spec.Target.Name = ""
  302. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  303. // check secret name
  304. Expect(secret.ObjectMeta.Name).To(Equal(ExternalSecretName))
  305. // check binding secret on external secret
  306. Expect(es.Status.Binding.Name).To(Equal(secret.ObjectMeta.Name))
  307. }
  308. }
  309. // if target Secret name is not specified it should use the ExternalSecret name.
  310. syncBigNames := func(tc *testCase) {
  311. tc.targetSecretName = "this-is-a-very-big-secret-name-that-wouldnt-be-generated-due-to-label-limits"
  312. tc.externalSecret.Spec.Target.Name = "this-is-a-very-big-secret-name-that-wouldnt-be-generated-due-to-label-limits"
  313. tc.checkSecret = func(es *esv1.ExternalSecret, _ *v1.Secret) {
  314. // check binding secret on external secret
  315. Expect(es.Status.Binding.Name).To(Equal(tc.externalSecret.Spec.Target.Name))
  316. }
  317. }
  318. // the secret name is reflected on the external secret's status as the binding secret
  319. syncBindingSecret := func(tc *testCase) {
  320. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  321. // check binding secret on external secret
  322. Expect(es.Status.Binding.Name).To(Equal(secret.ObjectMeta.Name))
  323. }
  324. }
  325. // their is no binding secret when a secret is not synced
  326. skipBindingSecret := func(tc *testCase) {
  327. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyNone
  328. tc.checkExternalSecret = func(es *esv1.ExternalSecret) {
  329. // check binding secret is not set
  330. Expect(es.Status.Binding.Name).To(BeEmpty())
  331. }
  332. }
  333. // labels and annotations from the Kind=ExternalSecret
  334. // should be copied over to the Kind=Secret
  335. syncLabelsAnnotations := func(tc *testCase) {
  336. tc.externalSecret.ObjectMeta.Labels = map[string]string{
  337. labelKey: labelValue,
  338. }
  339. tc.externalSecret.ObjectMeta.Annotations = map[string]string{
  340. annotationKey: annotationValue,
  341. }
  342. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  343. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  344. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
  345. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
  346. // ownerRef must not be set!
  347. Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretName)).To(BeTrue())
  348. }
  349. }
  350. // labels and annotations from the ExternalSecret
  351. // should be merged to the Secret if exists
  352. mergeLabelsAnnotations := func(tc *testCase) {
  353. tc.externalSecret.ObjectMeta.Labels = map[string]string{
  354. labelKey: labelValue,
  355. }
  356. tc.externalSecret.ObjectMeta.Annotations = map[string]string{
  357. annotationKey: annotationValue,
  358. }
  359. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  360. // Create a secret owned by another entity to test if the pre-existing metadata is preserved
  361. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  362. ObjectMeta: metav1.ObjectMeta{
  363. Name: ExternalSecretTargetSecretName,
  364. Namespace: ExternalSecretNamespace,
  365. Labels: map[string]string{
  366. existingLabelKey: existingLabelValue,
  367. },
  368. Annotations: map[string]string{
  369. "existing-annotation-key": "existing-annotation-value",
  370. },
  371. },
  372. }, client.FieldOwner(FakeManager))).To(Succeed())
  373. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  374. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
  375. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(existingLabelKey, existingLabelValue))
  376. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
  377. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
  378. }
  379. }
  380. removeOutdatedLabelsAnnotations := func(tc *testCase) {
  381. tc.externalSecret.ObjectMeta.Labels = map[string]string{
  382. labelKey: labelValue,
  383. }
  384. tc.externalSecret.ObjectMeta.Annotations = map[string]string{
  385. annotationKey: annotationValue,
  386. }
  387. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  388. // Create a secret owned by the operator to test if the outdated pre-existing metadata is removed
  389. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  390. ObjectMeta: metav1.ObjectMeta{
  391. Name: ExternalSecretTargetSecretName,
  392. Namespace: ExternalSecretNamespace,
  393. Labels: map[string]string{
  394. existingLabelKey: existingLabelValue,
  395. },
  396. Annotations: map[string]string{
  397. "existing-annotation-key": "existing-annotation-value",
  398. },
  399. },
  400. }, client.FieldOwner(ExternalSecretFQDN))).To(Succeed())
  401. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  402. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
  403. Expect(secret.ObjectMeta.Labels).NotTo(HaveKeyWithValue(existingLabelKey, existingLabelValue))
  404. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
  405. Expect(secret.ObjectMeta.Annotations).NotTo(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
  406. }
  407. }
  408. checkPrometheusCounters := func(tc *testCase) {
  409. const secretVal = "someValue"
  410. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  411. tc.checkSecret = func(_ *esv1.ExternalSecret, _ *v1.Secret) {
  412. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
  413. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 1.0)).To(BeTrue())
  414. Eventually(func() bool {
  415. Expect(testSyncCallsTotal.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  416. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  417. // three reconciliations: initial sync, status update, secret update
  418. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  419. }, timeout, interval).Should(BeTrue())
  420. }
  421. }
  422. // merge with existing secret using creationPolicy=Merge
  423. // it should NOT have a ownerReference
  424. // metadata.managedFields with the correct owner should be added to the secret
  425. mergeWithSecret := func(tc *testCase) {
  426. const secretVal = "someValue"
  427. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  428. tc.externalSecret.Labels = map[string]string{
  429. "es-label-key": "es-label-value",
  430. }
  431. tc.externalSecret.Annotations = map[string]string{
  432. "es-annotation-key": "es-annotation-value",
  433. }
  434. // create secret beforehand
  435. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  436. ObjectMeta: metav1.ObjectMeta{
  437. Name: ExternalSecretTargetSecretName,
  438. Namespace: ExternalSecretNamespace,
  439. Labels: map[string]string{
  440. existingLabelKey: existingLabelValue,
  441. },
  442. Annotations: map[string]string{
  443. "existing-annotation-key": "existing-annotation-value",
  444. },
  445. },
  446. Data: map[string][]byte{
  447. existingKey: []byte(existingVal),
  448. },
  449. }, client.FieldOwner(FakeManager))).To(Succeed())
  450. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  451. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  452. // check value
  453. Expect(string(secret.Data[existingKey])).To(Equal(existingVal))
  454. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  455. Expect(secret.ObjectMeta.Labels).To(HaveLen(3))
  456. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(existingLabelKey, existingLabelValue))
  457. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("es-label-key", "es-label-value"))
  458. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(esv1.LabelManaged, esv1.LabelManagedValue))
  459. Expect(secret.ObjectMeta.Annotations).To(HaveLen(3))
  460. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
  461. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("es-annotation-key", "es-annotation-value"))
  462. Expect(secret.ObjectMeta.Annotations).To(HaveKey(esv1.AnnotationDataHash))
  463. Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretFQDN)).To(BeFalse())
  464. Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(2))
  465. oldCharactersAroundMismatchToInclude := format.CharactersAroundMismatchToInclude
  466. format.CharactersAroundMismatchToInclude = 10
  467. Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, ExternalSecretFQDN)).To(
  468. Equal(
  469. fmt.Sprintf(
  470. `{"f:data":{"f:targetProperty":{}},"f:metadata":{"f:annotations":{"f:es-annotation-key":{},"f:%s":{}},"f:labels":{"f:es-label-key":{},"f:%s":{}}}}`,
  471. esv1.AnnotationDataHash,
  472. esv1.LabelManaged,
  473. ),
  474. ),
  475. )
  476. Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, FakeManager)).To(
  477. Equal(`{"f:data":{".":{},"f:pre-existing-key":{}},"f:metadata":{"f:annotations":{".":{},"f:existing-annotation-key":{}},"f:labels":{".":{},"f:existing-label-key":{}}},"f:type":{}}`),
  478. )
  479. format.CharactersAroundMismatchToInclude = oldCharactersAroundMismatchToInclude
  480. }
  481. }
  482. mergeWithSecretUpdate := func(tc *testCase) {
  483. const secretVal = "someValue"
  484. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  485. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Hour}
  486. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  487. ObjectMeta: metav1.ObjectMeta{
  488. Name: ExternalSecretTargetSecretName,
  489. Namespace: ExternalSecretNamespace,
  490. },
  491. Data: map[string][]byte{
  492. existingKey: []byte(existingVal),
  493. },
  494. }, client.FieldOwner(FakeManager))).To(Succeed())
  495. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  496. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  497. // Overwrite the secret value to check if the change kicks reconciliation and overwrites it again
  498. Expect(k8sClient.Update(context.Background(), &v1.Secret{
  499. ObjectMeta: metav1.ObjectMeta{
  500. Name: ExternalSecretTargetSecretName,
  501. Namespace: ExternalSecretNamespace,
  502. },
  503. Data: map[string][]byte{
  504. existingKey: []byte("differentValue"),
  505. },
  506. }, client.FieldOwner(FakeManager))).To(Succeed())
  507. Expect(string(secret.Data[existingKey])).To(Equal(existingVal))
  508. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  509. }
  510. }
  511. // should not update if no changes
  512. mergeWithSecretNoChange := func(tc *testCase) {
  513. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  514. // create secret beforehand
  515. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  516. ObjectMeta: metav1.ObjectMeta{
  517. Name: ExternalSecretTargetSecretName,
  518. Namespace: ExternalSecretNamespace,
  519. },
  520. Data: map[string][]byte{
  521. existingKey: []byte(existingVal),
  522. },
  523. }, client.FieldOwner(FakeManager))).To(Succeed())
  524. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  525. oldResourceVersion := secret.ResourceVersion
  526. cleanSecret := secret.DeepCopy()
  527. Expect(k8sClient.Patch(context.Background(), secret, client.MergeFrom(cleanSecret))).To(Succeed())
  528. newSecret := &v1.Secret{}
  529. Eventually(func() bool {
  530. secretLookupKey := types.NamespacedName{
  531. Name: ExternalSecretTargetSecretName,
  532. Namespace: ExternalSecretNamespace,
  533. }
  534. err := k8sClient.Get(context.Background(), secretLookupKey, newSecret)
  535. if err != nil {
  536. return false
  537. }
  538. return oldResourceVersion == newSecret.ResourceVersion
  539. }, timeout, interval).Should(Equal(true))
  540. }
  541. }
  542. // should not merge with secret if it doesn't exist
  543. mergeWithSecretErr := func(tc *testCase) {
  544. const secretVal = "someValue"
  545. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  546. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  547. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  548. expected := []esv1.ExternalSecretStatusCondition{
  549. {
  550. Type: esv1.ExternalSecretReady,
  551. Status: v1.ConditionTrue,
  552. Reason: esv1.ConditionReasonSecretMissing,
  553. Message: msgMissing,
  554. },
  555. }
  556. opts := cmpopts.IgnoreFields(esv1.ExternalSecretStatusCondition{}, "LastTransitionTime")
  557. if diff := cmp.Diff(expected, es.Status.Conditions, opts); diff != "" {
  558. GinkgoLogr.Info("(-got, +want)\n%s", "diff", diff)
  559. return false
  560. }
  561. return true
  562. }
  563. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  564. Eventually(func() bool {
  565. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  566. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  567. return metric.GetCounter().GetValue() == 0 && metricDuration.GetGauge().GetValue() > 0.0
  568. }, timeout, interval).Should(BeTrue())
  569. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
  570. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 1.0)).To(BeTrue())
  571. }
  572. }
  573. // controller should force ownership
  574. mergeWithConflict := func(tc *testCase) {
  575. const secretVal = "someValue"
  576. // this should confict
  577. const existingKey = targetProp
  578. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  579. // create secret beforehand
  580. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  581. ObjectMeta: metav1.ObjectMeta{
  582. Name: ExternalSecretTargetSecretName,
  583. Namespace: ExternalSecretNamespace,
  584. },
  585. Data: map[string][]byte{
  586. existingKey: []byte(existingVal),
  587. },
  588. }, client.FieldOwner(FakeManager))).To(Succeed())
  589. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  590. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  591. // check that value stays the same
  592. Expect(string(secret.Data[existingKey])).To(Equal(secretVal))
  593. // check owner/managedFields
  594. Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretFQDN)).To(BeFalse())
  595. Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(2))
  596. oldCharactersAroundMismatchToInclude := format.CharactersAroundMismatchToInclude
  597. format.CharactersAroundMismatchToInclude = 10
  598. Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, ExternalSecretFQDN)).To(
  599. Equal(fmt.Sprintf(`{"f:data":{"f:targetProperty":{}},"f:metadata":{"f:annotations":{".":{},"f:%s":{}},"f:labels":{".":{},"f:%s":{}}}}`, esv1.AnnotationDataHash, esv1.LabelManaged)),
  600. )
  601. format.CharactersAroundMismatchToInclude = oldCharactersAroundMismatchToInclude
  602. }
  603. }
  604. syncWithGeneratorRef := func(tc *testCase) {
  605. const secretKey = "somekey"
  606. const secretVal = "someValue"
  607. Expect(k8sClient.Create(context.Background(), &genv1alpha1.Fake{
  608. ObjectMeta: metav1.ObjectMeta{
  609. Name: "mytestfake",
  610. Namespace: ExternalSecretNamespace,
  611. },
  612. Spec: genv1alpha1.FakeSpec{
  613. Data: map[string]string{
  614. secretKey: secretVal,
  615. },
  616. },
  617. })).To(Succeed())
  618. // reset secretStoreRef
  619. tc.externalSecret.Spec.SecretStoreRef = esv1.SecretStoreRef{}
  620. tc.externalSecret.Spec.Data = nil
  621. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  622. {
  623. SourceRef: &esv1.StoreGeneratorSourceRef{
  624. GeneratorRef: &esv1.GeneratorRef{
  625. APIVersion: genv1alpha1.Group + "/" + genv1alpha1.Version,
  626. Kind: "Fake",
  627. Name: "mytestfake",
  628. },
  629. },
  630. },
  631. }
  632. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  633. // check values
  634. Expect(string(secret.Data[secretKey])).To(Equal(secretVal))
  635. }
  636. }
  637. syncWithClusterGeneratorRef := func(tc *testCase) {
  638. const secretKey = "somekey2"
  639. const secretVal = "someValue2"
  640. Expect(k8sClient.Create(context.Background(), &genv1alpha1.ClusterGenerator{
  641. ObjectMeta: metav1.ObjectMeta{
  642. Name: "mytestfake",
  643. },
  644. Spec: genv1alpha1.ClusterGeneratorSpec{
  645. Kind: "Fake",
  646. Generator: genv1alpha1.GeneratorSpec{
  647. FakeSpec: &genv1alpha1.FakeSpec{
  648. Data: map[string]string{
  649. secretKey: secretVal,
  650. },
  651. },
  652. },
  653. },
  654. })).To(Succeed())
  655. // reset secretStoreRef
  656. tc.externalSecret.Spec.SecretStoreRef = esv1.SecretStoreRef{}
  657. tc.externalSecret.Spec.Data = nil
  658. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  659. {
  660. SourceRef: &esv1.StoreGeneratorSourceRef{
  661. GeneratorRef: &esv1.GeneratorRef{
  662. APIVersion: genv1alpha1.Group + "/" + genv1alpha1.Version,
  663. Kind: "ClusterGenerator",
  664. Name: "mytestfake",
  665. },
  666. },
  667. },
  668. }
  669. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  670. // check values
  671. Expect(string(secret.Data[secretKey])).To(Equal(secretVal))
  672. }
  673. }
  674. deleteOrphanedSecrets := func(tc *testCase) {
  675. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  676. cleanEs := es.DeepCopy()
  677. oldSecret := v1.Secret{}
  678. oldSecretName := types.NamespacedName{
  679. Name: "test-secret",
  680. Namespace: secret.Namespace,
  681. }
  682. newSecret := v1.Secret{}
  683. secretName := types.NamespacedName{
  684. Name: "new-foo",
  685. Namespace: secret.Namespace,
  686. }
  687. Eventually(func() bool {
  688. err := k8sClient.Get(context.Background(), oldSecretName, &oldSecret)
  689. return err == nil
  690. }, time.Second*10, time.Millisecond*200).Should(BeTrue())
  691. es.Spec.Target.Name = "new-foo"
  692. Expect(k8sClient.Patch(context.Background(), es, client.MergeFrom(cleanEs))).To(Succeed())
  693. Eventually(func() bool {
  694. err := k8sClient.Get(context.Background(), secretName, &newSecret)
  695. return err == nil
  696. }, time.Second*10, time.Millisecond*200).Should(BeTrue())
  697. Eventually(func() bool {
  698. err := k8sClient.Get(context.Background(), oldSecretName, &oldSecret)
  699. return apierrors.IsNotFound(err)
  700. }, time.Second*10, time.Millisecond*200).Should(BeTrue())
  701. }
  702. }
  703. ignoreMismatchControllerForGeneratorRef := func(_ *testCase) {
  704. const secretKey = "somekey"
  705. const secretVal = "someValue"
  706. fakeGenerator := &genv1alpha1.Fake{
  707. ObjectMeta: metav1.ObjectMeta{
  708. Name: "mytestfake2",
  709. Namespace: ExternalSecretNamespace,
  710. },
  711. Spec: genv1alpha1.FakeSpec{
  712. Data: map[string]string{
  713. secretKey: secretVal,
  714. },
  715. Controller: "fakeControllerClass",
  716. },
  717. }
  718. fakeGeneratorJSON, _ := json.Marshal(fakeGenerator)
  719. Expect(shouldSkipGenerator(
  720. &Reconciler{
  721. ControllerClass: "default",
  722. },
  723. &apiextensions.JSON{Raw: fakeGeneratorJSON},
  724. )).To(BeTrue())
  725. }
  726. syncWithMultipleSecretStores := func(tc *testCase) {
  727. Expect(k8sClient.Create(context.Background(), &esv1.SecretStore{
  728. ObjectMeta: metav1.ObjectMeta{
  729. Name: "foo",
  730. Namespace: ExternalSecretNamespace,
  731. },
  732. Spec: esv1.SecretStoreSpec{
  733. Provider: &esv1.SecretStoreProvider{
  734. Fake: &esv1.FakeProvider{
  735. Data: []esv1.FakeProviderData{
  736. {
  737. Key: "foo",
  738. Version: "",
  739. Value: `{"foo":"bar","foo2":"bar2"}`,
  740. },
  741. },
  742. },
  743. },
  744. },
  745. })).To(Succeed())
  746. Expect(k8sClient.Create(context.Background(), &esv1.SecretStore{
  747. ObjectMeta: metav1.ObjectMeta{
  748. Name: "baz",
  749. Namespace: ExternalSecretNamespace,
  750. },
  751. Spec: esv1.SecretStoreSpec{
  752. Provider: &esv1.SecretStoreProvider{
  753. Fake: &esv1.FakeProvider{
  754. Data: []esv1.FakeProviderData{
  755. {
  756. Key: "baz",
  757. Version: "",
  758. Value: `{"baz":"bang","baz2":"bang2"}`,
  759. },
  760. },
  761. },
  762. },
  763. },
  764. })).To(Succeed())
  765. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  766. {
  767. Extract: &esv1.ExternalSecretDataRemoteRef{
  768. Key: "foo",
  769. },
  770. SourceRef: &esv1.StoreGeneratorSourceRef{
  771. SecretStoreRef: &esv1.SecretStoreRef{
  772. Name: "foo",
  773. Kind: esv1.SecretStoreKind,
  774. },
  775. },
  776. },
  777. {
  778. Extract: &esv1.ExternalSecretDataRemoteRef{
  779. Key: "baz",
  780. },
  781. SourceRef: &esv1.StoreGeneratorSourceRef{
  782. SecretStoreRef: &esv1.SecretStoreRef{
  783. Name: "baz",
  784. Kind: esv1.SecretStoreKind,
  785. },
  786. },
  787. },
  788. }
  789. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  790. // check values
  791. Expect(string(secret.Data["foo"])).To(Equal("bar"))
  792. Expect(string(secret.Data["foo2"])).To(Equal("bar2"))
  793. Expect(string(secret.Data["baz"])).To(Equal("bang"))
  794. Expect(string(secret.Data["baz2"])).To(Equal("bang2"))
  795. }
  796. }
  797. // when using a template it should be used as a blueprint
  798. // to construct a new secret: labels, annotations, finalizers and type
  799. syncWithTemplate := func(tc *testCase) {
  800. const secretVal = "someValue"
  801. const tplStaticKey = "tplstatickey"
  802. const tplStaticVal = "tplstaticvalue"
  803. tc.externalSecret.ObjectMeta.Labels = map[string]string{
  804. "fooobar": "bazz",
  805. }
  806. tc.externalSecret.ObjectMeta.Annotations = map[string]string{
  807. "hihihih": "hehehe",
  808. }
  809. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  810. Metadata: esv1.ExternalSecretTemplateMetadata{
  811. Labels: map[string]string{
  812. "foos": "ball",
  813. },
  814. Annotations: map[string]string{
  815. "hihi": "ga",
  816. },
  817. Finalizers: []string{
  818. "example.com/finalizer",
  819. },
  820. },
  821. Type: v1.SecretTypeOpaque,
  822. EngineVersion: esv1.TemplateEngineV2,
  823. Data: map[string]string{
  824. targetProp: targetPropObj,
  825. tplStaticKey: tplStaticVal,
  826. },
  827. }
  828. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  829. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  830. // check values
  831. Expect(string(secret.Data[targetProp])).To(Equal(expectedSecretVal))
  832. Expect(string(secret.Data[tplStaticKey])).To(Equal(tplStaticVal))
  833. // labels/annotations/finalizers should be taken from the template
  834. for k, v := range es.Spec.Target.Template.Metadata.Labels {
  835. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(k, v))
  836. }
  837. for k, v := range es.Spec.Target.Template.Metadata.Annotations {
  838. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(k, v))
  839. }
  840. for _, v := range es.Spec.Target.Template.Metadata.Finalizers {
  841. Expect(secret.ObjectMeta.Finalizers).To(ContainElement(v))
  842. }
  843. }
  844. }
  845. // when using a v2 template it should use the v2 engine version
  846. syncWithTemplateV2 := func(tc *testCase) {
  847. const secretVal = "someValue"
  848. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  849. Type: v1.SecretTypeOpaque,
  850. // it should default to v2 for beta11
  851. // EngineVersion: esv1.TemplateEngineV2,
  852. Data: map[string]string{
  853. targetProp: "{{ .targetProperty | upper }} was templated",
  854. },
  855. }
  856. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  857. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  858. // check values
  859. Expect(string(secret.Data[targetProp])).To(Equal(expectedSecretVal))
  860. }
  861. }
  862. // // secret should be synced with correct value precedence:
  863. // // * fromString
  864. // // * template data
  865. // // * templateFrom
  866. // // * data
  867. // // * dataFrom
  868. syncWithTemplatePrecedence := func(tc *testCase) {
  869. const secretVal = "someValue"
  870. const tplStaticKey = "tplstatickey"
  871. const tplStaticVal = "tplstaticvalue"
  872. const tplFromCMName = "template-cm"
  873. const tplFromSecretName = "template-secret"
  874. const tplFromKey = "tpl-from-key"
  875. const tplFromSecKey = "tpl-from-sec-key"
  876. const tplFromVal = "tpl-from-value: {{ .targetProperty | toString }} // {{ .bar | toString }}"
  877. const tplFromSecVal = "tpl-from-sec-value: {{ .targetProperty | toString }} // {{ .bar | toString }}"
  878. Expect(k8sClient.Create(context.Background(), &v1.ConfigMap{
  879. ObjectMeta: metav1.ObjectMeta{
  880. Name: tplFromCMName,
  881. Namespace: ExternalSecretNamespace,
  882. },
  883. Data: map[string]string{
  884. tplFromKey: tplFromVal,
  885. },
  886. })).To(Succeed())
  887. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  888. ObjectMeta: metav1.ObjectMeta{
  889. Name: tplFromSecretName,
  890. Namespace: ExternalSecretNamespace,
  891. },
  892. Data: map[string][]byte{
  893. tplFromSecKey: []byte(tplFromSecVal),
  894. },
  895. })).To(Succeed())
  896. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  897. Metadata: esv1.ExternalSecretTemplateMetadata{},
  898. Type: v1.SecretTypeOpaque,
  899. TemplateFrom: []esv1.TemplateFrom{
  900. {
  901. ConfigMap: &esv1.TemplateRef{
  902. Name: tplFromCMName,
  903. Items: []esv1.TemplateRefItem{
  904. {
  905. Key: tplFromKey,
  906. },
  907. },
  908. },
  909. },
  910. {
  911. Secret: &esv1.TemplateRef{
  912. Name: tplFromSecretName,
  913. Items: []esv1.TemplateRefItem{
  914. {
  915. Key: tplFromSecKey,
  916. },
  917. },
  918. },
  919. },
  920. },
  921. Data: map[string]string{
  922. // this should be the data value, not dataFrom
  923. targetProp: targetPropObj,
  924. // this should use the value from the map
  925. "bar": "value from map: {{ .bar | toString }}",
  926. // just a static value
  927. tplStaticKey: tplStaticVal,
  928. },
  929. }
  930. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  931. {
  932. Extract: &esv1.ExternalSecretDataRemoteRef{
  933. Key: "datamap",
  934. },
  935. },
  936. }
  937. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  938. fakeProvider.WithGetSecretMap(map[string][]byte{
  939. "targetProperty": []byte(FooValue),
  940. "bar": []byte(BarValue),
  941. }, nil)
  942. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  943. // check values
  944. Expect(string(secret.Data[targetProp])).To(Equal(expectedSecretVal))
  945. Expect(string(secret.Data[tplStaticKey])).To(Equal(tplStaticVal))
  946. Expect(string(secret.Data["bar"])).To(Equal("value from map: map-bar-value"))
  947. Expect(string(secret.Data[tplFromKey])).To(Equal("tpl-from-value: someValue // map-bar-value"))
  948. Expect(string(secret.Data[tplFromSecKey])).To(Equal("tpl-from-sec-value: someValue // map-bar-value"))
  949. }
  950. }
  951. syncTemplateFromKeysAndValues := func(tc *testCase) {
  952. const tplFromCMName = "template-cm"
  953. const tplFromSecretName = "template-secret"
  954. const tplFromKey = "tpl-from-key"
  955. const tplFromSecKey = "tpl-from-sec-key"
  956. const tplFromVal = "{{ .targetKey }}-cm: {{ .targetValue }}"
  957. const tplFromSecVal = "{{ .targetKey }}-sec: {{ .targetValue }}"
  958. Expect(k8sClient.Create(context.Background(), &v1.ConfigMap{
  959. ObjectMeta: metav1.ObjectMeta{
  960. Name: tplFromCMName,
  961. Namespace: ExternalSecretNamespace,
  962. },
  963. Data: map[string]string{
  964. tplFromKey: tplFromVal,
  965. },
  966. })).To(Succeed())
  967. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  968. ObjectMeta: metav1.ObjectMeta{
  969. Name: tplFromSecretName,
  970. Namespace: ExternalSecretNamespace,
  971. },
  972. Data: map[string][]byte{
  973. tplFromSecKey: []byte(tplFromSecVal),
  974. },
  975. })).To(Succeed())
  976. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  977. Metadata: esv1.ExternalSecretTemplateMetadata{},
  978. Type: v1.SecretTypeOpaque,
  979. TemplateFrom: []esv1.TemplateFrom{
  980. {
  981. ConfigMap: &esv1.TemplateRef{
  982. Name: tplFromCMName,
  983. Items: []esv1.TemplateRefItem{
  984. {
  985. Key: tplFromKey,
  986. TemplateAs: esv1.TemplateScopeKeysAndValues,
  987. },
  988. },
  989. },
  990. },
  991. {
  992. Secret: &esv1.TemplateRef{
  993. Name: tplFromSecretName,
  994. Items: []esv1.TemplateRefItem{
  995. {
  996. Key: tplFromSecKey,
  997. TemplateAs: esv1.TemplateScopeKeysAndValues,
  998. },
  999. },
  1000. },
  1001. },
  1002. },
  1003. }
  1004. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1005. {
  1006. Extract: &esv1.ExternalSecretDataRemoteRef{
  1007. Key: "datamap",
  1008. },
  1009. },
  1010. }
  1011. fakeProvider.WithGetSecretMap(map[string][]byte{
  1012. "targetKey": []byte(FooValue),
  1013. "targetValue": []byte(BarValue),
  1014. }, nil)
  1015. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1016. // check values
  1017. Expect(string(secret.Data["map-foo-value-cm"])).To(Equal(BarValue))
  1018. Expect(string(secret.Data["map-foo-value-sec"])).To(Equal(BarValue))
  1019. }
  1020. }
  1021. syncTemplateFromLiteral := func(tc *testCase) {
  1022. tplDataVal := "{{ .targetKey }}-literal: {{ .targetValue }}"
  1023. tplAnnotationsVal := "{{ .targetKey }}-annotations: {{ .targetValue }}"
  1024. tplLabelsVal := "{{ .targetKey }}-labels: {{ .targetValue }}"
  1025. tplComplexVal := `
  1026. {{- range $k, $v := ( .complex | fromJson )}}
  1027. {{ $k }}: {{ $v }}
  1028. {{- end }}
  1029. `
  1030. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  1031. Metadata: esv1.ExternalSecretTemplateMetadata{},
  1032. Type: v1.SecretTypeOpaque,
  1033. TemplateFrom: []esv1.TemplateFrom{
  1034. {
  1035. Literal: &tplDataVal,
  1036. },
  1037. {
  1038. Literal: &tplComplexVal,
  1039. },
  1040. {
  1041. Target: esv1.TemplateTargetAnnotations,
  1042. Literal: &tplAnnotationsVal,
  1043. },
  1044. {
  1045. Target: esv1.TemplateTargetLabels,
  1046. Literal: &tplLabelsVal,
  1047. },
  1048. },
  1049. }
  1050. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1051. {
  1052. Extract: &esv1.ExternalSecretDataRemoteRef{
  1053. Key: "datamap",
  1054. },
  1055. },
  1056. }
  1057. fakeProvider.WithGetSecretMap(map[string][]byte{
  1058. "targetKey": []byte(FooValue),
  1059. "targetValue": []byte(BarValue),
  1060. "complex": []byte("{\"nested\":\"json\",\"can\":\"be\",\"templated\":\"successfully\"}"),
  1061. }, nil)
  1062. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1063. // check values
  1064. Expect(string(secret.Data["map-foo-value-literal"])).To(Equal(BarValue))
  1065. Expect(string(secret.Data["nested"])).To(Equal("json"))
  1066. Expect(string(secret.Data["can"])).To(Equal("be"))
  1067. Expect(string(secret.Data["templated"])).To(Equal("successfully"))
  1068. Expect(secret.ObjectMeta.Annotations["map-foo-value-annotations"]).To(Equal(BarValue))
  1069. Expect(secret.ObjectMeta.Labels["map-foo-value-labels"]).To(Equal(BarValue))
  1070. }
  1071. }
  1072. refreshWithTemplate := func(tc *testCase) {
  1073. const secretVal = "someValue"
  1074. const tplStaticKey = "tplstatickey"
  1075. const tplStaticVal = "tplstaticvalue"
  1076. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1077. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  1078. Metadata: esv1.ExternalSecretTemplateMetadata{
  1079. Labels: map[string]string{"foo": "bar"},
  1080. Annotations: map[string]string{"foo": "bar"},
  1081. Finalizers: []string{"example.com/finalizer"},
  1082. },
  1083. Type: v1.SecretTypeOpaque,
  1084. Data: map[string]string{
  1085. targetProp: targetPropObj,
  1086. tplStaticKey: tplStaticVal,
  1087. },
  1088. }
  1089. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1090. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  1091. // check values
  1092. Expect(string(secret.Data[targetProp])).To(Equal(expectedSecretVal))
  1093. Expect(string(secret.Data[tplStaticKey])).To(Equal(tplStaticVal))
  1094. // labels/annotations/finalizers should be taken from the template
  1095. for k, v := range es.Spec.Target.Template.Metadata.Labels {
  1096. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(k, v))
  1097. }
  1098. // a secret will always have some extra annotations (i.e. hashmap check), so we only check for specific
  1099. // source annotations
  1100. for k, v := range es.Spec.Target.Template.Metadata.Annotations {
  1101. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(k, v))
  1102. }
  1103. for _, v := range es.Spec.Target.Template.Metadata.Finalizers {
  1104. Expect(secret.ObjectMeta.Finalizers).To(ContainElement(v))
  1105. }
  1106. cleanEs := tc.externalSecret.DeepCopy()
  1107. // now update ExternalSecret
  1108. tc.externalSecret.Spec.Target.Template.Metadata.Annotations["fuzz"] = "buzz"
  1109. tc.externalSecret.Spec.Target.Template.Metadata.Labels["fuzz"] = "buzz"
  1110. tc.externalSecret.Spec.Target.Template.Data["new"] = "value"
  1111. Expect(k8sClient.Patch(context.Background(), tc.externalSecret, client.MergeFrom(cleanEs))).To(Succeed())
  1112. // wait for secret
  1113. sec := &v1.Secret{}
  1114. secretLookupKey := types.NamespacedName{
  1115. Name: ExternalSecretTargetSecretName,
  1116. Namespace: ExternalSecretNamespace,
  1117. }
  1118. Eventually(func() bool {
  1119. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1120. if err != nil {
  1121. return false
  1122. }
  1123. // ensure new data value exist
  1124. return string(sec.Data["new"]) == "value"
  1125. }, time.Second*10, time.Millisecond*200).Should(BeTrue())
  1126. // also check labels/annotations have been updated
  1127. for k, v := range es.Spec.Target.Template.Metadata.Labels {
  1128. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(k, v))
  1129. }
  1130. for k, v := range es.Spec.Target.Template.Metadata.Annotations {
  1131. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(k, v))
  1132. }
  1133. }
  1134. }
  1135. onlyMetadataFromTemplate := func(tc *testCase) {
  1136. const secretVal = "someValue"
  1137. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1138. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  1139. Metadata: esv1.ExternalSecretTemplateMetadata{
  1140. Labels: map[string]string{"foo": "bar"},
  1141. Annotations: map[string]string{"foo": "bar"},
  1142. Finalizers: []string{"example.com/finalizer"},
  1143. },
  1144. }
  1145. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1146. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  1147. // check values
  1148. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1149. // labels/annotations/finalizers should be taken from the template
  1150. for k, v := range es.Spec.Target.Template.Metadata.Labels {
  1151. Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(k, v))
  1152. }
  1153. for k, v := range es.Spec.Target.Template.Metadata.Annotations {
  1154. Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(k, v))
  1155. }
  1156. for _, v := range es.Spec.Target.Template.Metadata.Finalizers {
  1157. Expect(secret.ObjectMeta.Finalizers).To(ContainElement(v))
  1158. }
  1159. }
  1160. }
  1161. // when the provider secret changes the Kind=Secret value
  1162. // must change, too.
  1163. refreshSecretValue := func(tc *testCase) {
  1164. const targetProp = "targetProperty"
  1165. const secretVal = "someValue"
  1166. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1167. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1168. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1169. // check values
  1170. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1171. // update provider secret
  1172. newValue := "NEW VALUE"
  1173. sec := &v1.Secret{}
  1174. fakeProvider.WithGetSecret([]byte(newValue), nil)
  1175. secretLookupKey := types.NamespacedName{
  1176. Name: ExternalSecretTargetSecretName,
  1177. Namespace: ExternalSecretNamespace,
  1178. }
  1179. Eventually(func() bool {
  1180. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1181. if err != nil {
  1182. return false
  1183. }
  1184. v := sec.Data[targetProp]
  1185. return string(v) == newValue
  1186. }, timeout, interval).Should(BeTrue())
  1187. }
  1188. }
  1189. // when a provider secret was deleted it must be deleted from
  1190. // the secret aswell
  1191. refreshSecretValueMap := func(tc *testCase) {
  1192. fakeProvider.WithGetSecretMap(map[string][]byte{
  1193. "foo": []byte("1111"),
  1194. "bar": []byte("2222"),
  1195. }, nil)
  1196. tc.externalSecret.Spec.Data = []esv1.ExternalSecretData{}
  1197. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1198. {
  1199. Extract: &esv1.ExternalSecretDataRemoteRef{
  1200. Key: remoteKey,
  1201. },
  1202. },
  1203. }
  1204. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1205. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1206. // check values
  1207. Expect(string(secret.Data["foo"])).To(Equal("1111"))
  1208. Expect(string(secret.Data["bar"])).To(Equal("2222"))
  1209. // update provider secret
  1210. sec := &v1.Secret{}
  1211. fakeProvider.WithGetSecretMap(map[string][]byte{
  1212. "foo": []byte("1111"),
  1213. }, nil)
  1214. secretLookupKey := types.NamespacedName{
  1215. Name: ExternalSecretTargetSecretName,
  1216. Namespace: ExternalSecretNamespace,
  1217. }
  1218. Eventually(func() bool {
  1219. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1220. if err != nil {
  1221. return false
  1222. }
  1223. return string(sec.Data["foo"]) == "1111" &&
  1224. sec.Data["bar"] == nil // must not be defined, it was deleted
  1225. }, timeout, interval).Should(BeTrue())
  1226. }
  1227. }
  1228. // when a provider secret was deleted it must be deleted from
  1229. // the secret aswell when using a template
  1230. refreshSecretValueMapTemplate := func(tc *testCase) {
  1231. fakeProvider.WithGetSecretMap(map[string][]byte{
  1232. "foo": []byte("1111"),
  1233. "bar": []byte("2222"),
  1234. }, nil)
  1235. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{}
  1236. tc.externalSecret.Spec.Data = []esv1.ExternalSecretData{}
  1237. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1238. {
  1239. Extract: &esv1.ExternalSecretDataRemoteRef{
  1240. Key: remoteKey,
  1241. },
  1242. },
  1243. }
  1244. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1245. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1246. // check values
  1247. Expect(string(secret.Data["foo"])).To(Equal("1111"))
  1248. Expect(string(secret.Data["bar"])).To(Equal("2222"))
  1249. // update provider secret
  1250. sec := &v1.Secret{}
  1251. fakeProvider.WithGetSecretMap(map[string][]byte{
  1252. "foo": []byte("1111"),
  1253. }, nil)
  1254. secretLookupKey := types.NamespacedName{
  1255. Name: ExternalSecretTargetSecretName,
  1256. Namespace: ExternalSecretNamespace,
  1257. }
  1258. Eventually(func() bool {
  1259. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1260. if err != nil {
  1261. return false
  1262. }
  1263. return string(sec.Data["foo"]) == "1111" &&
  1264. sec.Data["bar"] == nil // must not be defined, it was deleted
  1265. }, timeout, interval).Should(BeTrue())
  1266. }
  1267. }
  1268. refreshintervalZero := func(tc *testCase) {
  1269. const targetProp = "targetProperty"
  1270. const secretVal = "someValue"
  1271. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1272. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: 0}
  1273. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1274. // check values
  1275. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1276. // update provider secret
  1277. newValue := "NEW VALUE"
  1278. sec := &v1.Secret{}
  1279. fakeProvider.WithGetSecret([]byte(newValue), nil)
  1280. secretLookupKey := types.NamespacedName{
  1281. Name: ExternalSecretTargetSecretName,
  1282. Namespace: ExternalSecretNamespace,
  1283. }
  1284. Consistently(func() bool {
  1285. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1286. if err != nil {
  1287. return false
  1288. }
  1289. v := sec.Data[targetProp]
  1290. return string(v) == secretVal
  1291. }, time.Second*10, time.Second).Should(BeTrue())
  1292. }
  1293. }
  1294. deletionPolicyDelete := func(tc *testCase) {
  1295. expVal := []byte("1234")
  1296. // set initial value
  1297. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1298. "foo": expVal,
  1299. "bar": expVal,
  1300. }, nil)
  1301. tc.externalSecret.Spec.Data = nil
  1302. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1303. {
  1304. Find: &esv1.ExternalSecretFind{
  1305. Tags: map[string]string{},
  1306. },
  1307. },
  1308. }
  1309. tc.externalSecret.Spec.Target.DeletionPolicy = esv1.DeletionPolicyDelete
  1310. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1311. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1312. Expect(secret.Data["foo"]).To(Equal(expVal))
  1313. // update provider secret
  1314. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1315. "foo": expVal,
  1316. }, nil)
  1317. sec := &v1.Secret{}
  1318. secretLookupKey := types.NamespacedName{
  1319. Name: ExternalSecretTargetSecretName,
  1320. Namespace: ExternalSecretNamespace,
  1321. }
  1322. Eventually(func() bool {
  1323. By("checking secret value for foo=1234 and bar=nil")
  1324. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1325. if err != nil {
  1326. return false
  1327. }
  1328. return bytes.Equal(sec.Data["foo"], expVal) && sec.Data["bar"] == nil
  1329. }, time.Second*10, time.Second).Should(BeTrue())
  1330. // return specific delete err to indicate deletion
  1331. fakeProvider.WithGetAllSecrets(map[string][]byte{}, esv1.NoSecretErr)
  1332. Eventually(func() bool {
  1333. By("checking that secret has been deleted")
  1334. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1335. return apierrors.IsNotFound(err)
  1336. }, time.Second*10, time.Second).Should(BeTrue())
  1337. }
  1338. }
  1339. deletionPolicyRetain := func(tc *testCase) {
  1340. expVal := []byte("1234")
  1341. // set initial value
  1342. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1343. "foo": expVal,
  1344. "bar": expVal,
  1345. }, nil)
  1346. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1347. {
  1348. Find: &esv1.ExternalSecretFind{
  1349. Tags: map[string]string{},
  1350. },
  1351. },
  1352. }
  1353. tc.externalSecret.Spec.Target.DeletionPolicy = esv1.DeletionPolicyRetain
  1354. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1355. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1356. Expect(secret.Data["foo"]).To(Equal(expVal))
  1357. sec := &v1.Secret{}
  1358. secretLookupKey := types.NamespacedName{
  1359. Name: ExternalSecretTargetSecretName,
  1360. Namespace: ExternalSecretNamespace,
  1361. }
  1362. // return specific delete err to indicate deletion
  1363. // however this should not trigger a delete
  1364. fakeProvider.WithGetAllSecrets(map[string][]byte{}, esv1.NoSecretErr)
  1365. Consistently(func() bool {
  1366. By("checking that secret has not been deleted")
  1367. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1368. if err != nil {
  1369. GinkgoLogr.Error(err, "failed getting a secret")
  1370. return false
  1371. }
  1372. if got := sec.Data["foo"]; !bytes.Equal(got, expVal) {
  1373. GinkgoLogr.Info("received an unexpected secret value", "got", got, "expected", expVal)
  1374. return false
  1375. }
  1376. return true
  1377. }, time.Second*10, time.Second).Should(BeTrue())
  1378. }
  1379. }
  1380. deletionPolicyRetainEmptyData := func(tc *testCase) {
  1381. // set initial value
  1382. fakeProvider.WithGetAllSecrets(make(map[string][]byte), nil)
  1383. tc.externalSecret.Spec.Data = make([]esv1.ExternalSecretData, 0)
  1384. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1385. {
  1386. Find: &esv1.ExternalSecretFind{
  1387. Tags: map[string]string{
  1388. "non-existing-key": "non-existing-value",
  1389. },
  1390. },
  1391. },
  1392. }
  1393. tc.externalSecret.Spec.Target.DeletionPolicy = esv1.DeletionPolicyRetain
  1394. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1395. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1396. expected := []esv1.ExternalSecretStatusCondition{
  1397. {
  1398. Type: esv1.ExternalSecretReady,
  1399. Status: v1.ConditionTrue,
  1400. Reason: esv1.ConditionReasonSecretSynced,
  1401. Message: msgSyncedRetain,
  1402. },
  1403. }
  1404. opts := cmpopts.IgnoreFields(esv1.ExternalSecretStatusCondition{}, "LastTransitionTime")
  1405. if diff := cmp.Diff(expected, es.Status.Conditions, opts); diff != "" {
  1406. GinkgoLogr.Info("(-got, +want)\n%s", "diff", diff)
  1407. return false
  1408. }
  1409. return true
  1410. }
  1411. }
  1412. // merge with existing secret using creationPolicy=Merge
  1413. // if provider secret gets deleted only the managed field should get deleted
  1414. deletionPolicyMerge := func(tc *testCase) {
  1415. const secretVal = "someValue"
  1416. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1417. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  1418. tc.externalSecret.Spec.Target.DeletionPolicy = esv1.DeletionPolicyMerge
  1419. // create secret beforehand
  1420. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  1421. ObjectMeta: metav1.ObjectMeta{
  1422. Name: ExternalSecretTargetSecretName,
  1423. Namespace: ExternalSecretNamespace,
  1424. },
  1425. Data: map[string][]byte{
  1426. existingKey: []byte(existingVal),
  1427. },
  1428. }, client.FieldOwner(FakeManager))).To(Succeed())
  1429. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1430. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1431. // check value
  1432. Expect(string(secret.Data[existingKey])).To(Equal(existingVal))
  1433. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1434. sec := &v1.Secret{}
  1435. secretLookupKey := types.NamespacedName{
  1436. Name: ExternalSecretTargetSecretName,
  1437. Namespace: ExternalSecretNamespace,
  1438. }
  1439. // return specific delete err to indicate deletion
  1440. // however this should not trigger a delete
  1441. // instead expect that only the pre-existing value exists
  1442. fakeProvider.WithGetSecret(nil, esv1.NoSecretErr)
  1443. Eventually(func() bool {
  1444. By("checking that secret has not been deleted and pre-existing key exists")
  1445. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1446. return !apierrors.IsNotFound(err) &&
  1447. len(sec.Data) == 1 &&
  1448. bytes.Equal(sec.Data[existingKey], []byte(existingVal))
  1449. }, time.Second*30, time.Second).Should(BeTrue())
  1450. }
  1451. }
  1452. // orphan the secret after the external secret has been deleted
  1453. createSecretPolicyOrphan := func(tc *testCase) {
  1454. const secretVal = "someValue"
  1455. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
  1456. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyOrphan
  1457. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1458. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1459. // check value
  1460. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1461. sec := &v1.Secret{}
  1462. secretLookupKey := types.NamespacedName{
  1463. Name: ExternalSecretTargetSecretName,
  1464. Namespace: ExternalSecretNamespace,
  1465. }
  1466. err := k8sClient.Delete(context.Background(), tc.externalSecret)
  1467. Expect(err).ToNot(HaveOccurred())
  1468. Consistently(func() bool {
  1469. By("checking that secret has not been deleted")
  1470. err := k8sClient.Get(context.Background(), secretLookupKey, sec)
  1471. return !apierrors.IsNotFound(err)
  1472. }, time.Second*15, time.Second).Should(BeTrue())
  1473. }
  1474. }
  1475. // with rewrite all keys from a dataFrom operation
  1476. // should be put with new rewriting into the secret
  1477. syncAndRewriteWithDataFrom := func(tc *testCase) {
  1478. tc.externalSecret.Spec.Data = nil
  1479. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1480. {
  1481. Extract: &esv1.ExternalSecretDataRemoteRef{
  1482. Key: remoteKey,
  1483. },
  1484. Rewrite: []esv1.ExternalSecretRewrite{{
  1485. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1486. Source: "(.*)",
  1487. Target: "new-$1",
  1488. },
  1489. }},
  1490. },
  1491. {
  1492. Extract: &esv1.ExternalSecretDataRemoteRef{
  1493. Key: remoteKey,
  1494. },
  1495. Rewrite: []esv1.ExternalSecretRewrite{{
  1496. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1497. Source: "(.*)",
  1498. Target: "old-$1",
  1499. },
  1500. }},
  1501. },
  1502. }
  1503. fakeProvider.WithGetSecretMap(map[string][]byte{
  1504. "foo": []byte(FooValue),
  1505. "bar": []byte(BarValue),
  1506. }, nil)
  1507. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1508. // check values
  1509. Expect(string(secret.Data["new-foo"])).To(Equal(FooValue))
  1510. Expect(string(secret.Data["new-bar"])).To(Equal(BarValue))
  1511. Expect(string(secret.Data["old-foo"])).To(Equal(FooValue))
  1512. Expect(string(secret.Data["old-bar"])).To(Equal(BarValue))
  1513. }
  1514. }
  1515. // with rewrite keys from dataFrom
  1516. // should error if keys are not compliant
  1517. invalidExtractKeysErrCondition := func(tc *testCase) {
  1518. tc.externalSecret.Spec.Data = nil
  1519. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1520. {
  1521. Extract: &esv1.ExternalSecretDataRemoteRef{
  1522. Key: remoteKey,
  1523. },
  1524. Rewrite: []esv1.ExternalSecretRewrite{{
  1525. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1526. Source: "(.*)",
  1527. Target: "$1",
  1528. },
  1529. }},
  1530. },
  1531. }
  1532. fakeProvider.WithGetSecretMap(map[string][]byte{
  1533. "foo/bar": []byte(FooValue),
  1534. "bar/foo": []byte(BarValue),
  1535. }, nil)
  1536. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1537. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1538. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1539. return false
  1540. }
  1541. return true
  1542. }
  1543. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1544. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1545. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1546. return false
  1547. }
  1548. return true
  1549. }
  1550. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  1551. Eventually(func() bool {
  1552. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  1553. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  1554. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  1555. }, timeout, interval).Should(BeTrue())
  1556. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
  1557. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1558. }
  1559. }
  1560. // with rewrite keys from dataFrom
  1561. // should error if keys are not compliant
  1562. invalidFindKeysErrCondition := func(tc *testCase) {
  1563. tc.externalSecret.Spec.Data = nil
  1564. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1565. {
  1566. Find: &esv1.ExternalSecretFind{
  1567. Name: &esv1.FindName{
  1568. RegExp: ".*",
  1569. },
  1570. },
  1571. Rewrite: []esv1.ExternalSecretRewrite{{
  1572. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1573. Source: "(.*)",
  1574. Target: "$1",
  1575. },
  1576. }},
  1577. },
  1578. }
  1579. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1580. "foo/bar": []byte(FooValue),
  1581. "bar/foo": []byte(BarValue),
  1582. }, nil)
  1583. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1584. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1585. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1586. return false
  1587. }
  1588. return true
  1589. }
  1590. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1591. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1592. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1593. return false
  1594. }
  1595. return true
  1596. }
  1597. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  1598. Eventually(func() bool {
  1599. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  1600. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  1601. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  1602. }, timeout, interval).Should(BeTrue())
  1603. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
  1604. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1605. }
  1606. }
  1607. // with dataFrom all properties from the specified secret
  1608. // should be put into the secret
  1609. syncWithDataFrom := func(tc *testCase) {
  1610. tc.externalSecret.Spec.Data = nil
  1611. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1612. {
  1613. Extract: &esv1.ExternalSecretDataRemoteRef{
  1614. Key: remoteKey,
  1615. },
  1616. },
  1617. }
  1618. fakeProvider.WithGetSecretMap(map[string][]byte{
  1619. "foo": []byte(FooValue),
  1620. "bar": []byte(BarValue),
  1621. }, nil)
  1622. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1623. // check values
  1624. Expect(string(secret.Data["foo"])).To(Equal(FooValue))
  1625. Expect(string(secret.Data["bar"])).To(Equal(BarValue))
  1626. }
  1627. }
  1628. // with dataFrom.Find the change is on the called method GetAllSecrets
  1629. // all keys should be put into the secret
  1630. syncAndRewriteDataFromFind := func(tc *testCase) {
  1631. tc.externalSecret.Spec.Data = nil
  1632. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1633. {
  1634. Find: &esv1.ExternalSecretFind{
  1635. Name: &esv1.FindName{
  1636. RegExp: "foobar",
  1637. },
  1638. },
  1639. Rewrite: []esv1.ExternalSecretRewrite{
  1640. {
  1641. Regexp: &esv1.ExternalSecretRewriteRegexp{
  1642. Source: "(.*)",
  1643. Target: "new-$1",
  1644. },
  1645. },
  1646. },
  1647. },
  1648. }
  1649. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1650. "foo": []byte(FooValue),
  1651. "bar": []byte(BarValue),
  1652. }, nil)
  1653. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1654. // check values
  1655. Expect(string(secret.Data["new-foo"])).To(Equal(FooValue))
  1656. Expect(string(secret.Data["new-bar"])).To(Equal(BarValue))
  1657. }
  1658. }
  1659. // with dataFrom.Find the change is on the called method GetAllSecrets
  1660. // all keys should be put into the secret
  1661. syncDataFromFind := func(tc *testCase) {
  1662. tc.externalSecret.Spec.Data = nil
  1663. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1664. {
  1665. Find: &esv1.ExternalSecretFind{
  1666. Name: &esv1.FindName{
  1667. RegExp: "foobar",
  1668. },
  1669. },
  1670. },
  1671. }
  1672. fakeProvider.WithGetAllSecrets(map[string][]byte{
  1673. "foo": []byte(FooValue),
  1674. "bar": []byte(BarValue),
  1675. }, nil)
  1676. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1677. // check values
  1678. Expect(string(secret.Data["foo"])).To(Equal(FooValue))
  1679. Expect(string(secret.Data["bar"])).To(Equal(BarValue))
  1680. }
  1681. }
  1682. // with dataFrom and using a template
  1683. // should be put into the secret
  1684. syncWithDataFromTemplate := func(tc *testCase) {
  1685. tc.externalSecret.Spec.Data = nil
  1686. tc.externalSecret.Spec.Target = esv1.ExternalSecretTarget{
  1687. Name: ExternalSecretTargetSecretName,
  1688. Template: &esv1.ExternalSecretTemplate{
  1689. Type: v1.SecretTypeTLS,
  1690. },
  1691. }
  1692. tc.externalSecret.Spec.DataFrom = []esv1.ExternalSecretDataFromRemoteRef{
  1693. {
  1694. Extract: &esv1.ExternalSecretDataRemoteRef{
  1695. Key: remoteKey,
  1696. },
  1697. },
  1698. }
  1699. fakeProvider.WithGetSecretMap(map[string][]byte{
  1700. "tls.crt": []byte(FooValue),
  1701. "tls.key": []byte(BarValue),
  1702. }, nil)
  1703. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1704. Expect(secret.Type).To(Equal(v1.SecretTypeTLS))
  1705. // check values
  1706. Expect(string(secret.Data["tls.crt"])).To(Equal(FooValue))
  1707. Expect(string(secret.Data["tls.key"])).To(Equal(BarValue))
  1708. }
  1709. }
  1710. // when a provider errors in a GetSecret call
  1711. // a error condition must be set.
  1712. providerErrCondition := func(tc *testCase) {
  1713. const secretVal = "foobar"
  1714. fakeProvider.WithGetSecret(nil, errors.New("boom"))
  1715. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Millisecond * 100}
  1716. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1717. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1718. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1719. return false
  1720. }
  1721. return true
  1722. }
  1723. tc.checkExternalSecret = func(es *esv1.ExternalSecret) {
  1724. Eventually(func() bool {
  1725. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  1726. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  1727. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  1728. }, timeout, interval).Should(BeTrue())
  1729. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
  1730. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1731. // es condition should reflect recovered provider error
  1732. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1733. esKey := types.NamespacedName{Name: ExternalSecretName, Namespace: ExternalSecretNamespace}
  1734. Eventually(func() bool {
  1735. err := k8sClient.Get(context.Background(), esKey, es)
  1736. if err != nil {
  1737. return false
  1738. }
  1739. // condition must now be true!
  1740. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1741. if cond == nil && cond.Status != v1.ConditionTrue {
  1742. return false
  1743. }
  1744. return true
  1745. }, timeout, interval).Should(BeTrue())
  1746. }
  1747. }
  1748. // When a ExternalSecret references an non-existing SecretStore
  1749. // a error condition must be set.
  1750. storeMissingErrCondition := func(tc *testCase) {
  1751. tc.externalSecret.Spec.SecretStoreRef.Name = "nonexistent"
  1752. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1753. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1754. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1755. return false
  1756. }
  1757. return true
  1758. }
  1759. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  1760. Eventually(func() bool {
  1761. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  1762. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  1763. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  1764. }, timeout, interval).Should(BeTrue())
  1765. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
  1766. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1767. }
  1768. }
  1769. // when the provider constructor errors (e.g. invalid configuration)
  1770. // a SecretSyncedError status condition must be set
  1771. storeConstructErrCondition := func(tc *testCase) {
  1772. fakeProvider.WithNew(func(context.Context, esv1.GenericStore, client.Client,
  1773. string) (esv1.SecretsClient, error) {
  1774. return nil, errors.New("artificial constructor error")
  1775. })
  1776. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1777. // condition must be false
  1778. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1779. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  1780. return false
  1781. }
  1782. return true
  1783. }
  1784. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  1785. Eventually(func() bool {
  1786. Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
  1787. Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
  1788. return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
  1789. }, timeout, interval).Should(BeTrue())
  1790. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
  1791. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1792. }
  1793. }
  1794. // when a SecretStore has a controller field set which we don't care about
  1795. // the externalSecret must not be touched
  1796. ignoreMismatchController := func(tc *testCase) {
  1797. tc.secretStore.GetSpec().Controller = "nop"
  1798. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1799. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1800. return cond == nil
  1801. }
  1802. tc.checkExternalSecret = func(_ *esv1.ExternalSecret) {
  1803. // Condition True and False should be 0, since the Condition was not created
  1804. Eventually(func() float64 {
  1805. Expect(
  1806. testExternalSecretCondition.WithLabelValues(ExternalSecretName, ExternalSecretNamespace, string(esv1.ExternalSecretReady), string(v1.ConditionTrue)).Write(&metric),
  1807. ).To(Succeed())
  1808. return metric.GetGauge().GetValue()
  1809. }, timeout, interval).Should(Equal(0.0))
  1810. Eventually(func() float64 {
  1811. Expect(
  1812. testExternalSecretCondition.WithLabelValues(ExternalSecretName, ExternalSecretNamespace, string(esv1.ExternalSecretReady), string(v1.ConditionFalse)).Write(&metric),
  1813. ).To(Succeed())
  1814. return metric.GetGauge().GetValue()
  1815. }, timeout, interval).Should(Equal(0.0))
  1816. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
  1817. Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
  1818. }
  1819. }
  1820. ignoreClusterSecretStoreWhenDisabled := func(tc *testCase) {
  1821. tc.externalSecret.Spec.SecretStoreRef.Kind = esv1.ClusterSecretStoreKind
  1822. Expect(shouldSkipClusterSecretStore(
  1823. &Reconciler{
  1824. ClusterSecretStoreEnabled: false,
  1825. },
  1826. tc.externalSecret,
  1827. )).To(BeTrue())
  1828. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  1829. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  1830. return cond == nil
  1831. }
  1832. }
  1833. // When the ownership is set to owner, and we delete a dependent child kind=secret
  1834. // it should be recreated without waiting for refresh interval
  1835. checkDeletion := func(tc *testCase) {
  1836. const secretVal = "someValue"
  1837. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1838. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
  1839. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1840. // check values
  1841. oldUID := secret.UID
  1842. Expect(oldUID).NotTo(BeEmpty())
  1843. // delete the related config
  1844. Expect(k8sClient.Delete(context.TODO(), secret))
  1845. var newSecret v1.Secret
  1846. secretLookupKey := types.NamespacedName{
  1847. Name: ExternalSecretTargetSecretName,
  1848. Namespace: ExternalSecretNamespace,
  1849. }
  1850. Eventually(func() bool {
  1851. err := k8sClient.Get(context.Background(), secretLookupKey, &newSecret)
  1852. if err != nil {
  1853. return false
  1854. }
  1855. // new secret should be a new, recreated object with a different UID
  1856. return newSecret.UID != oldUID
  1857. }, timeout, interval).Should(BeTrue())
  1858. }
  1859. }
  1860. // Checks that secret annotation has been written based on the data
  1861. checkSecretDataHashAnnotation := func(tc *testCase) {
  1862. const secretVal = "someValue"
  1863. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1864. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1865. expectedHash := esutils.ObjectHash(map[string][]byte{
  1866. targetProp: []byte(secretVal),
  1867. })
  1868. Expect(secret.Annotations[esv1.AnnotationDataHash]).To(Equal(expectedHash))
  1869. }
  1870. }
  1871. // Checks that secret annotation has been written based on the all the data for merge keys
  1872. checkMergeSecretDataHashAnnotation := func(tc *testCase) {
  1873. const secretVal = "someValue"
  1874. tc.externalSecret.Spec.Target.CreationPolicy = esv1.CreatePolicyMerge
  1875. // create secret beforehand
  1876. Expect(k8sClient.Create(context.Background(), &v1.Secret{
  1877. ObjectMeta: metav1.ObjectMeta{
  1878. Name: ExternalSecretTargetSecretName,
  1879. Namespace: ExternalSecretNamespace,
  1880. },
  1881. Data: map[string][]byte{
  1882. existingKey: []byte(existingVal),
  1883. },
  1884. }, client.FieldOwner(FakeManager))).To(Succeed())
  1885. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1886. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1887. expectedHash := esutils.ObjectHash(map[string][]byte{
  1888. existingKey: []byte(existingVal),
  1889. targetProp: []byte(secretVal),
  1890. })
  1891. Expect(secret.Annotations[esv1.AnnotationDataHash]).To(Equal(expectedHash))
  1892. }
  1893. }
  1894. // When we amend the created kind=secret, refresh operation should be run again regardless of refresh interval
  1895. checkSecretDataHashAnnotationChange := func(tc *testCase) {
  1896. fakeData := map[string][]byte{
  1897. "targetProperty": []byte(FooValue),
  1898. }
  1899. fakeProvider.WithGetSecretMap(fakeData, nil)
  1900. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
  1901. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1902. oldHash := secret.Annotations[esv1.AnnotationDataHash]
  1903. oldResourceVersion := secret.ResourceVersion
  1904. Expect(oldHash).NotTo(BeEmpty())
  1905. cleanSecret := secret.DeepCopy()
  1906. secret.Data["new"] = []byte("value")
  1907. secret.ObjectMeta.Annotations[esv1.AnnotationDataHash] = "thisiswronghash"
  1908. Expect(k8sClient.Patch(context.Background(), secret, client.MergeFrom(cleanSecret))).To(Succeed())
  1909. var refreshedSecret v1.Secret
  1910. secretLookupKey := types.NamespacedName{
  1911. Name: ExternalSecretTargetSecretName,
  1912. Namespace: ExternalSecretNamespace,
  1913. }
  1914. Eventually(func() bool {
  1915. err := k8sClient.Get(context.Background(), secretLookupKey, &refreshedSecret)
  1916. if err != nil {
  1917. return false
  1918. }
  1919. // refreshed secret should have a different generation (sign that it was updated), but since
  1920. // the secret source is the same (not changed), the hash should be reverted to an old value
  1921. return refreshedSecret.ResourceVersion != oldResourceVersion && refreshedSecret.Annotations[esv1.AnnotationDataHash] == oldHash
  1922. }, timeout, interval).Should(BeTrue())
  1923. }
  1924. }
  1925. // When we update the template, remaining keys should not be preserved
  1926. templateShouldRewrite := func(tc *testCase) {
  1927. const secretVal = "someValue"
  1928. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1929. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
  1930. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  1931. Data: map[string]string{
  1932. "key": `{{.targetProperty}}-foo`,
  1933. },
  1934. }
  1935. tc.checkSecret = func(es *esv1.ExternalSecret, secret *v1.Secret) {
  1936. Expect(secret.Data["key"]).To(Equal([]byte("someValue-foo")))
  1937. newEs := es.DeepCopy()
  1938. newEs.Spec.Target.Template.Data = map[string]string{
  1939. "new": "foo",
  1940. }
  1941. Expect(k8sClient.Patch(context.Background(), newEs, client.MergeFrom(es))).To(Succeed())
  1942. var refreshedSecret v1.Secret
  1943. secretLookupKey := types.NamespacedName{
  1944. Name: ExternalSecretTargetSecretName,
  1945. Namespace: ExternalSecretNamespace,
  1946. }
  1947. Eventually(func() bool {
  1948. err := k8sClient.Get(context.Background(), secretLookupKey, &refreshedSecret)
  1949. if err != nil {
  1950. return false
  1951. }
  1952. // ensure new data value exist
  1953. return string(refreshedSecret.Data["new"]) == "foo"
  1954. }, time.Second*10, time.Millisecond*200).Should(BeTrue())
  1955. }
  1956. }
  1957. // When we update the template, remaining keys should not be preserved
  1958. templateShouldMerge := func(tc *testCase) {
  1959. const secretVal = "someValue"
  1960. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1961. tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
  1962. tc.externalSecret.Spec.Target.Template = &esv1.ExternalSecretTemplate{
  1963. MergePolicy: esv1.MergePolicyMerge,
  1964. Data: map[string]string{
  1965. "key": `{{.targetProperty}}-foo`,
  1966. },
  1967. }
  1968. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1969. Expect(secret.Data["key"]).To(Equal([]byte("someValue-foo")))
  1970. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1971. }
  1972. }
  1973. useClusterSecretStore := func(tc *testCase) {
  1974. tc.secretStore = &esv1.ClusterSecretStore{
  1975. ObjectMeta: metav1.ObjectMeta{
  1976. Name: ExternalSecretStore,
  1977. },
  1978. Spec: esv1.SecretStoreSpec{
  1979. Provider: &esv1.SecretStoreProvider{
  1980. AWS: &esv1.AWSProvider{
  1981. Service: esv1.AWSServiceSecretsManager,
  1982. },
  1983. },
  1984. },
  1985. }
  1986. tc.externalSecret.Spec.SecretStoreRef.Kind = esv1.ClusterSecretStoreKind
  1987. fakeProvider.WithGetSecret([]byte(secretVal), nil)
  1988. }
  1989. // Secret is created when ClusterSecretStore has no conditions
  1990. noConditionsSecretCreated := func(tc *testCase) {
  1991. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  1992. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  1993. }
  1994. }
  1995. // Secret is not created when ClusterSecretStore has a single non-matching string condition
  1996. noSecretCreatedWhenNamespaceDoesntMatchStringCondition := func(tc *testCase) {
  1997. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  1998. {
  1999. Namespaces: []string{"some-other-ns"},
  2000. },
  2001. }
  2002. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2003. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2004. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2005. return false
  2006. }
  2007. return true
  2008. }
  2009. }
  2010. // Secret is not created when ClusterSecretStore has a single non-matching string condition with multiple names
  2011. noSecretCreatedWhenNamespaceDoesntMatchStringConditionWithMultipleNames := func(tc *testCase) {
  2012. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2013. {
  2014. Namespaces: []string{"some-other-ns", "another-ns"},
  2015. },
  2016. }
  2017. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2018. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2019. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2020. return false
  2021. }
  2022. return true
  2023. }
  2024. }
  2025. // Secret is not created when ClusterSecretStore has a multiple non-matching string condition
  2026. noSecretCreatedWhenNamespaceDoesntMatchMultipleStringCondition := func(tc *testCase) {
  2027. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2028. {
  2029. Namespaces: []string{"some-other-ns"},
  2030. },
  2031. {
  2032. Namespaces: []string{"another-ns"},
  2033. },
  2034. }
  2035. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2036. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2037. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2038. return false
  2039. }
  2040. return true
  2041. }
  2042. }
  2043. // Secret is created when ClusterSecretStore has a single matching string condition
  2044. secretCreatedWhenNamespaceMatchesSingleStringCondition := func(tc *testCase) {
  2045. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2046. {
  2047. Namespaces: []string{ExternalSecretNamespace},
  2048. },
  2049. }
  2050. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  2051. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  2052. }
  2053. }
  2054. // Secret is created when ClusterSecretStore has a multiple string conditions, one matching
  2055. secretCreatedWhenNamespaceMatchesMultipleStringConditions := func(tc *testCase) {
  2056. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2057. {
  2058. Namespaces: []string{ExternalSecretNamespace, "some-other-ns"},
  2059. },
  2060. }
  2061. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  2062. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  2063. }
  2064. }
  2065. // Secret is not created when ClusterSecretStore has a single non-matching label condition
  2066. noSecretCreatedWhenNamespaceDoesntMatchLabelCondition := func(tc *testCase) {
  2067. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2068. {
  2069. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"some-label-key": "some-label-value"}},
  2070. },
  2071. }
  2072. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2073. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2074. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2075. return false
  2076. }
  2077. return true
  2078. }
  2079. }
  2080. // Secret is created when ClusterSecretStore has a single matching label condition
  2081. secretCreatedWhenNamespaceMatchOnlyLabelCondition := func(tc *testCase) {
  2082. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2083. {
  2084. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{NamespaceLabelKey: NamespaceLabelValue}},
  2085. },
  2086. }
  2087. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  2088. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  2089. }
  2090. }
  2091. // Secret is not created when ClusterSecretStore has a partially matching label condition
  2092. noSecretCreatedWhenNamespacePartiallyMatchLabelCondition := func(tc *testCase) {
  2093. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2094. {
  2095. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{NamespaceLabelKey: NamespaceLabelValue, "some-label-key": "some-label-value"}},
  2096. },
  2097. }
  2098. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2099. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2100. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2101. return false
  2102. }
  2103. return true
  2104. }
  2105. }
  2106. // Secret is created when ClusterSecretStore has at least one matching label condition
  2107. secretCreatedWhenNamespaceMatchOneLabelCondition := func(tc *testCase) {
  2108. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2109. {
  2110. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{NamespaceLabelKey: NamespaceLabelValue}},
  2111. },
  2112. {
  2113. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"some-label-key": "some-label-value"}},
  2114. },
  2115. }
  2116. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  2117. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  2118. }
  2119. }
  2120. // Secret is created when ClusterSecretStore has multiple matching conditions
  2121. secretCreatedWhenNamespaceMatchMultipleConditions := func(tc *testCase) {
  2122. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2123. {
  2124. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{NamespaceLabelKey: NamespaceLabelValue}},
  2125. },
  2126. {
  2127. Namespaces: []string{ExternalSecretNamespace},
  2128. },
  2129. }
  2130. tc.checkSecret = func(_ *esv1.ExternalSecret, secret *v1.Secret) {
  2131. Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
  2132. }
  2133. }
  2134. // Secret is not created when ClusterSecretStore has multiple non-matching conditions
  2135. noSecretCreatedWhenNamespaceMatchMultipleNonMatchingConditions := func(tc *testCase) {
  2136. tc.secretStore.GetSpec().Conditions = []esv1.ClusterSecretStoreCondition{
  2137. {
  2138. NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"some-label-key": "some-label-value"}},
  2139. },
  2140. {
  2141. Namespaces: []string{"some-other-ns"},
  2142. },
  2143. }
  2144. tc.checkCondition = func(es *esv1.ExternalSecret) bool {
  2145. cond := GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  2146. if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1.ConditionReasonSecretSyncedError {
  2147. return false
  2148. }
  2149. return true
  2150. }
  2151. }
  2152. DescribeTable(
  2153. "When reconciling an ExternalSecret",
  2154. func(tweaks ...testTweaks) {
  2155. tc := makeDefaultTestcase()
  2156. for _, tweak := range tweaks {
  2157. tweak(tc)
  2158. }
  2159. ctx := context.Background()
  2160. By("creating a secret store and external secret")
  2161. Expect(k8sClient.Create(ctx, tc.secretStore)).To(Succeed())
  2162. Expect(k8sClient.Create(ctx, tc.externalSecret)).Should(Succeed())
  2163. esKey := types.NamespacedName{Name: ExternalSecretName, Namespace: ExternalSecretNamespace}
  2164. createdES := &esv1.ExternalSecret{}
  2165. By("checking the es condition")
  2166. Eventually(func() bool {
  2167. err := k8sClient.Get(ctx, esKey, createdES)
  2168. if err != nil {
  2169. return false
  2170. }
  2171. return tc.checkCondition(createdES)
  2172. }, timeout, interval).Should(BeTrue())
  2173. tc.checkExternalSecret(createdES)
  2174. // this must be optional so we can test faulty es configuration
  2175. if tc.checkSecret != nil {
  2176. syncedSecret := &v1.Secret{}
  2177. secretLookupKey := types.NamespacedName{
  2178. Name: tc.targetSecretName,
  2179. Namespace: ExternalSecretNamespace,
  2180. }
  2181. if createdES.Spec.Target.Name == "" {
  2182. secretLookupKey = types.NamespacedName{
  2183. Name: ExternalSecretName,
  2184. Namespace: ExternalSecretNamespace,
  2185. }
  2186. }
  2187. Eventually(func() bool {
  2188. err := k8sClient.Get(ctx, secretLookupKey, syncedSecret)
  2189. return err == nil
  2190. }, timeout, interval).Should(BeTrue())
  2191. tc.checkSecret(createdES, syncedSecret)
  2192. }
  2193. },
  2194. Entry("should recreate deleted secret", checkDeletion),
  2195. Entry("should create proper hash annotation for the external secret", checkSecretDataHashAnnotation),
  2196. Entry("should create proper hash annotation for the external secret with creationPolicy=Merge", checkMergeSecretDataHashAnnotation),
  2197. Entry("es deletes orphaned secrets", deleteOrphanedSecrets),
  2198. Entry("should refresh when the hash annotation doesn't correspond to secret data", checkSecretDataHashAnnotationChange),
  2199. Entry("should use external secret name if target secret name isn't defined", syncWithoutTargetName),
  2200. Entry("should sync to target secrets with naming bigger than 63 characters", syncBigNames),
  2201. Entry("should expose the secret as a provisioned service binding secret", syncBindingSecret),
  2202. Entry("should not expose a provisioned service when no secret is synced", skipBindingSecret),
  2203. Entry("should set labels and annotations from the ExternalSecret", syncLabelsAnnotations),
  2204. Entry("should merge labels and annotations to the ones owned by other entity", mergeLabelsAnnotations),
  2205. Entry("should removed outdated labels and annotations", removeOutdatedLabelsAnnotations),
  2206. Entry("should set prometheus counters", checkPrometheusCounters),
  2207. Entry("should merge with existing secret using creationPolicy=Merge", mergeWithSecret),
  2208. Entry("should kick reconciliation when secret changes using creationPolicy=Merge", mergeWithSecretUpdate),
  2209. Entry("should error if secret doesn't exist when using creationPolicy=Merge", mergeWithSecretErr),
  2210. Entry("should not resolve conflicts with creationPolicy=Merge", mergeWithConflict),
  2211. Entry("should not update unchanged secret using creationPolicy=Merge", mergeWithSecretNoChange),
  2212. Entry("should not delete pre-existing secret with creationPolicy=Orphan", createSecretPolicyOrphan),
  2213. Entry("should sync cluster generator ref", syncWithClusterGeneratorRef),
  2214. Entry("should sync with generatorRef", syncWithGeneratorRef),
  2215. Entry("should not process generatorRef with mismatching controller field", ignoreMismatchControllerForGeneratorRef),
  2216. Entry("should sync with multiple secret stores via sourceRef", syncWithMultipleSecretStores),
  2217. Entry("should sync with template", syncWithTemplate),
  2218. Entry("should sync with template engine v2", syncWithTemplateV2),
  2219. Entry("should sync template with correct value precedence", syncWithTemplatePrecedence),
  2220. Entry("should sync template from keys and values", syncTemplateFromKeysAndValues),
  2221. Entry("should sync template from literal", syncTemplateFromLiteral),
  2222. Entry("should update template if ExternalSecret is updated", templateShouldRewrite),
  2223. Entry("should keep data with templates if MergePolicy=Merge", templateShouldMerge),
  2224. Entry("should refresh secret from template", refreshWithTemplate),
  2225. Entry("should be able to use only metadata from template", onlyMetadataFromTemplate),
  2226. Entry("should refresh secret value when provider secret changes", refreshSecretValue),
  2227. Entry("should refresh secret map when provider secret changes", refreshSecretValueMap),
  2228. Entry("should refresh secret map when provider secret changes when using a template", refreshSecretValueMapTemplate),
  2229. Entry("should not refresh secret value when provider secret changes but refreshInterval is zero", refreshintervalZero),
  2230. Entry("should fetch secret using dataFrom", syncWithDataFrom),
  2231. Entry("should rewrite secret using dataFrom", syncAndRewriteWithDataFrom),
  2232. Entry("should not automatically convert from extract if rewrite is used", invalidExtractKeysErrCondition),
  2233. Entry("should fetch secret using dataFrom.find", syncDataFromFind),
  2234. Entry("should rewrite secret using dataFrom.find", syncAndRewriteDataFromFind),
  2235. Entry("should not automatically convert from find if rewrite is used", invalidFindKeysErrCondition),
  2236. Entry("should fetch secret using dataFrom and a template", syncWithDataFromTemplate),
  2237. Entry("should set error condition when provider errors", providerErrCondition),
  2238. Entry("should set an error condition when store does not exist", storeMissingErrCondition),
  2239. Entry("should set an error condition when store provider constructor fails", storeConstructErrCondition),
  2240. Entry("should not process store with mismatching controller field", ignoreMismatchController),
  2241. Entry("should not process cluster secret store when it is disabled", ignoreClusterSecretStoreWhenDisabled),
  2242. Entry("should eventually delete target secret with deletionPolicy=Delete", deletionPolicyDelete),
  2243. Entry("should not delete target secret with deletionPolicy=Retain", deletionPolicyRetain),
  2244. Entry("should update the status properly even if the deletionPolicy is Retain and the data is empty", deletionPolicyRetainEmptyData),
  2245. Entry("should not delete pre-existing secret with deletionPolicy=Merge", deletionPolicyMerge),
  2246. Entry("secret is created when there are no conditions for the cluster secret store", useClusterSecretStore, noConditionsSecretCreated),
  2247. Entry(
  2248. "secret is not created when the condition for the cluster secret store states a different namespace single string condition",
  2249. useClusterSecretStore,
  2250. noSecretCreatedWhenNamespaceDoesntMatchStringCondition,
  2251. ),
  2252. Entry(
  2253. "secret is not created when the condition for the cluster secret store states a different namespace single string condition with multiple names",
  2254. useClusterSecretStore,
  2255. noSecretCreatedWhenNamespaceDoesntMatchStringConditionWithMultipleNames,
  2256. ),
  2257. Entry(
  2258. "secret is not created when the condition for the cluster secret store states a different namespace multiple string conditions",
  2259. useClusterSecretStore,
  2260. noSecretCreatedWhenNamespaceDoesntMatchMultipleStringCondition,
  2261. ),
  2262. Entry(
  2263. "secret is created when the condition for the cluster secret store has only one matching namespace by string condition",
  2264. useClusterSecretStore,
  2265. secretCreatedWhenNamespaceMatchesSingleStringCondition,
  2266. ),
  2267. Entry(
  2268. "secret is created when the condition for the cluster secret store has one matching namespace of multiple namespaces by string condition",
  2269. useClusterSecretStore,
  2270. secretCreatedWhenNamespaceMatchesMultipleStringConditions,
  2271. ),
  2272. Entry(
  2273. "secret is not created when the condition for the cluster secret store states a non-matching label condition",
  2274. useClusterSecretStore,
  2275. noSecretCreatedWhenNamespaceDoesntMatchLabelCondition,
  2276. ),
  2277. Entry("secret is created when the condition for the cluster secret store states a single matching label condition", useClusterSecretStore, secretCreatedWhenNamespaceMatchOnlyLabelCondition),
  2278. Entry(
  2279. "secret is not created when the condition for the cluster secret store states a partially-matching label condition",
  2280. useClusterSecretStore,
  2281. noSecretCreatedWhenNamespacePartiallyMatchLabelCondition,
  2282. ),
  2283. Entry("secret is created when one of the label conditions for the cluster secret store matches", useClusterSecretStore, secretCreatedWhenNamespaceMatchOneLabelCondition),
  2284. Entry("secret is created when the namespaces matches multiple cluster secret store conditions", useClusterSecretStore, secretCreatedWhenNamespaceMatchMultipleConditions),
  2285. Entry(
  2286. "secret is not created when the namespaces doesn't match any of multiple cluster secret store conditions",
  2287. useClusterSecretStore,
  2288. noSecretCreatedWhenNamespaceMatchMultipleNonMatchingConditions,
  2289. ),
  2290. )
  2291. })
  2292. var _ = Describe("ExternalSecret refresh logic", func() {
  2293. Context("secret refresh", func() {
  2294. It("should refresh when resource version does not match", func() {
  2295. Expect(shouldRefresh(&esv1.ExternalSecret{
  2296. Spec: esv1.ExternalSecretSpec{
  2297. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2298. },
  2299. Status: esv1.ExternalSecretStatus{
  2300. SyncedResourceVersion: "some resource version",
  2301. },
  2302. })).To(BeTrue())
  2303. })
  2304. It("should refresh when labels change", func() {
  2305. es := &esv1.ExternalSecret{
  2306. ObjectMeta: metav1.ObjectMeta{
  2307. Generation: 1,
  2308. Labels: map[string]string{
  2309. "foo": "bar",
  2310. },
  2311. },
  2312. Spec: esv1.ExternalSecretSpec{
  2313. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2314. },
  2315. Status: esv1.ExternalSecretStatus{
  2316. RefreshTime: metav1.Now(),
  2317. },
  2318. }
  2319. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2320. // this should not refresh, rv matches object
  2321. Expect(shouldRefresh(es)).To(BeFalse())
  2322. // change labels without changing the syncedResourceVersion and expect refresh
  2323. es.ObjectMeta.Labels["new"] = "w00t"
  2324. Expect(shouldRefresh(es)).To(BeTrue())
  2325. })
  2326. It("should refresh when annotations change", func() {
  2327. es := &esv1.ExternalSecret{
  2328. ObjectMeta: metav1.ObjectMeta{
  2329. Generation: 1,
  2330. Annotations: map[string]string{
  2331. "foo": "bar",
  2332. },
  2333. },
  2334. Spec: esv1.ExternalSecretSpec{
  2335. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2336. },
  2337. Status: esv1.ExternalSecretStatus{
  2338. RefreshTime: metav1.Now(),
  2339. },
  2340. }
  2341. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2342. // this should not refresh, rv matches object
  2343. Expect(shouldRefresh(es)).To(BeFalse())
  2344. // change annotations without changing the syncedResourceVersion and expect refresh
  2345. es.ObjectMeta.Annotations["new"] = "w00t"
  2346. Expect(shouldRefresh(es)).To(BeTrue())
  2347. })
  2348. It("should refresh when generation has changed", func() {
  2349. es := &esv1.ExternalSecret{
  2350. ObjectMeta: metav1.ObjectMeta{
  2351. Generation: 1,
  2352. },
  2353. Spec: esv1.ExternalSecretSpec{
  2354. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2355. },
  2356. Status: esv1.ExternalSecretStatus{
  2357. RefreshTime: metav1.Now(),
  2358. },
  2359. }
  2360. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2361. Expect(shouldRefresh(es)).To(BeFalse())
  2362. // update gen -> refresh
  2363. es.ObjectMeta.Generation = 2
  2364. Expect(shouldRefresh(es)).To(BeTrue())
  2365. })
  2366. It("should skip refresh when refreshInterval is 0", func() {
  2367. es := &esv1.ExternalSecret{
  2368. ObjectMeta: metav1.ObjectMeta{
  2369. Generation: 1,
  2370. },
  2371. Spec: esv1.ExternalSecretSpec{
  2372. RefreshInterval: &metav1.Duration{Duration: 0},
  2373. },
  2374. Status: esv1.ExternalSecretStatus{},
  2375. }
  2376. // resource version matches
  2377. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2378. Expect(shouldRefresh(es)).To(BeFalse())
  2379. })
  2380. It("should refresh when refresh interval has passed", func() {
  2381. es := &esv1.ExternalSecret{
  2382. ObjectMeta: metav1.ObjectMeta{
  2383. Generation: 1,
  2384. },
  2385. Spec: esv1.ExternalSecretSpec{
  2386. RefreshInterval: &metav1.Duration{Duration: time.Second},
  2387. },
  2388. Status: esv1.ExternalSecretStatus{
  2389. RefreshTime: metav1.NewTime(metav1.Now().Add(-time.Second * 5)),
  2390. },
  2391. }
  2392. // resource version matches
  2393. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2394. Expect(shouldRefresh(es)).To(BeTrue())
  2395. })
  2396. It("should refresh when no refresh time was set", func() {
  2397. es := &esv1.ExternalSecret{
  2398. ObjectMeta: metav1.ObjectMeta{
  2399. Generation: 1,
  2400. },
  2401. Spec: esv1.ExternalSecretSpec{
  2402. RefreshInterval: &metav1.Duration{Duration: time.Second},
  2403. },
  2404. Status: esv1.ExternalSecretStatus{},
  2405. }
  2406. // resource version matches
  2407. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2408. Expect(shouldRefresh(es)).To(BeTrue())
  2409. })
  2410. })
  2411. Context("objectmeta hash", func() {
  2412. It("should produce different hashes for different k/v pairs", func() {
  2413. h1 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2414. Generation: 1,
  2415. Annotations: map[string]string{
  2416. "foo": "bar",
  2417. },
  2418. })
  2419. h2 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2420. Generation: 1,
  2421. Annotations: map[string]string{
  2422. "foo": "bing",
  2423. },
  2424. })
  2425. Expect(h1).ToNot(Equal(h2))
  2426. })
  2427. It("should produce different hashes for different generations but same label/annotations", func() {
  2428. h1 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2429. Generation: 1,
  2430. Annotations: map[string]string{
  2431. "foo": "bar",
  2432. },
  2433. Labels: map[string]string{
  2434. "foo": "bar",
  2435. },
  2436. })
  2437. h2 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2438. Generation: 2,
  2439. Annotations: map[string]string{
  2440. "foo": "bar",
  2441. },
  2442. Labels: map[string]string{
  2443. "foo": "bar",
  2444. },
  2445. })
  2446. Expect(h1).To(Equal(h2))
  2447. })
  2448. It("should produce the same hash for the same k/v pairs", func() {
  2449. h1 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2450. Generation: 1,
  2451. })
  2452. h2 := ctrlutil.HashMeta(metav1.ObjectMeta{
  2453. Generation: 1,
  2454. })
  2455. Expect(h1).To(Equal(h2))
  2456. h1 = ctrlutil.HashMeta(metav1.ObjectMeta{
  2457. Generation: 1,
  2458. Annotations: map[string]string{
  2459. "foo": "bar",
  2460. },
  2461. })
  2462. h2 = ctrlutil.HashMeta(metav1.ObjectMeta{
  2463. Generation: 1,
  2464. Annotations: map[string]string{
  2465. "foo": "bar",
  2466. },
  2467. })
  2468. Expect(h1).To(Equal(h2))
  2469. })
  2470. })
  2471. })
  2472. var _ = Describe("ExternalSecret refresh policy", func() {
  2473. Context("RefreshPolicy=CreatedOnce", func() {
  2474. It("should refresh when SyncedResourceVersion is empty", func() {
  2475. es := &esv1.ExternalSecret{
  2476. Spec: esv1.ExternalSecretSpec{
  2477. RefreshPolicy: esv1.RefreshPolicyCreatedOnce,
  2478. },
  2479. Status: esv1.ExternalSecretStatus{
  2480. SyncedResourceVersion: "",
  2481. },
  2482. }
  2483. Expect(shouldRefresh(es)).To(BeTrue())
  2484. })
  2485. It("should refresh when RefreshTime is zero", func() {
  2486. es := &esv1.ExternalSecret{
  2487. Spec: esv1.ExternalSecretSpec{
  2488. RefreshPolicy: esv1.RefreshPolicyCreatedOnce,
  2489. },
  2490. Status: esv1.ExternalSecretStatus{
  2491. SyncedResourceVersion: "some-version",
  2492. RefreshTime: metav1.Time{},
  2493. },
  2494. }
  2495. Expect(shouldRefresh(es)).To(BeTrue())
  2496. })
  2497. It("should not refresh when already synced", func() {
  2498. es := &esv1.ExternalSecret{
  2499. Spec: esv1.ExternalSecretSpec{
  2500. RefreshPolicy: esv1.RefreshPolicyCreatedOnce,
  2501. },
  2502. Status: esv1.ExternalSecretStatus{
  2503. SyncedResourceVersion: "some-version",
  2504. RefreshTime: metav1.Now(),
  2505. },
  2506. }
  2507. Expect(shouldRefresh(es)).To(BeFalse())
  2508. })
  2509. })
  2510. Context("RefreshPolicy=OnChange", func() {
  2511. It("should refresh when SyncedResourceVersion is empty", func() {
  2512. es := &esv1.ExternalSecret{
  2513. Spec: esv1.ExternalSecretSpec{
  2514. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2515. },
  2516. Status: esv1.ExternalSecretStatus{
  2517. SyncedResourceVersion: "",
  2518. },
  2519. }
  2520. Expect(shouldRefresh(es)).To(BeTrue())
  2521. })
  2522. It("should refresh when RefreshTime is zero", func() {
  2523. es := &esv1.ExternalSecret{
  2524. Spec: esv1.ExternalSecretSpec{
  2525. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2526. },
  2527. Status: esv1.ExternalSecretStatus{
  2528. RefreshTime: metav1.Time{},
  2529. },
  2530. }
  2531. Expect(shouldRefresh(es)).To(BeTrue())
  2532. })
  2533. It("should refresh when resource version changes", func() {
  2534. es := &esv1.ExternalSecret{
  2535. ObjectMeta: metav1.ObjectMeta{
  2536. Generation: 1,
  2537. Annotations: map[string]string{
  2538. "foo": "bar",
  2539. },
  2540. },
  2541. Spec: esv1.ExternalSecretSpec{
  2542. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2543. },
  2544. Status: esv1.ExternalSecretStatus{
  2545. RefreshTime: metav1.Now(),
  2546. SyncedResourceVersion: "old-version",
  2547. },
  2548. }
  2549. // The temp annotation is added in the shouldRefresh function
  2550. Expect(shouldRefresh(es)).To(BeTrue())
  2551. })
  2552. It("should not refresh when resource version matches", func() {
  2553. es := &esv1.ExternalSecret{
  2554. ObjectMeta: metav1.ObjectMeta{
  2555. Generation: 1,
  2556. },
  2557. Spec: esv1.ExternalSecretSpec{
  2558. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2559. },
  2560. Status: esv1.ExternalSecretStatus{
  2561. RefreshTime: metav1.Now(),
  2562. },
  2563. }
  2564. // Set the synced resource version to match the current resource version
  2565. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2566. Expect(shouldRefresh(es)).To(BeFalse())
  2567. })
  2568. It("should refresh when annotations change", func() {
  2569. es := &esv1.ExternalSecret{
  2570. ObjectMeta: metav1.ObjectMeta{
  2571. Generation: 1,
  2572. Annotations: map[string]string{
  2573. "foo": "bar",
  2574. },
  2575. },
  2576. Spec: esv1.ExternalSecretSpec{
  2577. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2578. },
  2579. Status: esv1.ExternalSecretStatus{
  2580. RefreshTime: metav1.Now(),
  2581. },
  2582. }
  2583. // Set the synced resource version to match the current resource version
  2584. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2585. Expect(shouldRefresh(es)).To(BeFalse())
  2586. es.Annotations["foo"] = "bar1"
  2587. Expect(shouldRefresh(es)).To(BeTrue())
  2588. })
  2589. It("should refresh when spec changes", func() {
  2590. es := &esv1.ExternalSecret{
  2591. ObjectMeta: metav1.ObjectMeta{
  2592. Generation: 1,
  2593. },
  2594. Spec: esv1.ExternalSecretSpec{
  2595. RefreshPolicy: esv1.RefreshPolicyOnChange,
  2596. Data: []esv1.ExternalSecretData{
  2597. {
  2598. SecretKey: "key1",
  2599. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2600. Key: "remote-key1",
  2601. },
  2602. },
  2603. },
  2604. },
  2605. Status: esv1.ExternalSecretStatus{
  2606. RefreshTime: metav1.Now(),
  2607. },
  2608. }
  2609. // Set the synced resource version to match the current resource version
  2610. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2611. // Initially should not refresh
  2612. Expect(shouldRefresh(es)).To(BeFalse())
  2613. // Update the spec by adding a new data item
  2614. es.ObjectMeta.Generation = 2
  2615. es.Spec.Data = append(es.Spec.Data, esv1.ExternalSecretData{
  2616. SecretKey: "key2",
  2617. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2618. Key: "remote-key2",
  2619. },
  2620. })
  2621. // The temp annotation is added in the shouldRefresh function to track spec hash
  2622. Expect(shouldRefresh(es)).To(BeTrue())
  2623. })
  2624. })
  2625. Context("Default refresh policy (Periodic)", func() {
  2626. It("should behave like Periodic when RefreshPolicy is not set", func() {
  2627. es := &esv1.ExternalSecret{
  2628. ObjectMeta: metav1.ObjectMeta{
  2629. Generation: 1,
  2630. },
  2631. Spec: esv1.ExternalSecretSpec{
  2632. // No RefreshPolicy set, should default to Periodic behavior
  2633. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2634. },
  2635. Status: esv1.ExternalSecretStatus{
  2636. RefreshTime: metav1.Now(),
  2637. },
  2638. }
  2639. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2640. Expect(shouldRefresh(es)).To(BeFalse())
  2641. // When refresh interval has passed
  2642. es.Status.RefreshTime = metav1.NewTime(metav1.Now().Add(-time.Minute * 2))
  2643. Expect(shouldRefresh(es)).To(BeTrue())
  2644. })
  2645. })
  2646. Context("RefreshPolicy=Periodic", func() {
  2647. It("should not refresh when refreshInterval is 0 and already synced", func() {
  2648. es := &esv1.ExternalSecret{
  2649. ObjectMeta: metav1.ObjectMeta{
  2650. Generation: 1,
  2651. },
  2652. Spec: esv1.ExternalSecretSpec{
  2653. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2654. RefreshInterval: &metav1.Duration{Duration: 0},
  2655. },
  2656. Status: esv1.ExternalSecretStatus{
  2657. SyncedResourceVersion: "some-version",
  2658. },
  2659. }
  2660. // Set the synced resource version to match the current resource version
  2661. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2662. Expect(shouldRefresh(es)).To(BeFalse())
  2663. })
  2664. It("should refresh when resource version changes", func() {
  2665. es := &esv1.ExternalSecret{
  2666. ObjectMeta: metav1.ObjectMeta{
  2667. Generation: 2,
  2668. },
  2669. Spec: esv1.ExternalSecretSpec{
  2670. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2671. RefreshInterval: &metav1.Duration{Duration: time.Minute},
  2672. },
  2673. Status: esv1.ExternalSecretStatus{
  2674. RefreshTime: metav1.Now(),
  2675. SyncedResourceVersion: "old-version",
  2676. },
  2677. }
  2678. Expect(shouldRefresh(es)).To(BeTrue())
  2679. })
  2680. It("should refresh when refresh interval has passed", func() {
  2681. es := &esv1.ExternalSecret{
  2682. ObjectMeta: metav1.ObjectMeta{
  2683. Generation: 1,
  2684. },
  2685. Spec: esv1.ExternalSecretSpec{
  2686. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2687. RefreshInterval: &metav1.Duration{Duration: time.Second},
  2688. },
  2689. Status: esv1.ExternalSecretStatus{
  2690. RefreshTime: metav1.NewTime(metav1.Now().Add(-time.Second * 5)),
  2691. },
  2692. }
  2693. // Resource version matches
  2694. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2695. Expect(shouldRefresh(es)).To(BeTrue())
  2696. })
  2697. It("should refresh when no refresh time was set", func() {
  2698. es := &esv1.ExternalSecret{
  2699. ObjectMeta: metav1.ObjectMeta{
  2700. Generation: 1,
  2701. },
  2702. Spec: esv1.ExternalSecretSpec{
  2703. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2704. RefreshInterval: &metav1.Duration{Duration: time.Second},
  2705. },
  2706. Status: esv1.ExternalSecretStatus{},
  2707. }
  2708. // Resource version matches
  2709. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2710. Expect(shouldRefresh(es)).To(BeTrue())
  2711. })
  2712. It("should refresh when refresh time is in the future", func() {
  2713. es := &esv1.ExternalSecret{
  2714. ObjectMeta: metav1.ObjectMeta{
  2715. Generation: 1,
  2716. },
  2717. Spec: esv1.ExternalSecretSpec{
  2718. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2719. RefreshInterval: &metav1.Duration{Duration: time.Second},
  2720. },
  2721. Status: esv1.ExternalSecretStatus{
  2722. RefreshTime: metav1.NewTime(time.Now().Add(time.Hour)), // Future time
  2723. },
  2724. }
  2725. // Resource version matches
  2726. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2727. Expect(shouldRefresh(es)).To(BeTrue())
  2728. })
  2729. It("should refresh when refreshInterval not 0 and spec changes", func() {
  2730. es := &esv1.ExternalSecret{
  2731. ObjectMeta: metav1.ObjectMeta{
  2732. Generation: 1,
  2733. },
  2734. Spec: esv1.ExternalSecretSpec{
  2735. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2736. RefreshInterval: &metav1.Duration{Duration: 10 * time.Second},
  2737. Data: []esv1.ExternalSecretData{
  2738. {
  2739. SecretKey: "key1",
  2740. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2741. Key: "remote-key1",
  2742. },
  2743. },
  2744. },
  2745. },
  2746. Status: esv1.ExternalSecretStatus{
  2747. SyncedResourceVersion: "some-version",
  2748. RefreshTime: metav1.NewTime(metav1.Now().Add(-time.Second * 5)),
  2749. },
  2750. }
  2751. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2752. Expect(shouldRefresh(es)).To(BeFalse())
  2753. // Update the spec by adding a new data item
  2754. es.ObjectMeta.Generation = 2
  2755. es.Spec.Data = append(es.Spec.Data, esv1.ExternalSecretData{
  2756. SecretKey: "key2",
  2757. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2758. Key: "remote-key2",
  2759. },
  2760. })
  2761. Expect(shouldRefresh(es)).To(BeTrue())
  2762. })
  2763. It("should not refresh when refreshInterval is 0 even if spec changes", func() {
  2764. es := &esv1.ExternalSecret{
  2765. ObjectMeta: metav1.ObjectMeta{
  2766. Generation: 1,
  2767. },
  2768. Spec: esv1.ExternalSecretSpec{
  2769. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2770. RefreshInterval: &metav1.Duration{Duration: 0},
  2771. Data: []esv1.ExternalSecretData{
  2772. {
  2773. SecretKey: "key1",
  2774. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2775. Key: "remote-key1",
  2776. },
  2777. },
  2778. },
  2779. },
  2780. Status: esv1.ExternalSecretStatus{
  2781. SyncedResourceVersion: "some-version",
  2782. RefreshTime: metav1.Now(),
  2783. },
  2784. }
  2785. // Set the synced resource version to match the current resource version
  2786. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2787. Expect(shouldRefresh(es)).To(BeFalse())
  2788. // Update the spec by adding a new data item
  2789. es.ObjectMeta.Generation = 2
  2790. es.Spec.Data = append(es.Spec.Data, esv1.ExternalSecretData{
  2791. SecretKey: "key2",
  2792. RemoteRef: esv1.ExternalSecretDataRemoteRef{
  2793. Key: "remote-key2",
  2794. },
  2795. })
  2796. // Should still not refresh because interval is 0
  2797. Expect(shouldRefresh(es)).To(BeFalse())
  2798. })
  2799. It("should not refresh when refreshInterval is 0 even if labels or annotations change", func() {
  2800. es := &esv1.ExternalSecret{
  2801. ObjectMeta: metav1.ObjectMeta{
  2802. Generation: 1,
  2803. Labels: map[string]string{
  2804. "original-label": "value",
  2805. },
  2806. Annotations: map[string]string{
  2807. "original-annotation": "value",
  2808. },
  2809. },
  2810. Spec: esv1.ExternalSecretSpec{
  2811. RefreshPolicy: esv1.RefreshPolicyPeriodic,
  2812. RefreshInterval: &metav1.Duration{Duration: 0},
  2813. },
  2814. Status: esv1.ExternalSecretStatus{
  2815. SyncedResourceVersion: "some-version",
  2816. RefreshTime: metav1.Now(),
  2817. },
  2818. }
  2819. // Set the synced resource version to match the current resource version
  2820. es.Status.SyncedResourceVersion = ctrlutil.GetResourceVersion(es.ObjectMeta)
  2821. Expect(shouldRefresh(es)).To(BeFalse())
  2822. // Update labels and annotations
  2823. es.ObjectMeta.Labels["new-label"] = "new-value"
  2824. es.ObjectMeta.Annotations["new-annotation"] = "new-value"
  2825. // Should still not refresh because interval is 0
  2826. Expect(shouldRefresh(es)).To(BeFalse())
  2827. })
  2828. })
  2829. })
  2830. func externalSecretConditionShouldBe(name, ns string, ct esv1.ExternalSecretConditionType, cs v1.ConditionStatus, v float64) bool {
  2831. return Eventually(func() float64 {
  2832. Expect(testExternalSecretCondition.WithLabelValues(name, ns, string(ct), string(cs)).Write(&metric)).To(Succeed())
  2833. return metric.GetGauge().GetValue()
  2834. }, timeout, interval).Should(Equal(v))
  2835. }
  2836. func init() {
  2837. fakeProvider = fake.New()
  2838. esv1.ForceRegister(fakeProvider, &esv1.SecretStoreProvider{
  2839. AWS: &esv1.AWSProvider{
  2840. Service: esv1.AWSServiceSecretsManager,
  2841. },
  2842. }, esv1.MaintenanceStatusMaintained)
  2843. ctrlmetrics.SetUpLabelNames(false)
  2844. esmetrics.SetUpMetrics()
  2845. testSyncCallsTotal = esmetrics.GetCounterVec(esmetrics.SyncCallsKey)
  2846. testSyncCallsError = esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey)
  2847. testExternalSecretCondition = esmetrics.GetGaugeVec(esmetrics.ExternalSecretStatusConditionKey)
  2848. testExternalSecretReconcileDuration = esmetrics.GetGaugeVec(esmetrics.ExternalSecretReconcileDurationKey)
  2849. }