@@ -19,8 +19,6 @@ package controller
1919import (
2020 "context"
2121 "fmt"
22- "strconv"
23- "time"
2422
2523 apierrors "k8s.io/apimachinery/pkg/api/errors"
2624 "k8s.io/apimachinery/pkg/api/meta"
@@ -79,6 +77,13 @@ var _ = Describe("PostgresCluster Controller", func() {
7977 req reconcile.Request
8078 )
8179
80+ reconcileNTimes := func (times int ) {
81+ for i := 0 ; i < times ; i ++ {
82+ _ , err := reconciler .Reconcile (ctx , req )
83+ Expect (err ).NotTo (HaveOccurred ())
84+ }
85+ }
86+
8287 BeforeEach (func () {
8388 nameSuffix := fmt .Sprintf ("%d-%d-%d" ,
8489 GinkgoParallelProcess (),
@@ -177,82 +182,59 @@ var _ = Describe("PostgresCluster Controller", func() {
177182 // PC-02
178183 It ("adds finalizer on reconcile" , func () {
179184 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
185+ reconcileNTimes (1 )
180186
181- Eventually (func () bool {
182- pc := & enterprisev4.PostgresCluster {}
183- if err := k8sClient .Get (ctx , pgClusterKey , pc ); err != nil {
184- return false
185- }
186- return controllerutil .ContainsFinalizer (pc , core .PostgresClusterFinalizerName )
187- }, "10s" , "250ms" ).Should (BeTrue ())
187+ pc := & enterprisev4.PostgresCluster {}
188+ Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
189+ Expect (controllerutil .ContainsFinalizer (pc , core .PostgresClusterFinalizerName )).To (BeTrue ())
188190 })
189191
190192 // PC-01
191193 It ("creates managed resources and status refs" , func () {
192194 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
195+ // pass 1: add finalizer; pass 2: create CNPG cluster/secret/status.
196+ reconcileNTimes (2 )
193197
194- Eventually (func (g Gomega ) {
195- pc := & enterprisev4.PostgresCluster {}
196- g .Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
197-
198- cond := meta .FindStatusCondition (pc .Status .Conditions , "ClusterReady" )
199- g .Expect (cond ).NotTo (BeNil ())
200- g .Expect (cond .Status ).To (Equal (metav1 .ConditionFalse ))
201- g .Expect (cond .Reason ).To (Equal ("CNPGClusterProvisioning" ))
202- }, "20s" , "250ms" ).Should (Succeed ())
198+ pc := & enterprisev4.PostgresCluster {}
199+ Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
200+ cond := meta .FindStatusCondition (pc .Status .Conditions , "ClusterReady" )
201+ Expect (cond ).NotTo (BeNil ())
202+ Expect (cond .Status ).To (Equal (metav1 .ConditionFalse ))
203+ Expect (cond .Reason ).To (Equal ("ClusterBuildSucceeded" ))
203204
204205 // Simulate external CNPG controller status progression.
205- Eventually (func () error {
206- cnpg := & cnpgv1.Cluster {}
207- if err := k8sClient .Get (ctx , pgClusterKey , cnpg ); err != nil {
208- return err
209- }
210- cnpg .Status .Phase = cnpgv1 .PhaseHealthy
211- return k8sClient .Status ().Update (ctx , cnpg ) // update event
212- }, "10s" , "250ms" ).Should (Succeed ())
206+ cnpg := & cnpgv1.Cluster {}
207+ Expect (k8sClient .Get (ctx , pgClusterKey , cnpg )).To (Succeed ())
208+ cnpg .Status .Phase = cnpgv1 .PhaseHealthy
209+ Expect (k8sClient .Status ().Update (ctx , cnpg )).To (Succeed ())
210+ reconcileNTimes (1 )
213211
214212 // Expect cnpg status progression propagation
215- Eventually (func (g Gomega ) {
216- pc := & enterprisev4.PostgresCluster {}
217- g .Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
218-
219- cond := meta .FindStatusCondition (pc .Status .Conditions , "ClusterReady" )
220- g .Expect (cond ).NotTo (BeNil ())
221- g .Expect (cond .Status ).To (Equal (metav1 .ConditionTrue ))
222- g .Expect (cond .Reason ).To (Equal ("CNPGClusterHealthy" ))
223- }, "20s" , "250ms" ).Should (Succeed ())
224-
225- Eventually (func (g Gomega ) {
226- pc := & enterprisev4.PostgresCluster {}
227- g .Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
228- g .Expect (pc .Status .Resources ).NotTo (BeNil ())
229- g .Expect (pc .Status .Resources .SuperUserSecretRef ).NotTo (BeNil ())
230- g .Expect (pc .Status .Resources .ConfigMapRef ).NotTo (BeNil ())
231- }, "20s" , "250ms" ).Should (Succeed ())
213+ Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
214+ cond = meta .FindStatusCondition (pc .Status .Conditions , "ClusterReady" )
215+ Expect (cond ).NotTo (BeNil ())
216+ Expect (cond .Status ).To (Equal (metav1 .ConditionTrue ))
217+ Expect (cond .Reason ).To (Equal ("CNPGClusterHealthy" ))
218+ Expect (pc .Status .Resources ).NotTo (BeNil ())
219+ Expect (pc .Status .Resources .SuperUserSecretRef ).NotTo (BeNil ())
220+ Expect (pc .Status .Resources .ConfigMapRef ).NotTo (BeNil ())
232221 })
233222
234223 // PC-07
235224 It ("is idempotent across repeated reconciles" , func () {
236225 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
226+ reconcileNTimes (2 )
227+ reconcileNTimes (3 )
237228
238- // Trigger extra update events that should not change desired state semantics.
239- Eventually (func () error {
240- pc := & enterprisev4.PostgresCluster {}
241- if err := k8sClient .Get (ctx , pgClusterKey , pc ); err != nil {
242- return err
243- }
244- if pc .Annotations == nil {
245- pc .Annotations = map [string ]string {}
246- }
247- pc .Annotations ["test.bump" ] = strconv .FormatInt (time .Now ().UnixNano (), 10 )
248- return k8sClient .Update (ctx , pc ) // update event
249- }, "10s" , "250ms" ).Should (Succeed ())
250-
251- Eventually (func (g Gomega ) {
252- cnpg := & cnpgv1.Cluster {}
253- g .Expect (k8sClient .Get (ctx , pgClusterKey , cnpg )).To (Succeed ())
254- g .Expect (cnpg .Spec .Instances ).To (Equal (int (clusterMemberCount )))
255- }, "20s" , "250ms" ).Should (Succeed ())
229+ cnpg := & cnpgv1.Cluster {}
230+ Expect (k8sClient .Get (ctx , pgClusterKey , cnpg )).To (Succeed ())
231+ Expect (cnpg .Spec .Instances ).To (Equal (int (clusterMemberCount )))
232+
233+ pc := & enterprisev4.PostgresCluster {}
234+ Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
235+ cond := meta .FindStatusCondition (pc .Status .Conditions , "ClusterReady" )
236+ Expect (cond ).NotTo (BeNil ())
237+ Expect (cond .ObservedGeneration ).To (Equal (pc .Generation ))
256238 })
257239 })
258240 })
@@ -262,14 +244,19 @@ var _ = Describe("PostgresCluster Controller", func() {
262244 Context ("and clusterDeletionPolicy is set to Delete" , func () {
263245 It ("removes children and finalizer" , func () {
264246 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
247+ reconcileNTimes (2 )
265248
266249 pc := & enterprisev4.PostgresCluster {}
267250 Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
268- Expect (k8sClient .Delete (ctx , pc )).To (Succeed ()) // delete event
251+ Expect (k8sClient .Delete (ctx , pc )).To (Succeed ())
269252
270253 Eventually (func () bool {
271- err := k8sClient .Get (ctx , pgClusterKey , & enterprisev4.PostgresCluster {})
272- return apierrors .IsNotFound (err )
254+ _ , err := reconciler .Reconcile (ctx , req )
255+ if err != nil {
256+ return false
257+ }
258+ getErr := k8sClient .Get (ctx , pgClusterKey , & enterprisev4.PostgresCluster {})
259+ return apierrors .IsNotFound (getErr )
273260 }, "30s" , "250ms" ).Should (BeTrue ())
274261 })
275262 })
@@ -278,26 +265,20 @@ var _ = Describe("PostgresCluster Controller", func() {
278265 Context ("when clusterDeletionPolicy is set to Retain" , func () {
279266 It ("preserves retained resources and removes owner refs" , func () {
280267 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
281-
282- // Trigger update event: switch policy to Retain before delete.
283- Eventually (func () error {
284- pc := & enterprisev4.PostgresCluster {}
285- if err := k8sClient .Get (ctx , pgClusterKey , pc ); err != nil {
286- return err
287- }
288- pc .Spec .ClusterDeletionPolicy = & []string {retainPolicy }[0 ]
289- return k8sClient .Update (ctx , pc )
290- }, "10s" , "250ms" ).Should (Succeed ())
268+ reconcileNTimes (2 )
291269
292270 pc := & enterprisev4.PostgresCluster {}
293271 Expect (k8sClient .Get (ctx , pgClusterKey , pc )).To (Succeed ())
294- Expect (k8sClient .Delete (ctx , pc )).To (Succeed ()) // delete event
272+ Expect (k8sClient .Delete (ctx , pc )).To (Succeed ())
295273
296274 Eventually (func () bool {
297- err := k8sClient .Get (ctx , pgClusterKey , & enterprisev4.PostgresCluster {})
298- return apierrors .IsNotFound (err )
275+ _ , err := reconciler .Reconcile (ctx , req )
276+ if err != nil {
277+ return false
278+ }
279+ getErr := k8sClient .Get (ctx , pgClusterKey , & enterprisev4.PostgresCluster {})
280+ return apierrors .IsNotFound (getErr )
299281 }, "30s" , "250ms" ).Should (BeTrue ())
300-
301282 })
302283 })
303284 })
@@ -306,18 +287,25 @@ var _ = Describe("PostgresCluster Controller", func() {
306287 // PC-05
307288 Context ("when referenced class does not exist" , func () {
308289 It ("fails with class-not-found condition" , func () {
309- clusterName = "bad-" + clusterName
310- className = "missing-class"
290+ badName : = "bad-" + clusterName
291+ badKey := types. NamespacedName { Name : badName , Namespace : namespace }
311292
312293 bad := & enterprisev4.PostgresCluster {
313- ObjectMeta : metav1.ObjectMeta {Name : clusterName , Namespace : namespace },
314- Spec : enterprisev4.PostgresClusterSpec {Class : className },
294+ ObjectMeta : metav1.ObjectMeta {Name : badName , Namespace : namespace },
295+ Spec : enterprisev4.PostgresClusterSpec {Class : "missing-class" },
315296 }
316- Expect (k8sClient .Create (ctx , bad )).To (Succeed ()) // create event
297+ Expect (k8sClient .Create (ctx , bad )).To (Succeed ())
298+ DeferCleanup (func () { _ = k8sClient .Delete (ctx , bad ) })
299+
300+ // pass 1 adds finalizer, pass 2 reaches class lookup and sets failure condition.
301+ _ , err := reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : badKey })
302+ Expect (err ).NotTo (HaveOccurred ())
303+ _ , err = reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : badKey })
304+ Expect (err ).To (HaveOccurred ())
317305
318306 Eventually (func () bool {
319307 current := & enterprisev4.PostgresCluster {}
320- if err := k8sClient .Get (ctx , types. NamespacedName { Name : bad . Name , Namespace : namespace } , current ); err != nil {
308+ if err := k8sClient .Get (ctx , badKey , current ); err != nil {
321309 return false
322310 }
323311 cond := meta .FindStatusCondition (current .Status .Conditions , "ClusterReady" )
@@ -330,31 +318,16 @@ var _ = Describe("PostgresCluster Controller", func() {
330318 Context ("when managed child spec drifts from desired state" , func () {
331319 It ("restores drifted managed spec" , func () {
332320 Expect (k8sClient .Create (ctx , pgCluster )).To (Succeed ())
321+ reconcileNTimes (2 )
333322
334- Eventually (func () error {
335- return k8sClient .Get (ctx , pgClusterKey , & cnpgv1.Cluster {})
336- }, "20s" , "250ms" ).Should (Succeed ())
337-
338- Eventually (func () error {
339- pc := & enterprisev4.PostgresCluster {}
340- if err := k8sClient .Get (ctx , pgClusterKey , pc ); err != nil {
341- return err
342- }
343- if pc .Annotations == nil {
344- pc .Annotations = map [string ]string {}
345- }
346- pc .Annotations ["drift-trigger" ] = strconv .FormatInt (time .Now ().UnixNano (), 10 )
347- pc .Spec .Instances = & []int32 {8 }[0 ]
348- return k8sClient .Update (ctx , pc )
349- }, "10s" , "250ms" ).Should (Succeed ())
323+ cnpg := & cnpgv1.Cluster {}
324+ Expect (k8sClient .Get (ctx , pgClusterKey , cnpg )).To (Succeed ())
325+ cnpg .Spec .Instances = 8
326+ Expect (k8sClient .Update (ctx , cnpg )).To (Succeed ())
350327
351- Eventually (func () bool {
352- cnpg := & cnpgv1.Cluster {}
353- if err := k8sClient .Get (ctx , pgClusterKey , cnpg ); err != nil {
354- return false
355- }
356- return cnpg .Spec .Instances == int (clusterMemberCount )
357- }, "20s" , "250ms" ).Should (BeTrue ())
328+ reconcileNTimes (2 )
329+ Expect (k8sClient .Get (ctx , pgClusterKey , cnpg )).To (Succeed ())
330+ Expect (cnpg .Spec .Instances ).To (Equal (int (clusterMemberCount )))
358331 })
359332 })
360333 })
0 commit comments