You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(analyzer): delete AWS::CloudWatch::CompositeAlarm before the metric Alarms its AlarmRule references
A CompositeAlarm references its child alarms by NAME inside its AlarmRule
string (e.g. ALARM("cdkd-getatt-chain-alarm")), which is a plain string with
no Ref / Fn::GetAtt, so cdkd's DAG saw no dependency edge and could schedule
the referenced metric alarm for deletion while the composite still existed.
CloudWatch rejects that with "Cannot delete <alarm> as there are composite
alarm(s) depending on it." and the destroy failed.
Add a per-resource implicit delete-ordering edge: parse each CompositeAlarm's
AlarmRule for referenced alarm names (ALARM / OK / INSUFFICIENT_DATA tokens,
bare or quoted, plus the arn:...:alarm:<name> form) and emit an edge making the
composite alarm delete BEFORE every metric/composite Alarm it references in the
same stack (matched by AlarmName property or physical id). The per-AlarmRule
edge handles composite-of-composite chains. Both delete consumers (the deploy
engine DELETE phase and the standalone destroy command) add these edges
alongside the existing type-pair rules.
Adds computeImplicitDeleteEdges / extractReferencedAlarmNames to
src/analyzer/implicit-delete-deps.ts plus unit tests for the parser and the
edge computation (bare/quoted/ARN names, multi-reference rules,
composite-of-composite, self-cycle guard, references outside the delete set).
Copy file name to clipboardExpand all lines: .claude/rules/analyzer.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,5 +32,5 @@ paths:
32
32
- Determines execution order with topological sort
33
33
-**Implicit edge for Custom Resources**: any `AWS::IAM::Policy` / `AWS::IAM::RolePolicy` / `AWS::IAM::ManagedPolicy` attached to a Custom Resource's ServiceToken Lambda execution role automatically gets an edge to the Custom Resource, preventing the handler from being invoked before inline policy attachment returns (avoids mid-deploy AccessDenied race)
34
34
-**Implicit edge for Lambda VpcConfig**: every `AWS::EC2::Subnet` / `AWS::EC2::SecurityGroup` referenced by a Lambda's `Properties.VpcConfig.SubnetIds` / `SecurityGroupIds` gets an explicit edge to the Lambda (`src/analyzer/lambda-vpc-deps.ts`). Defense-in-depth on top of `extractDependencies`; for the reversed deletion traversal this guarantees Lambda is removed before its Subnet/SG so the asynchronous ENI detach has time to complete before EC2 rejects the subnet/SG delete with `DependencyViolation`.
35
-
-**Type-based deletion ordering rules**: `src/analyzer/implicit-delete-deps.ts` centralizes type-pair rules (e.g. VPC after Subnet, Subnet after Lambda, IGW + VPCGatewayAttachment after NatGateway) shared by the deploy DELETE phase and the standalone destroy command. The IGW / VPCGatewayAttachment after NatGateway edge (issue [#817](https://github.com/go-to-k/cdkd/issues/817)) mirrors the NAT-before-IGW ordering CloudFormation enforces: a NAT Gateway holds an Elastic IP mapped to the VPC's public address space, so detaching the IGW before the NAT is gone fails with `Network vpc-xxx has some mapped public address(es)` and the IGW delete then hangs (~19 min observed). No type-based rule is needed for the EIP itself — the NAT Ref's its EIP via `AllocationId`, so the reversed delete traversal already deletes the NAT before the EIP is released.
35
+
- **Type-based deletion ordering rules**: `src/analyzer/implicit-delete-deps.ts` centralizes type-pair rules (e.g. VPC after Subnet, Subnet after Lambda, IGW + VPCGatewayAttachment after NatGateway) shared by the deploy DELETE phase and the standalone destroy command. The IGW / VPCGatewayAttachment after NatGateway edge (issue [#817](https://github.com/go-to-k/cdkd/issues/817)) mirrors the NAT-before-IGW ordering CloudFormation enforces: a NAT Gateway holds an Elastic IP mapped to the VPC's public address space, so detaching the IGW before the NAT is gone fails with `Network vpc-xxx has some mapped public address(es)` and the IGW delete then hangs (~19 min observed). No type-based rule is needed for the EIP itself — the NAT Ref's its EIP via `AllocationId`, so the reversed delete traversal already deletes the NAT before the EIP is released. The same module also exposes `computeImplicitDeleteEdges(resources)` for per-RESOURCE delete-ordering edges no type-pair rule can express: an `AWS::CloudWatch::CompositeAlarm` references its child alarms (metric `AWS::CloudWatch::Alarm` or other composite alarms) by NAME inside its `AlarmRule` string (`ALARM("name")` / `OK(name)` / `INSUFFICIENT_DATA(name)`, plus the `arn:...:alarm:<name>` form) — a plain string, so cdkd's DAG sees no `Ref` / `Fn::GetAtt` edge. `extractReferencedAlarmNames` parses those names and the helper emits an edge making the composite alarm delete BEFORE each referenced alarm (matched by `AlarmName` property or physical id), since CloudWatch rejects deleting a metric alarm while a composite alarm still references it (`Cannot delete <alarm> as there are composite alarm(s) depending on it.`). The per-AlarmRule edge handles composite-of-composite chains; both delete consumers add these edges alongside the type-pair rules.
36
36
-**CDK-defensive DependsOn relaxation (default-on)**: `src/analyzer/cdk-defensive-deps.ts` lists the (depender, dependee) type pairs CDK adds defensively for VPC-Lambda runtime egress (IAM Role / Policy / Lambda::Function / Lambda::Url / Lambda::EventSourceMapping → EC2 Route / SubnetRouteTableAssociation). The deploy code path constructs `DagBuilder({ relaxCdkVpcDefensiveDeps: true })` by default; the matching DependsOn edges are dropped at graph-build time so CloudFront Distribution + Lambda::Url + VPC Lambda dispatch in parallel with NAT Gateway stabilization (~55% faster on `bench-cdk-sample`). Pass `cdkd deploy --no-aggressive-vpc-parallel` to opt out (escape hatch for stacks where the user wants the strict CDK-defensive ordering — e.g. a Custom Resource that synchronously invokes a VPC Lambda outside cdkd's Lambda-ServiceToken Active wait). Only DependsOn entries in the allowlist are dropped — Ref / GetAtt and other DependsOn pairs are untouched.
`Implicit delete dependency: ${before} (${state.resources[before]?.resourceType}) must be deleted before ${after} (${state.resources[after]?.resourceType})`
0 commit comments