Skip to content

verify system state and sled software when adding a sled - #11637

Merged
davepacheco merged 9 commits into
mainfrom
dap/add-sled-check-2
Nov 3, 2026
Merged

davepacheco merged 9 commits into
mainfrom
dap/add-sled-check-2

Conversation

@davepacheco

@davepacheco davepacheco commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Depends on #11636. This PR adds checks to the "add sled" API that verify the RFD 747 conditions for self-service sled add: that there's no self-service update in progress and that the sled's software is running the right versions (as best we can tell, which ought to be good enough in practice). This version of the PR removes the old, non-TQ-aware sled_add path that existed for compatibility with older clients. That was removed in R19 but was still being served for older clients. We should get rid of this altogether, but that's a lot more work. (See #11641.)

I've got a follow-up in #11638 that makes these new checks overridable. This is important because this is the only way to add sleds today and I think support should be able to say "I know this version is compatible enough" or something like that.

…ction

Add `Collection::caboose_with_version_for()`, which looks up a caboose
and parses its version, along with helpers for the active SP and RoT
bootloader cabooses.  Add `ExpectedVersion::for_inactive_caboose()` to
turn the result for an inactive slot into the version expected there.

Split the code that chooses each component's target artifact out of the
SP, RoT, RoT bootloader, and host OS planners into its own function.
The SP, RoT, and RoT bootloader versions take the active caboose that
the planner already looked up, so each caboose is looked up only once.

Split the planner's `Failed*UpdateReason` types so that errors from
looking up a caboose's version (`CabooseVersionError`) and from choosing
a target artifact (`TargetArtifactError`, `HostOsTargetArtifactError`)
are their own types.  The messages are unchanged.  This changes the
nexus-lockstep API and the shape of the planning report's JSON.
When a target release is set, refuse to add a sled unless the system
has settled on that release (no update in progress, no mupdate waiting
to be cleared, and updates not suspended) and the new sled's SP, RoT,
RoT bootloader, and host phase 1 images match it.  The RoT's boot
preferences must also show no update that has not fully taken effect.

This applies to both `sled_add` and `rack_membership_add_sleds`.

The comparison lives in the planner's `mgs_updates::deployed_versions`
module so that it uses the same artifact selection as the planner.

Add `Collection::sp_for()` and
`Collection::active_host_phase_1_hash_for()`, which report a missing
item with a new `NotInInventory` error.

@jgallagher jgallagher 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.

Do we also need to add these checks to the multirack join path (cc @andrewjstone)? I'm not sure if we can do that yet since it depends on polling the joining rack's MGS as part of inventory. But something to add to that TODO list (I hear it's not long enough).

Comment thread nexus/src/app/rack.rs Outdated
.find_hw_baseboard_id(opctx, &baseboard_id)
.await?;

// Check the sled's software before allowing it to be added to the

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.

I know you mentioned it's a lot of work to actually drop the API version that calls this, but should we drop this implementation and return a 410 GONE or something like that? If we allow old clients to hit this implementation, are we allowing a sled to be added without adding it to TQ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That's a fair question. I don't know why that wasn't done initially. I think it's an orthogonal question, in that this change doesn't make anything worse -- it actually adds the validation to that path.

@andrewjstone what do you think we should do here?

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.

I like the idea of returning a 410 Gone. I think it wasn't done originally because the endpoint was added before the code was done merging. Either that or I completely overlooked it. In practice, support has been the only ones adding sleds so far, and definitely so at the time, so I figured we'd just upgrade out of it and not have an issue. But sure, a customer could be holding on to a super old client.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment thread nexus/src/app/sled_add_checks.rs Outdated
self.datastore().target_release_get_current(opctx).await?;
let release_source = target_release.release_source().map_err(|e| {
Error::internal_error(&format!(
"loading current target release: {e:#}"

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.

This is right as long as e is an anyhow::Error, but I'd probably write this as

Suggested change
"loading current target release: {e:#}"
"loading current target release: {}", InlineErrorChain::new(&*e),

so if the error type changes we don't end up losing the inner errors.

Comment thread nexus/src/app/sled_add_checks.rs Resolved
Comment thread nexus/types/src/inventory.rs Resolved
@andrewjstone

Copy link
Copy Markdown
Contributor

Do we also need to add these checks to the multirack join path (cc @andrewjstone)? I'm not sure if we can do that yet since it depends on polling the joining rack's MGS as part of inventory. But something to add to that TODO list (I hear it's not long enough).

I think this is only possibly an issue upon rack adoption. The multirack join service shouldn't actually insert into the sled-table. I have the gate PR up still, although it relies on @sunshowers upcoming changes.

@jgallagher

Copy link
Copy Markdown
Contributor

Do we also need to add these checks to the multirack join path (cc @andrewjstone)? I'm not sure if we can do that yet since it depends on polling the joining rack's MGS as part of inventory. But something to add to that TODO list (I hear it's not long enough).

I think this is only possibly an issue upon rack adoption. The multirack join service shouldn't actually insert into the sled-table. I have the gate PR up still, although it relies on @sunshowers upcoming changes.

Yeah, sorry, I meant in the rack adoption path. Specifically, we don't want to adopt a rack if it's not running the same version as the protorack. Ideally we could reuse these checks to enforce that, although again there's the problem of getting inventory for the new rack's SPs before it's joined...

Base automatically changed from dap/add-sled-check-1 to main November 2, 2026 19:12
@davepacheco

Copy link
Copy Markdown
Collaborator Author

@jgallagher This is ready for re-review now, if you want to take another look.

@jgallagher jgallagher 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.

LGTM; thanks!

@davepacheco
davepacheco added this pull request to stack #11669 November 3, 2026 04:55
@davepacheco
davepacheco merged commit 03964ec into main Nov 3, 2026
19 checks passed
@davepacheco
davepacheco deleted the dap/add-sled-check-2 branch November 3, 2026 04:59
davepacheco added a commit that referenced this pull request Nov 3, 2026
Depends on #11637 and I intend to land these together, but I separated
them out for review.

This PR makes the checks added in 11637 overridable. The goal here is
safe-by-default, overridable by someone who knows what they're doing,
and a naive operator shouldn't be able to override it without realizing
it's risky.
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