Repository navigation
fix(cmd): propagate caller cancellation through lifecycle orchestration - #2167
Conversation
…chase The CLI ran on context.Background(), so Ctrl-C only set a flag polled between purchases. SIGINT now cancels the invocation context; further Ctrl-Cs are absorbed until the run returns. The purchase call runs on a context detached from that cancellation, so an in-flight purchase is not aborted with its result lost; its audit record is written (before the rate-limit delay, which previously ran first in the --input-csv loop), the delay returns on cancel, no further purchase starts, and the report and an "interrupted: N of M attempted" summary are still produced. Errored purchases in an interrupted run are flagged "verify in AWS before re-running". Refs #2151 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The lifecycle queries ran on context.Background() and, with a real context, swallowed cancellation into empty maps; region discovery, the recommendation fetch, the coverage fetch and the duplicate checks did the same. A canceled or expired context is now returned as an error at each boundary (nothing is purchased, partial data is discarded, a dry run is not exempt), while provider errors with an active context stay recoverable. Closes #2151 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Purchase calls now run on a context detached from cancellation, so mocks that matched the exact parent context no longer match. Refs #2151 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 12 billable files and costs up to $3.00.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 25 minutes for your next included review. View limit details
Comment |
|
Gate verdict: APPROVE at Local evidence (fresh
Mutations (each applied alone, targeted tests run):
Plan amendments 1-7 hold. PurchaseCommitment's single call site ( Non-blocking nits (follow-up if wanted): CI green at this SHA, mergeStateStatus CLEAN, labels mirror #2151. |
Summary
Caller cancellation now reaches the lifecycle queries and every pre-purchase boundary, and a purchase already in flight is never aborted.
runToolusessignal.NotifyContext(os.Interrupt)(invocationContext).stopis deferred, never called on the first signal, so SIGINT stays registered and further Ctrl-Cs are absorbed instead of hard-exiting mid-purchase. First cancel prints once: "stopping after the in-flight purchase; Ctrl-\ force-quits and may lose its audit record".shutdownRequestedis kept (set from the context); SIGINT only, SIGTERM out of scope.context.Background()calls infilterAndAdjustRecommendationsnow use the caller's ctx;queryInstanceVersions/queryMajorVersionsreturn the ctx error instead of an empty map), region discovery, the recommendation fetch, the coverage fetch,checkDuplicatesandcheckDuplicatesForCSVRegion. Provider errors with an active context stay recoverable. Pre-purchase interrupts exit non-zero via the existinglog.Fatalfstartup pattern.PurchaseCommitmentruns oncontext.WithoutCancel(ctx)inexecutePurchasePipelineandprocessPurchaseLoop(cancelling a request mid-flight can leave AWS having created the commitment while the CLI records a failure, so a re-run double-buys). Per-call SDK timeouts still bound it (~30s).processPurchaseLoopslept before writing the audit record; it now writes the record first, then waits (waitBetweenPurchases, which returns at once on cancel), then checksctx.checkDuplicates.Not in this PR
No
go.modbump.Verification (offline: stubbed transport and mocks, no live cloud calls, no purchases)
TestExecuteAndReport_CancelDuringPurchaseFinishesInFlightAndReportsWhatRan: the stub cancels the parent ctx duringCreateSavingsPlanand asserts the request context still hasErr()==nil, then asserts exactly one purchase request, one audit row (success), the CSV report, and "Run interrupted: 1 of 3".ctx.Err():fetchEngineVersionData,filterAndAdjustRecommendations,prepareCSVPurchaseRun(dry and real),fetchExistingCoverage(dry run too),handleRegionDiscoveryError,fetchRecommendationsForRegion,fetchAllRecs(mid-fan-out and last region),checkDuplicates,checkDuplicatesForCSVRegion; plus an active-context provider error that stays recoverable.WithoutCancel(pipeline and loop), moving the wait before the audit write (both loops), callingstop()early (the signal test kills the test binary), and removing eachctx.Err()guard are each caught (the two redundant lifecycle guards were mutated as a pair; one redundant after-fan-out guard was deleted as unreachable).make lintclean,gocyclo -over 10clean,go test ./cmd/ -timeout 25mpasses.PurchaseCommitmentmocks matched the exact parent ctx; they now match any ctx (purchases run on a detached ctx).Closes #2151
🤖 Generated with Claude Code