@go-to-k/cdkd 0.268.0 → 0.268.2
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-ZylgRJOY.js → asg-provider-gaXfXSH0.js} +2 -2
- package/dist/{asg-provider-ZylgRJOY.js.map → asg-provider-gaXfXSH0.js.map} +1 -1
- package/dist/cli.js +121 -32
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-B5cRuFij.js → deploy-engine-BEjF-h_6.js} +245 -59
- package/dist/{deploy-engine-B5cRuFij.js.map → deploy-engine-BEjF-h_6.js.map} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,24 @@ var SynthesisError = class SynthesisError extends CdkdError {
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
/**
|
|
105
|
+
* Control-flow signal: the user declined a pre-provisioning confirmation
|
|
106
|
+
* prompt, so the deploy must unwind WITHOUT being reported as a failure.
|
|
107
|
+
*
|
|
108
|
+
* Raised from `DeployEngineOptions.onCurrentStateLoaded` (the post-lock
|
|
109
|
+
* gate the `--prefix-user-supplied-names` migration check runs in). The
|
|
110
|
+
* engine does not catch it — it propagates out of `deploy()` through the
|
|
111
|
+
* usual `finally`, which releases the lock and stops the renderer — and the
|
|
112
|
+
* deploy CLI catches it and returns quietly instead of logging an error or
|
|
113
|
+
* recording a FAILED run event. Nothing has been provisioned at that point.
|
|
114
|
+
*/
|
|
115
|
+
var DeployCancelledError = class DeployCancelledError extends CdkdError {
|
|
116
|
+
constructor(message = "Deployment cancelled by user") {
|
|
117
|
+
super(message, "DEPLOY_CANCELLED");
|
|
118
|
+
this.name = "DeployCancelledError";
|
|
119
|
+
Object.setPrototypeOf(this, DeployCancelledError.prototype);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
105
123
|
* Asset errors
|
|
106
124
|
*/
|
|
107
125
|
var AssetError = class AssetError extends CdkdError {
|
|
@@ -7648,12 +7666,26 @@ var ReplacementRulesRegistry = class {
|
|
|
7648
7666
|
//#endregion
|
|
7649
7667
|
//#region src/deployment/retryable-errors.ts
|
|
7650
7668
|
/**
|
|
7651
|
-
*
|
|
7652
|
-
*
|
|
7653
|
-
*
|
|
7654
|
-
*
|
|
7655
|
-
|
|
7656
|
-
|
|
7669
|
+
* The **IAM-propagation** subset of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}:
|
|
7670
|
+
* an AWS service rejecting a call because a just-created IAM entity (role,
|
|
7671
|
+
* trust policy, inline policy, instance profile, principal) has not propagated
|
|
7672
|
+
* to that service's authorization layer yet.
|
|
7673
|
+
*
|
|
7674
|
+
* Kept as its own array — and composed back into the full transient table
|
|
7675
|
+
* below — so there is exactly ONE list per pattern (no parallel classifier to
|
|
7676
|
+
* drift). It exists because this class has a materially different RECOVERY
|
|
7677
|
+
* SHAPE from the other transient errors: it resolves in single-digit seconds,
|
|
7678
|
+
* so `withRetry` polls it on a dense sub-second schedule instead of the
|
|
7679
|
+
* generic 1s/2s/4s/8s exponential backoff (which is right for throttling and
|
|
7680
|
+
* for long resource-state transitions, and wrong here — see
|
|
7681
|
+
* {@link file://../deployment/retry.ts}).
|
|
7682
|
+
*
|
|
7683
|
+
* When adding a new pattern: put it here if the fix is "wait a moment and ask
|
|
7684
|
+
* IAM again", and in `OTHER_TRANSIENT_ERROR_MESSAGE_PATTERNS` otherwise. A
|
|
7685
|
+
* misfiled entry only changes the retry CADENCE, never whether the error is
|
|
7686
|
+
* retryable at all.
|
|
7687
|
+
*/
|
|
7688
|
+
const IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS = [
|
|
7657
7689
|
"cannot be assumed",
|
|
7658
7690
|
"Firehose is unable to assume role",
|
|
7659
7691
|
"is unable to assume provided role",
|
|
@@ -7664,12 +7696,6 @@ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
|
7664
7696
|
"Role validation failed",
|
|
7665
7697
|
"does not have required permissions",
|
|
7666
7698
|
"Trusted Entity",
|
|
7667
|
-
"currently in the following state: Pending",
|
|
7668
|
-
"has dependencies and cannot be deleted",
|
|
7669
|
-
"can't be deleted since it has",
|
|
7670
|
-
"DependencyViolation",
|
|
7671
|
-
"does not exist",
|
|
7672
|
-
"Schema is currently being altered",
|
|
7673
7699
|
"Invalid principal in policy",
|
|
7674
7700
|
"Policy Error: PrincipalNotFound",
|
|
7675
7701
|
"Invalid value for the parameter Policy",
|
|
@@ -7677,15 +7703,30 @@ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
|
7677
7703
|
"Caught ServiceAccessDeniedException",
|
|
7678
7704
|
"permissions required to assume the role",
|
|
7679
7705
|
"authorized to assume the provided role",
|
|
7680
|
-
"conflicting conditional operation",
|
|
7681
|
-
"scheduled for deletion",
|
|
7682
7706
|
"Cannot access stream",
|
|
7683
7707
|
"Please ensure the role can perform",
|
|
7684
7708
|
"KMS key is invalid for CreateGrant",
|
|
7685
7709
|
"Policy contains a statement with one or more invalid principals",
|
|
7686
7710
|
"Invalid IAM Instance Profile",
|
|
7687
7711
|
"Invalid InstanceProfile",
|
|
7688
|
-
"Failed to authorize instance profile"
|
|
7712
|
+
"Failed to authorize instance profile"
|
|
7713
|
+
];
|
|
7714
|
+
/**
|
|
7715
|
+
* The NON-IAM-propagation half of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}:
|
|
7716
|
+
* transient failures whose recovery window is either long (SQS's 60s same-name
|
|
7717
|
+
* cooldown, a resource still leaving a Pending/Creating state) or genuinely
|
|
7718
|
+
* load-related (throttling), where hammering AWS with dense retries is harmful
|
|
7719
|
+
* and exponential backoff is the correct shape.
|
|
7720
|
+
*/
|
|
7721
|
+
const OTHER_TRANSIENT_ERROR_MESSAGE_PATTERNS = [
|
|
7722
|
+
"currently in the following state: Pending",
|
|
7723
|
+
"has dependencies and cannot be deleted",
|
|
7724
|
+
"can't be deleted since it has",
|
|
7725
|
+
"DependencyViolation",
|
|
7726
|
+
"does not exist",
|
|
7727
|
+
"Schema is currently being altered",
|
|
7728
|
+
"conflicting conditional operation",
|
|
7729
|
+
"scheduled for deletion",
|
|
7689
7730
|
"Could not deliver test message",
|
|
7690
7731
|
"wait 60 seconds",
|
|
7691
7732
|
"concurrent update operation",
|
|
@@ -7693,6 +7734,16 @@ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
|
7693
7734
|
"Rate exceeded"
|
|
7694
7735
|
];
|
|
7695
7736
|
/**
|
|
7737
|
+
* Patterns that mark an AWS error as a transient/retryable failure.
|
|
7738
|
+
* Each entry is a substring match against the error message; all of these
|
|
7739
|
+
* are situations where the same call typically succeeds after a short delay
|
|
7740
|
+
* because of eventual consistency or just-created-dependency propagation.
|
|
7741
|
+
*
|
|
7742
|
+
* Composed from the two halves above so retryability has ONE source of truth
|
|
7743
|
+
* while `withRetry` can still pick a per-class backoff cadence.
|
|
7744
|
+
*/
|
|
7745
|
+
const RETRYABLE_ERROR_MESSAGE_PATTERNS = [...IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS, ...OTHER_TRANSIENT_ERROR_MESSAGE_PATTERNS];
|
|
7746
|
+
/**
|
|
7696
7747
|
* HTTP status codes that always indicate a transient failure worth retrying.
|
|
7697
7748
|
* 429 = Too Many Requests (throttle), 503 = Service Unavailable.
|
|
7698
7749
|
*/
|
|
@@ -7762,6 +7813,24 @@ function isRetryableTransientError(error, message) {
|
|
|
7762
7813
|
return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
|
|
7763
7814
|
}
|
|
7764
7815
|
/**
|
|
7816
|
+
* True when the message is a just-created-IAM-entity propagation rejection
|
|
7817
|
+
* ({@link IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS}).
|
|
7818
|
+
*
|
|
7819
|
+
* This does NOT decide retryability — every pattern it matches is already in
|
|
7820
|
+
* {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}. It only selects the retry CADENCE:
|
|
7821
|
+
* `withRetry` polls this class densely (sub-second initial delay, low cap)
|
|
7822
|
+
* because IAM propagation resolves in single-digit seconds, whereas the
|
|
7823
|
+
* generic exponential schedule is tuned for throttling and long resource-state
|
|
7824
|
+
* transitions.
|
|
7825
|
+
*
|
|
7826
|
+
* Deliberately message-only (no error-object inspection): the propagation
|
|
7827
|
+
* signal is always carried in the vendor's message text, and cdkd wraps the
|
|
7828
|
+
* original error in a `ProvisioningError` that preserves it.
|
|
7829
|
+
*/
|
|
7830
|
+
function isIamPropagationError(message) {
|
|
7831
|
+
return IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
|
|
7832
|
+
}
|
|
7833
|
+
/**
|
|
7765
7834
|
* Match the "already exists" name-collision signature raised when a create
|
|
7766
7835
|
* targets a physical name still held by another resource (or by the same
|
|
7767
7836
|
* name's not-yet-released tombstone after an async delete).
|
|
@@ -7820,6 +7889,48 @@ function isRecreateRetryableError(message) {
|
|
|
7820
7889
|
* in isolation. The retryable-error classifier itself lives in
|
|
7821
7890
|
* `./retryable-errors.ts`.
|
|
7822
7891
|
*/
|
|
7892
|
+
/**
|
|
7893
|
+
* Initial backoff for the IAM-propagation error class (issue: EC2 instance
|
|
7894
|
+
* launches burning ~10s of backoff waiting on a fresh instance profile).
|
|
7895
|
+
*
|
|
7896
|
+
* cdkd creates an `AWS::IAM::InstanceProfile` and issues `RunInstances` ~2.8s
|
|
7897
|
+
* later; CloudFormation and Terraform are slow enough between resources that
|
|
7898
|
+
* IAM has propagated by the time they call, cdkd outruns it. The measured
|
|
7899
|
+
* failure recovers in single-digit seconds, so the first re-probe should be
|
|
7900
|
+
* sub-second rather than the generic 1s.
|
|
7901
|
+
*/
|
|
7902
|
+
const IAM_PROPAGATION_INITIAL_DELAY_MS = 250;
|
|
7903
|
+
/**
|
|
7904
|
+
* Cap for the IAM-propagation backoff. Once the ramp reaches this value the
|
|
7905
|
+
* schedule stays FLAT — the whole point is a tight, predictable probe grid
|
|
7906
|
+
* across the window in which propagation completes, so the worst-case
|
|
7907
|
+
* overshoot is ~2s instead of the generic schedule's up-to-8s.
|
|
7908
|
+
*
|
|
7909
|
+
* Not lower than 2s on purpose: the retried call is usually a mutating,
|
|
7910
|
+
* tightly-rate-limited API (`RunInstances` refills ~2 req/s per account), and
|
|
7911
|
+
* the retry runs per-resource in parallel — three instances polling at 1s
|
|
7912
|
+
* would sit at 3 req/s and trade an IAM stall for a throttle stall. (If a
|
|
7913
|
+
* throttle DOES happen, its error classifies as non-propagation and the
|
|
7914
|
+
* generic exponential schedule takes over for that attempt, which is exactly
|
|
7915
|
+
* the desired self-correction.)
|
|
7916
|
+
*/
|
|
7917
|
+
const IAM_PROPAGATION_MAX_DELAY_MS = 2e3;
|
|
7918
|
+
/**
|
|
7919
|
+
* Retry budget for the IAM-propagation class.
|
|
7920
|
+
*
|
|
7921
|
+
* A denser schedule must NOT shrink the window in which propagation can still
|
|
7922
|
+
* be caught — that would trade latency for flakiness. The generic default
|
|
7923
|
+
* (1s/2s/4s/8s then capped, 8 retries) sleeps 47s in total, so the dense
|
|
7924
|
+
* schedule is given enough retries to cover at least as long:
|
|
7925
|
+
*
|
|
7926
|
+
* 0.25 + 0.5 + 1 + 2 x 23 = 47.75s over 26 retries
|
|
7927
|
+
*
|
|
7928
|
+
* Probe grid (seconds after the first failure): 0.25, 0.75, 1.75, 3.75, then
|
|
7929
|
+
* every 2s out to 47.75 — versus the generic 1, 3, 7, 15, 23, 31, 39, 47.
|
|
7930
|
+
* From 3.75s onwards the dense grid is strictly ahead, and it never lags the
|
|
7931
|
+
* generic one by more than 0.75s in the early band.
|
|
7932
|
+
*/
|
|
7933
|
+
const IAM_PROPAGATION_MAX_RETRIES = 26;
|
|
7823
7934
|
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7824
7935
|
/**
|
|
7825
7936
|
* Run `operation`, retrying transient failures with exponential backoff
|
|
@@ -7828,6 +7939,20 @@ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
7828
7939
|
* Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):
|
|
7829
7940
|
* 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)
|
|
7830
7941
|
*
|
|
7942
|
+
* IAM-propagation failures (see `isIamPropagationError`) instead use the dense
|
|
7943
|
+
* schedule 0.25s -> 0.5s -> 1s -> 2s -> 2s ... over
|
|
7944
|
+
* {@link IAM_PROPAGATION_MAX_RETRIES} retries (cumulative 47.75s), because that
|
|
7945
|
+
* class resolves in single-digit seconds and the generic schedule's coarse
|
|
7946
|
+
* 4s/8s steps overshoot it. The dense schedule applies ONLY when the caller
|
|
7947
|
+
* left the schedule at its defaults — a caller that passed its own
|
|
7948
|
+
* `maxRetries` / `initialDelayMs` / `maxDelayMs` / `isRetryable` picked that
|
|
7949
|
+
* schedule deliberately (e.g. the DELETE path's 3 x 5s, or the delete-then-
|
|
7950
|
+
* re-create sites' ~64s budget covering SQS's 60s name cooldown) and gets it
|
|
7951
|
+
* verbatim.
|
|
7952
|
+
*
|
|
7953
|
+
* The class is re-evaluated per attempt, so a propagation retry that runs into
|
|
7954
|
+
* a throttle backs OFF exponentially for that attempt instead of hammering.
|
|
7955
|
+
*
|
|
7831
7956
|
* Non-retryable errors are rethrown immediately. The transient-error
|
|
7832
7957
|
* classifier is `isRetryableTransientError` from ./retryable-errors.ts.
|
|
7833
7958
|
*/
|
|
@@ -7836,15 +7961,20 @@ async function withRetry(operation, logicalId, opts = {}) {
|
|
|
7836
7961
|
const initialDelayMs = opts.initialDelayMs ?? 1e3;
|
|
7837
7962
|
const maxDelayMs = opts.maxDelayMs ?? 8e3;
|
|
7838
7963
|
const sleep = opts.sleep ?? defaultSleep;
|
|
7964
|
+
const defaultSchedule = opts.maxRetries === void 0 && opts.initialDelayMs === void 0 && opts.maxDelayMs === void 0 && opts.isRetryable === void 0;
|
|
7965
|
+
const attemptCeiling = defaultSchedule ? Math.max(maxRetries, 26) : maxRetries;
|
|
7839
7966
|
let lastError;
|
|
7840
|
-
for (let attempt = 0; attempt <=
|
|
7967
|
+
for (let attempt = 0; attempt <= attemptCeiling; attempt++) try {
|
|
7841
7968
|
return await operation();
|
|
7842
7969
|
} catch (error) {
|
|
7843
7970
|
lastError = error;
|
|
7844
7971
|
const message = error instanceof Error ? error.message : String(error);
|
|
7845
|
-
|
|
7846
|
-
const
|
|
7847
|
-
|
|
7972
|
+
const retryable = opts.isRetryable ? opts.isRetryable(message, error) : isRetryableTransientError(error, message);
|
|
7973
|
+
const propagation = defaultSchedule && isIamPropagationError(message);
|
|
7974
|
+
const attemptLimit = propagation ? 26 : maxRetries;
|
|
7975
|
+
if (!retryable || attempt >= attemptLimit) throw error;
|
|
7976
|
+
const delay = propagation ? Math.min(250 * Math.pow(2, attempt), IAM_PROPAGATION_MAX_DELAY_MS) : Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
7977
|
+
opts.logger?.debug(` ⏳ Retrying ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${attemptLimit}) - ${message}`);
|
|
7848
7978
|
for (let waited = 0; waited < delay; waited += 1e3) {
|
|
7849
7979
|
if (opts.isInterrupted?.()) throw opts.onInterrupted ? opts.onInterrupted() : /* @__PURE__ */ new Error("Interrupted");
|
|
7850
7980
|
await sleep(Math.min(1e3, delay - waited));
|
|
@@ -7908,6 +8038,30 @@ function describeTypeWithThrottleRetry(resourceType, client) {
|
|
|
7908
8038
|
...describeTypeRetryDelays.sleep ? { sleep: describeTypeRetryDelays.sleep } : {}
|
|
7909
8039
|
});
|
|
7910
8040
|
}
|
|
8041
|
+
/**
|
|
8042
|
+
* Resource types that have NO CloudFormation registry schema, so
|
|
8043
|
+
* `DescribeType` can only ever fail for them:
|
|
8044
|
+
*
|
|
8045
|
+
* - `Custom::<Name>` — the two-segment form the `TypeName` parameter
|
|
8046
|
+
* rejects outright at validation time.
|
|
8047
|
+
* - `AWS::CloudFormation::CustomResource` — the generic custom-resource
|
|
8048
|
+
* alias; replacement semantics are handler-driven, not schema-driven.
|
|
8049
|
+
* - `AWS::CDK::Metadata` — the CDK-injected construct-tree marker. It is a
|
|
8050
|
+
* synth-only sentinel that cdkd never provisions (the deploy pre-flight,
|
|
8051
|
+
* the diff, `synth`, `import` and `export` all filter it), yet the
|
|
8052
|
+
* create-only schema PREFETCH iterated the raw template type set and so
|
|
8053
|
+
* issued a guaranteed-to-fail `DescribeType` for it on EVERY deploy —
|
|
8054
|
+
* burning one API call and emitting a "Grant cloudformation:DescribeType"
|
|
8055
|
+
* warning that named a pseudo-resource the user cannot act on.
|
|
8056
|
+
*
|
|
8057
|
+
* Callers must short-circuit on this predicate rather than paying the round
|
|
8058
|
+
* trip plus the misleading warning. Kept next to
|
|
8059
|
+
* {@link describeTypeWithThrottleRetry} so every DescribeType-backed
|
|
8060
|
+
* resolver shares ONE list instead of re-deriving its own inline literal.
|
|
8061
|
+
*/
|
|
8062
|
+
function hasNoRegistrySchema(resourceType) {
|
|
8063
|
+
return resourceType === "AWS::CDK::Metadata" || resourceType === "AWS::CloudFormation::CustomResource" || resourceType.startsWith("Custom::");
|
|
8064
|
+
}
|
|
7911
8065
|
|
|
7912
8066
|
//#endregion
|
|
7913
8067
|
//#region src/provisioning/create-only-properties.ts
|
|
@@ -7971,7 +8125,7 @@ const createOnlyPropertiesCache = /* @__PURE__ */ new Map();
|
|
|
7971
8125
|
* (a transient throttle must not poison the deploy's replacement detection).
|
|
7972
8126
|
*/
|
|
7973
8127
|
function getCreateOnlyPropertyPaths(resourceType) {
|
|
7974
|
-
if (
|
|
8128
|
+
if (hasNoRegistrySchema(resourceType)) return Promise.resolve([]);
|
|
7975
8129
|
const cached = createOnlyPropertiesCache.get(resourceType);
|
|
7976
8130
|
if (cached) return cached;
|
|
7977
8131
|
const entry = fetchCreateOnlyPropertyPaths(resourceType).catch((error) => {
|
|
@@ -7984,15 +8138,6 @@ function getCreateOnlyPropertyPaths(resourceType) {
|
|
|
7984
8138
|
return entry;
|
|
7985
8139
|
}
|
|
7986
8140
|
/**
|
|
7987
|
-
* True for the two custom-resource type shapes CloudFormation accepts:
|
|
7988
|
-
* `AWS::CloudFormation::CustomResource` and anything under the `Custom::`
|
|
7989
|
-
* prefix. Neither has a registry schema, so schema-driven lookups
|
|
7990
|
-
* (DescribeType) must be skipped for them.
|
|
7991
|
-
*/
|
|
7992
|
-
function isCustomResourceType(resourceType) {
|
|
7993
|
-
return resourceType === "AWS::CloudFormation::CustomResource" || resourceType.startsWith("Custom::");
|
|
7994
|
-
}
|
|
7995
|
-
/**
|
|
7996
8141
|
* Decide whether a change to top-level property `topLevelKey` requires
|
|
7997
8142
|
* replacement per the schema's createOnly paths.
|
|
7998
8143
|
*
|
|
@@ -11228,6 +11373,7 @@ const writeOnlyPropertiesCache = /* @__PURE__ */ new Map();
|
|
|
11228
11373
|
* DescribeType (a transient throttle must not poison the deploy).
|
|
11229
11374
|
*/
|
|
11230
11375
|
function getTopLevelWriteOnlyProperties(resourceType) {
|
|
11376
|
+
if (hasNoRegistrySchema(resourceType)) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
11231
11377
|
const cached = writeOnlyPropertiesCache.get(resourceType);
|
|
11232
11378
|
if (cached) return cached;
|
|
11233
11379
|
const entry = fetchTopLevelWriteOnlyProperties(resourceType).catch((error) => {
|
|
@@ -11793,7 +11939,7 @@ var CloudControlProvider = class {
|
|
|
11793
11939
|
this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
|
|
11794
11940
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
11795
11941
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
11796
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
11942
|
+
const { ASGProvider } = await import("./asg-provider-gaXfXSH0.js").then((n) => n.n);
|
|
11797
11943
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
11798
11944
|
return;
|
|
11799
11945
|
}
|
|
@@ -17505,7 +17651,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
17505
17651
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
17506
17652
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
17507
17653
|
function getCdkdVersion() {
|
|
17508
|
-
return "0.268.
|
|
17654
|
+
return "0.268.2";
|
|
17509
17655
|
}
|
|
17510
17656
|
/**
|
|
17511
17657
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -17609,11 +17755,33 @@ var DeploymentEventsStore = class {
|
|
|
17609
17755
|
}
|
|
17610
17756
|
if (this.events.length === 0) return;
|
|
17611
17757
|
await this.enqueueWrite(async () => {
|
|
17758
|
+
const indexRead = this.readIndexRuns();
|
|
17759
|
+
indexRead.catch(() => {});
|
|
17612
17760
|
await this.doFlush();
|
|
17613
|
-
|
|
17614
|
-
await this.pruneSupersededRunFiles(keptRunIds);
|
|
17761
|
+
await this.writeIndexAndPrune(result, await indexRead);
|
|
17615
17762
|
});
|
|
17616
17763
|
}
|
|
17764
|
+
/**
|
|
17765
|
+
* Second half of {@link finalize}'s write: build + PUT the index, then
|
|
17766
|
+
* delete the run streams that fell out of the retained window.
|
|
17767
|
+
*
|
|
17768
|
+
* The prune's LIST is issued concurrently with the index PUT — the cutoff
|
|
17769
|
+
* is derived from `runs` (already known before the PUT), the LIST is
|
|
17770
|
+
* read-only, and the DELETE still happens strictly AFTER the PUT resolves.
|
|
17771
|
+
* So the on-S3 ordering is unchanged (a stream is only deleted once the
|
|
17772
|
+
* index that dropped it is durable); only the round trip is overlapped.
|
|
17773
|
+
*/
|
|
17774
|
+
async writeIndexAndPrune(result, existingRuns) {
|
|
17775
|
+
const { key, file, runs } = this.buildIndexUpdate(result, existingRuns);
|
|
17776
|
+
const keptRunIds = runs.map((r) => r.runId);
|
|
17777
|
+
const willPrune = keptRunIds.length >= 20;
|
|
17778
|
+
const dirPrefix = deploymentsDirPrefix(this.backend.prefix, this.stackName, this.region);
|
|
17779
|
+
const staleKeysRead = willPrune ? this.backend.listRawKeys(dirPrefix) : void 0;
|
|
17780
|
+
staleKeysRead?.catch(() => {});
|
|
17781
|
+
await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
|
|
17782
|
+
if (!staleKeysRead) return;
|
|
17783
|
+
await this.pruneSupersededRunFiles(keptRunIds, dirPrefix, await staleKeysRead);
|
|
17784
|
+
}
|
|
17617
17785
|
/** Await any in-flight async flushes (used by tests). */
|
|
17618
17786
|
async drain() {
|
|
17619
17787
|
await this.writeChain;
|
|
@@ -17644,27 +17812,40 @@ var DeploymentEventsStore = class {
|
|
|
17644
17812
|
this.persistedCount = snapshotCount;
|
|
17645
17813
|
}
|
|
17646
17814
|
/**
|
|
17647
|
-
*
|
|
17648
|
-
* the
|
|
17649
|
-
* write
|
|
17650
|
-
*
|
|
17815
|
+
* READ half of the index read-modify-write. Returns the currently indexed
|
|
17816
|
+
* run summaries, or an empty list when the index is absent / corrupt /
|
|
17817
|
+
* unreadable (in which case the write half rebuilds from this run alone —
|
|
17818
|
+
* the .jsonl files remain readable directly via `cdkd events --run`).
|
|
17651
17819
|
*
|
|
17652
|
-
*
|
|
17653
|
-
*
|
|
17654
|
-
*
|
|
17820
|
+
* Split out of the former single `updateIndex` so {@link finalize} can
|
|
17821
|
+
* issue it CONCURRENTLY with the event-stream flush: the two touch
|
|
17822
|
+
* different objects and the read does not depend on the flush.
|
|
17655
17823
|
*/
|
|
17656
|
-
async
|
|
17824
|
+
async readIndexRuns() {
|
|
17657
17825
|
const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
|
|
17658
|
-
let existingRuns = [];
|
|
17659
17826
|
try {
|
|
17660
17827
|
const raw = await this.backend.getRawObject(key);
|
|
17661
17828
|
if (raw !== null) {
|
|
17662
17829
|
const parsed = JSON.parse(raw);
|
|
17663
|
-
if (Array.isArray(parsed.runs))
|
|
17830
|
+
if (Array.isArray(parsed.runs)) return parsed.runs;
|
|
17664
17831
|
}
|
|
17665
17832
|
} catch (err) {
|
|
17666
17833
|
this.logger.debug(`Deployment-events index unreadable, rewriting: ${err instanceof Error ? err.message : String(err)}`);
|
|
17667
17834
|
}
|
|
17835
|
+
return [];
|
|
17836
|
+
}
|
|
17837
|
+
/**
|
|
17838
|
+
* MODIFY half of the index read-modify-write (pure): prepend this run's
|
|
17839
|
+
* summary to `existingRuns`, truncated to the last
|
|
17840
|
+
* {@link DEPLOYMENT_EVENTS_MAX_INDEX_RUNS} runs. No optimistic locking —
|
|
17841
|
+
* last-writer-wins (documented trade-off; the per-run `.jsonl` files are
|
|
17842
|
+
* the source of truth).
|
|
17843
|
+
*
|
|
17844
|
+
* `runs` (newest-first) doubles as the retained-run window that
|
|
17845
|
+
* {@link pruneSupersededRunFiles} bounds the `.jsonl` files to.
|
|
17846
|
+
*/
|
|
17847
|
+
buildIndexUpdate(result, existingRuns) {
|
|
17848
|
+
const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
|
|
17668
17849
|
const runs = [{
|
|
17669
17850
|
runId: this.runId,
|
|
17670
17851
|
command: this.command,
|
|
@@ -17674,15 +17855,17 @@ var DeploymentEventsStore = class {
|
|
|
17674
17855
|
result,
|
|
17675
17856
|
eventCount: this.persistedCount
|
|
17676
17857
|
}, ...existingRuns.filter((r) => r.runId !== this.runId)].slice(0, 20);
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
|
|
17681
|
-
|
|
17682
|
-
|
|
17858
|
+
return {
|
|
17859
|
+
key,
|
|
17860
|
+
file: {
|
|
17861
|
+
indexVersion: 1,
|
|
17862
|
+
stackName: this.stackName,
|
|
17863
|
+
region: this.region,
|
|
17864
|
+
runs,
|
|
17865
|
+
lastModified: Date.now()
|
|
17866
|
+
},
|
|
17867
|
+
runs
|
|
17683
17868
|
};
|
|
17684
|
-
await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
|
|
17685
|
-
return runs.map((r) => r.runId);
|
|
17686
17869
|
}
|
|
17687
17870
|
/**
|
|
17688
17871
|
* Self-bounding prune (issue #885): delete `{runId}.jsonl` streams that
|
|
@@ -17699,12 +17882,15 @@ var DeploymentEventsStore = class {
|
|
|
17699
17882
|
* runs finalized while it ran — an extreme edge that self-heals anyway,
|
|
17700
17883
|
* since the whole JSONL body is re-PUT on that run's next flush / finalize
|
|
17701
17884
|
* (S3 has no append; each flush rewrites the full stream).
|
|
17885
|
+
*
|
|
17886
|
+
* `dirPrefix` + `keys` are supplied by the caller so the LIST can be
|
|
17887
|
+
* overlapped with the index PUT (see {@link writeIndexAndPrune}); the
|
|
17888
|
+
* DELETE this method issues still runs strictly after that PUT.
|
|
17702
17889
|
*/
|
|
17703
|
-
async pruneSupersededRunFiles(keptRunIds) {
|
|
17890
|
+
async pruneSupersededRunFiles(keptRunIds, dirPrefix, keys) {
|
|
17704
17891
|
if (keptRunIds.length < 20) return;
|
|
17705
17892
|
const cutoff = keptRunIds.reduce((min, id) => id < min ? id : min, keptRunIds[0]);
|
|
17706
|
-
const
|
|
17707
|
-
const stale = (await this.backend.listRawKeys(dirPrefix)).filter((k) => {
|
|
17893
|
+
const stale = keys.filter((k) => {
|
|
17708
17894
|
const runId = runIdFromJsonlKey(k, dirPrefix);
|
|
17709
17895
|
return runId !== null && runId < cutoff;
|
|
17710
17896
|
});
|
|
@@ -18314,7 +18500,7 @@ var DeployEngine = class {
|
|
|
18314
18500
|
async doDeploy(stackName, template) {
|
|
18315
18501
|
const startTime = Date.now();
|
|
18316
18502
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
18317
|
-
for (const type of new Set(Object.values(template.Resources).map((r) => r.Type))) getCreateOnlyPropertyPaths(type).catch(() => {});
|
|
18503
|
+
for (const type of new Set(Object.values(template.Resources).map((r) => r.Type).filter((type) => !hasNoRegistrySchema(type)))) getCreateOnlyPropertyPaths(type).catch(() => {});
|
|
18318
18504
|
await this.lockManager.acquireLockWithRetry(stackName, this.stackRegion, void 0, "deploy");
|
|
18319
18505
|
const renderer = getLiveRenderer();
|
|
18320
18506
|
renderer.start();
|
|
@@ -18341,6 +18527,7 @@ var DeployEngine = class {
|
|
|
18341
18527
|
const currentEtag = currentStateData?.etag;
|
|
18342
18528
|
const migrationPending = currentStateData?.migrationPending ?? false;
|
|
18343
18529
|
this.logger.debug(`Loaded current state: ${Object.keys(currentState.resources).length} resources`);
|
|
18530
|
+
if (this.options.onCurrentStateLoaded) await this.options.onCurrentStateLoaded(stackName, currentStateData?.state);
|
|
18344
18531
|
try {
|
|
18345
18532
|
const journal = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
|
|
18346
18533
|
if (journal && journal.segments.length > 0) {
|
|
@@ -18454,8 +18641,7 @@ var DeployEngine = class {
|
|
|
18454
18641
|
await this.drainObservedCaptures(newState.resources);
|
|
18455
18642
|
const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(newState));
|
|
18456
18643
|
this.logger.debug(`State saved (ETag: ${newEtag})`);
|
|
18457
|
-
await this.deleteRollbackJournalBestEffort(stackName);
|
|
18458
|
-
if (this.exportIndexStore) await this.exportIndexStore.updateForStack(stackName, this.stackRegion, newState.outputs ?? {});
|
|
18644
|
+
await Promise.all([this.deleteRollbackJournalBestEffort(stackName), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, newState.outputs ?? {}) : Promise.resolve()]);
|
|
18459
18645
|
const durationMs = Date.now() - startTime;
|
|
18460
18646
|
const unchangedCount = this.diffCalculator.filterByType(changes, "NO_CHANGE").length + actualCounts.skipped;
|
|
18461
18647
|
return {
|
|
@@ -19510,5 +19696,5 @@ var DeployEngine = class {
|
|
|
19510
19696
|
};
|
|
19511
19697
|
|
|
19512
19698
|
//#endregion
|
|
19513
|
-
export { WorkGraph as $,
|
|
19514
|
-
//# sourceMappingURL=deploy-engine-
|
|
19699
|
+
export { WorkGraph as $, LockError as $t, slowCcOperationTimeoutMs as A, warnDeprecatedNoPrefixCliFlag as At, applyRoleArnIfSet as B, resolveBucketRegion as Bt, yellow as C, resolveAutoAssetStorage as Ct, ProviderRegistry as D, resolveStateBucketWithDefaultAndSource as Dt, clearOnUpdateRemoval as E, resolveStateBucketWithDefault as Et, refStateLookupFromResource as F, uploadCfnTemplate as Ft, DagBuilder as G, AssetError as Gt, describeTypeWithThrottleRetry as H, getAwsClients as Ht, WAFv2WebACLProvider as I, expectedOwnerParam as It, S3StateBackend as J, DependencyError as Jt, TemplateParser as K, CdkdError as Kt, normalizeAwsTagsToCfn as L, AssemblyReader as Lt, isTerminationProtectionPropagationError as M, CFN_TEMPLATE_URL_LIMIT as Mt, IntrinsicFunctionResolver as N, MIGRATE_TMP_PREFIX as Nt, findActionableSilentDrops as O, resolveUseCdkBootstrapAssets as Ot, cfnRefValueFromPhysicalId as P, findLargeInlineResources as Pt, stringifyValue as Q, LocalStartServiceError as Qt, resolveExplicitPhysicalId as R, processStackMessages as Rt, red as S, resolveApp as St, collectInlinePolicyNamesManagedBySiblings as T, resolveSkipPrefix as Tt, withRetry as U, resetAwsClients as Ut, DiffCalculator as V, AwsClients as Vt, isRetryableTransientError as W, setAwsClients as Wt, shouldRetainResource as X, LocalInvokeBuildError as Xt, rebuildClientForBucketRegion as Y, DeployCancelledError as Yt, AssetPublisher as Z, LocalMigrateError as Zt, formatResourceLine as _, getDockerImageBySourceHash as _t, DeploymentEventsStore as a, ResourceUpdateNotSupportedError as an, BOOTSTRAP_MARKER_PREFIX as at, gray as b, getDefaultStateBucketName as bt, replayFailedOperations as c, StateError as cn, parseBootstrapMarker as ct, IMPLICIT_DELETE_DEPENDENCIES as d, isCdkdError as dn, buildDockerImage as dt, MissingCdkCliError as en, buildAssetRedirectMap as et, computeImplicitDeleteEdges as f, normalizeAwsError as fn, formatDockerLoginError as ft, renderStatefulReason as g, AssetManifestLoader as gt, isStatefulRecreateTargetSync as h, runDockerStreaming as ht, DeploymentEventsReader as i, ResourceTimeoutError as in, AssetModeResolver as it, disableInstanceApiTermination as j, CFN_TEMPLATE_BODY_LIMIT as jt, CloudControlProvider as k, stateBucketExistenceConfirmed as kt, replayRollback as l, SynthesisError as ln, validateAssetBucketName as lt, MULTI_REGION_RECREATE_BLOCKED_TYPES as m, __exportAll as mn, runDockerForeground as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, PartialFailureError as nn, loadPublishableAssetManifest as nt, planFailedOps as o, StackHasActiveImportsError as on, ensureAssetStorage as ot, extractDeploymentEventError as p, withErrorHandling as pn, getDockerCmd as pt, LockManager as q, ConfigError as qt, DeployEngine as r, ProvisioningError as rn, rewriteTemplateAssetReferences as rt, planRollback as s, StackTerminationProtectionError as sn, getBootstrapMarkerKey as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, NestedStackChildDirectDestroyError as tn, createAssetRedirectResolver as tt, withResourceDeadline as u, formatError 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, clearBucketRegionCache as zt };
|
|
19700
|
+
//# sourceMappingURL=deploy-engine-BEjF-h_6.js.map
|