diff --git a/controllers/nodelifecycle/node_lifecycle_controller.go b/controllers/nodelifecycle/node_lifecycle_controller.go index 2c8598b5..213fad17 100644 --- a/controllers/nodelifecycle/node_lifecycle_controller.go +++ b/controllers/nodelifecycle/node_lifecycle_controller.go @@ -44,6 +44,9 @@ import ( const ( deleteNodeEvent = "DeletingNode" deleteNodeFailedEvent = "DeletingNodeFailed" + + // LabelNodeLifecycleExclude is the label used to exclude a node from lifecycle management + LabelNodeLifecycleExclude = "node.kubernetes.io/exclude-from-lifecycle-management" ) var ShutdownTaint = &v1.Taint{ @@ -134,6 +137,12 @@ func (c *CloudNodeLifecycleController) MonitorNodes(ctx context.Context) { } for _, node := range nodes { + // Skip nodes with the lifecycle exclusion label + if _, excluded := node.Labels[LabelNodeLifecycleExclude]; excluded { + klog.V(4).Infof("Skipping node %s due to exclusion label %s", node.Name, LabelNodeLifecycleExclude) + continue + } + // Default NodeReady status to v1.ConditionUnknown status := v1.ConditionUnknown if _, c := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady); c != nil { diff --git a/controllers/nodelifecycle/node_lifecycle_controller_test.go b/controllers/nodelifecycle/node_lifecycle_controller_test.go index 7442f134..7a33debe 100644 --- a/controllers/nodelifecycle/node_lifecycle_controller_test.go +++ b/controllers/nodelifecycle/node_lifecycle_controller_test.go @@ -508,6 +508,51 @@ func Test_NodesDeleted(t *testing.T) { ExistsByProviderID: false, }, }, + { + name: "node is not ready and does not exist, but has exclusion label", + existingNode: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node0", + CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), + Labels: map[string]string{ + LabelNodeLifecycleExclude: "true", + }, + }, + Status: v1.NodeStatus{ + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionFalse, + LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + }, + }, + }, + }, + expectedNode: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node0", + CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), + Labels: map[string]string{ + LabelNodeLifecycleExclude: "true", + }, + }, + Status: v1.NodeStatus{ + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionFalse, + LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + }, + }, + }, + }, + expectedDeleted: false, + fakeCloud: &fakecloud.Cloud{ + ExistsByProviderID: false, + }, + }, } for _, testcase := range testcases {