feat(oabctl): programmatic delete API with explicit target contract#1415
feat(oabctl): programmatic delete API with explicit target contract#1415chaodu-agent wants to merge 7 commits into
Conversation
Designs the delete contract deferred by the lib-split review, symmetric
with apply_manifests:
- delete_services(&SdkConfig, &[DeleteTarget], &DeleteOptions)
-> Result<DeleteReport, DeleteError>
- DeleteOptions requires an explicit cluster and shares apply's bucket
resolution chain (explicit -> OAB_CONTROL_PLANE_BUCKET -> account) so
delete always cleans the bucket apply wrote to; never reads CLI config
- Contract guards before any AWS call: non-empty target set, non-empty
cluster, non-blank namespace/name (regression-tested without AWS)
- Typed DeleteError {Validation, Target, Teardown} preserving the report
of targets completed before a failure; teardown remains resumable
- Best-effort ingress skips surface in DeletedService.warnings; S3
cleanup failures still fail the target (safe to retry)
- delete.rs output now routes through the shared progress gate: CLI
behavior unchanged, programmatic calls are silent
- CLI paths (delete <name>, delete -f) unchanged
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
- needless_borrow in apply's registry-detach wait - allow too_many_arguments on validate_checkpoint / prepare_identity / ingress::delete_exact — these deliberately thread the full deployment identity (partition/account/region/cluster/bucket) as separate explicit parameters; bundling them would obscure the F1 ownership contract - box PreparedIdentity::Exact payload (large_enum_variant) No behavior change; 90 tests + doc-test pass, release build clean.
… refactor/oabctl-delete-api # Conflicts: # operator/src/apply.rs # operator/src/delete.rs
- apply: redundant_guards — match empty failures with a slice pattern - ingress: cloud_map_service_id_from_arn became test-only after the exact-identity merge (all production paths use the boundary-pinned parser); mark it #[cfg(test)] with a note instead of carrying dead code Verified with the CI toolchain (rustc 1.97.1): clippy -D warnings clean, 96 tests + doc-test pass.
Pre-merge verification — code review + real-AWS E2E at 1bd61e8Code review of the identity-checkpoint path (
E2E against real AWS (synthetic tenant
Also verified earlier on this branch: CI operator check green (clippy 1.97 Verdict: good to merge. The delete contract does what the two review rounds demanded — checkpointed exact identity, fail-closed ambiguity handling, resumable teardown with no false success — and it holds up against real AWS, including the failure paths. |
|
Important CHANGES REQUESTED What This PR DoesThis PR adds a public in-process How It WorksEach target is validated, resolved to its live ECS service, and checkpointed in S3 with canonical cluster/service ARNs, the ECS Findings
Finding Details🔴 F1: The logical target does not map injectively to its physical identityValidation rejects only blank components, while ECS and Cloud Map use The checkpoint does not close this initial ambiguity. If Requested change: define one injective canonical resource identity and verify the logical identity before minting a checkpoint. A reversible encoding or immutable namespace/name tags/metadata can preserve both components; validation alone is sufficient only if it proves the delimiter cannot occur. Add a regression proving that two distinct component pairs can never resolve to the same delete identity. 🔴 F2: Delete and apply have no shared target fence
A same-target apply can therefore start after the old ECS incarnation is observed gone but before cleanup completes. If it starts before dependency cleanup, Requested change: add a target-scoped fencing protocol shared by apply and delete—such as a lease/tombstone with a generation token that apply must reject or supersede explicitly, plus conditional S3 mutation. Revalidation alone only narrows the race. Add an interleaving test where apply recreates the target after ECS drain completion and prove delete cannot remove the new incarnation or its desired state. 🟢 F3: Exact checkpointing closes the earlier ambiguous-cleanup pathsThe current code now requires one identified ECS service before creating a checkpoint, binds retries to canonical ARNs plus Baseline Check
Previous Review Feedback
Addressing External Reviewer Feedback@chaodu-agent (pre-merge verification at
|
| Reviewer | Focus | Result |
|---|---|---|
| Reviewer A | Correctness / target identity | F1 |
| Reviewer B | Architecture / API contract | F1, F2 |
| Reviewer C | Safety / operability | F2 |
| Reviewer D | Testing / regression coverage | Collision and apply/delete interleaving tests required |
| Reviewer E | Independent exact-SHA audit | Confirmed the missing apply/delete fence; final aggregation strengthened the initial-lookup collision analysis |
What's Good (🟢)
- Contract validation and bucket selection are explicit and library-friendly.
DeleteErrorretains both the failed target and successfully completed prior targets.- Exact ARN, caller-boundary, registry, and
createdAtchecks eliminate the earlier wrong-cluster/name-only cleanup behavior. - Dependent and S3 failures retain the durable checkpoint for safe retry.
- The author supplied useful real-AWS evidence for initial delete, retry, and already-gone failure paths.
5️⃣ Three Reasons We Might Not Need This PR
- The CLI already owns teardown orchestration — if control-plane callers can safely invoke it, a second public contract adds a long-lived compatibility surface.
- Logical identity is not yet durable enough for a destructive API — a persisted deployment ID or immutable tagged identity may be safer than reconstructing ownership from two free-form strings.
- Apply/delete serialization may belong in the control plane — if this crate cannot provide a shared fence, exposing raw concurrent operations could create more risk than an orchestrated deprovision workflow.
chaodu-agent
left a comment
There was a problem hiding this comment.
Important
CHANGES REQUESTED
Consolidated review: #1415 (comment)
GitHub event: COMMENT — self-review delivery only; this is not an approval.
| ))); | ||
| } | ||
| for target in targets { | ||
| if target.namespace.trim().is_empty() || target.name.trim().is_empty() { |
There was a problem hiding this comment.
🔴 F1 — Make the target-to-resource identity injective
Blank-only validation allows distinct targets such as prod/team-bot and prod-team/bot to derive the same ECS, API Gateway, and Cloud Map names. On the first delete, prepare_identity can therefore resolve the other logical target’s live ECS service and mint a fully valid exact-resource checkpoint for the wrong pair.
Requested change: use a reversible/injective physical identity (or immutable logical-identity metadata verified before checkpoint creation) and add a regression proving distinct namespace/name pairs cannot collide.
| .await? { | ||
| PreparedIdentity::Exact(boxed) => { | ||
| let (checkpoint, state) = *boxed; | ||
| delete_checkpointed_ecs(&ecs, &checkpoint, state).await?; |
There was a problem hiding this comment.
🔴 F2 — Fence post-ECS cleanup against concurrent apply
Once this call observes the old incarnation gone, the code immediately deletes checkpointed API/Cloud Map resources and name-keyed S3 state. apply does not read delete-checkpoints and can reuse those dependency IDs or write a new manifest in this window, after which this delete removes the recreated deployment’s resources/state.
Requested change: introduce a target-scoped generation/lease/tombstone protocol shared by apply and delete, with conditional S3 mutation, and test an apply interleaving after ECS drain completion.
Summary
Designs and implements the programmatic delete contract that the #1404 review intentionally deferred ("until their programmatic contracts are designed independently"). Stacked on
refactor/oabctl-lib.Symmetric with
apply_manifests:DeleteOptionsrequires an explicit cluster; the library path never reads~/.oabctl/config.tomlOAB_CONTROL_PLANE_BUCKET→oab-control-plane-{account}) — delete always cleans the bucket apply wrote to, closing the round-2 bucket-mismatch concern for goodDeleteError { Validation | Target | Teardown }preserving theDeleteReportfor targets completed before a failure; teardown stays resumable (re-run the same target to continue)DeletedService.warnings; S3 cleanup failures still fail the target (safe to retry)delete.rsoutput now routes through the shared progress gate: CLI rendering unchanged, programmatic calls silentCLI
No behavior change:
oabctl delete <name>anddelete -fkeep their existing flow (config-file cluster resolution, printed progress, aggregate failure reporting).Motivation
NEST's manager needs
/delete(tenant deprovision) — webhook removal and Telegram-side disclosure stay in the control plane, while ECS/ingress/S3 teardown reuses this API in-process, matching how it already consumesapply_manifests.Testing
On 7a8cdbe:
cargo clippy --all-targets -- -D warningsclean;cargo test83 passed + doc-test (6 new delete contract tests); CLI surface untouched.