Skip to content

Commit 95edc0d

Browse files
committed
test(integ): add custom-resource-getatt-data failure-seeking integ
Add a failure-seeking integration test fixture that exercises Custom Resource Data-key GetAtt resolution: a CDK Custom Resource returns Data keys that downstream resources reference via Fn::GetAtt, validating that cdkd resolves CR Data-key attributes correctly during deploy and tears the stack down cleanly on destroy.
1 parent d4c80f1 commit 95edc0d

14 files changed

Lines changed: 474 additions & 5 deletions

File tree

docs/_generated/integ-coverage.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,7 @@
25422542
"cc-api-fallback-transitions",
25432543
"cloudfront-function-url",
25442544
"composite-stack",
2545+
"custom-resource-getatt-data",
25452546
"custom-resource-provider",
25462547
"data-pipeline",
25472548
"deep-getatt-chains",
@@ -2624,6 +2625,9 @@
26242625
"l2",
26252626
"literal"
26262627
],
2628+
"custom-resource-getatt-data": [
2629+
"l2"
2630+
],
26272631
"custom-resource-provider": [
26282632
"l2"
26292633
],
@@ -3465,6 +3469,7 @@
34653469
"context-test",
34663470
"cross-region-state-bucket",
34673471
"cross-stack-references",
3472+
"custom-resource-getatt-data",
34683473
"deep-getatt-chains",
34693474
"deletion-policy-retain",
34703475
"deployment-events",
@@ -3515,6 +3520,9 @@
35153520
"cross-stack-references": [
35163521
"l1"
35173522
],
3523+
"custom-resource-getatt-data": [
3524+
"l2"
3525+
],
35183526
"deep-getatt-chains": [
35193527
"l1"
35203528
],
@@ -3727,6 +3735,7 @@
37273735
"AWS::ApplicationAutoScaling::ScalableTarget",
37283736
"AWS::Athena::NamedQuery",
37293737
"AWS::Athena::WorkGroup",
3738+
"AWS::CloudFormation::CustomResource",
37303739
"AWS::CloudFront::OriginAccessControl",
37313740
"AWS::CloudWatch::CompositeAlarm",
37323741
"AWS::CodePipeline::Pipeline",

docs/_generated/scenario-coverage.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
"tag": "custom-resource-async-poll",
3333
"description": "Custom Resource backed by Lambda + cfn-response via S3 pre-signed URL polling."
3434
},
35+
{
36+
"tag": "custom-resource-getatt-data",
37+
"description": "Custom Resource response `Data` consumed via `Fn::GetAtt(CR, 'Data.<key>')` / `Fn::GetAtt(CR, '<key>')` into ANOTHER resource's property (e.g. an SSM Parameter Value) — the fragile CR response-Data attribute path (#756 / #804: CR attributes only exist after the CR Lambda runs). Asserts the dependent's on-AWS value equals the value the CR handler returned, across multiple Data keys + an explicit dependent->CR dependency."
38+
},
3539
{
3640
"tag": "deep-getatt-chain-resolution",
3741
"description": "Long GetAtt chain where each resource POST-CREATE attribute (ARN / generated name only known after the AWS create call) feeds the next resource property, spanning a SDK + CC-API type mix. A wrong / late attribute resolution on either path (SDK `attributes` write or CC-API stored attributes + `constructAttribute` fallback) is pinpointed by the failing link. Critical hop: an unregistered CC-API type (`AWS::CloudWatch::CompositeAlarm`) whose `Arn` feeds downstream SDK-resource properties (issue: deep-getatt-chains fixture)."
@@ -389,6 +393,13 @@
389393
"multi-stack-getstackoutput"
390394
]
391395
},
396+
{
397+
"name": "custom-resource-getatt-data",
398+
"annotated": true,
399+
"scenarios": [
400+
"custom-resource-getatt-data"
401+
]
402+
},
392403
{
393404
"name": "custom-resource-provider",
394405
"annotated": true,
@@ -1197,6 +1208,13 @@
11971208
"vpc-lambda-cr-race"
11981209
]
11991210
},
1211+
{
1212+
"scenario": "custom-resource-getatt-data",
1213+
"description": "Custom Resource response `Data` consumed via `Fn::GetAtt(CR, 'Data.<key>')` / `Fn::GetAtt(CR, '<key>')` into ANOTHER resource's property (e.g. an SSM Parameter Value) — the fragile CR response-Data attribute path (#756 / #804: CR attributes only exist after the CR Lambda runs). Asserts the dependent's on-AWS value equals the value the CR handler returned, across multiple Data keys + an explicit dependent->CR dependency.",
1214+
"fixtures": [
1215+
"custom-resource-getatt-data"
1216+
]
1217+
},
12001218
{
12011219
"scenario": "deep-getatt-chain-resolution",
12021220
"description": "Long GetAtt chain where each resource POST-CREATE attribute (ARN / generated name only known after the AWS create call) feeds the next resource property, spanning a SDK + CC-API type mix. A wrong / late attribute resolution on either path (SDK `attributes` write or CC-API stored attributes + `constructAttribute` fallback) is pinpointed by the failing link. Critical hop: an unregistered CC-API type (`AWS::CloudWatch::CompositeAlarm`) whose `Arn` feeds downstream SDK-resource properties (issue: deep-getatt-chains fixture).",

docs/changelog-cdkd.md

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

docs/integ-coverage.md

Lines changed: 4 additions & 3 deletions
Large diffs are not rendered by default.

docs/scenario-coverage.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
Run `vp run scenario-coverage` to regenerate.
66

7-
**58 / 58 canonical scenarios** have at least one integ fixture exercising them. **125 / 144 integ fixtures** carry a `.scenarios.json` sidecar (with 0+ tags); the rest are un-annotated and contributor-reviewed below.
7+
**59 / 59 canonical scenarios** have at least one integ fixture exercising them. **126 / 145 integ fixtures** carry a `.scenarios.json` sidecar (with 0+ tags); the rest are un-annotated and contributor-reviewed below.
88

99
## How this is computed
1010

@@ -26,7 +26,7 @@ This report is a visibility tool, not a commit-time gate. Many cdkd fixtures leg
2626

2727
_None._ Every canonical scenario has at least one integ fixture tagged with it.
2828

29-
## Per-scenario coverage (58 scenarios)
29+
## Per-scenario coverage (59 scenarios)
3030

3131
| Scenario | Description | Integ Fixture(s) |
3232
|---|---|---|
@@ -38,6 +38,7 @@ _None._ Every canonical scenario has at least one integ fixture tagged with it.
3838
| `conditions-update-semantics` | Harder CloudFormation-Conditions-on-UPDATE semantics beyond the simple flip in `conditions-and-if` (which surfaced #840). A CDK-context phase flip (-c phase=a|b) redeploys the SAME stack in place and asserts: a resource that MOVES gating conditions (IsPhaseA-gated -> condition-false -> DELETED) and its reverse (IsPhaseB-gated absent -> CREATED); `Fn::If` -> `AWS::NoValue` REMOVING a nested property block (SQS RedrivePolicy) on an in-place UPDATE (same physical id, not a replacement); a condition-gated OUTPUT present vs absent in cdkd state outputs; a `DependsOn` to a condition-EXCLUDED resource being dropped (the depender still deploys); and a `Ref` to a condition-excluded resource living inside another condition-excluded resource (both pruned together, no dangling-ref crash). | [`conditions-update-2`](../tests/integration/conditions-update-2/) |
3939
| `cross-cutting-deploy-destroy` | Broad real-AWS regression set (39+ resource VPC+NAT+CF+Lambda+SQS or comparable breadth). Refreshes the integ-broad gate. | [`bench-ccapi`](../tests/integration/bench-ccapi/)<br>[`bench-cdk-sample`](../tests/integration/bench-cdk-sample/)<br>[`bench-sdk`](../tests/integration/bench-sdk/)<br>[`full-stack-demo`](../tests/integration/full-stack-demo/)<br>[`lambda`](../tests/integration/lambda/)<br>[`microservices`](../tests/integration/microservices/)<br>[`multi-resource`](../tests/integration/multi-resource/) |
4040
| `custom-resource-async-poll` | Custom Resource backed by Lambda + cfn-response via S3 pre-signed URL polling. | [`cloudfront-function-url`](../tests/integration/cloudfront-function-url/)<br>[`custom-resource-provider`](../tests/integration/custom-resource-provider/)<br>[`destroy-interrupt`](../tests/integration/destroy-interrupt/)<br>[`vpc-lambda-cr-race`](../tests/integration/vpc-lambda-cr-race/) |
41+
| `custom-resource-getatt-data` | Custom Resource response `Data` consumed via `Fn::GetAtt(CR, 'Data.<key>')` / `Fn::GetAtt(CR, '<key>')` into ANOTHER resource's property (e.g. an SSM Parameter Value) — the fragile CR response-Data attribute path (#756 / #804: CR attributes only exist after the CR Lambda runs). Asserts the dependent's on-AWS value equals the value the CR handler returned, across multiple Data keys + an explicit dependent->CR dependency. | [`custom-resource-getatt-data`](../tests/integration/custom-resource-getatt-data/) |
4142
| `deep-getatt-chain-resolution` | Long GetAtt chain where each resource POST-CREATE attribute (ARN / generated name only known after the AWS create call) feeds the next resource property, spanning a SDK + CC-API type mix. A wrong / late attribute resolution on either path (SDK `attributes` write or CC-API stored attributes + `constructAttribute` fallback) is pinpointed by the failing link. Critical hop: an unregistered CC-API type (`AWS::CloudWatch::CompositeAlarm`) whose `Arn` feeds downstream SDK-resource properties (issue: deep-getatt-chains fixture). | [`deep-getatt-chains`](../tests/integration/deep-getatt-chains/) |
4243
| `deletion-policy-retain` | DeletionPolicy: Retain skip on destroy (schema v5 recorded value wins over template). | [`deletion-policy-retain`](../tests/integration/deletion-policy-retain/) |
4344
| `deployment-events` | Structured deployment events to S3 + `cdkd events` command (issue #808): per-run `deployments/{runId}.jsonl` + `index.json` (separate key family from state.json, no schema bump), events survive `cdkd destroy`, and carry error + metadata ONLY (no resource properties / secrets). | [`deployment-events`](../tests/integration/deployment-events/) |

scripts/build-scenario-coverage-matrix.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ const KNOWN_SCENARIOS: Record<string, string> = {
129129
'Custom Resource backed by Lambda + cfn-response via S3 pre-signed URL polling.',
130130
'vpc-lambda-cr-race':
131131
'Custom Resource invocation against a VPC Lambda mid-deploy (ENI-attach race window).',
132+
'custom-resource-getatt-data':
133+
"Custom Resource response `Data` consumed via `Fn::GetAtt(CR, 'Data.<key>')` / `Fn::GetAtt(CR, '<key>')` into ANOTHER resource's property (e.g. an SSM Parameter Value) — the fragile CR response-Data attribute path (#756 / #804: CR attributes only exist after the CR Lambda runs). Asserts the dependent's on-AWS value equals the value the CR handler returned, across multiple Data keys + an explicit dependent->CR dependency.",
132134
'destroy-interrupt':
133135
'Graceful SIGINT on destroy (#816 — first Ctrl-C drains in-flight deletes, flushes trimmed state, releases the lock, exits non-zero; no 30m stranded lock) + Custom Resource replay fail-fast on re-run (#804 — the CR delete does NOT stall ~10 minutes invoking GetFunction against the already-deleted backing Lambda; the re-run resumes cleanly and quickly).',
134136

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"scenarios": [
3+
"custom-resource-getatt-data"
4+
]
5+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Custom Resource `Data` GetAtt -> dependent property (failure-seeking)
2+
3+
Surfaces bugs where a Custom Resource's response `Data` — consumed by
4+
`Fn::GetAtt(CustomResource, '<key>')` (a.k.a. `Data.<key>`) — must flow into
5+
ANOTHER resource's property. The CR response-`Data` attribute path is fragile
6+
(issues [#756](https://github.com/go-to-k/cdkd/issues/756) /
7+
[#804](https://github.com/go-to-k/cdkd/issues/804)) because CR attributes only
8+
exist AFTER the CR's backing Lambda runs and returns a SUCCESS response.
9+
10+
## What it tests
11+
12+
- An inline Lambda-backed `AWS::CloudFormation::CustomResource` whose handler
13+
returns `Data: { ComputedValue, Another, NumericValue }` directly in its
14+
response payload.
15+
- Three `AWS::SSM::Parameter` dependents whose `Value` is
16+
`Fn::GetAtt(MyCustomResource, '<key>')` — one per Data key. Multiple keys
17+
catch a resolver that only wires the first attribute; the stringified-number
18+
key catches a resolver that mishandles non-text Data.
19+
- An explicit `addDependency(cr)` on one parameter, so the DAG ordering
20+
(CR must complete and have its `attributes` populated BEFORE the dependent
21+
is provisioned) is exercised.
22+
23+
No VPC. No Provider framework (the simple synchronous direct-payload-return
24+
path — the cheapest way to surface a GetAtt-of-CR-Data resolution bug).
25+
26+
## Architecture
27+
28+
```
29+
MyCustomResource (inline Lambda; returns Data: {ComputedValue, Another, NumericValue})
30+
|
31+
+--> SSM Parameter .../computed Value = Fn::GetAtt(CR, 'ComputedValue')
32+
+--> SSM Parameter .../another Value = Fn::GetAtt(CR, 'Another')
33+
+--> SSM Parameter .../numeric Value = Fn::GetAtt(CR, 'NumericValue')
34+
```
35+
36+
## verify.sh
37+
38+
Deploys, then reads each SSM parameter back from AWS with
39+
`aws ssm get-parameter` and asserts its `Value` equals the value the CR
40+
handler returned (`computed-integ` / `another-<region>` / `42`). This proves
41+
the CR `Data` attribute resolved THROUGH the intrinsic resolver INTO the
42+
dependent resource's property — a blank / wrong value would otherwise pass
43+
unnoticed because nothing else reads it. Then destroys and asserts the state
44+
file, both SSM parameters, and the backing Lambda are gone.
45+
46+
## Deploy / Destroy
47+
48+
```bash
49+
cdkd deploy CdkdCrGetAttDataExample
50+
cdkd destroy CdkdCrGetAttDataExample
51+
```
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env node
2+
import * as cdk from 'aws-cdk-lib';
3+
import { CrGetAttDataStack } from '../lib/cr-getatt-data-stack.ts';
4+
5+
const app = new cdk.App();
6+
new CrGetAttDataStack(app, 'CdkdCrGetAttDataExample', {
7+
env: {
8+
account: process.env.CDK_DEFAULT_ACCOUNT,
9+
region: process.env.CDK_DEFAULT_REGION,
10+
},
11+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"app": "node bin/app.ts",
3+
"context": {
4+
"cdkd": {
5+
"stateBucket": "your-cdkd-state-bucket"
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)