Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/rules/code-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ paths:
- **src/provisioning/register-providers.ts** - Shared provider registration (called from deploy.ts and destroy.ts)
- **src/provisioning/data-delete-intent.ts** - Shared destroy data-guard intent helpers (issue #1340): `hasCdkAutoDeleteTag(properties, tagKey)` / `isTruthyCfnBoolean(value)` plus the CDK tag-key constants `S3_AUTO_DELETE_OBJECTS_TAG` (`aws-cdk:auto-delete-objects`, stamped by `autoDeleteObjects: true`) and `ECR_AUTO_DELETE_IMAGES_TAG` (`aws-cdk:auto-delete-images`). Consumed by `S3BucketProvider.delete` (auto-empty of a non-empty bucket only with the tag / `DeleteContext.forceDataDelete`), `S3DirectoryBucketProvider.delete` (issue #1344 — same gate; no CDK opt-in sugar exists for directory buckets, so plain destroy of a non-empty one fails with a manual-empty remediation), and `ECRProvider.delete` (`force: true` only with `EmptyOnDelete: true`, the tag, or `forceDataDelete`) — without an opt-in the AWS not-empty error surfaces like CloudFormation DELETE_FAILED. `DeleteContext.forceDataDelete` (src/provisioning/region-check.ts) is set ONLY by the deploy engine's replacement/recreate delete sites under `--force-stateful-recreation`. See the "Destroy data guards" section in docs/cli-reference.md and the DeleteContext contract note in .claude/rules/providers.md.
- **src/provisioning/final-snapshot.ts** - `DeletionPolicy` / `UpdateReplacePolicy: Snapshot` support (issues #1352 / #1353 / #1354): `ATOMIC_FINAL_SNAPSHOT_TYPES` (RDS DBInstance / DBCluster, Neptune / DocDB clusters, ElastiCache CacheCluster — the delete call sites generate `buildFinalSnapshotIdentifier(physicalId, resourceType)` and thread it via `DeleteContext.finalSnapshotIdentifier`; each provider flips its delete from `SkipFinalSnapshot: true` to the API's atomic final-snapshot form; ONLY on the SDK route — a cc-api-routed atomic type is refused and `CloudControlProvider.delete` fail-closes on the field), `PRE_DELETE_SNAPSHOT_TYPES` + `createPreDeleteFinalSnapshot` dispatcher (all CC-routed: `AWS::EC2::Volume` via EC2 `CreateSnapshot` tagged `cdkd:final-snapshot-of`; `AWS::Redshift::Cluster` via `CreateClusterSnapshot`; `AWS::ElastiCache::ReplicationGroup` via ElastiCache `CreateSnapshot` — each waited to ready, idempotent reuse via the tag / the `finalSnapshotNamePrefix` name prefix across delete re-runs), `unsupportedFinalSnapshotError` / `ccRoutedFinalSnapshotError` refusals, and (issue #1366) `finalSnapshotMechanism(type, route)` / `refusesFinalSnapshot(type, route)` — the mechanism matrix as a PURE function, so the executor that ACTS on it and the `cdkd rollback` plan preview that DESCRIBES it read one source (issue #1368 extends that to the preview's STATE effect: a refused Snapshot delete no longer unwinds the record, since the next-older segment is classified against it). The two type sets are DISJOINT by construction — `finalSnapshotMechanism` tests the atomic set first, so a type in both would silently take the atomic arm and never reach the pre-delete snapshot; pinned in `final-snapshot.test.ts` alongside the union-equals-the-CFn-documented-list fence (re-homed there from the deleted `supportsFinalSnapshot` predicate, #1368). Consumed by the deploy engine (`prepareFinalSnapshotForDelete` — the shared gate for the DELETE branch AND the four replacement / recreate delete sites), `destroy-runner.ts`, and `rollback-executor.ts` — the latter twice: `rollbackFinalSnapshotId` for the delete-of-the-NEW-resource under `UpdateReplacePolicy` (honors only the atomic SDK-routed shape, plain-deletes otherwise — scope decision on #1354), and `prepareCreateRollbackFinalSnapshot` for a rolled-back CREATE under `DeletionPolicy` (the FULL matrix, refusing what it cannot snapshot — issue #1358). The engine's clients come from `DeployEngineOptions.finalSnapshotClients` (stack-region-pinned `AwsClients`, structurally a `PreDeleteSnapshotClients`), threaded on to `RollbackExecutorContext.finalSnapshotClients`; `--skip-final-snapshot` (deploy / destroy / state destroy / rollback, `skipFinalSnapshotOption` in `src/cli/options.ts` — deliberately NOT in the shared `destroyOptions` array `cdkd orphan` consumes) is the explicit data-loss opt-out.
- **src/provisioning/emr-configuration.ts** - Shared CFn -> SDK shape converters for the `AWS::EMR::*` nested config blobs whose CFn key spelling diverges from `@aws-sdk/client-emr` (issue #1383): `toSdkConfigurations` (renames `Configuration.ConfigurationProperties` -> the SDK's `Properties` at EVERY `Configurations` nesting level), `toSdkStepConfigs` (`HadoopJarStepConfig.StepProperties` -> `Properties`), and `toSdkInstanceTypeConfigs` (per-instance-type nested `Configurations`). Both are pure key renames — the VALUE shapes already match (`Record<string,string>` / `KeyValue[]`, verified against the live CFn registry schema) — but the AWS SDK v3 serializer drops unknown members, so before the conversion every EMR application configuration (spark-defaults / hive-site / yarn-site ...) silently vanished while cdkd reported success. Consumed by `EMRClusterProvider` (top-level `Configurations` / `Steps`, per-group `Configurations`, per-fleet `InstanceTypeConfigs`), `EMRInstanceGroupConfigProvider` (create), and `EMRInstanceFleetConfigProvider` (create + the `ModifyInstanceFleet` update). No inverse is needed: `Configurations` / `Steps` / `InstanceTypeConfigs` are all declared in `EMRClusterProvider.getDriftUnknownPaths` and neither instance provider implements `readCurrentState`. Non-object / non-array inputs (an unresolved intrinsic) pass through untouched so AWS surfaces the real validation error. The `AWS::EMR::*` types are NOT yet in `NESTED_KEY_TARGETS` (`scripts/gen-nested-key-coverage.ts`) — critic target expansion is tracked in issue #1393.
- **src/provisioning/ec2-termination-protection.ts** - Shared `--remove-protection` helper for `AWS::EC2::Instance`: `disableInstanceApiTermination()` (flip `DisableApiTermination` off, idempotent, errors swallowed at debug), `isTerminationProtectionPropagationError()` (matches the "may not be terminated. Modify its disableApiTermination" 400 from both `TerminateInstances` and the Cloud Control `DeleteResource` wrapper), and `TERMINATION_PROTECTION_MAX_ATTEMPTS`. Used by `EC2Provider.deleteInstance` (SDK path) and `CloudControlProvider.delete` (CC-API path — an instance routes through Cloud Control whenever its template trips the #614 silent-drop routing) so `--remove-protection` works regardless of which delete path the instance takes; the modify WRITE lags the delete READ, so both callers flip-off + retry the delete to close the propagation window. ALSO used by `ASGProvider.delete` (issue #796): an `AWS::AutoScaling::AutoScalingGroup` whose launch template sets `DisableApiTermination: true` launches instances that survive the group's `DeleteAutoScalingGroup(ForceDelete: true)` (ASG-level DeletionProtection + ForceDelete governs only the group + scale-in protection, not EC2-level termination protection), so under `--remove-protection` the provider enumerates the group's current instances and flips each one's `DisableApiTermination` off before the force delete — the ASG's own async terminate loop then absorbs the modify-WRITE propagation lag, so no per-instance delete retry is needed there. An ASG can ALSO route via Cloud Control when its template sets a silent-drop property such as `AvailabilityZoneIds` (#614 routing) — Cloud Control's `DeleteResource` cannot `ForceDelete` a protected ASG or clear its protection, so `CloudControlProvider.delete` detects `removeProtection === true && resourceType === 'AWS::AutoScaling::AutoScalingGroup'` and delegates to `new ASGProvider().delete(...)` (the single source of truth for protected-ASG deletion), keeping the SDK and CC routing paths behaviourally identical (issue #798; CDK's L2 emits `availabilityZones` names not `AvailabilityZoneIds`, so this CC path only fires for hand-written L1 / imported templates).
- **src/provisioning/unsupported-types.ts** + **unsupported-types.generated.ts** - Pre-flight unsupported-type rejection. The `.generated.ts` ships the provider-coverage Tier 3 set (`ProvisioningType: NON_PROVISIONABLE`) into the runtime, codegen'd from `docs/_generated/provider-coverage.json` by `scripts/gen-unsupported-types.ts` (`vp run gen:unsupported-types`; CI fails on drift). The hand-written `.ts` adds `isNonProvisionable()` + `unsupportedTypeIssueUrl()`; both are consulted by `CloudControlProvider.isSupportedResourceType` (rejects Tier 3) and `ProviderRegistry.validateResourceTypes` (per-type error + issue link). The `--allow-unsupported-types` escape hatch routes named types through Cloud Control via `ProviderRegistry.allowUnsupportedTypes()`.
- **src/provisioning/property-coverage.ts** + **property-coverage.generated.ts** - Pre-flight property-level rejection (parallel to unsupported-types but at top-level CFn property granularity). The `.generated.ts` ships per-Tier-1-type `{ handled, silentDrop }` records, codegen'd from `tests/fixtures/cfn-schemas/*.json` + each SDK provider's `handledProperties` / `unhandledByDesign` declarations by `scripts/gen-property-coverage.ts` (`vp run gen:property-coverage`; CI fails on drift; the codegen parses provider sources via the TypeScript Compiler API so no `dist/` bootstrap is needed). The hand-written `.ts` adds `getPropertyCoverage()` + `findSilentDropProperties()` + `unsupportedPropertyIssueUrl()`; all are consulted by `ProviderRegistry.validateResourceProperties` (per-resource per-property error + 1-click GitHub issue link + dedup'd re-run command). The `--allow-unsupported-properties` escape hatch (deploy only) routes named `<Type>:<Prop>` entries past the reject via `ProviderRegistry.allowUnsupportedProperties()`. Tier 2 (Cloud Control) types are intentionally NOT in the generated map — CC forwards the full property map to AWS, so no write-side silent drop is possible.
Expand Down
3 changes: 3 additions & 0 deletions docs/_generated/cli-flag-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,7 @@
"--include-non-importable",
"--include-planned-deletion",
"--input",
"--input-type",
"--instance-group-id",
"--instance-id",
"--instance-ids",
Expand Down Expand Up @@ -1360,10 +1361,12 @@
"--no-audit",
"--no-buffer",
"--no-build",
"--no-cli-pager",
"--no-deletion-protection",
"--no-deletion-protection-enabled",
"--no-disable-api-termination",
"--no-fund",
"--no-paginate",
"--no-path-metadata",
"--no-prefix-user-supplied-names",
"--no-pull",
Expand Down
4 changes: 2 additions & 2 deletions docs/_generated/integ-last-run.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ efs-immutable-replacement 2026-08-01T09:51:12Z PASS 63 verify.sh 0801 regression
efs-lambda 2026-07-26T18:36:31Z PASS 499 standard 0727b sweep-b4 staleness re-run (rc=0); account clean
efs-standalone 2026-07-26T18:36:31Z PASS 148 verify.sh 0727b sweep-b4 staleness re-run (rc=0); account clean
elasticache-replicationgroup-getatt 2026-07-20T14:51:32Z PASS 596 verify.sh CC GetAtt PrimaryEndPoint real hostname, 0 orphans
emr-cluster 2026-08-02T14:15:43Z PASS 605 verify.sh #1339 fix verified: cleanup warning gone on clean run (gone_probe-gated); 12 del/0 err, 0 orphans
emr-instance-configs 2026-08-02T06:50:46Z PASS 997 verify.sh 0802 heavy TTL sweep; deploy+resize+destroy ok, 0 orphans
emr-cluster 2026-08-09T04:01:57Z PASS 700 verify.sh #1383 top-level+nested+group Configurations and StepProperties asserted; 12 deleted, 0 errors, 0 orphans
emr-instance-configs 2026-08-09T04:19:21Z PASS 1250 verify.sh #1383 standalone-group ConfigurationProperties asserted; destroy clean, 0 orphans
event-driven 2026-07-26T18:36:31Z PASS 42 standard 0727b sweep-b4 staleness re-run (rc=0); account clean
eventbridge 2026-07-27T11:04:52Z PASS 87 verify.sh issue #1267 post-review re-run; 10 del 0 err, 0 orphans
eventbridge-api-destination 2026-07-21T14:48:06Z PASS 70 verify.sh rc ok, orph clean
Expand Down
Loading