mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-19 10:52:04 +00:00
feat: version-aware host-asset preflight for binary-swap deploys (#750)
A binary-swap deploy (make deploy or a manual spx copy) ships a new binary whose host-config prerequisites — the privileged helper scripts and their sudoers grant, placed only by setup.sh / setup-ovn.sh — it does not ship. The node looks healthy and the gap surfaces later as a cryptic launch failure or a silent security regression. Add a preflight package that checks the host against what this build expects, comparing asset content, not existence, so a stale-but-present asset is caught too. The managed helpers install from quoted heredocs, so their on-disk bytes equal the heredoc body verbatim; the binary carries the canonical sha256s and compares. - spinifex/preflight: CheckHost / CheckHostAt, content hash of the two managed helpers and a NOPASSWD grant-line check for the sysctl helper. - Generated canonicalHashes plus a drift guard that re-extracts the heredocs and fails if the committed manifest no longer matches. - Three surfaces: spx admin preflight, a make deploy gate (SKIP_ASSET_PREFLIGHT=1 to bypass), and a non-fatal daemon boot warning.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/mulgadc/spinifex/spinifex/preflight"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var adminPreflightQuiet bool
|
||||
|
||||
var adminPreflightCmd = &cobra.Command{
|
||||
Use: "preflight",
|
||||
Short: "Verify this host's managed assets against what this build expects",
|
||||
Long: `spx knows its own build (spx version) but not whether this host already
|
||||
has the privileged helper scripts and sudoers grants that build depends on —
|
||||
setup.sh and setup-ovn.sh install them, and a binary-swap deploy (make
|
||||
deploy or a manual binary copy) skips those steps.
|
||||
|
||||
Checks compare content, not mere existence, so a present-but-stale asset is
|
||||
caught too. Exits non-zero if any asset is Missing, Stale, or Ungranted.`,
|
||||
RunE: runAdminPreflight,
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
adminCmd.AddCommand(adminPreflightCmd)
|
||||
adminPreflightCmd.Flags().BoolVar(&adminPreflightQuiet, "quiet", false, "print only non-OK rows")
|
||||
}
|
||||
|
||||
func runAdminPreflight(_ *cobra.Command, _ []string) error {
|
||||
results := preflight.CheckHost()
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "PATH\tKIND\tSTATUS\tDETAIL")
|
||||
for _, r := range results {
|
||||
if adminPreflightQuiet && r.Status == preflight.OK {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", r.Path, r.Kind, r.Status, r.Detail)
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
if preflight.HasProblem(results) {
|
||||
return fmt.Errorf("host-asset preflight found problems — see table above")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user