fix: harden operations and configuration recovery
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const operationTimeout = 5 * time.Minute
|
||||
|
||||
// StatusKind gives presentation layers semantics without requiring them to
|
||||
// inspect user-facing status text.
|
||||
type StatusKind uint8
|
||||
@@ -27,6 +34,9 @@ type Confirmation struct {
|
||||
ConfirmLabel string
|
||||
CancelLabel string
|
||||
HighImpact bool
|
||||
// Token binds acceptance to the exact state and operation scope that was
|
||||
// presented. Presentation layers must return it with the confirmed Intent.
|
||||
Token string
|
||||
}
|
||||
|
||||
// TargetProgressPhase describes one Target's part in an operation stream.
|
||||
@@ -189,6 +199,7 @@ func (u OperationUpdate) IsTerminal() bool { return u.Decode().IsTerm
|
||||
type OperationStream struct {
|
||||
Snapshot Snapshot
|
||||
Updates <-chan OperationUpdate
|
||||
Cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// InteractiveModule is the presentation-independent Application seam used by
|
||||
@@ -216,16 +227,20 @@ func (a *Application) StartOperation(intent Intent) OperationStream {
|
||||
a.interactiveOperation = true
|
||||
a.interactiveMu.Unlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), operationTimeout)
|
||||
updates := make(chan OperationUpdate, 2*len(a.targets)+2)
|
||||
go func() {
|
||||
defer close(updates)
|
||||
defer a.finishInteractiveOperation()
|
||||
final := a.dispatch(intent, func(result Result) {
|
||||
defer cancel()
|
||||
final := a.dispatch(ctx, intent, func(result Result) {
|
||||
updates <- runningOperationUpdate(result)
|
||||
})
|
||||
// Release the Application gate before publishing the terminal update so
|
||||
// an immediate confirmation retry cannot observe a stale busy state.
|
||||
a.finishInteractiveOperation()
|
||||
updates <- terminalOperationUpdate(final)
|
||||
}()
|
||||
return OperationStream{Snapshot: initial, Updates: updates}
|
||||
return OperationStream{Snapshot: initial, Updates: updates, Cancel: cancel}
|
||||
}
|
||||
|
||||
func (a *Application) finishInteractiveOperation() {
|
||||
|
||||
Reference in New Issue
Block a user