Skip to content

test(breakfix): implement BFX01-04 cordon validation - #570

Closed
osu wants to merge 1 commit into
NVIDIA:cursor/bfx-remediation-suite-4f18from
osu:issue-209-cordon-e2e
Closed

test(breakfix): implement BFX01-04 cordon validation#570
osu wants to merge 1 commit into
NVIDIA:cursor/bfx-remediation-suite-4f18from
osu:issue-209-cordon-e2e

Conversation

@osu

@osu osu commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • replace the my-isv BFX01-04 placeholder with a real, reversible Kubernetes cordon workflow
  • prove that the selected node becomes unschedulable, an existing probe remains running, and a new node-targeted probe is rejected by the scheduler
  • always remove probe pods and uncordon the node, treating cleanup failures as test failures
  • support configured kubectl-compatible commands, optional node/namespace/image selection, and pre-existing GPU-node taints without tolerating the cordon itself
  • add unit coverage for success, failure cleanup, pre-cordoned nodes, uncordon failure, and taint handling

Why

PR #562 introduces the BFX01-04 validation contract but leaves the provider script as a dummy-success scaffold. This implements the actual Kubernetes behavior required by #209.

This PR is stacked on #562 because the break-fix validation and provider wiring do not exist on main yet.

Impact

Providers using the my-isv Kubernetes configuration now get evidence-backed BFX01-04 results instead of a placeholder. The workflow requires permission to create/delete pods and cordon/uncordon nodes.

Validation

  • live run against an isolated k3s v1.35.0 cluster: all three operation fields returned true
  • independently verified the node was Ready and schedulable afterward, with no BFX probe pods remaining
  • focused tests: 18 passed
  • full make test: 1505 passed, 58 passed, 1323 passed with 175 deselected, and 124 passed
  • pre-commit checks passed for both changed files

Closes #209

Summary by CodeRabbit

  • New Features

    • Added functional Kubernetes node cordoning validation.
    • Automatically selects or accepts a target node and verifies workloads before and after cordoning.
    • Supports configurable commands, namespace, image, node, timeout, and polling interval.
    • Provides structured results and reports cleanup failures.
  • Bug Fixes

    • Ensures temporary resources are removed and nodes are restored after execution.
  • Tests

    • Added coverage for GPU taints, existing workloads, already-cordoned nodes, successful cleanup, and cleanup failures.

Signed-off-by: Hasan Khan <hasank@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • release/*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 103a41b8-05e0-49f0-b66d-5ea11118bcfc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)

Comment @coderabbitai help to get the list of available commands.

@osu
osu marked this pull request as ready for review August 6, 2026 05:56
@osu
osu requested a review from a team as a code owner August 6, 2026 05:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
isvctl/tests/test_my_isv_cordon_node.py (1)

193-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add cases for the untested failure branches.

The suite covers the uncordon failure but not these branches:

  • _wait_for_unschedulable returning False on timeout, which produces the "New probe pod was not confirmed unschedulable" error.
  • The delete-pod failure branch in _cleanup, which appends a delete pod ... entry to cleanup_errors.
  • _kubectl_command with a blank or unbalanced-quote KUBECTL value.

If you add the ISVCTL_DEMO_MODE gate requested on the script, also add a demo-mode test that asserts no kubectl call occurs. I can draft these tests if you want.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@isvctl/tests/test_my_isv_cordon_node.py` around lines 193 - 207, Add tests
covering the missing failure branches: make _wait_for_unschedulable return False
and assert the “New probe pod was not confirmed unschedulable” error, make pod
deletion fail in _cleanup and assert cleanup_errors includes a “delete pod …”
entry, and verify _kubectl_command rejects blank and unbalanced-quote KUBECTL
values. If ISVCTL_DEMO_MODE is added, also test that the workflow makes no
kubectl calls in demo mode.
isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py (1)

142-147: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the probe pod lifetime with activeDeadlineSeconds.

Cleanup runs only in the finally block of main. If the process is killed, the probe pods stay in the namespace. A pod-level deadline lets Kubernetes remove them without the script.

♻️ Proposed change
             "spec": {
                 "restartPolicy": "Never",
+                "activeDeadlineSeconds": 3600,
                 "nodeSelector": {"kubernetes.io/hostname": hostname},
                 "tolerations": tolerations,
                 "containers": [{"name": "probe", "image": image}],
             },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py` around lines
142 - 147, Update the probe pod template in the relevant pod-creation flow to
include a pod-level activeDeadlineSeconds value, using an appropriate finite
timeout for the probe lifetime. Keep the existing restartPolicy, nodeSelector,
tolerations, and container configuration unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py`:
- Around line 238-254: Restore the ISVCTL_DEMO_MODE=1 check at the start of
main, before _kubectl_command() is called, and return the expected dummy
successful provider-neutral result without performing cluster operations.
Preserve the existing validation and cordon workflow for non-demo runs.

---

Nitpick comments:
In `@isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py`:
- Around line 142-147: Update the probe pod template in the relevant
pod-creation flow to include a pod-level activeDeadlineSeconds value, using an
appropriate finite timeout for the probe lifetime. Keep the existing
restartPolicy, nodeSelector, tolerations, and container configuration unchanged.

In `@isvctl/tests/test_my_isv_cordon_node.py`:
- Around line 193-207: Add tests covering the missing failure branches: make
_wait_for_unschedulable return False and assert the “New probe pod was not
confirmed unschedulable” error, make pod deletion fail in _cleanup and assert
cleanup_errors includes a “delete pod …” entry, and verify _kubectl_command
rejects blank and unbalanced-quote KUBECTL values. If ISVCTL_DEMO_MODE is added,
also test that the workflow makes no kubectl calls in demo mode.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6ac58286-9545-4069-9971-742327eaff9d

📥 Commits

Reviewing files that changed from the base of the PR and between b229d75 and 974d11f.

📒 Files selected for processing (2)
  • isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py
  • isvctl/tests/test_my_isv_cordon_node.py

Comment on lines +238 to +254
def main() -> int:
"""Run the reversible cordon test and emit its provider-neutral JSON result."""
args = _parser().parse_args()
operation: dict[str, Any] = {
"cordoned": False,
"new_workloads_blocked": False,
"existing_workloads_running": False,
}
result: dict[str, Any] = {"success": False, "platform": "my-isv", "test_name": "cordon_node"}
kubectl: list[str] = []
created_pods: list[str] = []
cordoned_node: str | None = None

try:
if args.timeout_seconds <= 0 or args.poll_interval_seconds <= 0:
raise CordonTestError("Timeout and poll interval must be greater than zero")
kubectl = _kubectl_command()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check the DEMO_MODE gate convention across my-isv provider scripts.
fd -e py . isvctl/configs/providers/my-isv/scripts --exec rg -n 'ISVCTL_DEMO_MODE|^DEMO_MODE|if DEMO_MODE' {} \;

Repository: NVIDIA/ai-cloud-validation

Length of output: 16092


🏁 Script executed:

#!/bin/bash
set -eu

file="isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py"

printf '%s\n' '--- imports and demo references ---'
rg -n '^(import|from) |DEMO_MODE|ISVCTL_DEMO_MODE|kubectl|def main' "$file"

printf '%s\n' '--- main body ---'
sed -n '230,330p' "$file"

printf '%s\n' '--- sibling convention with filenames ---'
fd -e py . isvctl/configs/providers/my-isv/scripts -0 |
  xargs -0 rg -l 'ISVCTL_DEMO_MODE' |
  head -n 8 |
  while IFS= read -r sibling; do
    printf '\n%s\n' "$sibling"
    rg -n -A8 -B2 'DEMO_MODE = os\.environ\.get\("ISVCTL_DEMO_MODE"\) == "1"|if DEMO_MODE' "$sibling" | head -n 24
  done

printf '%s\n' '--- static control-flow check ---'
python3 - <<'PY'
import ast
from pathlib import Path

path = Path("isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py")
tree = ast.parse(path.read_text())
main = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "main")
names = {
    node.id
    for node in ast.walk(main)
    if isinstance(node, ast.Name)
}
calls = [
    ast.unparse(node.func)
    for node in ast.walk(main)
    if isinstance(node, ast.Call)
]
print("main references DEMO_MODE:", "DEMO_MODE" in names)
print("main calls kubectl-related helpers:", [call for call in calls if "kubectl" in call.lower()])
print("main first statement:", ast.unparse(main.body[0]))
PY

Repository: NVIDIA/ai-cloud-validation

Length of output: 14944


Restore the ISVCTL_DEMO_MODE gate.

When ISVCTL_DEMO_MODE=1, return dummy success before _kubectl_command(). Without this gate, demo mode can cordon a real node and create probe pods in a live cluster. Keep the implemented workflow for real runs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@isvctl/configs/providers/my-isv/scripts/breakfix/cordon_node.py` around lines
238 - 254, Restore the ISVCTL_DEMO_MODE=1 check at the start of main, before
_kubectl_command() is called, and return the expected dummy successful
provider-neutral result without performing cluster operations. Preserve the
existing validation and cordon workflow for non-demo runs.

Source: Coding guidelines

@abegnoche
abegnoche deleted the branch NVIDIA:cursor/bfx-remediation-suite-4f18 August 7, 2026 15:08
@abegnoche abegnoche closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants