controller v0.47.0: backups page — whole-guest backup visibility + manual trigger
Part 2 of the USB/backup spec. agentapi: StatusResponse.Backup record, DueResponse
age_seconds, RestoreTestStatus(). New "Rendszermentés (teljes mentés)" section
(read-only: last backup/target PBS-vs-local/next-due/restore-test) + "Mentés most"
manual trigger that goes through the quiesce loop (controller owns quiescing):
quiesce.Loop gains mutex + TriggerNow() (single-flight, async). New
/api/guest-backup/{trigger,status} (distinct from apiRouter's /api/backup/*).
App-data rows relabeled under an "Alkalmazás-mentések" divider. Config → slice 10.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,7 @@ func main() {
|
||||
// --- Quiesce loop (slice 8B): app-consistent backup around the agent vzdump ---
|
||||
// Runs only when the local API is configured (a provisioned guest) and quiesce is enabled.
|
||||
// Recover FIRST (restart any stacks left stopped by a crash mid-quiesce), then start the loop.
|
||||
startQuiesceLoop(ctx, cfg, stackMgr, logger)
|
||||
quiesceLoop := startQuiesceLoop(ctx, cfg, stackMgr, logger)
|
||||
|
||||
// --- Start CPU collector ---
|
||||
cpuCollector := system.NewCPUCollector(5 * time.Second)
|
||||
@@ -607,6 +607,9 @@ func main() {
|
||||
webServer.SetEncryptionKey(encKey)
|
||||
webServer.SetAppExporter(appExporter)
|
||||
webServer.SetIntegrationManager(integrationMgr)
|
||||
if quiesceLoop != nil {
|
||||
webServer.SetBackupTrigger(quiesceLoop) // "Mentés most" → app-consistent backup via the quiesce loop
|
||||
}
|
||||
if assetsSyncer != nil {
|
||||
webServer.SetAssetsSyncer(assetsSyncer)
|
||||
}
|
||||
@@ -679,6 +682,9 @@ func main() {
|
||||
mux.Handle("/api/disks/", webServer.RequireAuth(webServer.CsrfProtect(http.HandlerFunc(webServer.ServeDiskAPI))))
|
||||
// Guided storage provisioning (init/attach/eject orchestration over the agent disk API + registry).
|
||||
mux.Handle("/api/storage/", webServer.RequireAuth(webServer.CsrfProtect(http.HandlerFunc(webServer.ServeStorageAPI))))
|
||||
// Whole-guest (appliance) backup visibility + manual trigger. Distinct prefix from apiRouter's
|
||||
// app-data /api/backup/{run,status} (DB dumps) to avoid shadowing the /api/ catch-all subtree.
|
||||
mux.Handle("/api/guest-backup/", webServer.RequireAuth(webServer.CsrfProtect(http.HandlerFunc(webServer.ServeBackupAPI))))
|
||||
// Host metrics API — thin proxy to the host agent (slice 9). Read-only host-wide health +
|
||||
// per-storage capacity for the monitoring view; the de-privileged controller can't read the
|
||||
// host itself. GET only, so no CSRF wrapper needed.
|
||||
@@ -1070,18 +1076,18 @@ func (b quiesceBackend) BackupStatus(ctx context.Context) (string, error) {
|
||||
// startQuiesceLoop wires + starts the slice-8B quiesce loop when the local API is configured and
|
||||
// quiesce is enabled. It Recovers (restarts stacks left stopped by a mid-quiesce crash) before
|
||||
// starting the loop goroutine. Non-fatal: any misconfig disables the loop with a log line.
|
||||
func startQuiesceLoop(ctx context.Context, cfg *config.Config, stackMgr *stacks.Manager, logger *log.Logger) {
|
||||
func startQuiesceLoop(ctx context.Context, cfg *config.Config, stackMgr *stacks.Manager, logger *log.Logger) *quiesce.Loop {
|
||||
if cfg.LocalAPI.Endpoint == "" || cfg.LocalAPI.Token == "" {
|
||||
return // not a provisioned guest — no agent to back up against
|
||||
return nil // not a provisioned guest — no agent to back up against
|
||||
}
|
||||
if !cfg.Quiesce.QuiesceEnabled() {
|
||||
logger.Printf("[INFO] [quiesce] disabled by config")
|
||||
return
|
||||
return nil
|
||||
}
|
||||
client, err := agentapi.New(cfg.LocalAPI.Endpoint, cfg.LocalAPI.Token, cfg.LocalAPI.Fingerprint)
|
||||
if err != nil {
|
||||
logger.Printf("[WARN] [quiesce] disabled (agent client init failed): %v", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
poll := parseDurationOr(cfg.Quiesce.PollInterval, 5*time.Minute)
|
||||
statusPoll := parseDurationOr(cfg.Quiesce.StatusPoll, 10*time.Second)
|
||||
@@ -1097,6 +1103,7 @@ func startQuiesceLoop(ctx context.Context, cfg *config.Config, stackMgr *stacks.
|
||||
})
|
||||
loop.Recover() // crash-safety: restart any stacks stranded-down by a mid-quiesce crash
|
||||
go loop.Run(ctx)
|
||||
return loop
|
||||
}
|
||||
|
||||
// parseDurationOr parses a duration string, falling back to def on empty/invalid input.
|
||||
|
||||
Reference in New Issue
Block a user