Skip to content

feat: detect verified boot configuration drift - #4472

Open
chet wants to merge 1 commit into
NVIDIA:mainfrom
chet:gh-issue-4248
Open

feat: detect verified boot configuration drift#4472
chet wants to merge 1 commit into
NVIDIA:mainfrom
chet:gh-issue-4248

Conversation

@chet

@chet chet commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

An already-verified desired boot interface was not observed again, so later Redfish drift could remain invisible. This adds a paced, restart-safe observer that reuses the existing boot-configuration state machine instead of creating a second remediation path.

The observer lazily schedules verified rows across the normal interval, claims due work by persisting its retry deadline before Redfish I/O, and compares the exact stored Pair or MacOnly target. A correct read refreshes the observation. A mismatch atomically reopens the same target as pending intent: Ready enters the existing BootConfiguring flow on its next sweep, while Assigned records the drift without disruption until release.

The schema migration is a nullable, data-free column addition with no backfill. Managed-DPU hosts wait for reports that are both newer than the current host state and inside the configured DPU health window. Locked Supermicro hosts are excluded because their boot-order view is not trustworthy without a disruptive unlock and reboot.

Related issues

This supports #4248

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Validation completed on base 12bd2cc2c:

  • 5 periodic-observation controller scenarios
  • 2 observer scheduling/freshness unit tests
  • 17 desired boot-interface database tests, including migration rollout and concurrency/CAS coverage
  • 72 configuration tests
  • nightly formatting and diff checks
  • Clippy for all targets/features of the four affected crates with warnings denied
  • workspace custom carbide lints
  • two independent final correctness/scope reviews

Additional Notes

Defaults are 24 hours between successful observations and 15 minutes after an inconclusive attempt. Both intervals are configurable and must be positive. The persisted retry deadline preserves pacing and last-known-good verification across BMC failures and controller restarts.

@copy-pr-bot

copy-pr-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

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: 0f3fb4a4-5f84-47e4-918c-be7164acf542

📥 Commits

Reviewing files that changed from the base of the PR and between eb6ac57 and 4e36647.

📒 Files selected for processing (10)
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/cfg/file.rs
  • crates/api-core/src/cfg/test_data/full_config.toml
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/test_data/site_config.toml
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-db/src/machine_desired_boot_interface.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/boot_interface_observation.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/api-core/src/cfg/test_data/site_config.toml
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/cfg/file.rs
  • crates/machine-controller/src/handler/boot_interface_observation.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/api-db/src/machine_desired_boot_interface.rs
  • crates/api-core/src/tests/machine_states.rs

Summary by CodeRabbit

  • New Features

    • Added periodic, read-only Redfish checks of host boot-interface configuration.
    • Configurable observation intervals are supported, with a 10-minute default.
    • Detected boot-configuration drift automatically reopens reconciliation for eligible Ready hosts.
    • Assigned hosts defer remediation until release; locked Supermicro systems remain unchanged unless lockdown is disabled.
  • Bug Fixes

    • Prevented stale observations and repeated pending updates from triggering incorrect reconciliation.
    • Checks now wait for current network observations before evaluating configuration.
    • Invalid zero or negative observation intervals are rejected.

Walkthrough

The change adds a positive boot-interface observation interval, periodic read-only Redfish checks, claim-validated drift reopening, and machine-state coverage for refresh, deferral, convergence, assigned hosts, and locked Supermicro systems.

Changes

Boot-interface observation

Layer / File(s) Summary
Observation configuration and fixtures
crates/machine-controller/src/config/controller.rs, crates/api-core/src/cfg/*
MachineStateControllerConfig validates positive observation intervals, provides a ten-minute default, and includes the setting in documentation, fixtures, serialization, and deserialization tests.
Claim-validated drift reopening
crates/api-db/src/machine_desired_boot_interface.rs
reopen_after_observed_drift validates the observed target and verified generation before creating one pending generation. Tests cover stale observations and repeated reopening.
Controller observation execution
crates/machine-controller/src/handler.rs, crates/machine-controller/src/handler/boot_interface_observation.rs
Ready and assigned state handling invokes the observer. The observer checks scheduling, verified generations, DPU freshness, vendor lockdown, and Redfish state before persisting observations or reopening drift.
End-to-end machine-state behavior
crates/api-core/src/tests/machine_states.rs
Tests cover unchanged-target refresh, stale-DPU deferral, boot-order drift, assigned hosts, and locked Supermicro hosts. They verify state, generations, observation metadata, and Redfish side effects.

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

Sequence Diagram(s)

sequenceDiagram
  participant MachineStateHandler
  participant BootInterfaceObservation
  participant Redfish
  participant machine_desired_boot_interface
  MachineStateHandler->>BootInterfaceObservation: run verified observation
  BootInterfaceObservation->>Redfish: read boot order
  Redfish-->>BootInterfaceObservation: observed boot interface
  BootInterfaceObservation->>machine_desired_boot_interface: record observation or reopen drift
  machine_desired_boot_interface-->>MachineStateHandler: return state-machine outcome
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: detecting drift in verified boot configuration.
Description check ✅ Passed The description accurately explains the observer, drift handling, scheduling, eligibility rules, schema change, and validation performed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@chet
chet marked this pull request as ready for review August 1, 2026 01:51
@chet
chet requested a review from a team as a code owner August 1, 2026 01:51
@chet

chet commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks!

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@chet: I will perform a complete review of pull request #4472.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (3)
crates/machine-controller/src/handler/boot_interface_observation.rs (1)

95-100: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider returning the desired version from the schedule guard to remove the expect.

The expect at line 99 is provably unreachable today. should_check_schedule at line 82 already rejects a None desired target, and machine is not mutated in between. However, the invariant is spread over two independent reads of the same Option, so a later change to should_check_schedule could turn this into a panic on a controller sweep.

Returning the version from the guard makes the invariant type-enforced instead of comment-enforced.

♻️ Proposed refactor to remove the panic path
-/// Returns whether the snapshot needs a database scheduling or claim check.
-fn should_check_schedule(machine: &Machine, now: DateTime<Utc>) -> bool {
+/// Returns the due desired generation that needs a database scheduling or
+/// claim check, if any.
+fn due_desired_version(machine: &Machine, now: DateTime<Utc>) -> Option<ConfigVersion> {
     let Some(desired) = machine.config.desired_boot_interface.as_ref() else {
-        return false;
+        return None;
     };
     let Some(verified) = machine.status.boot_interface_status_observation.as_ref() else {
-        return false;
+        return None;
     };
 
-    desired.version == verified.config_version
+    let due = desired.version == verified.config_version
         && machine
             .status
             .boot_interface_next_observation_at
-            .is_none_or(|next_observation_at| next_observation_at <= now)
+            .is_none_or(|next_observation_at| next_observation_at <= now);
+    due.then_some(desired.version)
 }

Then at the call site:

     let machine = &mh_snapshot.host_snapshot;
-    if !should_check_schedule(machine, Utc::now()) {
-        return Ok(None);
-    }
+    let Some(desired_version) = due_desired_version(machine, Utc::now()) else {
+        return Ok(None);
+    };
@@
-    let desired_version = machine
-        .config
-        .desired_boot_interface
-        .as_ref()
-        .expect("schedule check requires a desired boot interface")
-        .version;
     let config = &ctx.services.site_config.machine_state_controller;

The Supermicro guard at lines 91-93 stays between the two, and the unit test at line 234 adapts to assert on Option presence.

🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 95 - 100, Update should_check_schedule to return the desired boot
interface version when a schedule check is required, rather than only indicating
eligibility. At its caller, preserve the Supermicro guard, use the returned
version directly, and remove the independent desired_boot_interface lookup and
expect panic path. Adapt the unit test to verify the returned Option and
version.
crates/api-core/src/cfg/file.rs (1)

4059-4074: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Express the rejection cases as a table-driven test.

The manual for loop covers the four invalid inputs correctly. However, this module already imports Check and check_values (line 3559) and neighbouring tests use them. A table gives each case a scenario label, so a failure names the offending field and value directly.

♻️ Proposed table-driven rewrite
-    /// Observation cadence must always schedule work strictly in the future.
-    #[test]
-    fn reject_nonpositive_boot_interface_observation_intervals() {
-        for (field, value) in [
-            ("boot_interface_observation_interval", "0s"),
-            ("boot_interface_observation_interval", "-1s"),
-            ("boot_interface_observation_retry_interval", "0s"),
-            ("boot_interface_observation_retry_interval", "-1s"),
-        ] {
-            let config = format!(r#"{{"{field}": "{value}"}}"#);
-            assert!(
-                serde_json::from_str::<MachineStateControllerConfig>(&config).is_err(),
-                "{field}={value} must be rejected",
-            );
-        }
-    }
+    /// Observation cadence must always schedule work strictly in the future.
+    #[test]
+    fn reject_nonpositive_boot_interface_observation_intervals() {
+        check_values(
+            [
+                Check {
+                    scenario: "zero observation interval",
+                    input: ("boot_interface_observation_interval", "0s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "negative observation interval",
+                    input: ("boot_interface_observation_interval", "-1s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "zero retry interval",
+                    input: ("boot_interface_observation_retry_interval", "0s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "negative retry interval",
+                    input: ("boot_interface_observation_retry_interval", "-1s"),
+                    expect: true,
+                },
+            ],
+            |(field, value)| {
+                let config = format!(r#"{{"{field}": "{value}"}}"#);
+                serde_json::from_str::<MachineStateControllerConfig>(&config).is_err()
+            },
+        );
+    }

As per coding guidelines: "Use table-driven tests for functions mapping inputs to outputs, errors, or observable results. Use scenarios! with Outcome for Result-returning operations, value_scenarios! for total operations, and direct check_cases/check_values when macros obscure the table."

🤖 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 `@crates/api-core/src/cfg/file.rs` around lines 4059 - 4074, Rewrite
reject_nonpositive_boot_interface_observation_intervals as a table-driven test
using the module’s existing Check/check_values testing helpers, with a named
scenario for each invalid field/value combination. Preserve the current
assertion that every zero or negative observation interval causes
MachineStateControllerConfig deserialization to fail, while ensuring failure
output identifies the specific field and value.

Source: Coding guidelines

crates/api-db/src/machine_desired_boot_interface.rs (1)

758-818: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider folding the claim status read into load_for_update.

reopen_after_observed_drift locks the row twice: once through load_for_update and again through the SELECT verified_version, next_observation_at ... FOR UPDATE. The second statement exists only because the DesiredBootInterfaceRow projection omits the status columns. Extending that projection (or adding a dedicated load_status_for_update) would remove one round trip and keep the claim comparison next to the target comparison. The current behavior is correct, so treat this as cleanup rather than a defect.

🤖 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 `@crates/api-db/src/machine_desired_boot_interface.rs` around lines 758 - 818,
Refactor reopen_after_observed_drift to obtain verified_version and
next_observation_at through load_for_update, or a dedicated
load_status_for_update helper, instead of issuing the second SELECT ... FOR
UPDATE. Preserve the existing claim-status comparison and locking semantics
while eliminating the redundant round trip.
🤖 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 `@crates/api-core/src/tests/machine_states.rs`:
- Around line 3709-3714: Update the assertion in the deferred-read test around
host.status.boot_interface_next_observation_at to compare the persisted deadline
against the backdated baseline value rather than Utc::now(). Preserve the
existing Some/deadline validation while asserting that the deadline advanced
past the original backdated timestamp, making the test independent of wall-clock
timing.

---

Nitpick comments:
In `@crates/api-core/src/cfg/file.rs`:
- Around line 4059-4074: Rewrite
reject_nonpositive_boot_interface_observation_intervals as a table-driven test
using the module’s existing Check/check_values testing helpers, with a named
scenario for each invalid field/value combination. Preserve the current
assertion that every zero or negative observation interval causes
MachineStateControllerConfig deserialization to fail, while ensuring failure
output identifies the specific field and value.

In `@crates/api-db/src/machine_desired_boot_interface.rs`:
- Around line 758-818: Refactor reopen_after_observed_drift to obtain
verified_version and next_observation_at through load_for_update, or a dedicated
load_status_for_update helper, instead of issuing the second SELECT ... FOR
UPDATE. Preserve the existing claim-status comparison and locking semantics
while eliminating the redundant round trip.

In `@crates/machine-controller/src/handler/boot_interface_observation.rs`:
- Around line 95-100: Update should_check_schedule to return the desired boot
interface version when a schedule check is required, rather than only indicating
eligibility. At its caller, preserve the Supermicro guard, use the returned
version directly, and remove the independent desired_boot_interface lookup and
expect panic path. Adapt the unit test to verify the returned Option and
version.
🪄 Autofix (Beta)

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: 06e31a8c-2e35-4a85-8531-39c99ffdaf4f

📥 Commits

Reviewing files that changed from the base of the PR and between 12bd2cc and 1f71881.

📒 Files selected for processing (16)
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/cfg/file.rs
  • crates/api-core/src/cfg/test_data/full_config.toml
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/test_data/site_config.toml
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-db/migrations/20260731161510_machine_boot_interface_observation_schedule.sql
  • crates/api-db/src/machine_desired_boot_interface.rs
  • crates/api-db/src/sql/machine_snapshots.sql.template
  • crates/api-db/src/sql/managed_hosts.sql.template
  • crates/api-model/src/machine/json.rs
  • crates/api-model/src/machine/status.rs
  • crates/api-model/src/test_support/machine_snapshot.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/boot_interface_observation.rs

Comment thread crates/api-core/src/tests/machine_states.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/api-core/src/tests/machine_states.rs (1)

3709-3714: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Make this retry-deadline assertion independent of wall-clock timing.

The claimed deadline equals CURRENT_TIMESTAMP + boot_interface_observation_retry_interval, and MachineStateControllerConfig::test_default() sets that retry interval to one second. This assertion passes only if the controller iteration, the commit, and this follow-up read complete within one second of the claim. A loaded CI runner can exceed that budget, which makes the test flaky.

Assert that the deadline advanced past the backdated baseline value instead of comparing it against Utc::now().

💚 Suggested timing-independent assertion
+    let deadline_before = before
+        .host_snapshot
+        .status
+        .boot_interface_next_observation_at
+        .expect("the fixture deadline should be backdated and due");
     assert!(
         host.status
             .boot_interface_next_observation_at
-            .is_some_and(|deadline| deadline > Utc::now()),
-        "the deferred read should retain the persisted retry deadline"
+            .is_some_and(|deadline| deadline > deadline_before),
+        "the deferred read should persist a fresh retry deadline"
     );
🤖 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 `@crates/api-core/src/tests/machine_states.rs` around lines 3709 - 3714, Update
the retry-deadline assertion in the machine state test to compare
boot_interface_next_observation_at against the backdated baseline timestamp used
before claiming, asserting it advanced beyond that value rather than comparing
with Utc::now(). Preserve the existing validation that the deadline is present
and the deferred read retains the persisted retry deadline.
🧹 Nitpick comments (4)
crates/api-core/src/cfg/file.rs (1)

4059-4075: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use check_values for this table-style rejection test.

This test loops over four input pairs and asserts a boolean outcome for each. This file already uses check_values for the same pattern, for example in insecure_discovery_configuration_is_opt_in. Convert this test to check_values for a named scenario per case and for consistency with the rest of the file.

♻️ Proposed refactor to `check_values`
-    /// Observation cadence must always schedule work strictly in the future.
-    #[test]
-    fn reject_nonpositive_boot_interface_observation_intervals() {
-        for (field, value) in [
-            ("boot_interface_observation_interval", "0s"),
-            ("boot_interface_observation_interval", "-1s"),
-            ("boot_interface_observation_retry_interval", "0s"),
-            ("boot_interface_observation_retry_interval", "-1s"),
-        ] {
-            let config = format!(r#"{{"{field}": "{value}"}}"#);
-            assert!(
-                serde_json::from_str::<MachineStateControllerConfig>(&config).is_err(),
-                "{field}={value} must be rejected",
-            );
-        }
-    }
+    /// Observation cadence must always schedule work strictly in the future.
+    #[test]
+    fn reject_nonpositive_boot_interface_observation_intervals() {
+        check_values(
+            [
+                Check {
+                    scenario: "zero observation interval",
+                    input: ("boot_interface_observation_interval", "0s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "negative observation interval",
+                    input: ("boot_interface_observation_interval", "-1s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "zero retry interval",
+                    input: ("boot_interface_observation_retry_interval", "0s"),
+                    expect: true,
+                },
+                Check {
+                    scenario: "negative retry interval",
+                    input: ("boot_interface_observation_retry_interval", "-1s"),
+                    expect: true,
+                },
+            ],
+            |(field, value)| {
+                let config = format!(r#"{{"{field}": "{value}"}}"#);
+                serde_json::from_str::<MachineStateControllerConfig>(&config).is_err()
+            },
+        );
+    }

As per coding guidelines, "Use table-driven tests for functions mapping inputs to outputs, errors, or observable results... direct check_cases/check_values when macros obscure the table."

🤖 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 `@crates/api-core/src/cfg/file.rs` around lines 4059 - 4075, Refactor
reject_nonpositive_boot_interface_observation_intervals to use the existing
check_values helper, defining a named scenario for each interval field/value
pair and its expected rejection outcome. Match the established usage in
insecure_discovery_configuration_is_opt_in, preserving the current assertion
that all zero and negative intervals are rejected.

Source: Coding guidelines

crates/api-db/src/machine_desired_boot_interface.rs (1)

782-795: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider folding the claim status read into load_for_update.

load_for_update already locks and projects the row at Line 773. The additional SELECT ... FOR UPDATE re-reads the same row for two columns. If load_for_update also projected verified_version and next_observation_at, this second round trip would disappear and the claim check would stay in one place. This is optional; the current form is correct.

🤖 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 `@crates/api-db/src/machine_desired_boot_interface.rs` around lines 782 - 795,
Optionally extend load_for_update to project verified_version and
next_observation_at, then reuse those returned values for the claim status
comparison instead of issuing the separate claim_query SELECT ... FOR UPDATE.
Preserve the existing mismatch behavior that returns Ok(None).
crates/machine-controller/src/handler/boot_interface_observation.rs (2)

232-290: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Convert both unit tests to the repository's table-driven helpers.

should_check_schedule and dpu_observation_is_current are total functions that map inputs to a bool. The repository guideline requires table-driven coverage for this shape. value_scenarios! or check_values would express the deadline variants (None, past, future, superseded version) and the freshness boundary as an explicit table, and would report each failing case individually instead of stopping at the first assertion.

Based on the coding guideline "Use table-driven tests for functions mapping inputs to outputs, errors, or observable results. Use scenarios! with Outcome for Result-returning operations, value_scenarios! for total operations, and direct check_cases/check_values when macros obscure the table."

🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 232 - 290, Convert schedule_check_only_runs_for_due_verified_intent and
managed_dpu_observation_must_also_be_recent to table-driven tests using the
repository’s value_scenarios! or check_values helpers. Represent the None, past,
future, and superseded-version cases for should_check_schedule, plus the
freshness boundary cases for dpu_observation_is_current, as explicit independent
scenarios with expected boolean results.

Source: Coding guidelines


82-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Return the desired version from the schedule check to remove the expect.

should_check_schedule already proves that desired_boot_interface is Some. The expect at Line 99 re-derives that fact and leaves a panic that depends on an invariant held in another function. Returning the version makes the invariant explicit and hard to misuse.

♻️ Proposed refactor
-/// Returns whether the snapshot needs a database scheduling or claim check.
-fn should_check_schedule(machine: &Machine, now: DateTime<Utc>) -> bool {
+/// Returns the desired version that needs a database scheduling or claim
+/// check, or `None` when the snapshot is not eligible.
+fn schedule_check_version(machine: &Machine, now: DateTime<Utc>) -> Option<ConfigVersion> {
     let Some(desired) = machine.config.desired_boot_interface.as_ref() else {
-        return false;
+        return None;
     };
     let Some(verified) = machine.status.boot_interface_status_observation.as_ref() else {
-        return false;
+        return None;
     };
 
-    desired.version == verified.config_version
+    (desired.version == verified.config_version
         && machine
             .status
             .boot_interface_next_observation_at
-            .is_none_or(|next_observation_at| next_observation_at <= now)
+            .is_none_or(|next_observation_at| next_observation_at <= now))
+        .then_some(desired.version)
 }
     let machine = &mh_snapshot.host_snapshot;
-    if !should_check_schedule(machine, Utc::now()) {
-        return Ok(None);
-    }
+    let Some(desired_version) = schedule_check_version(machine, Utc::now()) else {
+        return Ok(None);
+    };
@@
-    let desired_version = machine
-        .config
-        .desired_boot_interface
-        .as_ref()
-        .expect("schedule check requires a desired boot interface")
-        .version;
     let config = &ctx.services.site_config.machine_state_controller;
🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 82 - 100, Update should_check_schedule to return the desired
boot-interface version when a schedule check is required, and adjust its callers
to use that returned value. In the boot observation flow, replace the config
lookup and expect call with the version from should_check_schedule while
preserving the existing early-return behavior.
🤖 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.

Duplicate comments:
In `@crates/api-core/src/tests/machine_states.rs`:
- Around line 3709-3714: Update the retry-deadline assertion in the machine
state test to compare boot_interface_next_observation_at against the backdated
baseline timestamp used before claiming, asserting it advanced beyond that value
rather than comparing with Utc::now(). Preserve the existing validation that the
deadline is present and the deferred read retains the persisted retry deadline.

---

Nitpick comments:
In `@crates/api-core/src/cfg/file.rs`:
- Around line 4059-4075: Refactor
reject_nonpositive_boot_interface_observation_intervals to use the existing
check_values helper, defining a named scenario for each interval field/value
pair and its expected rejection outcome. Match the established usage in
insecure_discovery_configuration_is_opt_in, preserving the current assertion
that all zero and negative intervals are rejected.

In `@crates/api-db/src/machine_desired_boot_interface.rs`:
- Around line 782-795: Optionally extend load_for_update to project
verified_version and next_observation_at, then reuse those returned values for
the claim status comparison instead of issuing the separate claim_query SELECT
... FOR UPDATE. Preserve the existing mismatch behavior that returns Ok(None).

In `@crates/machine-controller/src/handler/boot_interface_observation.rs`:
- Around line 232-290: Convert schedule_check_only_runs_for_due_verified_intent
and managed_dpu_observation_must_also_be_recent to table-driven tests using the
repository’s value_scenarios! or check_values helpers. Represent the None, past,
future, and superseded-version cases for should_check_schedule, plus the
freshness boundary cases for dpu_observation_is_current, as explicit independent
scenarios with expected boolean results.
- Around line 82-100: Update should_check_schedule to return the desired
boot-interface version when a schedule check is required, and adjust its callers
to use that returned value. In the boot observation flow, replace the config
lookup and expect call with the version from should_check_schedule while
preserving the existing early-return behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dea2a8ec-fd9e-4173-9546-ad3cda0afe2a

📥 Commits

Reviewing files that changed from the base of the PR and between 12bd2cc and 1f71881.

📒 Files selected for processing (16)
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/cfg/file.rs
  • crates/api-core/src/cfg/test_data/full_config.toml
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/test_data/site_config.toml
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-db/migrations/20260731161510_machine_boot_interface_observation_schedule.sql
  • crates/api-db/src/machine_desired_boot_interface.rs
  • crates/api-db/src/sql/machine_snapshots.sql.template
  • crates/api-db/src/sql/managed_hosts.sql.template
  • crates/api-model/src/machine/json.rs
  • crates/api-model/src/machine/status.rs
  • crates/api-model/src/test_support/machine_snapshot.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/boot_interface_observation.rs

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
crates/machine-controller/src/handler/boot_interface_observation.rs (3)

114-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a carbide_instrument::Event for observation outcomes.

This observer runs on every host on a fixed cadence. Redfish failures and detected drift are currently visible only in logs. A counter makes the fleet-wide drift rate and the BMC failure rate alertable, which is the main operational value of periodic observation.

If you add an event, keep machine_id and error as #[context] fields, not labels, and put only the bounded outcome and operation in labels.

Based on the coding guideline "declare and emit a carbide_instrument::Event when an event needs a count, rate, or duration" and "use #[context] rather than #[label] for high-cardinality values such as machine IDs, IPs, and error text".

🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 114 - 145, Add and emit a carbide_instrument::Event for each boot
observation outcome, including Redfish client creation failures and host boot
configuration inspection results or drift. Use bounded outcome and operation
values as #[label] fields, while keeping machine_id and error as #[context]
fields; emit the event at the corresponding success or failure points in the
observation flow.

Source: Coding guidelines


79-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider returning the due target from should_observe to remove the expect.

The expect at Line 100 relies on an invariant that should_observe established at Line 38. The invariant holds today, but the panic is only safe as long as the two functions stay in agreement. A future edit to should_observe could make this path panic in production.

If should_observe returns Option<&Versioned<MachineBootInterfaceTarget>>, the panic and the duplicate lookup both disappear.

♻️ Proposed refactor
-/// Returns whether an already-verified target is due for another observation.
-fn should_observe(machine: &Machine, now: DateTime<Utc>, observation_interval: Duration) -> bool {
+/// Returns the already-verified target when it is due for another observation.
+fn due_target(
+    machine: &Machine,
+    now: DateTime<Utc>,
+    observation_interval: Duration,
+) -> Option<&Versioned<MachineBootInterfaceTarget>> {
     let Some(desired) = machine.config.desired_boot_interface.as_ref() else {
-        return false;
+        return None;
     };
     let Some(verified) = machine.status.boot_interface_status_observation.as_ref() else {
-        return false;
+        return None;
     };
 
-    desired.version == verified.config_version
+    (desired.version == verified.config_version
         && now.signed_duration_since(verified.observed_at) >= observation_interval
+    )
+    .then_some(desired)
 }

Then at the call site:

-    if !should_observe(
-        machine,
-        Utc::now(),
-        config.boot_interface_observation_interval,
-    ) {
+    let Some(desired) = due_target(
+        machine,
+        Utc::now(),
+        config.boot_interface_observation_interval,
+    ) else {
         return Ok(None);
-    }
+    };
+    let desired = desired.clone();

The block at Lines 96-101 is then removed.

🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 79 - 101, Update should_observe to return the due
Versioned<MachineBootInterfaceTarget> target as an Option instead of only a
boolean. In the observation flow, consume that returned target for the
early-return check and continue using it as desired, removing the duplicate
desired_boot_interface lookup and its expect while preserving the existing
eligibility and Supermicro lockdown behavior.

217-256: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Convert should_observe coverage to a table and add the two early-return cases.

should_observe is a total function that maps (machine, now, interval) to bool. The current test mutates one shared machine across three assertions, so a failure in the first assertion cascades into the rest, and the exercised precondition is not visible at the failure site.

Two branches are also untested: the None desired target at Line 38 and the None status observation at Line 41. The first branch is the invariant that the expect at Line 100 depends on, so it deserves explicit coverage.

Use value_scenarios! with one row per variant: due, not yet due, version mismatch, no desired target, no status observation. Apply the same treatment to managed_dpu_observation_must_also_be_recent.

As per path instructions: "Prefer table-driven tests, using scenarios!, value_scenarios!, check_cases, or check_values to cover input variants."

🤖 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 `@crates/machine-controller/src/handler/boot_interface_observation.rs` around
lines 217 - 256, Convert the `observation_only_runs_for_due_verified_intent`
test to a `value_scenarios!` table with independent rows for due, not-yet-due,
version mismatch, no desired target, and no status observation, asserting each
expected `should_observe` result. Apply the same table-driven structure to
`managed_dpu_observation_must_also_be_recent`, covering its relevant input
variants and expected outcomes.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@crates/machine-controller/src/handler/boot_interface_observation.rs`:
- Around line 114-145: Add and emit a carbide_instrument::Event for each boot
observation outcome, including Redfish client creation failures and host boot
configuration inspection results or drift. Use bounded outcome and operation
values as #[label] fields, while keeping machine_id and error as #[context]
fields; emit the event at the corresponding success or failure points in the
observation flow.
- Around line 79-101: Update should_observe to return the due
Versioned<MachineBootInterfaceTarget> target as an Option instead of only a
boolean. In the observation flow, consume that returned target for the
early-return check and continue using it as desired, removing the duplicate
desired_boot_interface lookup and its expect while preserving the existing
eligibility and Supermicro lockdown behavior.
- Around line 217-256: Convert the
`observation_only_runs_for_due_verified_intent` test to a `value_scenarios!`
table with independent rows for due, not-yet-due, version mismatch, no desired
target, and no status observation, asserting each expected `should_observe`
result. Apply the same table-driven structure to
`managed_dpu_observation_must_also_be_recent`, covering its relevant input
variants and expected outcomes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 86372a17-18a2-462d-98c8-ec56a279c571

📥 Commits

Reviewing files that changed from the base of the PR and between 1f71881 and c5fc151.

📒 Files selected for processing (10)
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/cfg/file.rs
  • crates/api-core/src/cfg/test_data/full_config.toml
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/test_data/site_config.toml
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-db/src/machine_desired_boot_interface.rs
  • crates/machine-controller/src/config/controller.rs
  • crates/machine-controller/src/handler.rs
  • crates/machine-controller/src/handler/boot_interface_observation.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • crates/api-core/src/cfg/test_data/full_config_post_migration.toml
  • crates/api-core/src/cfg/test_data/full_config.toml
  • crates/api-core/src/cfg/file.rs
  • crates/machine-controller/src/handler.rs
  • crates/api-core/src/cfg/README.md
  • crates/api-core/src/tests/machine_states.rs
  • crates/api-core/src/cfg/test_data/site_config.toml

@chet

chet commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Review follow-up for eb6ac57:

  • Addressed the due-target nit: the eligibility helper now returns the exact desired target, removes the duplicate lookup and panic path, and the sweep samples time once.
  • Kept the compact stateful eligibility/freshness tests instead of expanding them into larger scenario fixtures. Their distinct assertions remain focused, while the integration coverage exercises Ready, Assigned, managed-DPU, and locked-Supermicro behavior.
  • Kept observation outcome instrumentation to structured logs in this scoped PR. Adding a new fleet metric and alerting contract can be a follow-up if operations identifies a concrete SLO.

The older deadline, claim, retry, and migration comments are obsolete after the simplification: this revision removes that entire persistence layer and derives cadence from the existing successful observation timestamp.

Reinspect verified host boot targets after a configurable interval derived from the last successful observation.

Reopen the exact desired target on mismatch so Ready reuses existing boot convergence while Assigned defers remediation until release.

Keep observation read-only and fence result writes by the desired configuration generation.

This supports NVIDIA#4248

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
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.

3 participants