@go-to-k/cdkd 0.207.1 → 0.207.3
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.
|
@@ -5,7 +5,7 @@ import { DeleteObjectCommand, GetBucketLocationCommand, GetObjectCommand, HeadBu
|
|
|
5
5
|
import { CloudControlClient, CreateResourceCommand, DeleteResourceCommand, GetResourceCommand, GetResourceRequestStatusCommand, ListResourcesCommand, UpdateResourceCommand } from "@aws-sdk/client-cloudcontrol";
|
|
6
6
|
import { AttachRolePolicyCommand, CreateRoleCommand, DeleteRoleCommand, DeleteRolePermissionsBoundaryCommand, DeleteRolePolicyCommand, DetachRolePolicyCommand, GetRoleCommand, GetRolePolicyCommand, IAMClient, ListAttachedRolePoliciesCommand, ListInstanceProfilesForRoleCommand, ListRolePoliciesCommand, ListRoleTagsCommand, ListRolesCommand, NoSuchEntityException, PutRolePermissionsBoundaryCommand, PutRolePolicyCommand, RemoveRoleFromInstanceProfileCommand, TagRoleCommand, UntagRoleCommand, UpdateAssumeRolePolicyCommand, UpdateRoleCommand } from "@aws-sdk/client-iam";
|
|
7
7
|
import { PublishCommand, SNSClient } from "@aws-sdk/client-sns";
|
|
8
|
-
import { GetFunctionUrlConfigCommand, InvokeCommand, LambdaClient, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
8
|
+
import { GetFunctionUrlConfigCommand, InvokeCommand, LambdaClient, UpdateFunctionConfigurationCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
9
9
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
10
10
|
import { DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeLaunchTemplatesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, EC2Client } from "@aws-sdk/client-ec2";
|
|
11
11
|
import { DescribeTableCommand } from "@aws-sdk/client-dynamodb";
|
|
@@ -8454,6 +8454,22 @@ function parseLambdaPayload(payloadBytes) {
|
|
|
8454
8454
|
return parsed;
|
|
8455
8455
|
}
|
|
8456
8456
|
/**
|
|
8457
|
+
* IAM-authorization-propagation signals in a custom resource FAILED reason that
|
|
8458
|
+
* indicate the backing Lambda's freshly-attached execution-role policy has not
|
|
8459
|
+
* yet taken effect for its assumed-role session (so a recycle + retry will
|
|
8460
|
+
* succeed once IAM settles). Lowercase substrings. Intentionally narrow — these
|
|
8461
|
+
* are the IAM-permission-not-yet-effective phrases only, NOT generic transient
|
|
8462
|
+
* errors (throttling / timeouts), which must not trigger a CR re-invoke.
|
|
8463
|
+
*/
|
|
8464
|
+
const CR_TRANSIENT_AUTHZ_SIGNALS = [
|
|
8465
|
+
"not authorized to perform",
|
|
8466
|
+
"no identity-based policy allows",
|
|
8467
|
+
"is not in the state functionactive",
|
|
8468
|
+
"not in the state functionactive",
|
|
8469
|
+
"cannot be assumed",
|
|
8470
|
+
"is unable to assume"
|
|
8471
|
+
];
|
|
8472
|
+
/**
|
|
8457
8473
|
* Custom Resource Provider
|
|
8458
8474
|
*
|
|
8459
8475
|
* Implements Lambda-backed custom resources by invoking the Lambda function
|
|
@@ -8517,6 +8533,28 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8517
8533
|
INITIAL_POLL_INTERVAL_MS = 2e3;
|
|
8518
8534
|
/** Max poll interval for async polling with exponential backoff (30 seconds) */
|
|
8519
8535
|
MAX_POLL_INTERVAL_MS = 3e4;
|
|
8536
|
+
/**
|
|
8537
|
+
* How many extra times to re-invoke a custom resource whose handler returned
|
|
8538
|
+
* FAILED with a *transient IAM-authorization* reason (e.g. the CDK Provider
|
|
8539
|
+
* framework's `lambda:GetFunction` / "not in the state functionActive" 403
|
|
8540
|
+
* when the framework role's freshly-attached inline policy has not yet
|
|
8541
|
+
* propagated to the assumed-role session). cdkd's fast SDK path invokes the
|
|
8542
|
+
* backing Lambda ~1s after `PutRolePolicy`, so the first cold-start can cache
|
|
8543
|
+
* stale credentials; CloudFormation never hits this because its deployment
|
|
8544
|
+
* latency gives IAM time to settle. This is the CR-path analogue of the
|
|
8545
|
+
* IAM-propagation retry cdkd's `withRetry` already applies to every other
|
|
8546
|
+
* resource (the CR provider opts out of that outer retry via
|
|
8547
|
+
* `disableOuterRetry` to avoid stranding a pre-signed response URL — so we
|
|
8548
|
+
* retry HERE instead, deriving a fresh response URL + RequestId per attempt
|
|
8549
|
+
* and recycling the backing function's execution environment between tries).
|
|
8550
|
+
* Override via `CDKD_CR_AUTHZ_MAX_RETRIES` (0 disables).
|
|
8551
|
+
*/
|
|
8552
|
+
transientAuthzMaxRetries = (() => {
|
|
8553
|
+
const raw = process.env["CDKD_CR_AUTHZ_MAX_RETRIES"];
|
|
8554
|
+
if (raw === void 0 || raw === "") return 2;
|
|
8555
|
+
const n = Number(raw);
|
|
8556
|
+
return Number.isFinite(n) && n >= 0 ? n : 2;
|
|
8557
|
+
})();
|
|
8520
8558
|
constructor(config) {
|
|
8521
8559
|
const awsClients = getAwsClients();
|
|
8522
8560
|
this.lambdaClient = awsClients.lambda;
|
|
@@ -8559,8 +8597,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8559
8597
|
if (!serviceToken) throw new ProvisioningError(`ServiceToken is required for custom resource ${logicalId}`, resourceType, logicalId);
|
|
8560
8598
|
if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run \`cdkd import\` or \`cdkd state orphan <stack>\` to recover.`, resourceType, logicalId);
|
|
8561
8599
|
try {
|
|
8562
|
-
const
|
|
8563
|
-
const request = {
|
|
8600
|
+
const cfnResponse = await this.invokeCustomResourceWithRetry(serviceToken, logicalId, "Create", (invocation) => ({
|
|
8564
8601
|
RequestType: "Create",
|
|
8565
8602
|
RequestId: invocation.requestId,
|
|
8566
8603
|
ResponseURL: invocation.responseURL,
|
|
@@ -8568,9 +8605,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8568
8605
|
LogicalResourceId: logicalId,
|
|
8569
8606
|
StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,
|
|
8570
8607
|
ResourceProperties: this.stringifyProperties(properties)
|
|
8571
|
-
};
|
|
8572
|
-
this.logger.debug(`Sending custom resource create request: ${serviceToken}`);
|
|
8573
|
-
const cfnResponse = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, "Create");
|
|
8608
|
+
}));
|
|
8574
8609
|
if (cfnResponse.Status === "FAILED") throw new Error(`Custom resource handler returned FAILED: ${cfnResponse.Reason || "Unknown reason"}`);
|
|
8575
8610
|
const physicalId = cfnResponse.PhysicalResourceId || logicalId;
|
|
8576
8611
|
const attributes = cfnResponse.Data || {};
|
|
@@ -8593,8 +8628,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8593
8628
|
if (!serviceToken) throw new ProvisioningError(`ServiceToken is required for custom resource ${logicalId}`, resourceType, logicalId, physicalId);
|
|
8594
8629
|
if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run \`cdkd import\` or \`cdkd state orphan <stack>\` to recover.`, resourceType, logicalId, physicalId);
|
|
8595
8630
|
try {
|
|
8596
|
-
const
|
|
8597
|
-
const request = {
|
|
8631
|
+
const cfnResponse = await this.invokeCustomResourceWithRetry(serviceToken, logicalId, "Update", (invocation) => ({
|
|
8598
8632
|
RequestType: "Update",
|
|
8599
8633
|
RequestId: invocation.requestId,
|
|
8600
8634
|
ResponseURL: invocation.responseURL,
|
|
@@ -8604,9 +8638,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8604
8638
|
StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,
|
|
8605
8639
|
ResourceProperties: this.stringifyProperties(properties),
|
|
8606
8640
|
OldResourceProperties: this.stringifyProperties(previousProperties)
|
|
8607
|
-
};
|
|
8608
|
-
this.logger.debug(`Sending custom resource update request: ${serviceToken}`);
|
|
8609
|
-
const cfnResponse = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, "Update");
|
|
8641
|
+
}));
|
|
8610
8642
|
if (cfnResponse.Status === "FAILED") throw new Error(`Custom resource handler returned FAILED: ${cfnResponse.Reason || "Unknown reason"}`);
|
|
8611
8643
|
const newPhysicalId = cfnResponse.PhysicalResourceId || physicalId;
|
|
8612
8644
|
const wasReplaced = newPhysicalId !== physicalId;
|
|
@@ -8638,8 +8670,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8638
8670
|
}
|
|
8639
8671
|
if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run \`cdkd import\` or \`cdkd state orphan <stack>\` to recover.`, resourceType, logicalId, physicalId);
|
|
8640
8672
|
try {
|
|
8641
|
-
const
|
|
8642
|
-
const request = {
|
|
8673
|
+
const cfnResponse = await this.invokeCustomResourceWithRetry(serviceToken, logicalId, "Delete", (invocation) => ({
|
|
8643
8674
|
RequestType: "Delete",
|
|
8644
8675
|
RequestId: invocation.requestId,
|
|
8645
8676
|
ResponseURL: invocation.responseURL,
|
|
@@ -8648,9 +8679,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8648
8679
|
PhysicalResourceId: physicalId,
|
|
8649
8680
|
StackId: `arn:aws:cloudformation:us-east-1:000000000000:stack/cdkd-${logicalId}/cdkd`,
|
|
8650
8681
|
ResourceProperties: this.stringifyProperties(properties)
|
|
8651
|
-
};
|
|
8652
|
-
this.logger.debug(`Sending custom resource delete request: ${serviceToken}`);
|
|
8653
|
-
const cfnResponse = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, "Delete");
|
|
8682
|
+
}));
|
|
8654
8683
|
if (cfnResponse.Status === "FAILED") this.logger.warn(`Custom resource delete handler returned FAILED for ${logicalId}: ${cfnResponse.Reason || "Unknown reason"}`);
|
|
8655
8684
|
else this.logger.debug(`Successfully deleted custom resource ${logicalId}`);
|
|
8656
8685
|
} catch (error) {
|
|
@@ -8664,6 +8693,98 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
8664
8693
|
return serviceToken.startsWith("arn:aws:sns:");
|
|
8665
8694
|
}
|
|
8666
8695
|
/**
|
|
8696
|
+
* Invoke a custom resource, retrying on a *transient IAM-authorization*
|
|
8697
|
+
* FAILED response.
|
|
8698
|
+
*
|
|
8699
|
+
* Why this exists: cdkd's fast SDK path attaches a backing Lambda's
|
|
8700
|
+
* execution-role inline policy and invokes the function ~1s later. If IAM has
|
|
8701
|
+
* not propagated the policy to the assumed-role session by the function's
|
|
8702
|
+
* first cold start, the session caches stale (policy-less) credentials for
|
|
8703
|
+
* the warm container's whole life — so the CDK Provider framework's
|
|
8704
|
+
* `lambda:GetFunction` / initial invoke 403s ("not authorized to perform" /
|
|
8705
|
+
* "not in the state functionActive") and the custom resource FAILS.
|
|
8706
|
+
* CloudFormation never hits this because its deployment latency lets IAM
|
|
8707
|
+
* settle first. This is the CR-path analogue of the IAM-propagation retry
|
|
8708
|
+
* cdkd's `withRetry` already applies to every other resource type — the CR
|
|
8709
|
+
* provider opts out of that outer retry (`disableOuterRetry`) to avoid
|
|
8710
|
+
* stranding a pre-signed response URL at an S3 key nobody polls, so we retry
|
|
8711
|
+
* HERE, deriving a FRESH response URL + RequestId per attempt (via
|
|
8712
|
+
* `prepareInvocation()`) and recycling the backing function's execution
|
|
8713
|
+
* environment between tries so its next cold start re-assumes the role.
|
|
8714
|
+
*
|
|
8715
|
+
* `buildRequest` is called once per attempt with the fresh invocation so the
|
|
8716
|
+
* CFn request body always carries the matching ResponseURL / RequestId.
|
|
8717
|
+
* Returns the final response; the caller decides what a terminal FAILED means
|
|
8718
|
+
* (create/update throw, delete warns-and-continues).
|
|
8719
|
+
*/
|
|
8720
|
+
async invokeCustomResourceWithRetry(serviceToken, logicalId, operation, buildRequest) {
|
|
8721
|
+
for (let attempt = 0;; attempt++) {
|
|
8722
|
+
const invocation = await this.prepareInvocation();
|
|
8723
|
+
const request = buildRequest(invocation);
|
|
8724
|
+
this.logger.debug(`Sending custom resource ${operation.toLowerCase()} request: ${serviceToken}`);
|
|
8725
|
+
const cfnResponse = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, operation);
|
|
8726
|
+
if (cfnResponse.Status === "FAILED" && attempt < this.transientAuthzMaxRetries && this.isTransientAuthzFailure(cfnResponse.Reason)) {
|
|
8727
|
+
this.logger.warn(`Custom resource ${operation} for ${logicalId} returned a transient IAM-authorization FAILED (attempt ${attempt + 1}/${this.transientAuthzMaxRetries + 1}): ${this.truncateReason(cfnResponse.Reason)}. Recycling the backing function's execution environment and retrying so its next cold start picks up the propagated policy.`);
|
|
8728
|
+
await this.recycleBackingFunctionExecEnv(serviceToken, logicalId);
|
|
8729
|
+
continue;
|
|
8730
|
+
}
|
|
8731
|
+
return cfnResponse;
|
|
8732
|
+
}
|
|
8733
|
+
}
|
|
8734
|
+
/**
|
|
8735
|
+
* Classify a custom resource FAILED reason as a transient IAM-authorization
|
|
8736
|
+
* race (worth retrying).
|
|
8737
|
+
*
|
|
8738
|
+
* Deliberately NARROW — only the IAM-permission-not-yet-effective signals,
|
|
8739
|
+
* NOT cdkd's broad transient classifier (`isRetryableTransientError`, which
|
|
8740
|
+
* also matches throttling / generic timeouts). A custom resource that FAILED
|
|
8741
|
+
* for an unrelated reason (user handler bug, a real timeout, a downstream API
|
|
8742
|
+
* error) must NOT be re-invoked — that would mask genuine failures and waste
|
|
8743
|
+
* the framework's ~minutes-long waiter per attempt. These phrases are the
|
|
8744
|
+
* IAM-authz subset of cdkd's `RETRYABLE_ERROR_MESSAGE_PATTERNS`, plus the CDK
|
|
8745
|
+
* Provider framework's `waitUntilFunctionActive` state phrasing.
|
|
8746
|
+
*/
|
|
8747
|
+
isTransientAuthzFailure(reason) {
|
|
8748
|
+
if (!reason) return false;
|
|
8749
|
+
const lower = reason.toLowerCase();
|
|
8750
|
+
return CR_TRANSIENT_AUTHZ_SIGNALS.some((p) => lower.includes(p));
|
|
8751
|
+
}
|
|
8752
|
+
/** Truncate a CR FAILED reason for log readability. */
|
|
8753
|
+
truncateReason(reason, max = 200) {
|
|
8754
|
+
const r = reason ?? "Unknown reason";
|
|
8755
|
+
return r.length > max ? `${r.slice(0, max)}...` : r;
|
|
8756
|
+
}
|
|
8757
|
+
/**
|
|
8758
|
+
* Force the backing Lambda to drop its warm execution environment(s) so the
|
|
8759
|
+
* next invoke cold-starts and re-assumes the execution role, picking up the
|
|
8760
|
+
* now-propagated inline policy. A plain re-invoke would otherwise reuse the
|
|
8761
|
+
* same warm container that cached the stale credentials. Best-effort: any
|
|
8762
|
+
* failure (e.g. cdkd's own creds lack `lambda:UpdateFunctionConfiguration`)
|
|
8763
|
+
* degrades to a debug log and we still retry the invoke.
|
|
8764
|
+
*
|
|
8765
|
+
* The no-op `Description` write is the least-intrusive way to invalidate warm
|
|
8766
|
+
* containers. It persists on the backing function, but cdkd never reconciles
|
|
8767
|
+
* the CDK Provider framework's backing Lambda against a template `Description`
|
|
8768
|
+
* (the synthesized template leaves it empty / CDK-default and cdkd's diff only
|
|
8769
|
+
* compares state-recorded properties), so it does not surface as drift on a
|
|
8770
|
+
* later deploy. Only the IAM-propagation retry path (rare) ever sets it.
|
|
8771
|
+
*/
|
|
8772
|
+
async recycleBackingFunctionExecEnv(serviceToken, logicalId) {
|
|
8773
|
+
if (this.isSnsServiceToken(serviceToken)) return;
|
|
8774
|
+
try {
|
|
8775
|
+
await this.lambdaClient.send(new UpdateFunctionConfigurationCommand({
|
|
8776
|
+
FunctionName: serviceToken,
|
|
8777
|
+
Description: `cdkd: recycled for IAM-propagation retry (${logicalId})`
|
|
8778
|
+
}));
|
|
8779
|
+
await waitUntilFunctionUpdatedV2({
|
|
8780
|
+
client: this.lambdaClient,
|
|
8781
|
+
maxWaitTime: 120
|
|
8782
|
+
}, { FunctionName: serviceToken });
|
|
8783
|
+
} catch (error) {
|
|
8784
|
+
this.logger.debug(`Could not recycle backing function for ${logicalId} (${error instanceof Error ? error.message : String(error)}); retrying invoke without a forced cold start`);
|
|
8785
|
+
}
|
|
8786
|
+
}
|
|
8787
|
+
/**
|
|
8667
8788
|
* Send custom resource request via the appropriate service (Lambda or SNS)
|
|
8668
8789
|
* For Lambda: invokes synchronously and returns the response
|
|
8669
8790
|
* For SNS: publishes to topic and polls S3 for response
|
|
@@ -12205,7 +12326,8 @@ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
|
|
|
12205
12326
|
"KMS key is invalid for CreateGrant",
|
|
12206
12327
|
"Could not deliver test message",
|
|
12207
12328
|
"wait 60 seconds",
|
|
12208
|
-
"concurrent update operation"
|
|
12329
|
+
"concurrent update operation",
|
|
12330
|
+
"because it is in use"
|
|
12209
12331
|
];
|
|
12210
12332
|
/**
|
|
12211
12333
|
* HTTP status codes that always indicate a transient failure worth retrying.
|
|
@@ -13584,5 +13706,5 @@ var DeployEngine = class {
|
|
|
13584
13706
|
};
|
|
13585
13707
|
|
|
13586
13708
|
//#endregion
|
|
13587
|
-
export {
|
|
13588
|
-
//# sourceMappingURL=deploy-engine-
|
|
13709
|
+
export { findLargeInlineResources as $, LockManager as A, getLiveRenderer as At, runDockerStreaming as B, CloudControlProvider as C, isCdkdError as Ct, DiffCalculator as D, getLogger as Dt, applyRoleArnIfSet as E, ConsoleLogger as Et, WorkGraph as F, withSkipPrefix as Ft, resolveCaptureObservedState as G, getDefaultStateBucketName as H, buildDockerImage as I, withStackName as It, resolveStateBucketWithDefaultAndSource as J, resolveSkipPrefix as K, formatDockerLoginError as L, shouldRetainResource as M, PATTERN_B_RESOURCE_TYPES as Mt, AssetPublisher as N, generateResourceName as Nt, DagBuilder as O, setLogger as Ot, stringifyValue as P, generateResourceNameWithFallback as Pt, MIGRATE_TMP_PREFIX as Q, getDockerCmd as R, findActionableSilentDrops as S, formatError as St, IntrinsicFunctionResolver as T, withErrorHandling as Tt, getLegacyStateBucketName as U, Synthesizer as V, resolveApp as W, CFN_TEMPLATE_BODY_LIMIT as X, warnDeprecatedNoPrefixCliFlag as Y, CFN_TEMPLATE_URL_LIMIT as Z, CDK_PATH_TAG as _, ResourceUpdateNotSupportedError as _t, withRetry as a, CdkdError as at, resolveExplicitPhysicalId as b, StateError as bt, formatResourceLine as c, LocalInvokeBuildError as ct, gray as d, LockError as dt, uploadCfnTemplate as et, green as f, MissingCdkCliError as ft, collectInlinePolicyNamesManagedBySiblings as g, ResourceTimeoutError as gt, IAMRoleProvider as h, ProvisioningError as ht, withResourceDeadline as i, AssetError as it, S3StateBackend as j, PATTERN_B_NAME_PROPERTIES as jt, TemplateParser as k, runStackBuffered as kt, bold as l, LocalMigrateError as lt, yellow as m, PartialFailureError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, clearBucketRegionCache as nt, isRetryableTransientError as o, ConfigError as ot, red as p, NestedStackChildDirectDestroyError as pt, resolveStateBucketWithDefault as q, DeployEngine as r, resolveBucketRegion as rt, IMPLICIT_DELETE_DEPENDENCIES as s, DependencyError as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, AssemblyReader as tt, cyan as u, LocalStartServiceError as ut, matchesCdkPath as v, StackHasActiveImportsError as vt, assertRegionMatch as w, normalizeAwsError as wt, ProviderRegistry as x, SynthesisError as xt, normalizeAwsTagsToCfn as y, StackTerminationProtectionError as yt, runDockerForeground as z };
|
|
13710
|
+
//# sourceMappingURL=deploy-engine-ai3rix-L.js.map
|