@@ -331,6 +331,56 @@ var _ = Describe("replicas alignment reconciler test", func() {
331331 Expect (tree .List (& corev1.Pod {})).Should (HaveLen (2 ))
332332 })
333333
334+ It ("retries scale-in when member leave fails without deleting the pod" , func () {
335+ attempts := 0
336+ testapps .MockKBAgentClient (func (recorder * kbacli.MockClientMockRecorder ) {
337+ recorder .Action (gomock .Any (), gomock .Any ()).DoAndReturn (func (ctx context.Context , req kbagentproto.ActionRequest ) (kbagentproto.ActionResponse , error ) {
338+ if req .Action != "memberLeave" {
339+ return kbagentproto.ActionResponse {}, nil
340+ }
341+ attempts ++
342+ if attempts == 1 {
343+ return kbagentproto.ActionResponse {}, fmt .Errorf ("temporary leave failure" )
344+ }
345+ return kbagentproto.ActionResponse {}, nil
346+ }).AnyTimes ()
347+ })
348+
349+ replicas := int32 (1 )
350+ its .Spec .Replicas = & replicas
351+ its .Spec .PodManagementPolicy = appsv1 .ParallelPodManagement
352+ its .Spec .LifecycleActions = & workloads.LifecycleActions {
353+ MemberLeave : testapps .NewLifecycleAction ("member-leave" ),
354+ }
355+ its .Spec .MemberUpdateStrategy = ptr .To (workloads .SerialUpdateStrategy )
356+ its .Status .InstanceStatus = []workloads.InstanceStatus {
357+ {PodName : its .Name + "-0" , Provisioned : true , MemberJoined : boolPtr (true )},
358+ {PodName : its .Name + "-1" , Provisioned : true , MemberJoined : boolPtr (true )},
359+ }
360+
361+ tree := kubebuilderx .NewObjectTree ()
362+ tree .SetRoot (its )
363+ for i := 0 ; i < 2 ; i ++ {
364+ pod := builder .NewPodBuilder (namespace , fmt .Sprintf ("%s-%d" , its .Name , i )).GetObject ()
365+ makePodAvailable (pod )
366+ Expect (tree .Add (pod )).Should (Succeed ())
367+ }
368+
369+ reconciler = NewReplicasAlignmentReconciler ()
370+ res , err := reconciler .Reconcile (tree )
371+ Expect (err ).Should (HaveOccurred ())
372+ Expect (res ).Should (Equal (kubebuilderx .Continue ))
373+ Expect (tree .List (& corev1.Pod {})).Should (HaveLen (2 ))
374+ Expect (* findInstanceStatus (its , its .Name + "-1" ).MemberJoined ).Should (BeTrue ())
375+
376+ res , err = reconciler .Reconcile (tree )
377+ Expect (err ).Should (BeNil ())
378+ Expect (res ).Should (Equal (kubebuilderx .Continue ))
379+ Expect (attempts ).Should (Equal (2 ))
380+ Expect (tree .List (& corev1.Pod {})).Should (HaveLen (1 ))
381+ Expect (* findInstanceStatus (its , its .Name + "-1" ).MemberJoined ).Should (BeFalse ())
382+ })
383+
334384 It ("best-effort parallel lifecycle advances all pending scale-out replicas in one reconcile" , func () {
335385 var actions []kbagentproto.ActionRequest
336386 testapps .MockKBAgentClient (func (recorder * kbacli.MockClientMockRecorder ) {
@@ -421,6 +471,12 @@ var _ = Describe("replicas alignment reconciler test", func() {
421471 Expect (res ).Should (Equal (kubebuilderx .Continue ))
422472 Expect (leaveNames ).Should (ConsistOf (its .Name + "-1" , its .Name + "-2" , its .Name + "-3" , its .Name + "-4" , its .Name + "-6" ))
423473 Expect (tree .List (& corev1.Pod {})).Should (HaveLen (2 ))
474+
475+ res , err = reconciler .Reconcile (tree )
476+ Expect (err ).Should (BeNil ())
477+ Expect (res ).Should (Equal (kubebuilderx .Continue ))
478+ Expect (leaveNames ).Should (HaveLen (6 ))
479+ Expect (tree .List (& corev1.Pod {})).Should (HaveLen (1 ))
424480 })
425481 })
426482})
0 commit comments