Skip to content

Commit fe34b4e

Browse files
committed
OPCT-379: Fix PinnedImages test to respect node taints
Backport of OCPBUGS-78617 fix from main to release-4.21. The PinnedImages conformance test was selecting worker nodes without considering node taints, causing test failures in environments with dedicated/tainted nodes (like OPCT test infrastructure). Changes: - Added GetReadySchedulableWorkerNodes() utility function in test/extended/util/nodes.go to filter out nodes with NoSchedule/NoExecute taints and nodes with control-plane/master roles - Updated addWorkerNodesToCustomPool() in pinnedimages.go to use the new utility function - Tests now skip gracefully when insufficient schedulable worker nodes exist (handles SNO and compact cluster scenarios) This ensures the test works correctly across cluster topologies. Original PR: openshift#30913
1 parent ac4798a commit fe34b4e

2 files changed

Lines changed: 63 additions & 7 deletions

File tree

test/extended/machine_config/pinnedimages.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -353,24 +353,27 @@ func applyPIS(oc *exutil.CLI, pisFixture string, pis *mcfgv1.PinnedImageSet, pis
353353
// `addWorkerNodesToCustomPool` labels the desired number of worker nodes with the MCP role
354354
// selector so that the nodes become part of the desired custom MCP
355355
func addWorkerNodesToCustomPool(oc *exutil.CLI, kubeClient *kubernetes.Clientset, numberOfNodes int, customMCP string) ([]string, error) {
356-
// Get the worker nodes
357-
nodes, err := kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{"node-role.kubernetes.io/worker": ""}).String()})
356+
// Get ready schedulable worker nodes (excludes nodes with NoSchedule/NoExecute taints and control-plane nodes)
357+
nodes, err := exutil.GetReadySchedulableWorkerNodes(context.TODO(), kubeClient)
358358
if err != nil {
359359
return nil, err
360360
}
361-
// Return an error if there are less worker nodes in the cluster than the desired number of nodes to add to the custom MCP
362-
if len(nodes.Items) < numberOfNodes {
363-
return nil, fmt.Errorf("Node in Worker MCP %d < Number of nodes needed in %d MCP", len(nodes.Items), numberOfNodes)
361+
362+
// Skip test gracefully if there are not enough schedulable worker nodes
363+
// This handles SNO and compact cluster scenarios where nodes may have taints or dual roles
364+
if len(nodes) < numberOfNodes {
365+
framework.Skipf("Insufficient schedulable worker nodes: have %d, need %d (nodes may have taints or control-plane role)", len(nodes), numberOfNodes)
366+
return nil, nil
364367
}
365368

366369
// Label the nodes with the custom MCP role selector
367370
var optedNodes []string
368371
for node_i := 0; node_i < numberOfNodes; node_i++ {
369-
err = oc.AsAdmin().Run("label").Args("node", nodes.Items[node_i].Name, fmt.Sprintf("node-role.kubernetes.io/%s=", customMCP)).Execute()
372+
err = oc.AsAdmin().Run("label").Args("node", nodes[node_i].Name, fmt.Sprintf("node-role.kubernetes.io/%s=", customMCP)).Execute()
370373
if err != nil {
371374
return nil, err
372375
}
373-
optedNodes = append(optedNodes, nodes.Items[node_i].Name)
376+
optedNodes = append(optedNodes, nodes[node_i].Name)
374377
}
375378
return optedNodes, nil
376379
}

test/extended/util/nodes.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,56 @@ func createNodeDisruptionPod(kubeClient kubernetes.Interface, nodeName string, a
193193
}
194194
return err
195195
}
196+
197+
// GetReadySchedulableWorkerNodes returns ready schedulable worker nodes.
198+
// This function filters out nodes with NoSchedule/NoExecute taints and nodes
199+
// with control-plane/master roles, making it suitable for tests that need to
200+
// select pure worker nodes for workload placement (like MachineConfigPool assignments).
201+
func GetReadySchedulableWorkerNodes(ctx context.Context, client kubernetes.Interface) ([]corev1.Node, error) {
202+
// Get all nodes
203+
allNodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
204+
if err != nil {
205+
return nil, err
206+
}
207+
208+
var schedulableWorkerNodes []corev1.Node
209+
for _, node := range allNodes.Items {
210+
// Skip if node is not ready
211+
ready := false
212+
for _, condition := range node.Status.Conditions {
213+
if condition.Type == corev1.NodeReady && condition.Status == corev1.ConditionTrue {
214+
ready = true
215+
break
216+
}
217+
}
218+
if !ready {
219+
continue
220+
}
221+
222+
// Skip if node has NoSchedule or NoExecute taints
223+
hasUnschedulableTaint := false
224+
for _, taint := range node.Spec.Taints {
225+
if taint.Effect == corev1.TaintEffectNoSchedule || taint.Effect == corev1.TaintEffectNoExecute {
226+
hasUnschedulableTaint = true
227+
break
228+
}
229+
}
230+
if hasUnschedulableTaint {
231+
continue
232+
}
233+
234+
// Skip if node has control-plane or master role (we want pure worker nodes)
235+
_, hasControlPlane := node.Labels["node-role.kubernetes.io/control-plane"]
236+
_, hasMaster := node.Labels["node-role.kubernetes.io/master"]
237+
if hasControlPlane || hasMaster {
238+
continue
239+
}
240+
241+
// Only include if node has worker role
242+
if _, hasWorker := node.Labels["node-role.kubernetes.io/worker"]; hasWorker {
243+
schedulableWorkerNodes = append(schedulableWorkerNodes, node)
244+
}
245+
}
246+
247+
return schedulableWorkerNodes, nil
248+
}

0 commit comments

Comments
 (0)