@go-to-k/cdkd 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/dist/cli.js +356 -36
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.28.0.tgz +0 -0
- package/dist/index.js +96 -9
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.26.0.tgz +0 -0
package/README.md
CHANGED
|
@@ -539,9 +539,6 @@ Both flags accept either form on each invocation:
|
|
|
539
539
|
`TYPE` must look like `AWS::Service::Resource`; malformed types are rejected at parse time. `warn < timeout` is enforced both globally and per-type — so `--resource-warn-after AWS::X=10m --resource-timeout AWS::X=5m` is a parse-time error.
|
|
540
540
|
|
|
541
541
|
```bash
|
|
542
|
-
# Bump the per-resource budget to one hour (matches the Custom Resource provider's polling cap)
|
|
543
|
-
cdkd deploy --resource-timeout 1h
|
|
544
|
-
|
|
545
542
|
# Surface "still running" warnings sooner on a fast-feedback dev loop
|
|
546
543
|
cdkd deploy --resource-warn-after 90s --resource-timeout 10m
|
|
547
544
|
|
|
@@ -550,19 +547,25 @@ cdkd deploy \
|
|
|
550
547
|
--resource-timeout 30m \
|
|
551
548
|
--resource-timeout AWS::CloudFront::Distribution=1h \
|
|
552
549
|
--resource-timeout AWS::RDS::DBCluster=1h30m
|
|
550
|
+
|
|
551
|
+
# Force Custom Resources to abort earlier than their 1h self-reported polling cap
|
|
552
|
+
cdkd deploy --resource-timeout AWS::CloudFormation::CustomResource=5m
|
|
553
553
|
```
|
|
554
554
|
|
|
555
555
|
### Why the default is 30m, not 1h
|
|
556
556
|
|
|
557
|
-
cdkd's Custom Resource provider polls async handlers (`isCompleteHandler` pattern) for up to one hour before giving up. Setting the per-resource timeout to 1h by default would make a single hung
|
|
557
|
+
cdkd's Custom Resource provider polls async handlers (`isCompleteHandler` pattern) for up to one hour before giving up. Setting the per-resource timeout to 1h by default would make a single hung non-CR resource hold the whole stack for an hour even though no other resource type ever needs more than a few minutes. The 30m global default catches stuck operations faster.
|
|
558
|
+
|
|
559
|
+
For Custom Resources specifically, the provider self-reports its 1h polling cap to the engine via the `getMinResourceTimeoutMs()` interface — the deploy engine resolves the per-resource budget as `max(provider self-report, --resource-timeout global)`, so CR resources get their full hour automatically without the user having to remember `--resource-timeout 1h`. To force CR to abort earlier than its self-reported cap, pass an explicit per-type override (`--resource-timeout AWS::CloudFormation::CustomResource=5m`). Per-type overrides always win over the provider's self-report — they're the documented escape hatch.
|
|
558
560
|
|
|
559
|
-
The error message on timeout names the resource, type, region, elapsed time, and operation, and reminds you
|
|
561
|
+
The error message on timeout names the resource, type, region, elapsed time, and operation, and reminds you that long-running resources self-report their needed budget — when you see CR time out, the cause is genuinely the handler, not too-tight a default:
|
|
560
562
|
|
|
561
563
|
```text
|
|
562
564
|
Resource MyBucket (AWS::S3::Bucket) in us-east-1 timed out after 30m during CREATE (elapsed 30m).
|
|
563
565
|
This may indicate a stuck Cloud Control polling loop, hung Custom Resource, or
|
|
564
|
-
slow ENI provisioning. Re-run with --resource-timeout
|
|
565
|
-
|
|
566
|
+
slow ENI provisioning. Re-run with --resource-timeout AWS::S3::Bucket=<DURATION>
|
|
567
|
+
to bump the budget for this resource type only, or --verbose to see the
|
|
568
|
+
underlying provider activity.
|
|
566
569
|
```
|
|
567
570
|
|
|
568
571
|
Note: `--resource-warn-after` must be less than `--resource-timeout`. Reversed values are rejected at parse time.
|
package/dist/cli.js
CHANGED
|
@@ -1133,8 +1133,9 @@ var ResourceTimeoutError = class _ResourceTimeoutError extends CdkdError {
|
|
|
1133
1133
|
super(
|
|
1134
1134
|
`Resource ${logicalId} (${resourceType}) in ${region} timed out after ${timeoutLabel} during ${operation} (elapsed ${elapsedLabel}).
|
|
1135
1135
|
This may indicate a stuck Cloud Control polling loop, hung Custom Resource, or
|
|
1136
|
-
slow ENI provisioning. Re-run with --resource-timeout
|
|
1137
|
-
|
|
1136
|
+
slow ENI provisioning. Re-run with --resource-timeout ${resourceType}=<DURATION>
|
|
1137
|
+
to bump the budget for this resource type only, or --verbose to see the
|
|
1138
|
+
underlying provider activity.`,
|
|
1138
1139
|
"RESOURCE_TIMEOUT"
|
|
1139
1140
|
);
|
|
1140
1141
|
this.logicalId = logicalId;
|
|
@@ -7448,6 +7449,23 @@ var CustomResourceProvider = class _CustomResourceProvider {
|
|
|
7448
7449
|
logger = getLogger().child("CustomResourceProvider");
|
|
7449
7450
|
responseBucket;
|
|
7450
7451
|
responsePrefix;
|
|
7452
|
+
/**
|
|
7453
|
+
* Opt out of the deploy engine's outer transient-error retry loop.
|
|
7454
|
+
*
|
|
7455
|
+
* The loop re-invokes `provider.create()` from the top on a transient
|
|
7456
|
+
* SDK error (IAM propagation, HTTP 429/503, etc.). Each invocation
|
|
7457
|
+
* generates a brand-new RequestId and a brand-new pre-signed S3
|
|
7458
|
+
* response URL via `prepareInvocation()`. If the underlying Lambda has
|
|
7459
|
+
* already started — e.g. an outer retry fired between the placeholder
|
|
7460
|
+
* `PutObject` and the `Invoke`, or after the `Invoke` returned but a
|
|
7461
|
+
* spurious downstream error fired — the first attempt's Lambda
|
|
7462
|
+
* response lands at an S3 key that nobody polls, hanging the deploy
|
|
7463
|
+
* until the polling timeout. The provider already polls with its own
|
|
7464
|
+
* exponential backoff for async patterns (CDK Provider framework with
|
|
7465
|
+
* isCompleteHandler), so an outer retry adds nothing but the multi-
|
|
7466
|
+
* key bug.
|
|
7467
|
+
*/
|
|
7468
|
+
disableOuterRetry = true;
|
|
7451
7469
|
/** Max time to wait for synchronous S3 response after Lambda invocation (30 seconds) */
|
|
7452
7470
|
SYNC_RESPONSE_TIMEOUT_MS = 3e4;
|
|
7453
7471
|
/** Max time to wait for async S3 response (CDK Provider framework with isCompleteHandler) */
|
|
@@ -7467,6 +7485,22 @@ var CustomResourceProvider = class _CustomResourceProvider {
|
|
|
7467
7485
|
this.responsePrefix = config?.responsePrefix ?? "custom-resource-responses";
|
|
7468
7486
|
this.asyncResponseTimeoutMs = config?.asyncResponseTimeoutMs ?? _CustomResourceProvider.DEFAULT_ASYNC_RESPONSE_TIMEOUT_MS;
|
|
7469
7487
|
}
|
|
7488
|
+
/**
|
|
7489
|
+
* Self-reported minimum per-resource timeout.
|
|
7490
|
+
*
|
|
7491
|
+
* Custom Resource async invocations (CDK Provider framework with
|
|
7492
|
+
* `isCompleteHandler`) poll for up to `asyncResponseTimeoutMs`
|
|
7493
|
+
* (default 1 hour, matching CDK's `totalTimeout` default). The deploy
|
|
7494
|
+
* engine's global `--resource-timeout` default is 30 minutes, which
|
|
7495
|
+
* would abort a perfectly healthy CR mid-poll. By self-reporting the
|
|
7496
|
+
* polling cap, the engine lifts the deadline to `max(self-report,
|
|
7497
|
+
* global)` for CR resources only; a user-supplied per-type override
|
|
7498
|
+
* (`--resource-timeout AWS::CloudFormation::CustomResource=5m`) still
|
|
7499
|
+
* wins for explicit escape-hatching.
|
|
7500
|
+
*/
|
|
7501
|
+
getMinResourceTimeoutMs() {
|
|
7502
|
+
return this.asyncResponseTimeoutMs;
|
|
7503
|
+
}
|
|
7470
7504
|
/**
|
|
7471
7505
|
* Set the S3 bucket for custom resource responses
|
|
7472
7506
|
* Called by ProviderRegistry when state bucket is configured
|
|
@@ -8638,6 +8672,33 @@ var IAMRoleProvider = class {
|
|
|
8638
8672
|
this.logger.debug(`Added/updated ${tagsToAdd.length} tags on role ${roleName}`);
|
|
8639
8673
|
}
|
|
8640
8674
|
}
|
|
8675
|
+
/**
|
|
8676
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing IAM role.
|
|
8677
|
+
*
|
|
8678
|
+
* CloudFormation's `AWS::IAM::Role` exposes `Arn` and `RoleId`; both are
|
|
8679
|
+
* available from the `GetRole` response. See:
|
|
8680
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#aws-resource-iam-role-return-values
|
|
8681
|
+
*
|
|
8682
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
8683
|
+
* substituted into sibling references.
|
|
8684
|
+
*/
|
|
8685
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
8686
|
+
try {
|
|
8687
|
+
const resp = await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
|
|
8688
|
+
switch (attributeName) {
|
|
8689
|
+
case "Arn":
|
|
8690
|
+
return resp.Role?.Arn;
|
|
8691
|
+
case "RoleId":
|
|
8692
|
+
return resp.Role?.RoleId;
|
|
8693
|
+
default:
|
|
8694
|
+
return void 0;
|
|
8695
|
+
}
|
|
8696
|
+
} catch (err) {
|
|
8697
|
+
if (err instanceof NoSuchEntityException)
|
|
8698
|
+
return void 0;
|
|
8699
|
+
throw err;
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8641
8702
|
/**
|
|
8642
8703
|
* Adopt an existing IAM role into cdkd state.
|
|
8643
8704
|
*
|
|
@@ -10488,17 +10549,35 @@ var S3BucketProvider = class {
|
|
|
10488
10549
|
return region || "us-east-1";
|
|
10489
10550
|
}
|
|
10490
10551
|
/**
|
|
10491
|
-
* Build attributes for an S3 bucket
|
|
10552
|
+
* Build attributes for an S3 bucket.
|
|
10553
|
+
*
|
|
10554
|
+
* Covers every CloudFormation `Fn::GetAtt` return value for
|
|
10555
|
+
* `AWS::S3::Bucket`. All fields are derivable from `bucketName` + region —
|
|
10556
|
+
* no extra AWS API call is needed. See:
|
|
10557
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html#aws-properties-s3-bucket-return-values
|
|
10492
10558
|
*/
|
|
10493
10559
|
async buildAttributes(bucketName) {
|
|
10494
10560
|
const region = await this.getRegion();
|
|
10495
10561
|
return {
|
|
10496
10562
|
Arn: `arn:aws:s3:::${bucketName}`,
|
|
10497
10563
|
DomainName: `${bucketName}.s3.amazonaws.com`,
|
|
10564
|
+
DualStackDomainName: `${bucketName}.s3.dualstack.${region}.amazonaws.com`,
|
|
10498
10565
|
RegionalDomainName: `${bucketName}.s3.${region}.amazonaws.com`,
|
|
10499
10566
|
WebsiteURL: `http://${bucketName}.s3-website-${region}.amazonaws.com`
|
|
10500
10567
|
};
|
|
10501
10568
|
}
|
|
10569
|
+
/**
|
|
10570
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing bucket.
|
|
10571
|
+
*
|
|
10572
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
10573
|
+
* substituted into sibling references. All S3 Bucket attributes are
|
|
10574
|
+
* derivable from bucket name + region, so this avoids the round trip and
|
|
10575
|
+
* reuses the same templating as `buildAttributes`.
|
|
10576
|
+
*/
|
|
10577
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
10578
|
+
const attrs = await this.buildAttributes(physicalId);
|
|
10579
|
+
return attrs[attributeName];
|
|
10580
|
+
}
|
|
10502
10581
|
/**
|
|
10503
10582
|
* Apply versioning configuration if specified
|
|
10504
10583
|
*/
|
|
@@ -11793,6 +11872,43 @@ var SQSQueueProvider = class {
|
|
|
11793
11872
|
return `arn:aws:sqs:unknown:unknown:${queueName}`;
|
|
11794
11873
|
}
|
|
11795
11874
|
}
|
|
11875
|
+
/**
|
|
11876
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing SQS queue.
|
|
11877
|
+
*
|
|
11878
|
+
* CloudFormation's `AWS::SQS::Queue` exposes `Arn`, `QueueName` and
|
|
11879
|
+
* `QueueUrl`. The cdkd physicalId is the queue URL; `QueueUrl` and
|
|
11880
|
+
* `QueueName` are derivable from it without an AWS call, while `Arn`
|
|
11881
|
+
* requires `GetQueueAttributes`. See:
|
|
11882
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html#aws-properties-sqs-queues-return-values
|
|
11883
|
+
*
|
|
11884
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
11885
|
+
* substituted into sibling references.
|
|
11886
|
+
*/
|
|
11887
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
11888
|
+
switch (attributeName) {
|
|
11889
|
+
case "QueueUrl":
|
|
11890
|
+
return physicalId;
|
|
11891
|
+
case "QueueName":
|
|
11892
|
+
return physicalId.substring(physicalId.lastIndexOf("/") + 1);
|
|
11893
|
+
case "Arn": {
|
|
11894
|
+
try {
|
|
11895
|
+
const resp = await this.sqsClient.send(
|
|
11896
|
+
new GetQueueAttributesCommand({
|
|
11897
|
+
QueueUrl: physicalId,
|
|
11898
|
+
AttributeNames: ["QueueArn"]
|
|
11899
|
+
})
|
|
11900
|
+
);
|
|
11901
|
+
return resp.Attributes?.["QueueArn"];
|
|
11902
|
+
} catch (err) {
|
|
11903
|
+
if (err instanceof QueueDoesNotExist)
|
|
11904
|
+
return void 0;
|
|
11905
|
+
throw err;
|
|
11906
|
+
}
|
|
11907
|
+
}
|
|
11908
|
+
default:
|
|
11909
|
+
return void 0;
|
|
11910
|
+
}
|
|
11911
|
+
}
|
|
11796
11912
|
/**
|
|
11797
11913
|
* Adopt an existing SQS queue into cdkd state.
|
|
11798
11914
|
*
|
|
@@ -12328,6 +12444,28 @@ var SNSTopicProvider = class {
|
|
|
12328
12444
|
);
|
|
12329
12445
|
}
|
|
12330
12446
|
}
|
|
12447
|
+
/**
|
|
12448
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing SNS topic.
|
|
12449
|
+
*
|
|
12450
|
+
* CloudFormation's `AWS::SNS::Topic` exposes `TopicName` and `TopicArn`.
|
|
12451
|
+
* The cdkd physicalId is the topic ARN, so both are derivable without
|
|
12452
|
+
* an AWS call. See:
|
|
12453
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html#aws-properties-sns-topic-return-values
|
|
12454
|
+
*
|
|
12455
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
12456
|
+
* substituted into sibling references.
|
|
12457
|
+
*/
|
|
12458
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- consistent async signature with other providers
|
|
12459
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
12460
|
+
switch (attributeName) {
|
|
12461
|
+
case "TopicArn":
|
|
12462
|
+
return physicalId;
|
|
12463
|
+
case "TopicName":
|
|
12464
|
+
return physicalId.split(":").pop();
|
|
12465
|
+
default:
|
|
12466
|
+
return void 0;
|
|
12467
|
+
}
|
|
12468
|
+
}
|
|
12331
12469
|
/**
|
|
12332
12470
|
* Adopt an existing SNS topic into cdkd state.
|
|
12333
12471
|
*
|
|
@@ -13309,6 +13447,43 @@ var LambdaFunctionProvider = class {
|
|
|
13309
13447
|
}
|
|
13310
13448
|
return (crc ^ 4294967295) >>> 0;
|
|
13311
13449
|
}
|
|
13450
|
+
/**
|
|
13451
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing Lambda function.
|
|
13452
|
+
*
|
|
13453
|
+
* CloudFormation's `AWS::Lambda::Function` exposes `Arn`,
|
|
13454
|
+
* `SnapStartResponse.ApplyOn`, and `SnapStartResponse.OptimizationStatus`
|
|
13455
|
+
* as documented at
|
|
13456
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#aws-resource-lambda-function-return-values.
|
|
13457
|
+
*
|
|
13458
|
+
* All three live in the same `GetFunction` response (`Configuration.FunctionArn`
|
|
13459
|
+
* and `Configuration.SnapStart.{ApplyOn,OptimizationStatus}`), so a single API
|
|
13460
|
+
* call covers every supported attr. Used by `cdkd orphan` to live-fetch
|
|
13461
|
+
* attribute values that need to be substituted into sibling references.
|
|
13462
|
+
*/
|
|
13463
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
13464
|
+
if (attributeName !== "Arn" && attributeName !== "SnapStartResponse.ApplyOn" && attributeName !== "SnapStartResponse.OptimizationStatus") {
|
|
13465
|
+
return void 0;
|
|
13466
|
+
}
|
|
13467
|
+
try {
|
|
13468
|
+
const resp = await this.lambdaClient.send(
|
|
13469
|
+
new GetFunctionCommand({ FunctionName: physicalId })
|
|
13470
|
+
);
|
|
13471
|
+
switch (attributeName) {
|
|
13472
|
+
case "Arn":
|
|
13473
|
+
return resp.Configuration?.FunctionArn;
|
|
13474
|
+
case "SnapStartResponse.ApplyOn":
|
|
13475
|
+
return resp.Configuration?.SnapStart?.ApplyOn;
|
|
13476
|
+
case "SnapStartResponse.OptimizationStatus":
|
|
13477
|
+
return resp.Configuration?.SnapStart?.OptimizationStatus;
|
|
13478
|
+
default:
|
|
13479
|
+
return void 0;
|
|
13480
|
+
}
|
|
13481
|
+
} catch (err) {
|
|
13482
|
+
if (err instanceof ResourceNotFoundException)
|
|
13483
|
+
return void 0;
|
|
13484
|
+
throw err;
|
|
13485
|
+
}
|
|
13486
|
+
}
|
|
13312
13487
|
/**
|
|
13313
13488
|
* Adopt an existing Lambda function into cdkd state.
|
|
13314
13489
|
*
|
|
@@ -13581,6 +13756,7 @@ var LambdaPermissionProvider = class {
|
|
|
13581
13756
|
import {
|
|
13582
13757
|
CreateFunctionUrlConfigCommand,
|
|
13583
13758
|
DeleteFunctionUrlConfigCommand,
|
|
13759
|
+
GetFunctionUrlConfigCommand as GetFunctionUrlConfigCommand2,
|
|
13584
13760
|
UpdateFunctionUrlConfigCommand,
|
|
13585
13761
|
ResourceNotFoundException as ResourceNotFoundException3
|
|
13586
13762
|
} from "@aws-sdk/client-lambda";
|
|
@@ -13708,6 +13884,36 @@ var LambdaUrlProvider = class {
|
|
|
13708
13884
|
);
|
|
13709
13885
|
}
|
|
13710
13886
|
}
|
|
13887
|
+
/**
|
|
13888
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing Lambda Function
|
|
13889
|
+
* URL.
|
|
13890
|
+
*
|
|
13891
|
+
* CloudFormation's `AWS::Lambda::Url` exposes `FunctionArn` and
|
|
13892
|
+
* `FunctionUrl`. Both come from `GetFunctionUrlConfig`. See:
|
|
13893
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-url.html#aws-resource-lambda-url-return-values
|
|
13894
|
+
*
|
|
13895
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
13896
|
+
* substituted into sibling references.
|
|
13897
|
+
*/
|
|
13898
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
13899
|
+
try {
|
|
13900
|
+
const resp = await this.lambdaClient.send(
|
|
13901
|
+
new GetFunctionUrlConfigCommand2({ FunctionName: physicalId })
|
|
13902
|
+
);
|
|
13903
|
+
switch (attributeName) {
|
|
13904
|
+
case "FunctionArn":
|
|
13905
|
+
return resp.FunctionArn;
|
|
13906
|
+
case "FunctionUrl":
|
|
13907
|
+
return resp.FunctionUrl;
|
|
13908
|
+
default:
|
|
13909
|
+
return void 0;
|
|
13910
|
+
}
|
|
13911
|
+
} catch (err) {
|
|
13912
|
+
if (err instanceof ResourceNotFoundException3)
|
|
13913
|
+
return void 0;
|
|
13914
|
+
throw err;
|
|
13915
|
+
}
|
|
13916
|
+
}
|
|
13711
13917
|
/**
|
|
13712
13918
|
* Adopt an existing Lambda Function URL into cdkd state.
|
|
13713
13919
|
*
|
|
@@ -14419,6 +14625,40 @@ var DynamoDBTableProvider = class {
|
|
|
14419
14625
|
}
|
|
14420
14626
|
throw new Error(`Table ${tableName} did not reach ACTIVE status within ${maxAttempts} seconds`);
|
|
14421
14627
|
}
|
|
14628
|
+
/**
|
|
14629
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing DynamoDB table.
|
|
14630
|
+
*
|
|
14631
|
+
* CloudFormation's `AWS::DynamoDB::Table` exposes `Arn`, `StreamArn`
|
|
14632
|
+
* (a.k.a. `LatestStreamArn` in the SDK; CFn returns the latest enabled
|
|
14633
|
+
* stream's ARN), and `LatestStreamLabel`. All three are sibling fields on
|
|
14634
|
+
* the same `DescribeTable` response, so a single API call covers every
|
|
14635
|
+
* supported attr. See:
|
|
14636
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html#aws-resource-dynamodb-table-return-values
|
|
14637
|
+
*
|
|
14638
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
14639
|
+
* substituted into sibling references.
|
|
14640
|
+
*/
|
|
14641
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
14642
|
+
try {
|
|
14643
|
+
const resp = await this.dynamoDBClient.send(
|
|
14644
|
+
new DescribeTableCommand2({ TableName: physicalId })
|
|
14645
|
+
);
|
|
14646
|
+
switch (attributeName) {
|
|
14647
|
+
case "Arn":
|
|
14648
|
+
return resp.Table?.TableArn;
|
|
14649
|
+
case "StreamArn":
|
|
14650
|
+
return resp.Table?.LatestStreamArn;
|
|
14651
|
+
case "LatestStreamLabel":
|
|
14652
|
+
return resp.Table?.LatestStreamLabel;
|
|
14653
|
+
default:
|
|
14654
|
+
return void 0;
|
|
14655
|
+
}
|
|
14656
|
+
} catch (err) {
|
|
14657
|
+
if (err instanceof ResourceNotFoundException6)
|
|
14658
|
+
return void 0;
|
|
14659
|
+
throw err;
|
|
14660
|
+
}
|
|
14661
|
+
}
|
|
14422
14662
|
/**
|
|
14423
14663
|
* Adopt an existing DynamoDB table into cdkd state.
|
|
14424
14664
|
*
|
|
@@ -14706,6 +14946,23 @@ var LogsLogGroupProvider = class {
|
|
|
14706
14946
|
return `arn:aws:logs:unknown:unknown:log-group:${logGroupName}:*`;
|
|
14707
14947
|
}
|
|
14708
14948
|
}
|
|
14949
|
+
/**
|
|
14950
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing log group.
|
|
14951
|
+
*
|
|
14952
|
+
* CloudFormation's `AWS::Logs::LogGroup` exposes only `Arn`. The ARN is
|
|
14953
|
+
* derivable from the log group name + account + region via the existing
|
|
14954
|
+
* `buildArn` helper. See:
|
|
14955
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#aws-resource-logs-loggroup-return-values
|
|
14956
|
+
*
|
|
14957
|
+
* Used by `cdkd orphan` to live-fetch attribute values that need to be
|
|
14958
|
+
* substituted into sibling references.
|
|
14959
|
+
*/
|
|
14960
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
14961
|
+
if (attributeName !== "Arn") {
|
|
14962
|
+
return void 0;
|
|
14963
|
+
}
|
|
14964
|
+
return this.buildArn(physicalId);
|
|
14965
|
+
}
|
|
14709
14966
|
/**
|
|
14710
14967
|
* Adopt an existing CloudWatch Logs log group into cdkd state.
|
|
14711
14968
|
*
|
|
@@ -16743,29 +17000,63 @@ var EC2Provider = class {
|
|
|
16743
17000
|
}
|
|
16744
17001
|
}
|
|
16745
17002
|
}
|
|
17003
|
+
/**
|
|
17004
|
+
* Resolve a single `Fn::GetAtt` attribute for an `AWS::EC2::VPC`.
|
|
17005
|
+
*
|
|
17006
|
+
* CloudFormation returns `CidrBlock`, `CidrBlockAssociations`,
|
|
17007
|
+
* `DefaultNetworkAcl`, `DefaultSecurityGroup`, and `Ipv6CidrBlocks`. See:
|
|
17008
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html#aws-resource-ec2-vpc-return-values
|
|
17009
|
+
*
|
|
17010
|
+
* `DefaultNetworkAcl` and `DefaultSecurityGroup` previously returned wrong
|
|
17011
|
+
* values (DHCP options id and `undefined` respectively); the AWS console
|
|
17012
|
+
* surfaces these the same way as CFn — by filtering the relevant
|
|
17013
|
+
* `Describe*` API on `vpc-id` + the `default` flag.
|
|
17014
|
+
*/
|
|
16746
17015
|
async getVpcAttribute(physicalId, attributeName) {
|
|
16747
|
-
if (attributeName === "VpcId")
|
|
16748
|
-
return physicalId;
|
|
16749
17016
|
try {
|
|
16750
|
-
const response = await this.ec2Client.send(new DescribeVpcsCommand2({ VpcIds: [physicalId] }));
|
|
16751
|
-
const vpc = response.Vpcs?.[0];
|
|
16752
|
-
if (!vpc)
|
|
16753
|
-
return void 0;
|
|
16754
17017
|
switch (attributeName) {
|
|
16755
|
-
case "
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
return
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
|
|
16768
|
-
|
|
17018
|
+
case "DefaultNetworkAcl": {
|
|
17019
|
+
const resp = await this.ec2Client.send(
|
|
17020
|
+
new DescribeNetworkAclsCommand({
|
|
17021
|
+
Filters: [
|
|
17022
|
+
{ Name: "vpc-id", Values: [physicalId] },
|
|
17023
|
+
{ Name: "default", Values: ["true"] }
|
|
17024
|
+
]
|
|
17025
|
+
})
|
|
17026
|
+
);
|
|
17027
|
+
return resp.NetworkAcls?.[0]?.NetworkAclId;
|
|
17028
|
+
}
|
|
17029
|
+
case "DefaultSecurityGroup": {
|
|
17030
|
+
const resp = await this.ec2Client.send(
|
|
17031
|
+
new DescribeSecurityGroupsCommand2({
|
|
17032
|
+
Filters: [
|
|
17033
|
+
{ Name: "vpc-id", Values: [physicalId] },
|
|
17034
|
+
{ Name: "group-name", Values: ["default"] }
|
|
17035
|
+
]
|
|
17036
|
+
})
|
|
17037
|
+
);
|
|
17038
|
+
return resp.SecurityGroups?.[0]?.GroupId;
|
|
17039
|
+
}
|
|
17040
|
+
default: {
|
|
17041
|
+
const response = await this.ec2Client.send(
|
|
17042
|
+
new DescribeVpcsCommand2({ VpcIds: [physicalId] })
|
|
17043
|
+
);
|
|
17044
|
+
const vpc = response.Vpcs?.[0];
|
|
17045
|
+
if (!vpc)
|
|
17046
|
+
return void 0;
|
|
17047
|
+
switch (attributeName) {
|
|
17048
|
+
case "CidrBlock":
|
|
17049
|
+
return vpc.CidrBlock;
|
|
17050
|
+
case "Ipv6CidrBlocks":
|
|
17051
|
+
return vpc.Ipv6CidrBlockAssociationSet?.filter(
|
|
17052
|
+
(a) => a.Ipv6CidrBlockState?.State === "associated"
|
|
17053
|
+
).map((a) => a.Ipv6CidrBlock) || [];
|
|
17054
|
+
case "CidrBlockAssociations":
|
|
17055
|
+
return vpc.CidrBlockAssociationSet?.map((a) => a.AssociationId) || [];
|
|
17056
|
+
default:
|
|
17057
|
+
return void 0;
|
|
17058
|
+
}
|
|
17059
|
+
}
|
|
16769
17060
|
}
|
|
16770
17061
|
} catch {
|
|
16771
17062
|
return void 0;
|
|
@@ -31633,8 +31924,11 @@ var DeployEngine = class {
|
|
|
31633
31924
|
const baseLabel = `${verb} ${logicalId} (${resourceType})`;
|
|
31634
31925
|
renderer.addTask(logicalId, baseLabel);
|
|
31635
31926
|
const operationKind = change.changeType === "CREATE" ? "CREATE" : change.changeType === "DELETE" ? "DELETE" : "UPDATE";
|
|
31927
|
+
const provider = this.providerRegistry.getProvider(resourceType);
|
|
31928
|
+
const providerMinTimeoutMs = provider.getMinResourceTimeoutMs?.() ?? 0;
|
|
31636
31929
|
const warnAfterMs = this.options.resourceWarnAfterByType?.[resourceType] ?? this.options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
|
|
31637
|
-
const
|
|
31930
|
+
const globalTimeoutMs = this.options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
|
|
31931
|
+
const timeoutMs = this.options.resourceTimeoutByType?.[resourceType] ?? Math.max(providerMinTimeoutMs, globalTimeoutMs);
|
|
31638
31932
|
try {
|
|
31639
31933
|
await withResourceDeadline(
|
|
31640
31934
|
async () => {
|
|
@@ -31713,7 +32007,10 @@ var DeployEngine = class {
|
|
|
31713
32007
|
const { provider: createProvider, properties: createProps } = this.selectProviderWithSafetyNet(provider, resourceType, resolvedProps, logicalId);
|
|
31714
32008
|
const result = await this.withRetry(
|
|
31715
32009
|
() => createProvider.create(logicalId, resourceType, createProps),
|
|
31716
|
-
logicalId
|
|
32010
|
+
logicalId,
|
|
32011
|
+
void 0,
|
|
32012
|
+
void 0,
|
|
32013
|
+
provider
|
|
31717
32014
|
);
|
|
31718
32015
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
31719
32016
|
stateResources[logicalId] = {
|
|
@@ -31767,7 +32064,10 @@ var DeployEngine = class {
|
|
|
31767
32064
|
const { provider: replaceProvider, properties: replaceProps } = this.selectProviderWithSafetyNet(provider, resourceType, resolvedProps, logicalId);
|
|
31768
32065
|
const createResult = await this.withRetry(
|
|
31769
32066
|
() => replaceProvider.create(logicalId, resourceType, replaceProps),
|
|
31770
|
-
logicalId
|
|
32067
|
+
logicalId,
|
|
32068
|
+
void 0,
|
|
32069
|
+
void 0,
|
|
32070
|
+
provider
|
|
31771
32071
|
);
|
|
31772
32072
|
const updateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy;
|
|
31773
32073
|
if (updateReplacePolicy === "Retain") {
|
|
@@ -31818,7 +32118,10 @@ var DeployEngine = class {
|
|
|
31818
32118
|
updateProps,
|
|
31819
32119
|
currentProps
|
|
31820
32120
|
),
|
|
31821
|
-
logicalId
|
|
32121
|
+
logicalId,
|
|
32122
|
+
void 0,
|
|
32123
|
+
void 0,
|
|
32124
|
+
provider
|
|
31822
32125
|
);
|
|
31823
32126
|
} catch (updateError) {
|
|
31824
32127
|
const msg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
@@ -31847,7 +32150,10 @@ var DeployEngine = class {
|
|
|
31847
32150
|
const { provider: replProvider, properties: replProps } = this.selectProviderWithSafetyNet(provider, resourceType, resolvedProps, logicalId);
|
|
31848
32151
|
const createResult = await this.withRetry(
|
|
31849
32152
|
() => replProvider.create(logicalId, resourceType, replProps),
|
|
31850
|
-
logicalId
|
|
32153
|
+
logicalId,
|
|
32154
|
+
void 0,
|
|
32155
|
+
void 0,
|
|
32156
|
+
provider
|
|
31851
32157
|
);
|
|
31852
32158
|
result = {
|
|
31853
32159
|
physicalId: createResult.physicalId,
|
|
@@ -31904,7 +32210,8 @@ var DeployEngine = class {
|
|
|
31904
32210
|
logicalId,
|
|
31905
32211
|
3,
|
|
31906
32212
|
// fewer retries for DELETE
|
|
31907
|
-
5e3
|
|
32213
|
+
5e3,
|
|
32214
|
+
provider
|
|
31908
32215
|
);
|
|
31909
32216
|
} catch (deleteError) {
|
|
31910
32217
|
const msg = deleteError instanceof Error ? deleteError.message : String(deleteError);
|
|
@@ -32081,8 +32388,18 @@ var DeployEngine = class {
|
|
|
32081
32388
|
* Thin wrapper over `withRetry` from ./retry.js that injects this engine's
|
|
32082
32389
|
* SIGINT-aware interrupt check and logger. The actual backoff schedule
|
|
32083
32390
|
* lives there.
|
|
32391
|
+
*
|
|
32392
|
+
* When the provider opts out via `disableOuterRetry`, the operation is
|
|
32393
|
+
* invoked exactly once and the retry loop is skipped entirely. The
|
|
32394
|
+
* Custom Resource provider uses this to avoid re-running its `create()`
|
|
32395
|
+
* — each invocation derives a fresh pre-signed S3 URL and RequestId,
|
|
32396
|
+
* so an outer retry leaves the previous attempt's Lambda response
|
|
32397
|
+
* stranded at an S3 key nobody polls.
|
|
32084
32398
|
*/
|
|
32085
|
-
async withRetry(operation, logicalId, maxRetries, initialDelayMs) {
|
|
32399
|
+
async withRetry(operation, logicalId, maxRetries, initialDelayMs, provider) {
|
|
32400
|
+
if (provider?.disableOuterRetry) {
|
|
32401
|
+
return operation();
|
|
32402
|
+
}
|
|
32086
32403
|
return withRetry(operation, logicalId, {
|
|
32087
32404
|
...maxRetries !== void 0 && { maxRetries },
|
|
32088
32405
|
...initialDelayMs !== void 0 && { initialDelayMs },
|
|
@@ -32762,16 +33079,19 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
32762
33079
|
logger.warn(`Resource ${logicalId} not found in state, skipping`);
|
|
32763
33080
|
return;
|
|
32764
33081
|
}
|
|
32765
|
-
const warnAfterMs = ctx.resourceWarnAfterByType?.[resource.resourceType] ?? ctx.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
|
|
32766
|
-
const timeoutMs = ctx.resourceTimeoutByType?.[resource.resourceType] ?? ctx.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
|
|
32767
33082
|
const baseLabel = `Deleting ${logicalId} (${resource.resourceType})`;
|
|
32768
33083
|
renderer.addTask(logicalId, baseLabel);
|
|
32769
33084
|
try {
|
|
32770
33085
|
const provider = destroyProviderRegistry.getProvider(resource.resourceType);
|
|
33086
|
+
const providerMinTimeoutMs = provider.getMinResourceTimeoutMs?.() ?? 0;
|
|
33087
|
+
const warnAfterMs = ctx.resourceWarnAfterByType?.[resource.resourceType] ?? ctx.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
|
|
33088
|
+
const globalTimeoutMs = ctx.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
|
|
33089
|
+
const timeoutMs = ctx.resourceTimeoutByType?.[resource.resourceType] ?? Math.max(providerMinTimeoutMs, globalTimeoutMs);
|
|
32771
33090
|
await withResourceDeadline(
|
|
32772
33091
|
async () => {
|
|
33092
|
+
const maxAttempts = provider.disableOuterRetry ? 0 : 3;
|
|
32773
33093
|
let lastDeleteError;
|
|
32774
|
-
for (let attempt = 0; attempt <=
|
|
33094
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
32775
33095
|
try {
|
|
32776
33096
|
await provider.delete(
|
|
32777
33097
|
logicalId,
|
|
@@ -32785,11 +33105,11 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
32785
33105
|
lastDeleteError = retryError;
|
|
32786
33106
|
const msg = retryError instanceof Error ? retryError.message : String(retryError);
|
|
32787
33107
|
const isRetryable = msg.includes("Too Many Requests") || msg.includes("has dependencies") || msg.includes("can't be deleted since") || msg.includes("DependencyViolation");
|
|
32788
|
-
if (!isRetryable || attempt >=
|
|
33108
|
+
if (!isRetryable || attempt >= maxAttempts)
|
|
32789
33109
|
break;
|
|
32790
33110
|
const delay = 5e3 * Math.pow(2, attempt);
|
|
32791
33111
|
logger.debug(
|
|
32792
|
-
` \u23F3 Retrying delete ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}
|
|
33112
|
+
` \u23F3 Retrying delete ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${maxAttempts})`
|
|
32793
33113
|
);
|
|
32794
33114
|
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
32795
33115
|
}
|
|
@@ -35316,7 +35636,7 @@ function reorderArgs(argv) {
|
|
|
35316
35636
|
}
|
|
35317
35637
|
async function main() {
|
|
35318
35638
|
const program = new Command13();
|
|
35319
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
35639
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.28.0");
|
|
35320
35640
|
program.addCommand(createBootstrapCommand());
|
|
35321
35641
|
program.addCommand(createSynthCommand());
|
|
35322
35642
|
program.addCommand(createListCommand());
|