Repository navigation
fix: revoke registry access before handing a deletion to setup teardown - #985
Merged
Merged
Conversation
A registry revoke at TeardownRequired ran after that state was persisted and failed as retryable. When the registry denied it, the runner retried every 30 seconds while holding the lease, so destroy waited up to 45 minutes and timed out. A manager restart in that loop left the grant in place behind a saved handoff. In the reconcile route the final Deleted step also removed the record before the revoke finished, so a deploy CLI renewing its lease during the revoke failed with a lost lease. Revoke before persisting TeardownRequired or Deleted. A denied revoke fails the delete with an error naming the repository, which a retry revokes once the permission is granted. Other failures persist nothing and are retried. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Enclave skipped this draft pull request. It will run automatically when you mark the PR as Ready for review. |
Member
Author
|
@greptileai review |
|
GCP removes a project's shared Cloud Run grant only when no other deployment in the project is saved as active. Asking before saving its own deletion, two deletions finishing together could each see the other active and both keep the grant. Revoke before persisting only on AWS, where setup teardown takes the deployment once TeardownRequired is saved, and keep GCP's revoke after the state is persisted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
@greptileai review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Deleting an AWS deployment whose Lambda pulls from the manager's ECR registry could hang for 45 minutes and then time out. This happened when the manager's registry role was not allowed to delete a repository policy.
TeardownRequired. It persisted that state first and revoked afterwards. When the registry denied the revoke, the step failed as retryable, and the runner retried every 30 s while holding the deployment lease.alien-deploy destroywaited on "runtime cleanup handoff" until it timed out.teardown-requiredwith the grant still in place. Setup teardown then deleted the role and the record, and nothing revoked the grant afterwards. The account root principal stayed in the repository policy.POST /v1/sync/reconcile. On its finalDeletedstep the route deleted the record and then ran the revoke, which takes tens of seconds when the policy is already gone. A lease renewal during that window got 404, and destroy failed withDEPLOYMENT_LEASE_LOSTeven though everything had been deleted. This was reproduced twice against a real manager.Root cause
The revoke ran after the state that hands the deployment on was saved. Any revoke failure was also treated as a retryable checkpoint failure, including a denial that no retry can fix.
Fix
revoke_before_persistingruns the revoke beforeTeardownRequiredorDeletedis persisted. It is used by the manager loop transport, the reconcile route and the runtime-less destroy path.revoke_after_persisting, same as main). Its shared Cloud Run grant is removed only when no other deployment in the project is saved as active, and each deletion is saved before it checks. So of two deletions finishing together, the one that checks last sees both deleted and removes the grant.RemoteAccessDeniedfrom the registry) becomesREGISTRY_ACCESS_REVOKE_DENIED. The deployment is persisted asDeleteFailedwith that error, which names the repository and the missing permission. The runner and the deploy CLI adopt that state and stop. Once the permission is granted,retryruns runtime cleanup again and revokes.Deleting.Alternatives considered:
retryable. It doesn't help:REMOTE_ACCESS_DENIEDis retryable for IAM propagation. It also leaks silently, because the runner would exit, setup teardown would take the deployment, and the grant would stay.Proof
Each new test fails on main and passes with this change. Both runs used the same test code, in a sandbox.
registry_access::tests::revoke_before_handoffdrives the realManagerTransportagainst a real SQLite store. Its fake registry records the deployment's persisted status at the moment of each revoke.the_revoke_runs_before_setup_teardown_can_take_the_deployment: main revokes when the record already saysteardown-required; with the fix it still saysdeleting.a_denied_revoke_fails_the_delete_and_a_retry_hands_off_once_granted: main returnsREGISTRY_ACCESS_CLEANUP_FAILEDwithretryable: true, which is the endless retry. With the fix the record isdelete-failedand carries the error. After the permission is granted, the retried handoff revokes and persiststeardown-required.a_failed_revoke_persists_nothing_for_the_runner_to_retry: main leavesteardown-requiredpersisted; with the fix it staysdeleting.gcp_revokes_the_shared_grant_only_after_its_deletion_is_savedguards the GCP ordering. It passes on main and with the fix. It fails if GCP is moved before persisting: the revoke then runs while the record is stilldeleting.tests/registry_revoke_handoff.rsruns the same scenario throughPOST /v1/sync/reconcile:Deletedstep, main revokes after the record isdeleted; with the fix it revokes while the record is stillteardown-required.current.status = delete-failed.cargo test -p alien-manager --no-fail-fast: everything passes exceptroutes::airgap::tests::a_backlog_is_forwarded_..., a timing-budget test. It fails under full parallel load and passes 3/3 alone. It is unrelated to this change.Live, against a real hosted manager deploying a Lambda worker into a second AWS account, with this PR's base and the registry permission present:
TeardownRequired. Both policies were deleted and the target resources removed.DEPLOYMENT_LEASE_LOST. That is item 3 above.REGISTRY_ACCESS_CLEANUP_FAILEDevery 30 s for over 40 minutes. That is items 1 and 2.Rollout
Manager-only change. A deployment already stuck at
teardown-requiredwith a denied revoke is picked up again only when something re-runs its delete. After this ships, a denied revoke shows as a failed delete with the missing permission named.🤖 Generated with Claude Code