@go-to-k/cdkd 0.267.3 → 0.267.5
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/dist/{asg-provider-Dj8krtJ3.js → asg-provider-CIZpj4w5.js} +2 -2
- package/dist/{asg-provider-Dj8krtJ3.js.map → asg-provider-CIZpj4w5.js.map} +1 -1
- package/dist/cli.js +5 -8
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-CXHDJKCt.js → deploy-engine-B7if6--O.js} +270 -220
- package/dist/deploy-engine-B7if6--O.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-CXHDJKCt.js.map +0 -1
|
@@ -7486,6 +7486,270 @@ var ReplacementRulesRegistry = class {
|
|
|
7486
7486
|
}
|
|
7487
7487
|
};
|
|
7488
7488
|
|
|
7489
|
+
//#endregion
|
|
7490
|
+
//#region src/deployment/retryable-errors.ts
|
|
7491
|
+
/**
|
|
7492
|
+
* Patterns that mark an AWS error as a transient/retryable failure.
|
|
7493
|
+
* Each entry is a substring match against the error message; all of these
|
|
7494
|
+
* are situations where the same call typically succeeds after a short delay
|
|
7495
|
+
* because of eventual consistency or just-created-dependency propagation.
|
|
7496
|
+
*/
|
|
7497
|
+
const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
7498
|
+
"cannot be assumed",
|
|
7499
|
+
"Firehose is unable to assume role",
|
|
7500
|
+
"is unable to assume provided role",
|
|
7501
|
+
"role defined for the function",
|
|
7502
|
+
"not authorized to perform",
|
|
7503
|
+
"execution role",
|
|
7504
|
+
"trust policy",
|
|
7505
|
+
"Role validation failed",
|
|
7506
|
+
"does not have required permissions",
|
|
7507
|
+
"Trusted Entity",
|
|
7508
|
+
"currently in the following state: Pending",
|
|
7509
|
+
"has dependencies and cannot be deleted",
|
|
7510
|
+
"can't be deleted since it has",
|
|
7511
|
+
"DependencyViolation",
|
|
7512
|
+
"does not exist",
|
|
7513
|
+
"Schema is currently being altered",
|
|
7514
|
+
"Invalid principal in policy",
|
|
7515
|
+
"Policy Error: PrincipalNotFound",
|
|
7516
|
+
"Invalid value for the parameter Policy",
|
|
7517
|
+
"required permissions for: ENHANCED_MONITORING",
|
|
7518
|
+
"Caught ServiceAccessDeniedException",
|
|
7519
|
+
"permissions required to assume the role",
|
|
7520
|
+
"authorized to assume the provided role",
|
|
7521
|
+
"conflicting conditional operation",
|
|
7522
|
+
"scheduled for deletion",
|
|
7523
|
+
"Cannot access stream",
|
|
7524
|
+
"Please ensure the role can perform",
|
|
7525
|
+
"KMS key is invalid for CreateGrant",
|
|
7526
|
+
"Policy contains a statement with one or more invalid principals",
|
|
7527
|
+
"Invalid IAM Instance Profile",
|
|
7528
|
+
"Invalid InstanceProfile",
|
|
7529
|
+
"Failed to authorize instance profile",
|
|
7530
|
+
"Could not deliver test message",
|
|
7531
|
+
"wait 60 seconds",
|
|
7532
|
+
"concurrent update operation",
|
|
7533
|
+
"because it is in use",
|
|
7534
|
+
"Rate exceeded"
|
|
7535
|
+
];
|
|
7536
|
+
/**
|
|
7537
|
+
* HTTP status codes that always indicate a transient failure worth retrying.
|
|
7538
|
+
* 429 = Too Many Requests (throttle), 503 = Service Unavailable.
|
|
7539
|
+
*/
|
|
7540
|
+
const RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
|
|
7541
|
+
/**
|
|
7542
|
+
* AWS SDK v3 canonical throttling error names. Mirrors
|
|
7543
|
+
* `@aws-sdk/service-error-classification`'s `THROTTLING_ERROR_CODES` — any
|
|
7544
|
+
* error (or wrapped cause) whose `name` is one of these is a transient rate-
|
|
7545
|
+
* limit rejection worth retrying with backoff. Detecting by NAME is more
|
|
7546
|
+
* robust than by HTTP status because most AWS throttles surface as HTTP 400
|
|
7547
|
+
* (not 429) with the throttling signal carried only in the error code / name
|
|
7548
|
+
* (e.g. SSM `ThrottlingException` for the `Rate exceeded` message).
|
|
7549
|
+
*/
|
|
7550
|
+
const THROTTLING_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
7551
|
+
"BandwidthLimitExceeded",
|
|
7552
|
+
"EC2ThrottledException",
|
|
7553
|
+
"LimitExceededException",
|
|
7554
|
+
"PriorRequestNotComplete",
|
|
7555
|
+
"ProvisionedThroughputExceededException",
|
|
7556
|
+
"RequestLimitExceeded",
|
|
7557
|
+
"RequestThrottled",
|
|
7558
|
+
"RequestThrottledException",
|
|
7559
|
+
"SlowDown",
|
|
7560
|
+
"ThrottledException",
|
|
7561
|
+
"Throttling",
|
|
7562
|
+
"ThrottlingException",
|
|
7563
|
+
"TooManyRequestsException",
|
|
7564
|
+
"TransactionInProgressException"
|
|
7565
|
+
]);
|
|
7566
|
+
/**
|
|
7567
|
+
* Walk the error + its `.cause` chain (bounded) looking for a rate-limit
|
|
7568
|
+
* signal — either an AWS SDK v3 throttling error `name`
|
|
7569
|
+
* ({@link THROTTLING_ERROR_NAMES}) or a retryable HTTP status
|
|
7570
|
+
* ({@link RETRYABLE_HTTP_STATUS_CODES}) on `$metadata`.
|
|
7571
|
+
*
|
|
7572
|
+
* cdkd wraps the original AWS error in a `ProvisioningError`, so the signal is
|
|
7573
|
+
* typically one cause-link deep; the bounded walk also tolerates SDK errors
|
|
7574
|
+
* that nest a `$response`/cause without exploding on a cyclic chain.
|
|
7575
|
+
*
|
|
7576
|
+
* BOTH signals are checked at EVERY depth. An earlier version checked the name
|
|
7577
|
+
* to depth 5 but the HTTP status only at depths 0 and 1, so a 429 nested two
|
|
7578
|
+
* links deep was missed.
|
|
7579
|
+
*/
|
|
7580
|
+
function isThrottlingError(error) {
|
|
7581
|
+
let current = error;
|
|
7582
|
+
for (let depth = 0; depth < 5 && current != null; depth++) {
|
|
7583
|
+
const name = current.name;
|
|
7584
|
+
if (typeof name === "string" && THROTTLING_ERROR_NAMES.has(name)) return true;
|
|
7585
|
+
const status = current.$metadata?.httpStatusCode;
|
|
7586
|
+
if (status !== void 0 && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;
|
|
7587
|
+
current = current.cause;
|
|
7588
|
+
}
|
|
7589
|
+
return false;
|
|
7590
|
+
}
|
|
7591
|
+
/**
|
|
7592
|
+
* Determine whether an AWS error should be retried.
|
|
7593
|
+
*
|
|
7594
|
+
* Checks (in order):
|
|
7595
|
+
* 1. Rate-limit signal on the error or any wrapped cause — throttling error
|
|
7596
|
+
* `name` or retryable HTTP status (most AWS throttles are HTTP 400, not
|
|
7597
|
+
* 429, so the name check carries most of the weight). See
|
|
7598
|
+
* {@link isThrottlingError}.
|
|
7599
|
+
* 2. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}
|
|
7600
|
+
*/
|
|
7601
|
+
function isRetryableTransientError(error, message) {
|
|
7602
|
+
if (isThrottlingError(error)) return true;
|
|
7603
|
+
return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
|
|
7604
|
+
}
|
|
7605
|
+
/**
|
|
7606
|
+
* Match the "already exists" name-collision signature raised when a create
|
|
7607
|
+
* targets a physical name still held by another resource (or by the same
|
|
7608
|
+
* name's not-yet-released tombstone after an async delete).
|
|
7609
|
+
*
|
|
7610
|
+
* Deliberately NOT part of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}: a name
|
|
7611
|
+
* collision is only worth retrying at the specific re-create sites that just
|
|
7612
|
+
* deleted the old holder (the deploy engine's --replace delete-first fallback
|
|
7613
|
+
* and the rollback executor's reverse-replacement) — everywhere else it is a
|
|
7614
|
+
* genuine conflict that must fail fast. Shared by those sites' collision
|
|
7615
|
+
* detection + retry filters so a signature extension lands in one place.
|
|
7616
|
+
*/
|
|
7617
|
+
function isNameCollisionError(message) {
|
|
7618
|
+
return /already exists/i.test(message) || message.includes("AlreadyExists");
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Match the SQS same-name re-creation cooldown: after `DeleteQueue`, creating
|
|
7622
|
+
* a queue with the SAME name inside ~60s fails with
|
|
7623
|
+
* `AWS.SimpleQueueService.QueueDeletedRecently` ("You must wait 60 seconds
|
|
7624
|
+
* after deleting a queue before you can create another with the same name").
|
|
7625
|
+
*
|
|
7626
|
+
* The generic transient table above already carries 'wait 60 seconds' for
|
|
7627
|
+
* plain CREATEs (rapid destroy → redeploy loops), but the delete-then-re-create
|
|
7628
|
+
* sites (the deploy engine's --replace delete-first fallback and the rollback
|
|
7629
|
+
* executor's reverse-replacement) override the retry filter with
|
|
7630
|
+
* {@link isNameCollisionError}, which this signature does NOT match — so a
|
|
7631
|
+
* replacement revert used to fail fast mid-flight with the resource absent
|
|
7632
|
+
* from both AWS and state (issue #1206). Those sites now OR this matcher into
|
|
7633
|
+
* their retry filter, with a schedule long enough to cover the 60s window.
|
|
7634
|
+
*
|
|
7635
|
+
* Kept separate from {@link isNameCollisionError} on purpose: a cooldown at a
|
|
7636
|
+
* create-first site must NOT be treated as a collision (deleting the new
|
|
7637
|
+
* resource would not release the cooldown on the old name).
|
|
7638
|
+
*/
|
|
7639
|
+
function isNameCooldownError(message) {
|
|
7640
|
+
return message.includes("QueueDeletedRecently") || message.includes("wait 60 seconds");
|
|
7641
|
+
}
|
|
7642
|
+
/**
|
|
7643
|
+
* Retry filter for the delete-then-re-create sites: the old name holder was
|
|
7644
|
+
* just deleted, so both the late name release ("already exists" from an async
|
|
7645
|
+
* delete) and the SQS 60s name cooldown are worth waiting out. Pair with a
|
|
7646
|
+
* schedule that covers the full cooldown window (maxRetries 8, delays
|
|
7647
|
+
* 2s/4s/8s then capped at 10s ≈ 64s total sleep).
|
|
7648
|
+
*/
|
|
7649
|
+
function isRecreateRetryableError(message) {
|
|
7650
|
+
return isNameCollisionError(message) || isNameCooldownError(message);
|
|
7651
|
+
}
|
|
7652
|
+
|
|
7653
|
+
//#endregion
|
|
7654
|
+
//#region src/deployment/retry.ts
|
|
7655
|
+
/**
|
|
7656
|
+
* Retry helper for resource provisioning operations that hit transient
|
|
7657
|
+
* AWS eventual-consistency errors (IAM propagation, Lambda Pending state,
|
|
7658
|
+
* dependency violations, etc.).
|
|
7659
|
+
*
|
|
7660
|
+
* Extracted from DeployEngine so the backoff schedule can be unit-tested
|
|
7661
|
+
* in isolation. The retryable-error classifier itself lives in
|
|
7662
|
+
* `./retryable-errors.ts`.
|
|
7663
|
+
*/
|
|
7664
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7665
|
+
/**
|
|
7666
|
+
* Run `operation`, retrying transient failures with exponential backoff
|
|
7667
|
+
* capped at `maxDelayMs`.
|
|
7668
|
+
*
|
|
7669
|
+
* Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):
|
|
7670
|
+
* 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)
|
|
7671
|
+
*
|
|
7672
|
+
* Non-retryable errors are rethrown immediately. The transient-error
|
|
7673
|
+
* classifier is `isRetryableTransientError` from ./retryable-errors.ts.
|
|
7674
|
+
*/
|
|
7675
|
+
async function withRetry(operation, logicalId, opts = {}) {
|
|
7676
|
+
const maxRetries = opts.maxRetries ?? 8;
|
|
7677
|
+
const initialDelayMs = opts.initialDelayMs ?? 1e3;
|
|
7678
|
+
const maxDelayMs = opts.maxDelayMs ?? 8e3;
|
|
7679
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
7680
|
+
let lastError;
|
|
7681
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
7682
|
+
return await operation();
|
|
7683
|
+
} catch (error) {
|
|
7684
|
+
lastError = error;
|
|
7685
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7686
|
+
if (!(opts.isRetryable ? opts.isRetryable(message, error) : isRetryableTransientError(error, message)) || attempt >= maxRetries) throw error;
|
|
7687
|
+
const delay = Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
7688
|
+
opts.logger?.debug(` ⏳ Retrying ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${maxRetries}) - ${message}`);
|
|
7689
|
+
for (let waited = 0; waited < delay; waited += 1e3) {
|
|
7690
|
+
if (opts.isInterrupted?.()) throw opts.onInterrupted ? opts.onInterrupted() : /* @__PURE__ */ new Error("Interrupted");
|
|
7691
|
+
await sleep(Math.min(1e3, delay - waited));
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
throw lastError;
|
|
7695
|
+
}
|
|
7696
|
+
|
|
7697
|
+
//#endregion
|
|
7698
|
+
//#region src/provisioning/describe-type.ts
|
|
7699
|
+
/**
|
|
7700
|
+
* Shared `cloudformation:DescribeType` invocation with throttle-only retry
|
|
7701
|
+
* (issue #1236).
|
|
7702
|
+
*
|
|
7703
|
+
* DescribeType is throttled per-account, and a deploy can issue a burst of
|
|
7704
|
+
* them (the #1182 create-only schema prefetch describes every type in the
|
|
7705
|
+
* template at deploy start). A lookup issued moments later — the write-only
|
|
7706
|
+
* property resolution during a CC-routed UPDATE is the critical case — can
|
|
7707
|
+
* then be throttled past the SDK's own short retry, and the caller's graceful
|
|
7708
|
+
* fallback turns the transient throttle into a real failure: the minimal
|
|
7709
|
+
* update patch drops a load-bearing write-only property and the update
|
|
7710
|
+
* hard-fails (`AWS::ECS::Service.VolumeConfigurations`), or a replacement is
|
|
7711
|
+
* mis-classified from a missing create-only schema.
|
|
7712
|
+
*
|
|
7713
|
+
* The fix is to retry ONLY throttle-shaped failures ({@link isThrottlingError})
|
|
7714
|
+
* with the standard backoff before surfacing the error. Non-throttle failures
|
|
7715
|
+
* (a missing `cloudformation:DescribeType` permission being the important one)
|
|
7716
|
+
* are rethrown immediately so the callers' warn-and-fall-back path stays as
|
|
7717
|
+
* fast as before — a caller permanently without the permission must not pay a
|
|
7718
|
+
* retry sleep on every lookup.
|
|
7719
|
+
*/
|
|
7720
|
+
/**
|
|
7721
|
+
* Test seam: overriding `sleep` lets unit tests drive the backoff schedule
|
|
7722
|
+
* without real waits (mirrors macro-expander's `retryDelays`).
|
|
7723
|
+
*/
|
|
7724
|
+
const describeTypeRetryDelays = {};
|
|
7725
|
+
/**
|
|
7726
|
+
* Retries after the first attempt, throttle-shaped failures only. At the
|
|
7727
|
+
* default backoff (1s -> 2s -> 4s -> 8s) this adds at most ~15s of sleep —
|
|
7728
|
+
* enough to ride out a prefetch-burst throttle window, small next to the
|
|
7729
|
+
* failed-update + rollback cycle it prevents.
|
|
7730
|
+
*/
|
|
7731
|
+
const MAX_THROTTLE_RETRIES = 4;
|
|
7732
|
+
/**
|
|
7733
|
+
* Issue `DescribeType` for a resource type, retrying throttle-shaped failures
|
|
7734
|
+
* with exponential backoff. Any other failure (or a throttle persisting past
|
|
7735
|
+
* the retry budget) is thrown to the caller unchanged.
|
|
7736
|
+
*
|
|
7737
|
+
* `client` defaults to the shared `AwsClients.cloudFormation`; callers that
|
|
7738
|
+
* carry their own injected client (`cdkd export`'s primary-identifier
|
|
7739
|
+
* resolution) pass it so test doubles keep intercepting.
|
|
7740
|
+
*/
|
|
7741
|
+
function describeTypeWithThrottleRetry(resourceType, client) {
|
|
7742
|
+
return withRetry(() => (client ?? getAwsClients().cloudFormation).send(new DescribeTypeCommand({
|
|
7743
|
+
Type: "RESOURCE",
|
|
7744
|
+
TypeName: resourceType
|
|
7745
|
+
})), resourceType, {
|
|
7746
|
+
maxRetries: MAX_THROTTLE_RETRIES,
|
|
7747
|
+
isRetryable: (_message, error) => isThrottlingError(error),
|
|
7748
|
+
logger: getLogger().child("DescribeType"),
|
|
7749
|
+
...describeTypeRetryDelays.sleep ? { sleep: describeTypeRetryDelays.sleep } : {}
|
|
7750
|
+
});
|
|
7751
|
+
}
|
|
7752
|
+
|
|
7489
7753
|
//#endregion
|
|
7490
7754
|
//#region src/provisioning/create-only-properties.ts
|
|
7491
7755
|
/**
|
|
@@ -7642,10 +7906,7 @@ function isIntrinsicShaped(value) {
|
|
|
7642
7906
|
*/
|
|
7643
7907
|
async function fetchCreateOnlyPropertyPaths(resourceType) {
|
|
7644
7908
|
const logger = getLogger().child("CreateOnlyProperties");
|
|
7645
|
-
const response = await
|
|
7646
|
-
Type: "RESOURCE",
|
|
7647
|
-
TypeName: resourceType
|
|
7648
|
-
}));
|
|
7909
|
+
const response = await describeTypeWithThrottleRetry(resourceType);
|
|
7649
7910
|
const result = [];
|
|
7650
7911
|
if (response.Schema) {
|
|
7651
7912
|
const createOnly = JSON.parse(response.Schema).createOnlyProperties;
|
|
@@ -10826,10 +11087,7 @@ function getTopLevelWriteOnlyProperties(resourceType) {
|
|
|
10826
11087
|
*/
|
|
10827
11088
|
async function fetchTopLevelWriteOnlyProperties(resourceType) {
|
|
10828
11089
|
const logger = getLogger().child("WriteOnlyProperties");
|
|
10829
|
-
const response = await
|
|
10830
|
-
Type: "RESOURCE",
|
|
10831
|
-
TypeName: resourceType
|
|
10832
|
-
}));
|
|
11090
|
+
const response = await describeTypeWithThrottleRetry(resourceType);
|
|
10833
11091
|
const result = /* @__PURE__ */ new Set();
|
|
10834
11092
|
if (response.Schema) {
|
|
10835
11093
|
const writeOnly = JSON.parse(response.Schema).writeOnlyProperties;
|
|
@@ -11354,7 +11612,7 @@ var CloudControlProvider = class {
|
|
|
11354
11612
|
this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
|
|
11355
11613
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
11356
11614
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
11357
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
11615
|
+
const { ASGProvider } = await import("./asg-provider-CIZpj4w5.js").then((n) => n.n);
|
|
11358
11616
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
11359
11617
|
return;
|
|
11360
11618
|
}
|
|
@@ -16401,214 +16659,6 @@ function computeImplicitDeleteEdges(resources) {
|
|
|
16401
16659
|
return edges;
|
|
16402
16660
|
}
|
|
16403
16661
|
|
|
16404
|
-
//#endregion
|
|
16405
|
-
//#region src/deployment/retryable-errors.ts
|
|
16406
|
-
/**
|
|
16407
|
-
* Patterns that mark an AWS error as a transient/retryable failure.
|
|
16408
|
-
* Each entry is a substring match against the error message; all of these
|
|
16409
|
-
* are situations where the same call typically succeeds after a short delay
|
|
16410
|
-
* because of eventual consistency or just-created-dependency propagation.
|
|
16411
|
-
*/
|
|
16412
|
-
const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
16413
|
-
"cannot be assumed",
|
|
16414
|
-
"Firehose is unable to assume role",
|
|
16415
|
-
"is unable to assume provided role",
|
|
16416
|
-
"role defined for the function",
|
|
16417
|
-
"not authorized to perform",
|
|
16418
|
-
"execution role",
|
|
16419
|
-
"trust policy",
|
|
16420
|
-
"Role validation failed",
|
|
16421
|
-
"does not have required permissions",
|
|
16422
|
-
"Trusted Entity",
|
|
16423
|
-
"currently in the following state: Pending",
|
|
16424
|
-
"has dependencies and cannot be deleted",
|
|
16425
|
-
"can't be deleted since it has",
|
|
16426
|
-
"DependencyViolation",
|
|
16427
|
-
"does not exist",
|
|
16428
|
-
"Schema is currently being altered",
|
|
16429
|
-
"Invalid principal in policy",
|
|
16430
|
-
"Policy Error: PrincipalNotFound",
|
|
16431
|
-
"Invalid value for the parameter Policy",
|
|
16432
|
-
"required permissions for: ENHANCED_MONITORING",
|
|
16433
|
-
"Caught ServiceAccessDeniedException",
|
|
16434
|
-
"permissions required to assume the role",
|
|
16435
|
-
"authorized to assume the provided role",
|
|
16436
|
-
"conflicting conditional operation",
|
|
16437
|
-
"scheduled for deletion",
|
|
16438
|
-
"Cannot access stream",
|
|
16439
|
-
"Please ensure the role can perform",
|
|
16440
|
-
"KMS key is invalid for CreateGrant",
|
|
16441
|
-
"Policy contains a statement with one or more invalid principals",
|
|
16442
|
-
"Invalid IAM Instance Profile",
|
|
16443
|
-
"Invalid InstanceProfile",
|
|
16444
|
-
"Failed to authorize instance profile",
|
|
16445
|
-
"Could not deliver test message",
|
|
16446
|
-
"wait 60 seconds",
|
|
16447
|
-
"concurrent update operation",
|
|
16448
|
-
"because it is in use",
|
|
16449
|
-
"Rate exceeded"
|
|
16450
|
-
];
|
|
16451
|
-
/**
|
|
16452
|
-
* HTTP status codes that always indicate a transient failure worth retrying.
|
|
16453
|
-
* 429 = Too Many Requests (throttle), 503 = Service Unavailable.
|
|
16454
|
-
*/
|
|
16455
|
-
const RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
|
|
16456
|
-
/**
|
|
16457
|
-
* AWS SDK v3 canonical throttling error names. Mirrors
|
|
16458
|
-
* `@aws-sdk/service-error-classification`'s `THROTTLING_ERROR_CODES` — any
|
|
16459
|
-
* error (or wrapped cause) whose `name` is one of these is a transient rate-
|
|
16460
|
-
* limit rejection worth retrying with backoff. Detecting by NAME is more
|
|
16461
|
-
* robust than by HTTP status because most AWS throttles surface as HTTP 400
|
|
16462
|
-
* (not 429) with the throttling signal carried only in the error code / name
|
|
16463
|
-
* (e.g. SSM `ThrottlingException` for the `Rate exceeded` message).
|
|
16464
|
-
*/
|
|
16465
|
-
const THROTTLING_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
16466
|
-
"BandwidthLimitExceeded",
|
|
16467
|
-
"EC2ThrottledException",
|
|
16468
|
-
"LimitExceededException",
|
|
16469
|
-
"PriorRequestNotComplete",
|
|
16470
|
-
"ProvisionedThroughputExceededException",
|
|
16471
|
-
"RequestLimitExceeded",
|
|
16472
|
-
"RequestThrottled",
|
|
16473
|
-
"RequestThrottledException",
|
|
16474
|
-
"SlowDown",
|
|
16475
|
-
"ThrottledException",
|
|
16476
|
-
"Throttling",
|
|
16477
|
-
"ThrottlingException",
|
|
16478
|
-
"TooManyRequestsException",
|
|
16479
|
-
"TransactionInProgressException"
|
|
16480
|
-
]);
|
|
16481
|
-
/**
|
|
16482
|
-
* Walk the error + its `.cause` chain (bounded) looking for a rate-limit
|
|
16483
|
-
* signal — either an AWS SDK v3 throttling error `name`
|
|
16484
|
-
* ({@link THROTTLING_ERROR_NAMES}) or a retryable HTTP status
|
|
16485
|
-
* ({@link RETRYABLE_HTTP_STATUS_CODES}) on `$metadata`.
|
|
16486
|
-
*
|
|
16487
|
-
* cdkd wraps the original AWS error in a `ProvisioningError`, so the signal is
|
|
16488
|
-
* typically one cause-link deep; the bounded walk also tolerates SDK errors
|
|
16489
|
-
* that nest a `$response`/cause without exploding on a cyclic chain.
|
|
16490
|
-
*
|
|
16491
|
-
* BOTH signals are checked at EVERY depth. An earlier version checked the name
|
|
16492
|
-
* to depth 5 but the HTTP status only at depths 0 and 1, so a 429 nested two
|
|
16493
|
-
* links deep was missed.
|
|
16494
|
-
*/
|
|
16495
|
-
function isThrottlingError(error) {
|
|
16496
|
-
let current = error;
|
|
16497
|
-
for (let depth = 0; depth < 5 && current != null; depth++) {
|
|
16498
|
-
const name = current.name;
|
|
16499
|
-
if (typeof name === "string" && THROTTLING_ERROR_NAMES.has(name)) return true;
|
|
16500
|
-
const status = current.$metadata?.httpStatusCode;
|
|
16501
|
-
if (status !== void 0 && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;
|
|
16502
|
-
current = current.cause;
|
|
16503
|
-
}
|
|
16504
|
-
return false;
|
|
16505
|
-
}
|
|
16506
|
-
/**
|
|
16507
|
-
* Determine whether an AWS error should be retried.
|
|
16508
|
-
*
|
|
16509
|
-
* Checks (in order):
|
|
16510
|
-
* 1. Rate-limit signal on the error or any wrapped cause — throttling error
|
|
16511
|
-
* `name` or retryable HTTP status (most AWS throttles are HTTP 400, not
|
|
16512
|
-
* 429, so the name check carries most of the weight). See
|
|
16513
|
-
* {@link isThrottlingError}.
|
|
16514
|
-
* 2. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}
|
|
16515
|
-
*/
|
|
16516
|
-
function isRetryableTransientError(error, message) {
|
|
16517
|
-
if (isThrottlingError(error)) return true;
|
|
16518
|
-
return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
|
|
16519
|
-
}
|
|
16520
|
-
/**
|
|
16521
|
-
* Match the "already exists" name-collision signature raised when a create
|
|
16522
|
-
* targets a physical name still held by another resource (or by the same
|
|
16523
|
-
* name's not-yet-released tombstone after an async delete).
|
|
16524
|
-
*
|
|
16525
|
-
* Deliberately NOT part of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}: a name
|
|
16526
|
-
* collision is only worth retrying at the specific re-create sites that just
|
|
16527
|
-
* deleted the old holder (the deploy engine's --replace delete-first fallback
|
|
16528
|
-
* and the rollback executor's reverse-replacement) — everywhere else it is a
|
|
16529
|
-
* genuine conflict that must fail fast. Shared by those sites' collision
|
|
16530
|
-
* detection + retry filters so a signature extension lands in one place.
|
|
16531
|
-
*/
|
|
16532
|
-
function isNameCollisionError(message) {
|
|
16533
|
-
return /already exists/i.test(message) || message.includes("AlreadyExists");
|
|
16534
|
-
}
|
|
16535
|
-
/**
|
|
16536
|
-
* Match the SQS same-name re-creation cooldown: after `DeleteQueue`, creating
|
|
16537
|
-
* a queue with the SAME name inside ~60s fails with
|
|
16538
|
-
* `AWS.SimpleQueueService.QueueDeletedRecently` ("You must wait 60 seconds
|
|
16539
|
-
* after deleting a queue before you can create another with the same name").
|
|
16540
|
-
*
|
|
16541
|
-
* The generic transient table above already carries 'wait 60 seconds' for
|
|
16542
|
-
* plain CREATEs (rapid destroy → redeploy loops), but the delete-then-re-create
|
|
16543
|
-
* sites (the deploy engine's --replace delete-first fallback and the rollback
|
|
16544
|
-
* executor's reverse-replacement) override the retry filter with
|
|
16545
|
-
* {@link isNameCollisionError}, which this signature does NOT match — so a
|
|
16546
|
-
* replacement revert used to fail fast mid-flight with the resource absent
|
|
16547
|
-
* from both AWS and state (issue #1206). Those sites now OR this matcher into
|
|
16548
|
-
* their retry filter, with a schedule long enough to cover the 60s window.
|
|
16549
|
-
*
|
|
16550
|
-
* Kept separate from {@link isNameCollisionError} on purpose: a cooldown at a
|
|
16551
|
-
* create-first site must NOT be treated as a collision (deleting the new
|
|
16552
|
-
* resource would not release the cooldown on the old name).
|
|
16553
|
-
*/
|
|
16554
|
-
function isNameCooldownError(message) {
|
|
16555
|
-
return message.includes("QueueDeletedRecently") || message.includes("wait 60 seconds");
|
|
16556
|
-
}
|
|
16557
|
-
/**
|
|
16558
|
-
* Retry filter for the delete-then-re-create sites: the old name holder was
|
|
16559
|
-
* just deleted, so both the late name release ("already exists" from an async
|
|
16560
|
-
* delete) and the SQS 60s name cooldown are worth waiting out. Pair with a
|
|
16561
|
-
* schedule that covers the full cooldown window (maxRetries 8, delays
|
|
16562
|
-
* 2s/4s/8s then capped at 10s ≈ 64s total sleep).
|
|
16563
|
-
*/
|
|
16564
|
-
function isRecreateRetryableError(message) {
|
|
16565
|
-
return isNameCollisionError(message) || isNameCooldownError(message);
|
|
16566
|
-
}
|
|
16567
|
-
|
|
16568
|
-
//#endregion
|
|
16569
|
-
//#region src/deployment/retry.ts
|
|
16570
|
-
/**
|
|
16571
|
-
* Retry helper for resource provisioning operations that hit transient
|
|
16572
|
-
* AWS eventual-consistency errors (IAM propagation, Lambda Pending state,
|
|
16573
|
-
* dependency violations, etc.).
|
|
16574
|
-
*
|
|
16575
|
-
* Extracted from DeployEngine so the backoff schedule can be unit-tested
|
|
16576
|
-
* in isolation. The retryable-error classifier itself lives in
|
|
16577
|
-
* `./retryable-errors.ts`.
|
|
16578
|
-
*/
|
|
16579
|
-
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
16580
|
-
/**
|
|
16581
|
-
* Run `operation`, retrying transient failures with exponential backoff
|
|
16582
|
-
* capped at `maxDelayMs`.
|
|
16583
|
-
*
|
|
16584
|
-
* Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):
|
|
16585
|
-
* 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)
|
|
16586
|
-
*
|
|
16587
|
-
* Non-retryable errors are rethrown immediately. The transient-error
|
|
16588
|
-
* classifier is `isRetryableTransientError` from ./retryable-errors.ts.
|
|
16589
|
-
*/
|
|
16590
|
-
async function withRetry(operation, logicalId, opts = {}) {
|
|
16591
|
-
const maxRetries = opts.maxRetries ?? 8;
|
|
16592
|
-
const initialDelayMs = opts.initialDelayMs ?? 1e3;
|
|
16593
|
-
const maxDelayMs = opts.maxDelayMs ?? 8e3;
|
|
16594
|
-
const sleep = opts.sleep ?? defaultSleep;
|
|
16595
|
-
let lastError;
|
|
16596
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
16597
|
-
return await operation();
|
|
16598
|
-
} catch (error) {
|
|
16599
|
-
lastError = error;
|
|
16600
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
16601
|
-
if (!(opts.isRetryable ? opts.isRetryable(message, error) : isRetryableTransientError(error, message)) || attempt >= maxRetries) throw error;
|
|
16602
|
-
const delay = Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
16603
|
-
opts.logger?.debug(` ⏳ Retrying ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${maxRetries}) - ${message}`);
|
|
16604
|
-
for (let waited = 0; waited < delay; waited += 1e3) {
|
|
16605
|
-
if (opts.isInterrupted?.()) throw opts.onInterrupted ? opts.onInterrupted() : /* @__PURE__ */ new Error("Interrupted");
|
|
16606
|
-
await sleep(Math.min(1e3, delay - waited));
|
|
16607
|
-
}
|
|
16608
|
-
}
|
|
16609
|
-
throw lastError;
|
|
16610
|
-
}
|
|
16611
|
-
|
|
16612
16662
|
//#endregion
|
|
16613
16663
|
//#region src/deployment/resource-deadline.ts
|
|
16614
16664
|
/**
|
|
@@ -17265,7 +17315,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
17265
17315
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
17266
17316
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
17267
17317
|
function getCdkdVersion() {
|
|
17268
|
-
return "0.267.
|
|
17318
|
+
return "0.267.5";
|
|
17269
17319
|
}
|
|
17270
17320
|
/**
|
|
17271
17321
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -19252,5 +19302,5 @@ var DeployEngine = class {
|
|
|
19252
19302
|
};
|
|
19253
19303
|
|
|
19254
19304
|
//#endregion
|
|
19255
|
-
export {
|
|
19256
|
-
//# sourceMappingURL=deploy-engine-
|
|
19305
|
+
export { WorkGraph as $, NestedStackChildDirectDestroyError as $t, slowCcOperationTimeoutMs as A, CFN_TEMPLATE_BODY_LIMIT as At, applyRoleArnIfSet as B, AwsClients as Bt, yellow as C, resolveAutoAssetStorage as Ct, ProviderRegistry as D, resolveStateBucketWithDefaultAndSource as Dt, clearOnUpdateRemoval as E, resolveStateBucketWithDefault as Et, refStateLookupFromResource as F, expectedOwnerParam as Ft, DagBuilder as G, CdkdError as Gt, describeTypeWithThrottleRetry as H, resetAwsClients as Ht, WAFv2WebACLProvider as I, AssemblyReader as It, S3StateBackend as J, LocalInvokeBuildError as Jt, TemplateParser as K, ConfigError as Kt, normalizeAwsTagsToCfn as L, processStackMessages as Lt, isTerminationProtectionPropagationError as M, MIGRATE_TMP_PREFIX as Mt, IntrinsicFunctionResolver as N, findLargeInlineResources as Nt, findActionableSilentDrops as O, resolveUseCdkBootstrapAssets as Ot, cfnRefValueFromPhysicalId as P, uploadCfnTemplate as Pt, stringifyValue as Q, MissingCdkCliError as Qt, resolveExplicitPhysicalId as R, clearBucketRegionCache as Rt, red as S, resolveApp as St, collectInlinePolicyNamesManagedBySiblings as T, resolveSkipPrefix as Tt, withRetry as U, setAwsClients as Ut, DiffCalculator as V, getAwsClients as Vt, isRetryableTransientError as W, AssetError as Wt, shouldRetainResource as X, LocalStartServiceError as Xt, rebuildClientForBucketRegion as Y, LocalMigrateError as Yt, AssetPublisher as Z, LockError as Zt, formatResourceLine as _, getDockerImageBySourceHash as _t, DeploymentEventsStore as a, StackTerminationProtectionError as an, BOOTSTRAP_MARKER_PREFIX as at, gray as b, getDefaultStateBucketName as bt, replayFailedOperations as c, formatError as cn, parseBootstrapMarker as ct, IMPLICIT_DELETE_DEPENDENCIES as d, withErrorHandling as dn, buildDockerImage as dt, PartialFailureError as en, buildAssetRedirectMap as et, computeImplicitDeleteEdges as f, __exportAll as fn, formatDockerLoginError as ft, renderStatefulReason as g, AssetManifestLoader as gt, isStatefulRecreateTargetSync as h, runDockerStreaming as ht, DeploymentEventsReader as i, StackHasActiveImportsError as in, AssetModeResolver as it, disableInstanceApiTermination as j, CFN_TEMPLATE_URL_LIMIT as jt, CloudControlProvider as k, warnDeprecatedNoPrefixCliFlag as kt, replayRollback as l, isCdkdError as ln, validateAssetBucketName as lt, MULTI_REGION_RECREATE_BLOCKED_TYPES as m, runDockerForeground as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, ResourceTimeoutError as nn, loadPublishableAssetManifest as nt, planFailedOps as o, StateError as on, ensureAssetStorage as ot, extractDeploymentEventError as p, getDockerCmd as pt, LockManager as q, DependencyError as qt, DeployEngine as r, ResourceUpdateNotSupportedError as rn, rewriteTemplateAssetReferences as rt, planRollback as s, SynthesisError as sn, getBootstrapMarkerKey as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, ProvisioningError as tn, createAssetRedirectResolver as tt, withResourceDeadline as u, normalizeAwsError as un, validateContainerRepoName as ut, bold as v, Synthesizer as vt, IAMRoleProvider as w, resolveCaptureObservedState as wt, green as x, getLegacyStateBucketName as xt, cyan as y, synthesisStatusMessage as yt, assertRegionMatch as z, resolveBucketRegion as zt };
|
|
19306
|
+
//# sourceMappingURL=deploy-engine-B7if6--O.js.map
|