hub: gofmt -w on 22 files, formatting only, no behaviour change (R-1160)
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
// report handler to the ungated EffectiveMinControllerVersion → the held case leaks the floor.
|
||||
func TestReportACK_ManagedFloorHold(t *testing.T) {
|
||||
h, st, _ := newTestHandler(t)
|
||||
st.SetDefaultMinControllerVersion("0.113.0") // controller floor
|
||||
st.SetDefaultMinControllerVersion("0.113.0") // controller floor
|
||||
_ = st.SetArtifactManifest(store.ArtifactManifest{GoldenVersion: "0.113.0", MinAgent: "0.81.0"}) // coupled golden
|
||||
if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c", RetrievalPassword: "pw", APIKey: "k", ConfigJSON: "{}"}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -68,7 +68,9 @@ func (f *fakeKeySvc) ConfirmKey(_ context.Context, _ string, fp string) (int, er
|
||||
func (f *fakeKeySvc) OpenWindowFor(context.Context, string, int) (offsitekeys.WindowGrant, error) {
|
||||
return offsitekeys.WindowGrant{Granted: false, Reason: "not due"}, f.err
|
||||
}
|
||||
func (f *fakeKeySvc) CloseWindowFor(context.Context, string, offsitekeys.WindowResult) error { return f.err }
|
||||
func (f *fakeKeySvc) CloseWindowFor(context.Context, string, offsitekeys.WindowResult) error {
|
||||
return f.err
|
||||
}
|
||||
func (f *fakeKeySvc) RequestAbandon(context.Context, string, string) (offsitekeys.AbandonStatus, error) {
|
||||
return offsitekeys.AbandonStatus{State: "pending"}, f.err
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ package api
|
||||
type apiAuth string
|
||||
|
||||
const (
|
||||
authPublic apiAuth = "public" // no credential by design — the reason is on the entry
|
||||
authCustomer apiAuth = "customer" // the household's controller key (or the global key)
|
||||
authHost apiAuth = "host" // the box's own host key, self-scoped (or the global key)
|
||||
authGlobal apiAuth = "global" // the operator/global key only
|
||||
authPassphrase apiAuth = "passphrase" // X-Retrieval-Password of that household
|
||||
authAppliance apiAuth = "appliance" // the appliance's own bearer token
|
||||
authRecovery apiAuth = "recovery" // only while the operator has armed recovery mode for the host
|
||||
authPublic apiAuth = "public" // no credential by design — the reason is on the entry
|
||||
authCustomer apiAuth = "customer" // the household's controller key (or the global key)
|
||||
authHost apiAuth = "host" // the box's own host key, self-scoped (or the global key)
|
||||
authGlobal apiAuth = "global" // the operator/global key only
|
||||
authPassphrase apiAuth = "passphrase" // X-Retrieval-Password of that household
|
||||
authAppliance apiAuth = "appliance" // the appliance's own bearer token
|
||||
authRecovery apiAuth = "recovery" // only while the operator has armed recovery mode for the host
|
||||
)
|
||||
|
||||
type apiRoute struct {
|
||||
|
||||
@@ -47,8 +47,8 @@ type AccessSettings struct {
|
||||
type Subaccount struct {
|
||||
ID int64 `json:"id"`
|
||||
StorageBox int64 `json:"storage_box"`
|
||||
Username string `json:"username"` // uXXXXXX-subN
|
||||
Server string `json:"server"` // …-subN.your-storagebox.de
|
||||
Username string `json:"username"` // uXXXXXX-subN
|
||||
Server string `json:"server"` // …-subN.your-storagebox.de
|
||||
HomeDirectory string `json:"home_directory"`
|
||||
AccessSettings AccessSettings `json:"access_settings"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
@@ -56,11 +56,11 @@ type Subaccount struct {
|
||||
|
||||
// StorageBox mirrors a dedicated storage box (non-secret fields).
|
||||
type StorageBox struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"` // uXXXXXX (empty until active)
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // initializing | active | …
|
||||
Server string `json:"server"` // uXXXXXX.your-storagebox.de
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"` // uXXXXXX (empty until active)
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // initializing | active | …
|
||||
Server string `json:"server"` // uXXXXXX.your-storagebox.de
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// StorageBoxType + Stats added v0.64.0 (R-5 pool-box aggregate). Shape pinned by the Phase-0 probe
|
||||
|
||||
@@ -34,8 +34,8 @@ func TestR723_ANewBoxIsNotARecovery(t *testing.T) {
|
||||
if sc.GetState("c1") != "down" {
|
||||
t.Fatalf("setup: want down, got %q", sc.GetState("c1"))
|
||||
}
|
||||
hostFor(t, st, path, 2*time.Minute) // the new box enrolled 2 min ago
|
||||
saveReportAged(t, st, path, "ok", 0) // …and reported
|
||||
hostFor(t, st, path, 2*time.Minute) // the new box enrolled 2 min ago
|
||||
saveReportAged(t, st, path, "ok", 0) // …and reported
|
||||
sc.Check()
|
||||
if len(*events) != 0 {
|
||||
t.Fatalf("a new box's first report emitted %v", *events)
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestStorageFill_BornPersistent(t *testing.T) {
|
||||
func TestStorageFill_RootExcluded(t *testing.T) {
|
||||
st := newDiskStore(t)
|
||||
saveStorageReport(t, st,
|
||||
stTarget{"local", "local", "", 96}, // PVE builtin local = root-backed → excluded by Type
|
||||
stTarget{"local", "local", "", 96}, // PVE builtin local = root-backed → excluded by Type
|
||||
stTarget{"rootmnt", "local-dir", "/", 96}, // a target literally at "/" → excluded by mount path
|
||||
)
|
||||
var types []string
|
||||
|
||||
@@ -100,7 +100,9 @@ func (a storeActions) Reissue(ctx context.Context, customerID string) error {
|
||||
}
|
||||
|
||||
// NewActions builds the production mutation seam.
|
||||
func NewActions(st *store.Store, reissuer Reissuer) Actions { return storeActions{st: st, reissuer: reissuer} }
|
||||
func NewActions(st *store.Store, reissuer Reissuer) Actions {
|
||||
return storeActions{st: st, reissuer: reissuer}
|
||||
}
|
||||
|
||||
// debounceState tracks, per customer, the last DISTINCT report seen and how many consecutive
|
||||
// distinct reports have carried the declaration.
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/offsite"
|
||||
@@ -247,12 +247,12 @@ func (s *Service) AuditAll(ctx context.Context, windowOpen WindowOpenFunc) []Aud
|
||||
// ── Decision 68: the clean-up window ──────────────────────────────────────────────────────────────
|
||||
|
||||
const (
|
||||
EventWindowDrop = "offsite_window_drop" // error: more snapshots went than a window may remove
|
||||
EventWindowFailed = "offsite_window_failed" // warning: a window errored or was left open
|
||||
EventGuardRefused = "offsite_prune_guard_refused" // error: the box's fake-snapshot guard refused (R-822)
|
||||
EventWindowLargeGrant = "offsite_window_large_grant" // warning: the operator raised one window's cap (R-833)
|
||||
windowLength = 20 * time.Minute
|
||||
windowCadence = 6*24*time.Hour + 12*time.Hour // "weekly", with slack for the night chain's drift
|
||||
EventWindowDrop = "offsite_window_drop" // error: more snapshots went than a window may remove
|
||||
EventWindowFailed = "offsite_window_failed" // warning: a window errored or was left open
|
||||
EventGuardRefused = "offsite_prune_guard_refused" // error: the box's fake-snapshot guard refused (R-822)
|
||||
EventWindowLargeGrant = "offsite_window_large_grant" // warning: the operator raised one window's cap (R-833)
|
||||
windowLength = 20 * time.Minute
|
||||
windowCadence = 6*24*time.Hour + 12*time.Hour // "weekly", with slack for the night chain's drift
|
||||
)
|
||||
|
||||
// WindowGrant is the hub's answer to the box.
|
||||
|
||||
@@ -2,10 +2,10 @@ package offsitekeys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
|
||||
|
||||
@@ -10,12 +10,12 @@ func TestCompare(t *testing.T) {
|
||||
{"0.81.0", "0.82.0", -1},
|
||||
{"0.82.0", "0.81.0", 1},
|
||||
{"0.81.0", "0.81.0", 0},
|
||||
{"v0.81.0", "0.81.0", 0}, // v-prefix tolerated
|
||||
{"0.100.0", "0.81.0", 1}, // numeric, not lexicographic
|
||||
{"1.0.0", "0.99.9", 1}, // major
|
||||
{"1.2.10", "1.2.3", 1}, // patch numeric
|
||||
{"garbage", "0.81.0", 0}, // parse error → 0 (callers guard with Valid)
|
||||
{"0.81", "0.81.0", 0}, // too few parts → 0
|
||||
{"v0.81.0", "0.81.0", 0}, // v-prefix tolerated
|
||||
{"0.100.0", "0.81.0", 1}, // numeric, not lexicographic
|
||||
{"1.0.0", "0.99.9", 1}, // major
|
||||
{"1.2.10", "1.2.3", 1}, // patch numeric
|
||||
{"garbage", "0.81.0", 0}, // parse error → 0 (callers guard with Valid)
|
||||
{"0.81", "0.81.0", 0}, // too few parts → 0
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := Compare(c.a, c.b); got != c.want {
|
||||
|
||||
@@ -58,10 +58,10 @@ func TestDRTierBackfill_InitializesFromReality(t *testing.T) {
|
||||
mustExec(`INSERT INTO customer_configs (customer_id, retrieval_password, api_key) VALUES (?, ?, ?)`,
|
||||
id, "pw", "key-"+id)
|
||||
}
|
||||
seedCustomer("applied") // host carries an ENABLED descriptor → must flip ON
|
||||
seedCustomer("disabled") // descriptor present but enabled:false → must stay OFF
|
||||
seedCustomer("plain") // no descriptor at all → must stay OFF
|
||||
seedCustomer("hostless") // no host row → must stay OFF
|
||||
seedCustomer("applied") // host carries an ENABLED descriptor → must flip ON
|
||||
seedCustomer("disabled") // descriptor present but enabled:false → must stay OFF
|
||||
seedCustomer("plain") // no descriptor at all → must stay OFF
|
||||
seedCustomer("hostless") // no host row → must stay OFF
|
||||
mustExec(`INSERT INTO hosts (host_id, customer_id, api_key, desired_json) VALUES (?, ?, ?, ?)`,
|
||||
"applied-01", "applied", "h1",
|
||||
`{"pbs_dr":{"enabled":true,"storage_id":"felhom-pbs","namespace":"applied"}}`)
|
||||
|
||||
@@ -76,9 +76,9 @@ func TestGlobalFloor_DBOverridesDefault(t *testing.T) {
|
||||
// EffectiveMinControllerVersion ever returns the global when an override is set.
|
||||
func TestEffectiveFloor_OverrideBeatsGlobal(t *testing.T) {
|
||||
s := newFloorTestStore(t)
|
||||
s.SetDefaultMinControllerVersion("0.85.0") // global
|
||||
mkCustomer(t, s, "c", "0.87.0") // per-customer override
|
||||
mkCustomer(t, s, "other", "") // no override → uses global
|
||||
s.SetDefaultMinControllerVersion("0.85.0") // global
|
||||
mkCustomer(t, s, "c", "0.87.0") // per-customer override
|
||||
mkCustomer(t, s, "other", "") // no override → uses global
|
||||
|
||||
if got := s.EffectiveMinControllerVersion("c"); got != "0.87.0" {
|
||||
t.Errorf("effective floor for c = %q, want 0.87.0 (override beats global)", got)
|
||||
|
||||
@@ -42,7 +42,7 @@ const (
|
||||
// matcher: fingerprints/checksums are logged by design and are public.
|
||||
var secretPatterns = []*regexp.Regexp{
|
||||
regexp.MustCompile(`-----BEGIN [A-Z ]*PRIVATE KEY-----`),
|
||||
regexp.MustCompile(`\bre_[A-Za-z0-9]{10,}`), // Resend API key shape
|
||||
regexp.MustCompile(`\bre_[A-Za-z0-9]{10,}`), // Resend API key shape
|
||||
regexp.MustCompile(`PVEAPIToken=[^\s"']+:\S+`), // Proxmox token WITH secret part
|
||||
regexp.MustCompile(`(?i)\b(password|passwd|api_key|apikey|token_secret|client_secret)=[^\s"']{6,}`),
|
||||
regexp.MustCompile(`Authorization:\s*Bearer\s+\S{16,}`),
|
||||
|
||||
@@ -24,10 +24,10 @@ func TestLastCustomerSentAt_PairingQuery(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
// Noise that must NOT count:
|
||||
insertNotifLog(t, s, "c1", "node_down", "sent", "operator", "2026-07-22 15:00:00") // wrong channel
|
||||
insertNotifLog(t, s, "c1", "node_down", "failed", "customer", "2026-07-22 16:00:00") // wrong status
|
||||
insertNotifLog(t, s, "c1", "node_down", "sent", "operator", "2026-07-22 15:00:00") // wrong channel
|
||||
insertNotifLog(t, s, "c1", "node_down", "failed", "customer", "2026-07-22 16:00:00") // wrong status
|
||||
insertNotifLog(t, s, "c1", "backup_failed", "sent", "customer", "2026-07-22 17:00:00") // wrong type
|
||||
insertNotifLog(t, s, "c2", "node_down", "sent", "customer", "2026-07-22 18:00:00") // wrong customer
|
||||
insertNotifLog(t, s, "c2", "node_down", "sent", "customer", "2026-07-22 18:00:00") // wrong customer
|
||||
|
||||
// No qualifying row yet:
|
||||
_, ok, err := s.LastCustomerSentAt("c1", []string{"node_stale", "node_down"})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
var (
|
||||
reANSI = regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||
reTimestamp = regexp.MustCompile(`\d{4}[-/]\d{2}[-/]\d{2}[T ]\d{2}:\d{2}:\d{2}[.\d]*([+-]\d{2}:?\d{2})?[Z ]?:? ?`)
|
||||
reTimestamp = regexp.MustCompile(`\d{4}[-/]\d{2}[-/]\d{2}[T ]\d{2}:\d{2}:\d{2}[.\d]*([+-]\d{2}:?\d{2})?[Z ]?:? ?`)
|
||||
reSyslog = regexp.MustCompile(`[A-Z][a-z]{2}\s+\d{1,2} \d{2}:\d{2}:\d{2} `)
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// OperatorOOBNote marks the single fleet-wide operator OOB peer among wg_peers (TASK H1). It is an
|
||||
// UNBOUND peer (host_id ''), so it rides the existing empty-host_id support + the partial-unique
|
||||
// UNBOUND peer (host_id ”), so it rides the existing empty-host_id support + the partial-unique
|
||||
// idx_wg_peers_host (which only constrains non-empty host_id) without collision [OF-4]. It is pushed
|
||||
// to the endpoint by peersync like any peer (so the endpoint accepts the operator's handshake), and
|
||||
// its assigned_ip becomes each box's desired-state oob_peer_ip (rendered into wg-felhom AllowedIPs).
|
||||
@@ -33,7 +33,7 @@ func (s *Store) GetOperatorOOBPeer() (*WGPeer, error) {
|
||||
// endpoint's STATIC forward chain can hardcode the operator /32). Last-write-wins: any prior
|
||||
// operator-oob row is replaced (key rotation / IP change). Validated: assignedIP is an IPv4 host
|
||||
// address inside the endpoint's tunnel_subnet, not the network/broadcast/pbs_tunnel_ip, and not
|
||||
// already taken by a DIFFERENT (customer) peer. host_id stays '' (unbound) so it is never counted as
|
||||
// already taken by a DIFFERENT (customer) peer. host_id stays ” (unbound) so it is never counted as
|
||||
// a customer host.
|
||||
func (s *Store) SetOperatorOOBPeer(pubkey, assignedIP string) error {
|
||||
ip, err := netip.ParseAddr(assignedIP)
|
||||
@@ -100,7 +100,9 @@ const oobOperatorSSHKeyKey = "oob_operator_ssh_pubkey"
|
||||
|
||||
// SetOOBOperatorSSHKey stores the operator's SSH PUBLIC key (an authorized_keys line, delivered to
|
||||
// every box via the desired-state so felhom-sshd honours the operator login). "" clears it.
|
||||
func (s *Store) SetOOBOperatorSSHKey(pubkey string) error { return s.setSetting(oobOperatorSSHKeyKey, pubkey) }
|
||||
func (s *Store) SetOOBOperatorSSHKey(pubkey string) error {
|
||||
return s.setSetting(oobOperatorSSHKeyKey, pubkey)
|
||||
}
|
||||
|
||||
// GetOOBOperatorSSHKey returns the operator SSH public key ("" if unset).
|
||||
func (s *Store) GetOOBOperatorSSHKey() string { return s.getSetting(oobOperatorSSHKeyKey) }
|
||||
|
||||
@@ -29,7 +29,7 @@ type fakeRegistry struct {
|
||||
downloadGone map[string]bool
|
||||
// shaless models a version whose metadata answers but carries no sha256.
|
||||
shaless map[string]bool
|
||||
hits int
|
||||
hits int
|
||||
}
|
||||
|
||||
func (f *fakeRegistry) start(t *testing.T) *gitea.Client {
|
||||
|
||||
@@ -116,10 +116,10 @@ func TestTemplates_CustomerHostTab(t *testing.T) {
|
||||
|
||||
// The Host tab panel carries the host_detail_body content.
|
||||
for _, want := range []string{
|
||||
"demo-felhom-01", // host identity
|
||||
"Storage Targets", // shared body section
|
||||
"felhom-usb", // storage target from the report
|
||||
">9201<", // guest vmid
|
||||
"demo-felhom-01", // host identity
|
||||
"Storage Targets", // shared body section
|
||||
"felhom-usb", // storage target from the report
|
||||
">9201<", // guest vmid
|
||||
`href="/hosts/demo-felhom-01"`, // cross-link to the standalone page
|
||||
} {
|
||||
if !strings.Contains(html, want) {
|
||||
|
||||
@@ -13,15 +13,15 @@ func TestStatusColorTruthTable(t *testing.T) {
|
||||
status string
|
||||
want string
|
||||
}{
|
||||
{"ok", "nominal"}, // operating normally — blue
|
||||
{"warn", "warn"}, // degraded / stale report — amber
|
||||
{"stale", "warn"}, // host stale state
|
||||
{"down", "crit"}, // outage — red
|
||||
{"fail", "crit"}, // health=fail — red
|
||||
{"pending", "neutral"}, // not-yet-provisioned customer is a NORMAL fleet state
|
||||
{"disabled", "neutral"}, // deliberately paused — not a deviation
|
||||
{"blocked", "warn"}, // operator cut-off: intentional but attention-worthy
|
||||
{"bogus", "neutral"}, // unknown values stay quiet
|
||||
{"ok", "nominal"}, // operating normally — blue
|
||||
{"warn", "warn"}, // degraded / stale report — amber
|
||||
{"stale", "warn"}, // host stale state
|
||||
{"down", "crit"}, // outage — red
|
||||
{"fail", "crit"}, // health=fail — red
|
||||
{"pending", "neutral"}, // not-yet-provisioned customer is a NORMAL fleet state
|
||||
{"disabled", "neutral"}, // deliberately paused — not a deviation
|
||||
{"blocked", "warn"}, // operator cut-off: intentional but attention-worthy
|
||||
{"bogus", "neutral"}, // unknown values stay quiet
|
||||
{"", "neutral"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -143,14 +143,14 @@ func (s *Server) handleOffsite(w http.ResponseWriter, r *http.Request) {
|
||||
pbsView := s.pbsdrBoxData() // R-5 v0.65.0 PBS-DR datastore panel (PBS DR tab)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"Endpoints": cards,
|
||||
"HasEndpoints": len(cards) > 0,
|
||||
"Peers": rows,
|
||||
"OffsiteBox": boxView,
|
||||
"OffsiteCusts": custRows,
|
||||
"PBSBox": pbsView,
|
||||
"CSRFToken": s.getCSRFToken(r),
|
||||
"Flash": r.URL.Query().Get("flash"),
|
||||
"Endpoints": cards,
|
||||
"HasEndpoints": len(cards) > 0,
|
||||
"Peers": rows,
|
||||
"OffsiteBox": boxView,
|
||||
"OffsiteCusts": custRows,
|
||||
"PBSBox": pbsView,
|
||||
"CSRFToken": s.getCSRFToken(r),
|
||||
"Flash": r.URL.Query().Get("flash"),
|
||||
}
|
||||
if err := s.templates.ExecuteTemplate(w, "offsite.html", data); err != nil {
|
||||
s.logger.Printf("[ERROR] offsite.html template: %v", err)
|
||||
|
||||
@@ -147,7 +147,10 @@ func TestFmtBytesAuto_ScalesInsteadOfCollapsingToZero(t *testing.T) {
|
||||
// The Offsite page's fmtBytesGB renders demo-hp's real size as "0.1 GB". On a card whose entire
|
||||
// defect was under-reporting a real backup, that reads as "nearly nothing" — which is why this
|
||||
// formatter exists rather than reusing that one.
|
||||
for _, tc := range []struct{ in int64; want string }{
|
||||
for _, tc := range []struct {
|
||||
in int64
|
||||
want string
|
||||
}{
|
||||
{140829678, "134.3 MB"}, // demo-hp, measured
|
||||
{0, "0 B"},
|
||||
{1 << 10, "1.0 KB"},
|
||||
|
||||
@@ -254,7 +254,6 @@ func (s *Server) tokenRows() []tokenRow {
|
||||
// tokenDaysChoices are the expiry options of the create form; "never" is not offered.
|
||||
var tokenDaysChoices = []int{30, tokenDefaultDays, 180, tokenMaxDays}
|
||||
|
||||
|
||||
// Handler is the operator web surface as main mounts it: ServeHTTP behind RequireAuth, unconditionally.
|
||||
func (s *Server) Handler() http.Handler {
|
||||
return s.RequireAuth(http.HandlerFunc(s.ServeHTTP))
|
||||
|
||||
Reference in New Issue
Block a user