gates / gates (push) Successful in 31s
- clear_hold: a fifth verb in the closed operator-action list; lifts a hold of any reason in the RUNNING controller (memory + settings.json together), forgets the update's held flag, drops a waiting put-back from the app-stop marker (copy kept). Refused on a bad name or no hold. No new event type. - --clear-restore-hold asks the running controller over <data dir>/ctl/hold.sock (unix, 0600, peer uid checked, no TCP); edits settings.json only when no controller answers, and says so (in-guest restart). - R-1340: the operator's HELD STOPPED mail is built from the hold record (restore kind, mixed cause, failed put-back step, database replay or not, kept copy) — never the option-C database story by default. - R-1341: the OOM WARN line once per new kill (container, start time, kill count); event unchanged.
69 lines
2.7 KiB
Go
69 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/report"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/scheduler"
|
|
)
|
|
|
|
// opActionReportFire holds the out-of-cycle report trigger once main has built it (func()). The
|
|
// executor is built earlier than the trigger, so it reads it through this holder.
|
|
var opActionReportFire atomic.Value
|
|
|
|
// operatorActionHandlers wires the five operator actions (R-314/R-279/R-177, `09` §3 decision 185)
|
|
// to the running process. ONE wiring site, called from main and from the wiring test, so the test
|
|
// exercises the doors production uses (the seam-built-but-never-wired trap).
|
|
//
|
|
// offsite_backup_now makes the SAME four checks as the household's „back up now"
|
|
// (offboxRunHandler) and then runs the same call, so an operator press can never do what the
|
|
// household's own press cannot.
|
|
//
|
|
// clear_hold (D64 = B) goes through clearHoldNow with hold — the same door the box command's socket uses
|
|
// (holdclear.go), so the hub's button and the box command can never differ. hold.clear nil = not available.
|
|
func operatorActionHandlers(backupMgr *backup.Manager, sched *scheduler.Scheduler, hold holdClearDeps, resultReady func(), logger *log.Logger) report.OperatorActionHandlers {
|
|
h := report.OperatorActionHandlers{ResultReady: resultReady}
|
|
if sched != nil {
|
|
h.RunJob = sched.RunNow
|
|
}
|
|
if hold.clear != nil {
|
|
h.ClearHold = func(stack string, actionID int64) (string, error) {
|
|
return clearHoldNow(hold, stack, fmt.Sprintf("the operator via the hub, action #%d", actionID))
|
|
}
|
|
}
|
|
if backupMgr == nil {
|
|
return h
|
|
}
|
|
h.OffsiteBackupNow = func() (string, func(ctx context.Context) error) {
|
|
switch {
|
|
case !backupMgr.OffboxConfigured():
|
|
return "no off-site target is set on this box", nil
|
|
case !backupMgr.OffboxRunnable():
|
|
return "the off-site key step (escrow) is not done yet — the run is refused until it is", nil
|
|
case backupMgr.OffboxOrphaned():
|
|
return "the off-site store is orphaned (written with an older key) — a run cannot write to it", nil
|
|
case backupMgr.IsRunning():
|
|
return "a backup run is already in flight", nil
|
|
}
|
|
return "", func(ctx context.Context) error {
|
|
if logger != nil {
|
|
logger.Printf("[INFO] [opaction] off-site backup started at the operator's request")
|
|
}
|
|
err := backupMgr.RunOffboxBackupWithProgress(ctx)
|
|
if errors.Is(err, backup.ErrOffboxRunInFlight) {
|
|
return fmt.Errorf("%w: %v", report.ErrActionRefused, err)
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
h.AbandonStop = backupMgr.StopAbandon
|
|
h.AbandonExtend = func(days int) (time.Time, error) { return backupMgr.ExtendAbandon(days) }
|
|
return h
|
|
}
|