From 088023bdd6e719440ab1d9ee3ce3939c5ddce7d6 Mon Sep 17 00:00:00 2001 From: Bruno De Assis Marques Date: Sat, 21 Feb 2026 07:38:32 +1100 Subject: [PATCH] feat: add --unsafe-skip-hardware-checks hidden option Signed-off-by: Bruno De Assis Marques --- cmd/weaver/commands/block/node/install.go | 6 +++- cmd/weaver/commands/common/flags.go | 16 ++++++++++ cmd/weaver/commands/kube/cluster/install.go | 6 +++- cmd/weaver/commands/root.go | 12 ++++++-- docs/dev/hidden-flags.md | 33 +++++++++++++++++++++ internal/workflows/blocknode.go | 10 +++---- internal/workflows/cluster.go | 6 ++-- internal/workflows/cluster_it_test.go | 2 +- internal/workflows/preflight.go | 31 ++++++++++++------- internal/workflows/setup.go | 6 ++-- 10 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 docs/dev/hidden-flags.md diff --git a/cmd/weaver/commands/block/node/install.go b/cmd/weaver/commands/block/node/install.go index e751fce2..3287cc65 100644 --- a/cmd/weaver/commands/block/node/install.go +++ b/cmd/weaver/commands/block/node/install.go @@ -72,8 +72,12 @@ var installCmd = &cobra.Command{ Any("opts", opts). Msg("Installing Hedera Block Node") + skipHardwareChecks, err := cmd.Flags().GetBool(common.FlagSkipHardwareChecks.Name) + if err != nil { + return errorx.IllegalArgument.Wrap(err, "failed to get %s flag", common.FlagSkipHardwareChecks.Name) + } wb := workflows.WithWorkflowExecutionMode( - workflows.NewBlockNodeInstallWorkflow(flagProfile, validatedValuesFile), opts) + workflows.NewBlockNodeInstallWorkflow(flagProfile, validatedValuesFile, skipHardwareChecks), opts) common.RunWorkflow(cmd.Context(), wb) diff --git a/cmd/weaver/commands/common/flags.go b/cmd/weaver/commands/common/flags.go index b8e2b0e9..da0f633a 100644 --- a/cmd/weaver/commands/common/flags.go +++ b/cmd/weaver/commands/common/flags.go @@ -65,6 +65,22 @@ var ( Description: "Install Metrics Server", Default: true, } + + // FlagSkipHardwareChecks is a hidden persistent flag registered on the root command. + // When set, it skips CPU, memory, and storage validation in NewNodeSafetyCheckWorkflow + // (see internal/workflows/preflight.go). Privilege, user, and host profile checks + // still run. This flag is intentionally not supported by the "check" command since its + // purpose is to validate hardware requirements. + // + // Used by: block node install, kube cluster install. + // Registered in: cmd/weaver/commands/root.go (hidden). + // See docs/dev/hidden-flags.md for full documentation. + FlagSkipHardwareChecks = FlagDefinition[bool]{ + Name: "skip-hardware-checks", + ShortName: "", + Description: "DANGEROUS: Skip hardware validation checks. May cause node instability or data loss.", + Default: false, + } ) // FlagDefinition defines a command-line flag typed by T. diff --git a/cmd/weaver/commands/kube/cluster/install.go b/cmd/weaver/commands/kube/cluster/install.go index 76ffe587..0e37be6e 100644 --- a/cmd/weaver/commands/kube/cluster/install.go +++ b/cmd/weaver/commands/kube/cluster/install.go @@ -53,7 +53,11 @@ var installCmd = &cobra.Command{ Any("opts", opts). Msg("Installing Kubernetes Cluster") - wb := workflows.WithWorkflowExecutionMode(workflows.InstallClusterWorkflow(flagNodeType, flagProfile), opts) + skipHardwareChecks, err := cmd.Flags().GetBool(common.FlagSkipHardwareChecks.Name) + if err != nil { + return errorx.IllegalArgument.Wrap(err, "failed to get %s flag", common.FlagSkipHardwareChecks.Name) + } + wb := workflows.WithWorkflowExecutionMode(workflows.InstallClusterWorkflow(flagNodeType, flagProfile, skipHardwareChecks), opts) common.RunWorkflow(cmd.Context(), wb) logx.As().Info().Msg("Successfully installed Kubernetes Cluster") diff --git a/cmd/weaver/commands/root.go b/cmd/weaver/commands/root.go index 2ad64f29..f5aed493 100644 --- a/cmd/weaver/commands/root.go +++ b/cmd/weaver/commands/root.go @@ -30,9 +30,10 @@ import ( // rootCmd represents the base command when called without any subcommands var ( // Used for flags. - flagConfig string - flagVersion bool - flagOutputFormat string + flagConfig string + flagVersion bool + flagOutputFormat string + flagSkipHardwareChecks bool rootCmd = &cobra.Command{ Use: "solo-provisioner", @@ -60,6 +61,11 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&flagVersion, "version", "v", false, "Show version") rootCmd.PersistentFlags().StringVarP(&flagOutputFormat, "output", "o", "yaml", "Output format (yaml|json)") + // Hardware check override flag - hidden to discourage casual use + rootCmd.PersistentFlags().BoolVar(&flagSkipHardwareChecks, common.FlagSkipHardwareChecks.Name, false, + "DANGEROUS: Skip hardware validation checks. May cause node instability or data loss.") + _ = rootCmd.PersistentFlags().MarkHidden(common.FlagSkipHardwareChecks.Name) + // disable command sorting to keep the order of commands as added cobra.EnableCommandSorting = false diff --git a/docs/dev/hidden-flags.md b/docs/dev/hidden-flags.md new file mode 100644 index 00000000..7e80d6c2 --- /dev/null +++ b/docs/dev/hidden-flags.md @@ -0,0 +1,33 @@ +# Hidden Flags + +This document describes hidden CLI flags that are not shown in `--help` output. These flags exist for +support and debugging purposes and should not be used in normal operations. + +## `--skip-hardware-checks` + +**Scope:** `block node install`, `kube cluster install` + +Skips OS, CPU, memory, and storage validation during installation workflows. The following preflight checks +still run even when this flag is set: + +- Privilege validation (root user) +- Weaver user/group validation +- Host profile validation + +**When to use:** + +- Working around a false-positive hardware check failure. +- Development or testing on machines that don't meet production hardware requirements. + +**Not supported by:** `block node check` — that command always runs all checks since its purpose +is to validate system requirements. + +**Implementation:** + +- Flag defined in `cmd/weaver/commands/common/flags.go` (`FlagSkipHardwareChecks`). +- Registered as a hidden persistent flag on the root command in `cmd/weaver/commands/root.go`. +- Read via `cmd.Flags().GetBool(common.FlagSkipHardwareChecks.Name)` in install commands. +- Passed as `skipHardwareChecks bool` through the workflow chain to `NewNodeSafetyCheckWorkflow` + in `internal/workflows/preflight.go`, which conditionally excludes hardware steps. + + diff --git a/internal/workflows/blocknode.go b/internal/workflows/blocknode.go index 7d6ef2a2..d4eddd53 100644 --- a/internal/workflows/blocknode.go +++ b/internal/workflows/blocknode.go @@ -8,15 +8,15 @@ import ( "github.com/hashgraph/solo-weaver/internal/workflows/steps" ) -// NewBlockNodePreflightCheckWorkflow creates a safety check workflow for block node +// NewBlockNodePreflightCheckWorkflow creates a safety check workflow for block node. func NewBlockNodePreflightCheckWorkflow(profile string) *automa.WorkflowBuilder { - return NewNodeSafetyCheckWorkflow(core.NodeTypeBlock, profile) + return NewNodeSafetyCheckWorkflow(core.NodeTypeBlock, profile, false) } -// NewBlockNodeInstallWorkflow creates a comprehensive install workflow for block node -func NewBlockNodeInstallWorkflow(profile string, valuesFile string) *automa.WorkflowBuilder { +// NewBlockNodeInstallWorkflow creates a comprehensive install workflow for block node. +func NewBlockNodeInstallWorkflow(profile string, valuesFile string, skipHardwareChecks bool) *automa.WorkflowBuilder { return automa.NewWorkflowBuilder().WithId("block-node-install").Steps( - InstallClusterWorkflow(core.NodeTypeBlock, profile), + InstallClusterWorkflow(core.NodeTypeBlock, profile, skipHardwareChecks), steps.SetupBlockNode(profile, valuesFile), ) } diff --git a/internal/workflows/cluster.go b/internal/workflows/cluster.go index 08270616..e1d549cd 100644 --- a/internal/workflows/cluster.go +++ b/internal/workflows/cluster.go @@ -22,11 +22,11 @@ func DefaultWorkflowExecutionOptions() *WorkflowExecutionOptions { } } -// InstallClusterWorkflow creates a workflow to set up a kubernetes cluster -func InstallClusterWorkflow(nodeType string, profile string) *automa.WorkflowBuilder { +// InstallClusterWorkflow creates a workflow to set up a kubernetes cluster. +func InstallClusterWorkflow(nodeType string, profile string, skipHardwareChecks bool) *automa.WorkflowBuilder { // Build the base steps that are common to all node types baseSteps := []automa.Builder{ - NodeSetupWorkflow(nodeType, profile), + NodeSetupWorkflow(nodeType, profile, skipHardwareChecks), // setup env for k8s steps.DisableSwap(), diff --git a/internal/workflows/cluster_it_test.go b/internal/workflows/cluster_it_test.go index 2d919a72..f34af500 100644 --- a/internal/workflows/cluster_it_test.go +++ b/internal/workflows/cluster_it_test.go @@ -46,7 +46,7 @@ import ( func Test_ClusterSetup(t *testing.T) { testutil.Reset(t) - installWf, err := InstallClusterWorkflow(core.NodeTypeBlock, core.ProfileLocal). + installWf, err := InstallClusterWorkflow(core.NodeTypeBlock, core.ProfileLocal, false). WithExecutionMode(automa.StopOnError). Build() require.NoError(t, err) diff --git a/internal/workflows/preflight.go b/internal/workflows/preflight.go index 6f876bcf..57b888ea 100644 --- a/internal/workflows/preflight.go +++ b/internal/workflows/preflight.go @@ -343,6 +343,7 @@ func CheckMemoryStep(nodeType string, profile string) automa.Builder { func CheckStorageStep(nodeType string, profile string) automa.Builder { return automa.NewStepBuilder().WithId("validate-storage"). WithExecute(func(ctx context.Context, stp automa.Step) *automa.Report { + hostProfile := hardware.GetHostProfile() nodeSpec, err := createNodeSpec(nodeType, profile, hostProfile) if err != nil { @@ -369,19 +370,29 @@ func CheckStorageStep(nodeType string, profile string) automa.Builder { }) } -// NewNodeSafetyCheckWorkflow creates a safety check workflow for any node type -func NewNodeSafetyCheckWorkflow(nodeType string, profile string) *automa.WorkflowBuilder { - return automa.NewWorkflowBuilder(). - WithId(nodeType+"-node-preflight").Steps( +// NewNodeSafetyCheckWorkflow creates a safety check workflow for any node type. +// If skipHardwareChecks is true, hardware validation steps (OS, CPU, memory, storage) are excluded. +func NewNodeSafetyCheckWorkflow(nodeType string, profile string, skipHardwareChecks bool) *automa.WorkflowBuilder { + preflightSteps := []automa.Builder{ CheckPrivilegesStep(), CheckWeaverUserStep(), CheckHostProfileStep(nodeType, profile), - CheckOSStep(nodeType, profile), - CheckCPUStep(nodeType, profile), - CheckMemoryStep(nodeType, profile), - CheckStorageStep(nodeType, profile), - //CheckDockerStep(), - ). + } + + if skipHardwareChecks { + logx.As().Warn().Msg("Hardware validation steps (OS, CPU, memory, storage) will be skipped due to --skip-hardware-checks flag") + } else { + preflightSteps = append(preflightSteps, + CheckOSStep(nodeType, profile), + CheckCPUStep(nodeType, profile), + CheckMemoryStep(nodeType, profile), + CheckStorageStep(nodeType, profile), + ) + } + + return automa.NewWorkflowBuilder(). + WithId(nodeType + "-node-preflight"). + Steps(preflightSteps...). WithPrepare(func(ctx context.Context, stp automa.Step) (context.Context, error) { notify.As().StepStart(ctx, stp, "Starting node preflight checks") return ctx, nil diff --git a/internal/workflows/setup.go b/internal/workflows/setup.go index 089a2bec..06cba993 100644 --- a/internal/workflows/setup.go +++ b/internal/workflows/setup.go @@ -10,13 +10,13 @@ import ( ) // NodeSetupWorkflow creates a comprehensive setup workflow for any node type -// It runs preflight checks first, then performs the actual setup -func NodeSetupWorkflow(nodeType string, profile string) *automa.WorkflowBuilder { +// It runs preflight checks first, then performs the actual setup. +func NodeSetupWorkflow(nodeType string, profile string, skipHardwareChecks bool) *automa.WorkflowBuilder { return automa.NewWorkflowBuilder(). WithId(nodeType+"-node-setup"). Steps( // First run preflight checks to ensure system readiness - NewNodeSafetyCheckWorkflow(nodeType, profile), + NewNodeSafetyCheckWorkflow(nodeType, profile, skipHardwareChecks), // Then perform the actual setup steps.SetupHomeDirectoryStructure(core.Paths()), steps.RefreshSystemPackageIndex(),