@@ -2018,7 +2018,7 @@ var _ = Describe("Consolidation", func() {
20182018 Entry ("if the candidate is on-demand node" , false ),
20192019 Entry ("if the candidate is spot node" , true ),
20202020 )
2021- DescribeTable ("can replace nodes, considers karpenter.sh/do-not-disrupt on nodes" ,
2021+ DescribeTable ("can replace nodes, considers karpenter.sh/do-not-disrupt set to true on nodes" ,
20222022 func (spotToSpot bool ) {
20232023 nodeClaim = lo .Ternary (spotToSpot , spotNodeClaim , nodeClaim )
20242024 node = lo .Ternary (spotToSpot , spotNode , node )
@@ -2108,7 +2108,7 @@ var _ = Describe("Consolidation", func() {
21082108 Entry ("if the candidate is on-demand node" , false ),
21092109 Entry ("if the candidate is spot node" , true ),
21102110 )
2111- DescribeTable ("can replace nodes, considers karpenter.sh/do-not-disrupt on pods" ,
2111+ DescribeTable ("can replace nodes, considers karpenter.sh/do-not-disrupt set to true on pods" ,
21122112 func (spotToSpot bool ) {
21132113 nodeClaim = lo .Ternary (spotToSpot , spotNodeClaim , nodeClaim )
21142114 node = lo .Ternary (spotToSpot , spotNode , node )
@@ -2627,7 +2627,7 @@ var _ = Describe("Consolidation", func() {
26272627 // eviction
26282628 ExpectNotFound (ctx , env .Client , nodeClaims [0 ], nodes [0 ])
26292629 })
2630- It ("can delete nodes, considers karpenter.sh/do-not-disrupt on nodes" , func () {
2630+ It ("can delete nodes, considers karpenter.sh/do-not-disrupt set to true on nodes" , func () {
26312631 // create our RS so we can link a pod to it
26322632 rs := test .ReplicaSet ()
26332633 ExpectApplied (ctx , env .Client , rs )
@@ -2669,7 +2669,7 @@ var _ = Describe("Consolidation", func() {
26692669 Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (1 ))
26702670 ExpectNotFound (ctx , env .Client , nodeClaims [0 ], nodes [0 ])
26712671 })
2672- It ("can delete nodes, considers karpenter.sh/do-not-disrupt on pods" , func () {
2672+ It ("can delete nodes, considers karpenter.sh/do-not-disrupt set to true on pods" , func () {
26732673 // create our RS so we can link a pod to it
26742674 rs := test .ReplicaSet ()
26752675 ExpectApplied (ctx , env .Client , rs )
@@ -2712,7 +2712,7 @@ var _ = Describe("Consolidation", func() {
27122712 Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (1 ))
27132713 ExpectNotFound (ctx , env .Client , nodeClaims [0 ], nodes [0 ])
27142714 })
2715- It ("does not consolidate nodes with karpenter.sh/do-not-disrupt on pods when the NodePool's TerminationGracePeriod is not nil" , func () {
2715+ It ("does not consolidate nodes with karpenter.sh/do-not-disrupt set to true on pods when the NodePool's TerminationGracePeriod is not nil" , func () {
27162716 // create our RS so we can link a pod to it
27172717 rs := test .ReplicaSet ()
27182718 ExpectApplied (ctx , env .Client , rs )
@@ -2807,6 +2807,138 @@ var _ = Describe("Consolidation", func() {
28072807 Expect (ExpectNodeClaims (ctx , env .Client )).To (HaveLen (2 ))
28082808 Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (2 ))
28092809 })
2810+ It ("does not consolidate nodes with pods that have a duration-based do-not-disrupt annotation that is still active" , func () {
2811+ rs := test .ReplicaSet ()
2812+ ExpectApplied (ctx , env .Client , rs )
2813+ Expect (env .Client .Get (ctx , client .ObjectKeyFromObject (rs ), rs )).To (Succeed ())
2814+
2815+ pods := test .Pods (3 , test.PodOptions {
2816+ ObjectMeta : metav1.ObjectMeta {Labels : labels ,
2817+ OwnerReferences : []metav1.OwnerReference {
2818+ {
2819+ APIVersion : "apps/v1" ,
2820+ Kind : "ReplicaSet" ,
2821+ Name : rs .Name ,
2822+ UID : rs .UID ,
2823+ Controller : lo .ToPtr (true ),
2824+ BlockOwnerDeletion : lo .ToPtr (true ),
2825+ },
2826+ }}})
2827+ // Set a 2m duration-based do-not-disrupt annotation on pod[2]
2828+ pods [2 ].Annotations = lo .Assign (pods [2 ].Annotations , map [string ]string {v1 .DoNotDisruptAnnotationKey : "2m" })
2829+ pods [2 ].Status .StartTime = & metav1.Time {Time : fakeClock .Now ()}
2830+
2831+ ExpectApplied (ctx , env .Client , rs , pods [0 ], pods [1 ], pods [2 ], nodePool )
2832+ ExpectApplied (ctx , env .Client , nodeClaims [0 ], nodes [0 ], nodeClaims [1 ], nodes [1 ])
2833+
2834+ ExpectManualBinding (ctx , env .Client , pods [0 ], nodes [0 ])
2835+ ExpectManualBinding (ctx , env .Client , pods [1 ], nodes [0 ])
2836+ ExpectManualBinding (ctx , env .Client , pods [2 ], nodes [1 ])
2837+
2838+ ExpectMakeNodesAndNodeClaimsInitializedAndStateUpdated (ctx , env .Client , nodeStateController , nodeClaimStateController , []* corev1.Node {nodes [0 ], nodes [1 ]}, []* v1.NodeClaim {nodeClaims [0 ], nodeClaims [1 ]})
2839+ ExpectSingletonReconciled (ctx , disruptionController )
2840+
2841+ // Grace period is still active on pod[2], so nodes[1] should not be consolidated
2842+ // Only nodes[0] (no annotated pods) can be consolidated
2843+ cmds := queue .GetCommands ()
2844+ Expect (cmds ).To (HaveLen (1 ))
2845+ ExpectObjectReconciled (ctx , env .Client , queue , cmds [0 ].Candidates [0 ].NodeClaim )
2846+ ExpectNodeClaimsCascadeDeletion (ctx , env .Client , nodeClaims [0 ])
2847+
2848+ // nodes[0] deleted, nodes[1] still protected
2849+ Expect (ExpectNodeClaims (ctx , env .Client )).To (HaveLen (1 ))
2850+ Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (1 ))
2851+ ExpectNotFound (ctx , env .Client , nodeClaims [0 ], nodes [0 ])
2852+ })
2853+ It ("can consolidate nodes after duration-based do-not-disrupt annotation expires" , func () {
2854+ rs := test .ReplicaSet ()
2855+ ExpectApplied (ctx , env .Client , rs )
2856+ Expect (env .Client .Get (ctx , client .ObjectKeyFromObject (rs ), rs )).To (Succeed ())
2857+
2858+ pods := test .Pods (3 , test.PodOptions {
2859+ ObjectMeta : metav1.ObjectMeta {Labels : labels ,
2860+ OwnerReferences : []metav1.OwnerReference {
2861+ {
2862+ APIVersion : "apps/v1" ,
2863+ Kind : "ReplicaSet" ,
2864+ Name : rs .Name ,
2865+ UID : rs .UID ,
2866+ Controller : lo .ToPtr (true ),
2867+ BlockOwnerDeletion : lo .ToPtr (true ),
2868+ },
2869+ }}})
2870+ // All pods have a 2m duration-based do-not-disrupt annotation
2871+ for _ , p := range pods {
2872+ p .Annotations = lo .Assign (p .Annotations , map [string ]string {v1 .DoNotDisruptAnnotationKey : "2m" })
2873+ p .Status .StartTime = & metav1.Time {Time : fakeClock .Now ()}
2874+ }
2875+
2876+ ExpectApplied (ctx , env .Client , rs , pods [0 ], pods [1 ], pods [2 ], nodePool )
2877+ ExpectApplied (ctx , env .Client , nodeClaims [0 ], nodes [0 ], nodeClaims [1 ], nodes [1 ])
2878+
2879+ ExpectManualBinding (ctx , env .Client , pods [0 ], nodes [0 ])
2880+ ExpectManualBinding (ctx , env .Client , pods [1 ], nodes [0 ])
2881+ ExpectManualBinding (ctx , env .Client , pods [2 ], nodes [1 ])
2882+
2883+ ExpectMakeNodesAndNodeClaimsInitializedAndStateUpdated (ctx , env .Client , nodeStateController , nodeClaimStateController , []* corev1.Node {nodes [0 ], nodes [1 ]}, []* v1.NodeClaim {nodeClaims [0 ], nodeClaims [1 ]})
2884+ ExpectSingletonReconciled (ctx , disruptionController )
2885+
2886+ // All pods have active grace periods, no consolidation should happen
2887+ cmds := queue .GetCommands ()
2888+ Expect (cmds ).To (HaveLen (0 ))
2889+ Expect (ExpectNodeClaims (ctx , env .Client )).To (HaveLen (2 ))
2890+ Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (2 ))
2891+
2892+ // Advance clock past the 2m grace period
2893+ fakeClock .Step (3 * time .Minute )
2894+
2895+ ExpectMakeNodesAndNodeClaimsInitializedAndStateUpdated (ctx , env .Client , nodeStateController , nodeClaimStateController , []* corev1.Node {nodes [0 ], nodes [1 ]}, []* v1.NodeClaim {nodeClaims [0 ], nodeClaims [1 ]})
2896+ ExpectSingletonReconciled (ctx , disruptionController )
2897+
2898+ // Grace periods expired, consolidation should now proceed
2899+ cmds = queue .GetCommands ()
2900+ Expect (cmds ).To (HaveLen (1 ))
2901+ })
2902+ It ("can delete nodes, considers invalid do-not-disrupt annotation format as not blocking" , func () {
2903+ rs := test .ReplicaSet ()
2904+ ExpectApplied (ctx , env .Client , rs )
2905+ Expect (env .Client .Get (ctx , client .ObjectKeyFromObject (rs ), rs )).To (Succeed ())
2906+
2907+ pods := test .Pods (3 , test.PodOptions {
2908+ ObjectMeta : metav1.ObjectMeta {Labels : labels ,
2909+ OwnerReferences : []metav1.OwnerReference {
2910+ {
2911+ APIVersion : "apps/v1" ,
2912+ Kind : "ReplicaSet" ,
2913+ Name : rs .Name ,
2914+ UID : rs .UID ,
2915+ Controller : lo .ToPtr (true ),
2916+ BlockOwnerDeletion : lo .ToPtr (true ),
2917+ },
2918+ }}})
2919+ // Set an invalid format annotation - should not block consolidation
2920+ pods [2 ].Annotations = lo .Assign (pods [2 ].Annotations , map [string ]string {v1 .DoNotDisruptAnnotationKey : "invalid-format" })
2921+
2922+ ExpectApplied (ctx , env .Client , rs , pods [0 ], pods [1 ], pods [2 ], nodePool )
2923+ ExpectApplied (ctx , env .Client , nodeClaims [0 ], nodes [0 ], nodeClaims [1 ], nodes [1 ])
2924+
2925+ ExpectManualBinding (ctx , env .Client , pods [0 ], nodes [0 ])
2926+ ExpectManualBinding (ctx , env .Client , pods [1 ], nodes [0 ])
2927+ ExpectManualBinding (ctx , env .Client , pods [2 ], nodes [1 ])
2928+
2929+ ExpectMakeNodesAndNodeClaimsInitializedAndStateUpdated (ctx , env .Client , nodeStateController , nodeClaimStateController , []* corev1.Node {nodes [0 ], nodes [1 ]}, []* v1.NodeClaim {nodeClaims [0 ], nodeClaims [1 ]})
2930+ ExpectSingletonReconciled (ctx , disruptionController )
2931+
2932+ // Invalid annotation format should not block consolidation
2933+ cmds := queue .GetCommands ()
2934+ Expect (cmds ).To (HaveLen (1 ))
2935+ ExpectObjectReconciled (ctx , env .Client , queue , cmds [0 ].Candidates [0 ].NodeClaim )
2936+ ExpectNodeClaimsCascadeDeletion (ctx , env .Client , nodeClaims [1 ])
2937+
2938+ Expect (ExpectNodeClaims (ctx , env .Client )).To (HaveLen (1 ))
2939+ Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (1 ))
2940+ ExpectNotFound (ctx , env .Client , nodeClaims [1 ], nodes [1 ])
2941+ })
28102942 It ("can delete nodes, evicts pods without an ownerRef" , func () {
28112943 // create our RS so we can link a pod to it
28122944 rs := test .ReplicaSet ()
@@ -3443,7 +3575,7 @@ var _ = Describe("Consolidation", func() {
34433575 Expect (ExpectNodes (ctx , env .Client )).To (HaveLen (1 ))
34443576 ExpectExists (ctx , env .Client , nodeClaims [0 ])
34453577 })
3446- It ("should not replace node if a pod schedules with karpenter.sh/do-not-disrupt during the TTL wait" , func () {
3578+ It ("should not replace node if a pod schedules with karpenter.sh/do-not-disrupt set to true during the TTL wait" , func () {
34473579 pod := test .Pod ()
34483580 ExpectApplied (ctx , env .Client , nodePool , nodeClaim , node , pod )
34493581
@@ -3515,7 +3647,7 @@ var _ = Describe("Consolidation", func() {
35153647 },
35163648 )
35173649 })
3518- It ("should not delete node if pods schedule with karpenter.sh/do-not-disrupt during the TTL wait" , func () {
3650+ It ("should not delete node if pods schedule with karpenter.sh/do-not-disrupt set to true during the TTL wait" , func () {
35193651 pods := test .Pods (2 , test.PodOptions {})
35203652 ExpectApplied (ctx , env .Client , nodePool , nodeClaims [0 ], nodes [0 ], nodeClaims [1 ], nodes [1 ], pods [0 ], pods [1 ])
35213653
0 commit comments