refactor(hardware): replace flat requirements lookup with per-node RequirementsProvider#796
Merged
Merged
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors hardware requirement computation from a static (nodeType, profile) lookup table into a provider-based rules engine keyed by DeploymentSpec{NodeType, Profile, Options}. It also plumbs “why” attribution through failing preflight checks so doctor can display which rule set the binding floor, and introduces a "k8s-substrate" provider intended for substrate-only validation work.
Changes:
- Replaces the flat requirements registry with
RequirementsProvider+ per-node providers, driven byRule+Reduce()(CPU/memory=max, storage=sum). - Wires plugin preset / plugin list inputs into block node checks via
DeploymentSpec.Options. - Adds a
whyFloorerrorx property and displays"Set by: …"in the doctor compact error panel.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/hardware/provider.go | Adds RequirementsProvider, DeploymentSpec, and a global provider registry. |
| pkg/hardware/rules.go | Introduces Rule/Contribution and the Reduce() reducer logic. |
| pkg/hardware/predicates.go | Adds predicate combinators for rules (profile/preset/plugins). |
| pkg/hardware/provider_block.go | Block-node provider rules reproducing existing floors (with TODOs for TBD values). |
| pkg/hardware/provider_consensus.go | Consensus provider stub using rules/reducer to reproduce current floors. |
| pkg/hardware/provider_substrate.go | Adds "k8s-substrate" provider for Kubernetes substrate minimums. |
| pkg/hardware/requirements.go | Removes the old requirements registry and GetRequirements. |
| pkg/hardware/node_spec.go | Updates node spec creation to compute requirements via providers. |
| pkg/hardware/factory.go | Updates CreateNodeSpec to accept DeploymentSpec and validates inputs. |
| pkg/hardware/hardware_test.go | Updates tests to use DeploymentSpec-driven spec creation. |
| pkg/hardware/requirements_test.go | Updates legacy-coverage tests to use provider registry lookups. |
| pkg/hardware/provider_block_test.go | Adds rule/reducer semantics and why-attribution unit tests for block rules. |
| pkg/hardware/provider_consensus_test.go | Adds unit tests verifying consensus numbers unchanged. |
| pkg/hardware/provider_substrate_test.go | Adds unit tests for substrate provider values and registry presence. |
| internal/workflows/preflight.go | Threads DeploymentSpec through preflight and attaches whyFloor on failures. |
| internal/workflows/setup.go | Keeps NodeSetupWorkflow signature while internally building DeploymentSpec. |
| internal/workflows/blocknode.go | Updates block node preflight workflow to accept DeploymentSpec. |
| cmd/cli/commands/block/node/check.go | Parses preset/plugins flags into DeploymentSpec.Options for sizing. |
| pkg/models/errors.go | Adds ErrPropertyWhyFloor property key. |
| internal/doctor/diagnose.go | Extracts and displays whyFloor as “Set by:” in compact error output. |
| docs/quickstart.md | Documents --plugin-preset / --plugins usage for block node check. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
693150f to
b5a12cd
Compare
b5a12cd to
3fc47ec
Compare
…quirementsProvider
Replace the static 2-D map in pkg/hardware/requirements.go with a
RequirementsProvider interface. Each node type owns a []Rule list;
predicates fire on DeploymentSpec{NodeType, Profile, Options}, and
Reduce() accumulates contributions (Max for CPU/memory, Sum for storage).
All existing hardware floors are reproduced exactly with empty Options.
Plugin-preset-aware sizing (e.g. tier1-rfh reduced local disk) is wired
end-to-end: check.go reads --plugin-preset/--plugins into DeploymentSpec,
preflight steps call ComputeWithWhy() on failure and attach Why strings
via ErrPropertyWhyFloor, and the doctor compact panel surfaces them as
"Set by: <reason>" below the Cause line.
NodeSetupWorkflow public signature is unchanged (no cascade to cluster.go
or install_handler.go in this PR). SubstrateProvider is registered under
"k8s-substrate" for use by #685.
Closes #684
Signed-off-by: alex-au <alex.w.aus@gmail.com>
3fc47ec to
3c2188a
Compare
brunodam
approved these changes
Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Replaces the static 2-D
requirementsRegistrymap inpkg/hardware/requirements.gowith aRequirementsProviderinterface. Each node type owns a[]Rulelist; predicates fire onDeploymentSpec{NodeType, Profile, Options}, andReduce()accumulates contributions with Max for CPU/memory and Sum for storage.All existing hardware floors are reproduced exactly with empty
Options(regression-safe). Plugin-preset-aware sizing is wired end-to-end through bothblock node checkandblock node install: both commands build aDeploymentSpecwithOptions["preset"], which flows intoNewNodeSafetyCheckWorkflow. Failing checks callComputeWithWhy()and attach the binding rule'sWhystring viaErrPropertyWhyFloor; the doctor compact panel surfaces it as" Set by: <reason>"below the Cause line.Block-node hardware floors are updated to the BN team's confirmed numbers (Slack thread 2026-06-17): testnet/perfnet LFH (n2d-standard-16) 16 vCPU / 64 GB / 5 TB; testnet/perfnet RFH (c3d-standard-8) 8 vCPU / 32 GB / 150 GB; previewnet LFH 16 vCPU / 64 GB / 3 TB; previewnet RFH 8 vCPU / 32 GB / 150 GB; mainnet cloud (n2d-highmem-32) 32 vCPU / 256 GB / 150 GB. A
notpredicate combinator was added so LFH and RFH rules are mutually exclusive — without it the Max reducer would always select the larger LFH numbers.SubstrateProvideris registered under"k8s-substrate"for use by #685.Files changed
pkg/hardware/provider.goRequirementsProviderinterface (Compute,ComputeWithWhy),DeploymentSpec, copy-on-readProviders()registrypkg/hardware/rules.goRule,Contribution(no Op field),Reduce()with hardcoded Max/Sum reducerpkg/hardware/predicates.goprofilePredicate,presetPredicate,hasPlugin,anyProfile,and,or,not(new — inverts a predicate; needed to make LFH/RFH rules mutually exclusive),alwayspkg/hardware/provider_block.gopkg/hardware/provider_consensus.goReducepkg/hardware/provider_substrate.go"k8s-substrate"pkg/hardware/requirements.gorequirementsRegistryandGetRequirementsremovedpkg/hardware/node_spec.goNewNodeSpec(spec DeploymentSpec, host HostProfile)— delegates to providerpkg/hardware/factory.goCreateNodeSpec(spec DeploymentSpec, host HostProfile)new signature; addedSupportedNodeTypes()pkg/hardware/hardware_test.goDeploymentSpec; mainnet and previewnet test cases updated for new numberspkg/hardware/requirements_test.goGetRequirementscalls replaced; previewnet block-node comparisons updated; mainnet block no-preset explicitly asserted as no-oppkg/hardware/provider_block_test.goTestNotPredicateandTestBlockNodeProvider_MainnetLFHaddedpkg/hardware/provider_consensus_test.gopkg/hardware/provider_substrate_test.gointernal/workflows/preflight.goNewNodeSafetyCheckWorkflow(spec hardware.DeploymentSpec, skipHardwareChecks bool); failed checks attachErrPropertyWhyFloorviaComputeWithWhyinternal/workflows/setup.goNodeSetupWorkflowgainspluginPreset string; non-empty value placed inDeploymentSpec.Options["preset"]internal/workflows/cluster.goInstallClusterWorkflowgainspluginPreset string; forwarded toNodeSetupWorkflowinternal/workflows/blocknode.goNewBlockNodePreflightCheckWorkflow(spec hardware.DeploymentSpec)— always runs checks (skipHardwareChecks: false)internal/bll/blocknode/install_handler.goins.PluginPresettoInstallClusterWorkflowso install path enforces preset-aware floorscmd/cli/commands/block/node/check.go--plugins, resolves--plugins/--plugin-presetprecedence (mirrorsinit.go); builds normalisedDeploymentSpecwith Optionscmd/cli/commands/kube/cluster/install.go""(no preset for kube cluster install)pkg/models/errors.goErrPropertyWhyFloorerrorx propertyinternal/doctor/diagnose.goWhyFloortoErrorDiagnosis;findWhyFloor()helper; displays" Set by: <why>"below Cause incheckErrCompactdocs/quickstart.md--plugin-presetand--pluginsforblock node checkReview guide
Code review checklist
rules.go—Reduce()uses Max for CPU and memory, Sum for storage — not the other way aroundprovider_block.go— LFH and RFH rules are mutually exclusive vianot(presetPredicate("tier1-rfh")); without this, Max would always pick the larger LFH numbers since RFH has lower requirementsprovider_block.go— numbers match the BN team Slack thread (2026-06-17): testnet/perfnet LFH 16/64/5000; RFH 8/32/150; previewnet LFH 16/64/3000; RFH 8/32/150; mainnet cloud 32/256/150; mainnet LFH no-opblocknode.go—NewBlockNodePreflightCheckWorkflowhardcodesskipHardwareChecks: false;--skip-hardware-checkshas no effect onblock node check(intentional — the command's purpose is to run the checks)setup.go—NodeSetupWorkflowpassespluginPresetintoDeploymentSpec.Options; empty string means no preset rules fireinstall_handler.go—ins.PluginPresetis now forwarded; before this PR it was silently dropped at theInstallClusterWorkflowcall siteerrors.go—ErrPropertyWhyFlooris declared inpkg/models/(notinternal/doctor/) to avoid circular importsdiagnose.go—findWhyFloor()walks the full errorx cause chain, not just the top errorpreflight.go—ComputeWithWhyis called in the failing branch only (not on every check) to avoid double-computeerrors.Neworfmt.Errorf— all production errors useerrorx(enforced by forbidigo lint,task lintclean).gofiles carry// SPDX-License-Identifier: Apache-2.0headerprovider_substrate.goregistered under"k8s-substrate"key (consumed by refactor(cli): drop --profile and --node-type from kube cluster install #685)Unit tests
Manual UAT
Build for Linux and run inside the UTM VM (or any amd64/arm64 Linux host).
task build:cli GOOS=linux GOARCH=amd64 # scp binary to the VMDefault check (no preset) — testnet LFH requires 16 cores / 5 TB ✅
tier1-rfh preset reduces the floor to 8 cores / 150 GB (c3d-standard-8) ✅
Install path also enforces preset-aware checks ✅
kube cluster installalso enforces hardware checks via the same path ✅--skip-hardware-checksbypasses checks on install but has no effect on check ✅Why-string appears in compact error panel on hardware failure ✅
Invalid profile gives a clear error listing supported profiles
sudo solo-provisioner block node check --profile does-not-exist # Expected: errorx.IllegalArgument with message listing supported profile namesInvalid
--pluginsvalue is rejected earlyRisks / rollback
(nodeType, profile)pairs return numbers from the confirmed BN team Slack thread — verified byrequirements_test.goandprovider_block_test.go. No behavioral regression relative to what was intended.--skip-hardware-checksbehaviour is intentionally asymmetric: respected byinstall, ignored bycheck. This is by design and documented in the checklist above.GetRequirementsandrequirementsRegistryare restored, callers revert to their prior signatures.Related Issues