Skip to content

feat(breakfix): implement BFX01-01 as a provider repair-API test - #608

Open
abegnoche wants to merge 5 commits into
mainfrom
feat/bfx01-01-nico-online-repair
Open

feat(breakfix): implement BFX01-01 as a provider repair-API test#608
abegnoche wants to merge 5 commits into
mainfrom
feat/bfx01-01-nico-online-repair

Conversation

@abegnoche

@abegnoche abegnoche commented Aug 27, 2026

Copy link
Copy Markdown
Member

Reworks BFX01-01. Supersedes the approach in #603. Refs #206.

Why

BFX01-01 asked to reset GPUs "via the Breakfix API", but no provider break-fix API exposes an on-demand GPU reset. NICo's machine sub-resources are only bmc/reset, dpu/reprovision, health-report, power, decommission, validation/run — nothing GPU-scoped, and power is PROVIDER_ADMIN-only. The repair automation behind it discovers work by polling and then waits on human tickets, so there is no GPU-reset request/response anywhere to test.

What a tenant can do synchronously is report a GPU fault and watch the provider move the node into a repair state. That is what this tests.

What changed

  • BFX01-01 → the provider API. New request_gpu_repair step uses NICo online repair (PATCH machine with onlineRepair + healthIssue) and asserts the instance goes Ready → Repairing → Ready. New GpuRepairRequestCheck validates it. NICo's own runbook uses a GPU fault as its worked example.
  • Tenant-side reset → BFX01-06. GpuResetCheck keeps its in-cluster SSH behaviour under a new id, with a summary saying what it proves: it exercises the tenant's own node access and passes with or without a provider. The two can't share a test_id — coverage requires shared ids to carry identical labels.
  • Nothing regresses: BFX01-01's only implementation today is the my-isv stub that returns "Not implemented" outside demo mode.

Safety

Mutating requires an opt-in. An auto-discovered target is reported in a skip naming the node (so a plain run is a dry run); the operator confirms with --machine-id or NICO_ALLOW_ONLINE_REPAIR=1. Shared lab sites often carry exactly one tenant instance and it usually isn't yours.

Restore is belt-and-braces: re-mint the token first (a long run can outlive it, and a 401 there would strand the node), fall back to deleting the request-online-repair override, and report needing that fallback on operation.message rather than passing silently. Deadlines are sized so enter + clear + fallback fit the step timeout.

Skips rather than fails when no GPU machine has a Ready instance — unmet precondition, not a provider defect.

Verification

Against a live NICo v2.2.0-pr-242 deployment: both request bodies accepted (enter, and the flag-only exit), credentials clear the permission gate, the rejection path is side-effect-free (machine.go returns on !machine.IsAssigned before the transaction opens), and includeMetadata=false still returns machineCapabilities so GPU detection survives.

make lint, make test, isvctl test validate, plan coverage, suite wiring, and uvx pre-commit run -a pass.

Not verified: the happy-path transition. It needs a site with a tenant instance; ours has none (no OS registered, imageBasedOperatingSystem: false). Skip path, request bodies, polling, and restore are unit-tested — the live transition is not.

Also here

  • delete_if_present promoted into nico_client, fixing a bug in the fallback: a 404 means the override is already gone (success) but was reported as a cleanup failure.
  • list_site_machines takes an optional pre-resolved auth and include_metadata flag, so the step mints one token instead of two. Defaults keep its three other callers unchanged.
  • my-isv/scripts/breakfix/reset_gpus.py renumbered to BFX01-06 — the file external ISVs copy.

Open question

Whether BFX01-06 should exist at all: it has an honest summary but no real implementation, so if #603 is abandoned it's an empty shell. Out of scope here.

abegnoche and others added 5 commits August 27, 2026 10:54
…PU reset

BFX01-01 asked to reset GPUs "via the Breakfix API". No provider break-fix API
exposes an on-demand GPU reset: NICo has none, and the repair automation behind
it discovers work by polling a flag every few minutes and then waits on human
ticket workflows. There is no request/response for a GPU reset to test.

What a tenant can do synchronously is report a GPU fault and watch the provider
move the node into a repair state. BFX01-01 now tests that, through NICo online
repair (PATCH machine with onlineRepair + healthIssue), asserting the assigned
instance goes Ready -> Repairing and back again.

The step emits a structured skip when the site has no GPU machine with a Ready
instance, since that is an unmet precondition rather than a provider defect.
Online repair is cleared in a finally so a node cannot be stranded out of the
allocatable pool, and a failure to clear it fails the step.

The in-cluster tenant-side reset keeps its own check (GpuResetCheck) under the
new id BFX01-06, with a summary stating what it actually proves: it exercises
the tenant's own node access and passes with or without a provider. Its former
description claimed "via the breakfix API", which it never did. The two checks
cannot share one test_id because coverage requires shared ids to carry
identical labels.

Verified against a live NICo v2.2.0 deployment: the request contract is
accepted, and the online-repair guard rejects a machine with no instance before
opening a transaction. The happy-path transition is not yet exercised; it needs
a site with a tenant instance.

Refs #206

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
…node

The worst outcome this step can produce is a node left in Repairing, out of the
allocatable pool and needing a human. Three changes remove the ways that happened.

Re-mint the access token before restoring. Auth was resolved once and reused in
the finally, so a run that outlived a short-lived token (NICo SSA tokens are
minutes) would 401 exactly when clearing online repair -- failing the step and
stranding the node in the same breath.

Fall back to deleting the request-online-repair health override when clearing
online repair does not take effect. Needing the fallback is reported as a
cleanup_warnings finding rather than a failure, since nothing was left behind; a
node still in Repairing after both attempts remains a hard error.

Raise the state-poll deadline from 180s to 300s, because NICo applies the
override through a site workflow that may queue, and a false "never entered
repair" is worse than waiting. The step timeout goes to 900s so two full polls
plus overhead cannot be killed mid-run -- being killed is now the only remaining
way to strand a node, since no finally executes.

Test note: _await_status binds its deadline as a default argument, so tests
advance a fake clock instead of lowering the constant. Patching only sleep left
monotonic real and spun for the full deadline.

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
Follow-up cleanup on the BFX01-01 online-repair step. No behavior change
beyond the two noted below.

Reuse:
- Collapse _clear_online_repair/_remove_repair_override, two copies of the
  same mutate/await/compare skeleton, into one _attempt_exit taking the
  mutation as a callable.
- Promote _delete_if_present from auth/_key_access.py into nico_client as
  delete_if_present, and use it for the override-removal fallback. A 404
  there means the override is already gone, which the bare forge_delete
  reported as a cleanup failure.
- Read instance status through common.inventory.first_string, which also
  tolerates the state/instanceState spellings NICo uses elsewhere.

Simplification:
- Drop `entered` (a duplicate of operation["requested"]) and the
  unreachable-false `auth is not None` guard from the restore finally.
- Build the operation contract literal once instead of twice.
- Fetch status in one place in the poll loop; inline _restore_token.
- Trim the module docstring's duplicated JSON contract and restore
  narrative, both already stated at _restore and in the suite README.

Efficiency:
- list_site_machines accepts a pre-resolved auth and an include_metadata
  flag, so the step mints one token instead of two and skips metadata it
  never reads.
- Give the override-removal fallback a shorter deadline. It previously
  re-armed a full 300s, so enter+clear+fallback could reach the 900s step
  timeout and be killed mid-restore -- the outcome the finally exists to
  prevent.

Altitude:
- cleanup_warnings was a top-level key no validation read, so a provider
  whose documented exit path silently failed still passed with a clean
  message. Carry the finding on operation.message, which _OperationCheck
  already surfaces, replacing the constant (restored=True) suffix.
- Point the my-isv reset_gpus scaffold at BFX01-06 after the renumbering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
…pair

The step auto-selected the first eligible machine and mutated it. On a shared lab
site that is dangerous: such sites routinely carry exactly one tenant instance,
and it is usually somebody else's. Pointing the NICo config at one would have put
a stranger's workload into Repairing.

An auto-discovered target is now reported in a structured skip that names the node
instead of mutating it, which also makes a plain run a dry run. The operator opts
in with --machine-id <id> to confirm that node, or NICO_ALLOW_ONLINE_REPAIR=1 to
accept whichever eligible node is found.

This inverts the sibling query_key_access.py convention, which mutates by default
and opts out via --no-provision. The difference is blast radius: minting a
throwaway SSH key affects nobody, moving a stranger's instance into a repair state
does. Noted at the constant so the divergence reads as deliberate.

Also drop the speculative state/instanceState fallbacks when reading instance
status. Only DpuMachine spells it `state`; Instance has just `status`, so the
extra keys were flexibility nothing asked for.

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
@abegnoche
abegnoche requested a review from a team as a code owner August 27, 2026 15:59
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 49d0e993-df9d-4c4c-8a5c-281c9e271a04

📥 Commits

Reviewing files that changed from the base of the PR and between dfed195 and cbb8868.

📒 Files selected for processing (16)
  • docs/requirements/test-requirements-matrix.adoc
  • docs/test-plan.adoc
  • docs/test-plan.yaml
  • isvctl/configs/providers/my-isv/scripts/breakfix/reset_gpus.py
  • isvctl/configs/providers/nico/config/bare_metal.yaml
  • isvctl/configs/providers/nico/scripts/auth/_key_access.py
  • isvctl/configs/providers/nico/scripts/breakfix/_common.py
  • isvctl/configs/providers/nico/scripts/breakfix/request_gpu_repair.py
  • isvctl/configs/providers/nico/scripts/common/nico_client.py
  • isvctl/configs/suites/README.md
  • isvctl/configs/suites/bare_metal.yaml
  • isvctl/configs/suites/k8s.yaml
  • isvctl/src/isvctl/config/env_catalog.py
  • isvctl/tests/providers/nico/test_nico_provider.py
  • isvtest/src/isvtest/validations/breakfix.py
  • isvtest/tests/test_breakfix.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


📝 Walkthrough

Walkthrough

The PR separates provider GPU repair validation from tenant-side GPU reset validation. It adds a NICo GPU repair workflow with polling and restoration, shared cleanup support, suite wiring, test coverage, and updated test-plan metadata.

Changes

GPU repair validation

Layer / File(s) Summary
Test contracts and validation checks
docs/requirements/test-requirements-matrix.adoc, docs/test-plan.*, isvtest/src/isvtest/validations/breakfix.py, isvtest/tests/test_breakfix.py
BFX01-01 now reports a GPU fault and checks for repair state. BFX01-06 covers tenant-side GPU reset. Validation and unit tests cover repair-state failures, node identification, provider messages, and cleanup results.
NICo shared API and cleanup support
isvctl/configs/providers/nico/scripts/common/nico_client.py, isvctl/configs/providers/nico/scripts/auth/_key_access.py, isvctl/configs/providers/nico/scripts/breakfix/_common.py, isvctl/src/isvctl/config/env_catalog.py, isvctl/tests/providers/nico/test_nico_provider.py
NICo uses shared 404-tolerant deletion, optional authentication reuse, configurable metadata requests, and the NICO_ALLOW_ONLINE_REPAIR flag. Tests cover deletion behavior.
NICo GPU repair workflow
isvctl/configs/providers/nico/scripts/breakfix/request_gpu_repair.py, isvctl/configs/providers/nico/config/bare_metal.yaml, isvctl/tests/providers/nico/test_nico_provider.py
The workflow selects eligible machines, reports GPU faults, polls for Repairing, restores the machine, handles authentication fallback, and reports structured errors.
Suite wiring and reset classification
isvctl/configs/suites/bare_metal.yaml, isvctl/configs/suites/k8s.yaml, isvctl/configs/suites/README.md, isvctl/configs/providers/my-isv/scripts/breakfix/reset_gpus.py
The bare-metal suite adds BFX01-01 GPU repair validation. Kubernetes reset documentation and identifiers change to BFX01-06.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to cbb88

The change adds an opt-in provider repair test with guarded target selection and restoration handling, while preserving the existing tenant-side check under a corrected identifier. No actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant GPURepairCLI
  participant NicoAPI
  participant RepairRestoration
  GPURepairCLI->>NicoAPI: Discover eligible GPU machines
  GPURepairCLI->>NicoAPI: Report GPU health issue
  NicoAPI-->>GPURepairCLI: Return Repairing state
  GPURepairCLI->>RepairRestoration: Begin cleanup
  RepairRestoration->>NicoAPI: Clear online repair
  NicoAPI-->>RepairRestoration: Return Ready state
Loading

Suggested reviewers: daluz, mresvanis

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 96.30% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 54 functions across 9 files. (7 skipped: 7 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: implementing BFX01-01 as a provider repair API test.
Full details: Docstring Coverage

Explanation

Docstring coverage is 96.30% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 54 functions across 9 files. (7 skipped: 7 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bfx01-01-nico-online-repair

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

@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-08-27 16:00:41 UTC | Commit: cbb8868

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.

1 participant