@@ -24,11 +24,14 @@ import (
2424
2525 batchv1 "k8s.io/api/batch/v1"
2626 corev1 "k8s.io/api/core/v1"
27+ "k8s.io/apimachinery/pkg/api/meta"
28+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2729 "k8s.io/apimachinery/pkg/types"
2830 "k8s.io/apimachinery/pkg/util/rand"
2931
3032 v1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
3133 "github.com/cloudnative-pg/cloudnative-pg/pkg/certs"
34+ "github.com/cloudnative-pg/cloudnative-pg/pkg/postgres"
3235 "github.com/cloudnative-pg/cloudnative-pg/pkg/reconciler/persistentvolumeclaim"
3336
3437 . "github.com/onsi/ginkgo/v2"
@@ -174,3 +177,214 @@ var _ = Describe("cluster_status unit tests", func() {
174177 })
175178 })
176179})
180+
181+ var _ = Describe ("updateClusterStatusThatRequiresInstancesState tests" , func () {
182+ var (
183+ env * testingEnvironment
184+ cluster * v1.Cluster
185+ )
186+
187+ BeforeEach (func () {
188+ env = buildTestEnvironment ()
189+ cluster = newFakeCNPGCluster (env .client , newFakeNamespace (env .client ))
190+ })
191+
192+ It ("should handle empty status list" , func (ctx SpecContext ) {
193+ statuses := postgres.PostgresqlStatusList {}
194+
195+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
196+ Expect (err ).ToNot (HaveOccurred ())
197+
198+ Expect (cluster .Status .InstancesReportedState ).To (BeEmpty ())
199+ Expect (cluster .Status .SystemID ).To (BeEmpty ())
200+
201+ condition := meta .FindStatusCondition (cluster .Status .Conditions , string (v1 .ConditionConsistentSystemID ))
202+ Expect (condition ).ToNot (BeNil ())
203+ Expect (condition .Status ).To (Equal (metav1 .ConditionFalse ))
204+ Expect (condition .Reason ).To (Equal ("NotFound" ))
205+ Expect (condition .Message ).To (Equal ("No instances are present in the cluster to report a system ID." ))
206+ })
207+
208+ It ("should handle instances without SystemID" , func (ctx SpecContext ) {
209+ statuses := postgres.PostgresqlStatusList {
210+ Items : []postgres.PostgresqlStatus {
211+ {
212+ Pod : & corev1.Pod {
213+ ObjectMeta : metav1.ObjectMeta {Name : "pod-1" },
214+ Status : corev1.PodStatus {PodIP : "192.168.1.1" },
215+ },
216+ IsPrimary : true ,
217+ TimeLineID : 123 ,
218+ SystemID : "" ,
219+ },
220+ {
221+ Pod : & corev1.Pod {
222+ ObjectMeta : metav1.ObjectMeta {Name : "pod-2" },
223+ Status : corev1.PodStatus {PodIP : "192.168.1.2" },
224+ },
225+ IsPrimary : false ,
226+ SystemID : "" ,
227+ },
228+ },
229+ }
230+
231+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
232+ Expect (err ).ToNot (HaveOccurred ())
233+
234+ Expect (cluster .Status .InstancesReportedState ).To (HaveLen (2 ))
235+ Expect (cluster .Status .TimelineID ).To (Equal (123 ))
236+ Expect (cluster .Status .SystemID ).To (BeEmpty ())
237+
238+ condition := meta .FindStatusCondition (cluster .Status .Conditions , string (v1 .ConditionConsistentSystemID ))
239+ Expect (condition ).ToNot (BeNil ())
240+ Expect (condition .Status ).To (Equal (metav1 .ConditionFalse ))
241+ Expect (condition .Reason ).To (Equal ("NotFound" ))
242+ Expect (condition .Message ).To (Equal ("Instances are present, but none have reported a system ID." ))
243+ })
244+
245+ It ("should handle instances with a single SystemID" , func (ctx SpecContext ) {
246+ const systemID = "system123"
247+ statuses := postgres.PostgresqlStatusList {
248+ Items : []postgres.PostgresqlStatus {
249+ {
250+ Pod : & corev1.Pod {
251+ ObjectMeta : metav1.ObjectMeta {Name : "pod-1" },
252+ Status : corev1.PodStatus {PodIP : "192.168.1.1" },
253+ },
254+ IsPrimary : true ,
255+ TimeLineID : 123 ,
256+ SystemID : systemID ,
257+ },
258+ {
259+ Pod : & corev1.Pod {
260+ ObjectMeta : metav1.ObjectMeta {Name : "pod-2" },
261+ Status : corev1.PodStatus {PodIP : "192.168.1.2" },
262+ },
263+ IsPrimary : false ,
264+ SystemID : systemID ,
265+ },
266+ },
267+ }
268+
269+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
270+ Expect (err ).ToNot (HaveOccurred ())
271+
272+ Expect (cluster .Status .InstancesReportedState ).To (HaveLen (2 ))
273+ Expect (cluster .Status .TimelineID ).To (Equal (123 ))
274+ Expect (cluster .Status .SystemID ).To (Equal (systemID ))
275+
276+ condition := meta .FindStatusCondition (cluster .Status .Conditions , string (v1 .ConditionConsistentSystemID ))
277+ Expect (condition ).ToNot (BeNil ())
278+ Expect (condition .Status ).To (Equal (metav1 .ConditionTrue ))
279+ Expect (condition .Reason ).To (Equal ("Unique" ))
280+ Expect (condition .Message ).To (Equal ("A single, unique system ID was found across reporting instances." ))
281+ })
282+
283+ It ("should handle instances with multiple SystemIDs" , func (ctx SpecContext ) {
284+ statuses := postgres.PostgresqlStatusList {
285+ Items : []postgres.PostgresqlStatus {
286+ {
287+ Pod : & corev1.Pod {
288+ ObjectMeta : metav1.ObjectMeta {Name : "pod-1" },
289+ Status : corev1.PodStatus {PodIP : "192.168.1.1" },
290+ },
291+ IsPrimary : true ,
292+ TimeLineID : 123 ,
293+ SystemID : "system1" ,
294+ },
295+ {
296+ Pod : & corev1.Pod {
297+ ObjectMeta : metav1.ObjectMeta {Name : "pod-2" },
298+ Status : corev1.PodStatus {PodIP : "192.168.1.2" },
299+ },
300+ IsPrimary : false ,
301+ SystemID : "system2" ,
302+ },
303+ },
304+ }
305+
306+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
307+ Expect (err ).ToNot (HaveOccurred ())
308+
309+ Expect (cluster .Status .InstancesReportedState ).To (HaveLen (2 ))
310+ Expect (cluster .Status .TimelineID ).To (Equal (123 ))
311+ Expect (cluster .Status .SystemID ).To (BeEmpty ())
312+
313+ condition := meta .FindStatusCondition (cluster .Status .Conditions , string (v1 .ConditionConsistentSystemID ))
314+ Expect (condition ).ToNot (BeNil ())
315+ Expect (condition .Status ).To (Equal (metav1 .ConditionFalse ))
316+ Expect (condition .Reason ).To (Equal ("Mismatch" ))
317+ Expect (condition .Message ).To (ContainSubstring ("Multiple differing system IDs reported by instances:" ))
318+ Expect (condition .Message ).To (ContainSubstring ("system1" ))
319+ Expect (condition .Message ).To (ContainSubstring ("system2" ))
320+ })
321+
322+ It ("should update timeline ID from the primary instance" , func (ctx SpecContext ) {
323+ const timelineID = 999
324+ statuses := postgres.PostgresqlStatusList {
325+ Items : []postgres.PostgresqlStatus {
326+ {
327+ Pod : & corev1.Pod {
328+ ObjectMeta : metav1.ObjectMeta {Name : "pod-1" },
329+ Status : corev1.PodStatus {PodIP : "192.168.1.1" },
330+ },
331+ IsPrimary : true ,
332+ TimeLineID : timelineID ,
333+ SystemID : "system1" ,
334+ },
335+ {
336+ Pod : & corev1.Pod {
337+ ObjectMeta : metav1.ObjectMeta {Name : "pod-2" },
338+ Status : corev1.PodStatus {PodIP : "192.168.1.2" },
339+ },
340+ IsPrimary : false ,
341+ TimeLineID : 123 ,
342+ SystemID : "system1" ,
343+ },
344+ },
345+ }
346+
347+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
348+ Expect (err ).ToNot (HaveOccurred ())
349+
350+ Expect (cluster .Status .TimelineID ).To (Equal (timelineID ))
351+ })
352+
353+ It ("should correctly populate InstancesReportedState" , func (ctx SpecContext ) {
354+ statuses := postgres.PostgresqlStatusList {
355+ Items : []postgres.PostgresqlStatus {
356+ {
357+ Pod : & corev1.Pod {
358+ ObjectMeta : metav1.ObjectMeta {Name : "pod-1" },
359+ Status : corev1.PodStatus {PodIP : "192.168.1.1" },
360+ },
361+ IsPrimary : true ,
362+ TimeLineID : 123 ,
363+ },
364+ {
365+ Pod : & corev1.Pod {
366+ ObjectMeta : metav1.ObjectMeta {Name : "pod-2" },
367+ Status : corev1.PodStatus {PodIP : "192.168.1.2" },
368+ },
369+ IsPrimary : false ,
370+ TimeLineID : 123 ,
371+ },
372+ },
373+ }
374+
375+ err := env .clusterReconciler .updateClusterStatusThatRequiresInstancesState (ctx , cluster , statuses )
376+ Expect (err ).ToNot (HaveOccurred ())
377+
378+ Expect (cluster .Status .InstancesReportedState ).To (HaveLen (2 ))
379+
380+ state1 := cluster .Status .InstancesReportedState ["pod-1" ]
381+ Expect (state1 .IsPrimary ).To (BeTrue ())
382+ Expect (state1 .TimeLineID ).To (Equal (123 ))
383+ Expect (state1 .IP ).To (Equal ("192.168.1.1" ))
384+
385+ state2 := cluster .Status .InstancesReportedState ["pod-2" ]
386+ Expect (state2 .IsPrimary ).To (BeFalse ())
387+ Expect (state2 .TimeLineID ).To (Equal (123 ))
388+ Expect (state2 .IP ).To (Equal ("192.168.1.2" ))
389+ })
390+ })
0 commit comments