fix: acknowledge pi maintenance barrier
This commit is contained in:
@@ -166,7 +166,7 @@ func Update(ctx context.Context, runner Runner, request Request) (result Result,
|
||||
}
|
||||
|
||||
// Rollback restores the image recorded in durable update state. It is safe for interrupted runs.
|
||||
func Rollback(ctx context.Context, runner Runner, statePath string, confirm bool) (Result, error) {
|
||||
func Rollback(ctx context.Context, runner Runner, statePath string, confirm bool) (result Result, retErr error) {
|
||||
lock, err := acquireLock(statePath)
|
||||
if err != nil {
|
||||
return Result{StatePath: statePath}, err
|
||||
@@ -175,6 +175,16 @@ func Rollback(ctx context.Context, runner Runner, statePath string, confirm bool
|
||||
if !confirm {
|
||||
return Result{StatePath: statePath}, ErrConfirmationRequired
|
||||
}
|
||||
if err := setMaintenance(ctx, runner, true); err != nil { return Result{StatePath: statePath}, err }
|
||||
defer func() {
|
||||
if clearErr := setMaintenance(context.Background(), runner, false); clearErr != nil {
|
||||
result = Result{Phase: PhaseFailed, StatePath: statePath}
|
||||
if retErr == nil { retErr = errors.New("maintenance admission gate could not be cleared: recovery required")
|
||||
} else { retErr = fmt.Errorf("%w; maintenance admission gate could not be cleared: recovery required", retErr) }
|
||||
}
|
||||
}()
|
||||
if active, err := activeSessions(ctx, runner); err != nil { return Result{StatePath: statePath}, err
|
||||
} else if active { return Result{StatePath: statePath}, ErrActiveSessions }
|
||||
state, err := readState(statePath)
|
||||
if err != nil {
|
||||
return Result{StatePath: statePath}, err
|
||||
@@ -232,30 +242,34 @@ func canonicalDigestReference(value string) (string, error) {
|
||||
return reference.FamiliarString(canonical), nil
|
||||
}
|
||||
|
||||
// The command text is fixed; no operator input or host path is interpolated into the core shell.
|
||||
// The marker lives alongside SETTINGS_FILE's named/bind-mounted directory and is read by backend.
|
||||
func setMaintenance(ctx context.Context, runner Runner, enabled bool) error {
|
||||
command := "mkdir -p /data/settings && : > /data/settings/maintenance.json && chmod 600 /data/settings/maintenance.json"
|
||||
if !enabled { command = "rm -f /data/settings/maintenance.json" }
|
||||
result, err := runCompose(ctx, runner, "exec", "-T", "core", "sh", "-ceu", command)
|
||||
path := "deactivate"
|
||||
if enabled { path = "activate" }
|
||||
args := append([]string{"exec", "-T", "core", "curl", "-fsS", "-X", "POST"}, internalIdentityHeaders...)
|
||||
args = append(args, "http://127.0.0.1:8787/internal/maintenance/"+path)
|
||||
result, err := runCompose(ctx, runner, args...)
|
||||
if err != nil { return commandError("maintenance admission gate", result, err) }
|
||||
var status struct { Active bool `json:"active"`; Admissions int `json:"admissions"` }
|
||||
if json.Unmarshal([]byte(result.Stdout), &status) != nil || status.Active != enabled || status.Admissions != 0 { return errors.New("maintenance admission gate did not acknowledge a quiescent state") }
|
||||
return nil
|
||||
}
|
||||
|
||||
func activeSessions(ctx context.Context, runner Runner) (bool, error) {
|
||||
result, err := runCompose(ctx, runner, "exec", "-T", "core", "tht", "session", "list", "--json")
|
||||
args := append([]string{"exec", "-T", "core", "curl", "-fsS"}, internalIdentityHeaders...)
|
||||
args = append(args, "http://127.0.0.1:8787/sessions?scope=all")
|
||||
result, err := runCompose(ctx, runner, args...)
|
||||
if err != nil {
|
||||
return false, commandError("active-session check", result, err)
|
||||
}
|
||||
var sessions []struct {
|
||||
var payload struct { Sessions []struct {
|
||||
Status string `json:"status"`
|
||||
Archived bool `json:"archived"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(result.Stdout), &sessions); err != nil {
|
||||
} `json:"sessions"` }
|
||||
if err := json.Unmarshal([]byte(result.Stdout), &payload); err != nil {
|
||||
return false, errors.New("active-session check returned invalid session data")
|
||||
}
|
||||
for _, session := range sessions {
|
||||
if !session.Archived && session.Status == "open" {
|
||||
for _, session := range payload.Sessions {
|
||||
if !session.Archived && session.Status != "finalized" && session.Status != "closed" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user