externalsecret_controller_test.go 96 KB

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