@go-to-k/cdkd 0.280.38 → 0.280.40
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-CVra7Nuk.js → asg-provider-BOGjxxxb.js} +2 -2
- package/dist/{asg-provider-CVra7Nuk.js.map → asg-provider-BOGjxxxb.js.map} +1 -1
- package/dist/cli.js +114 -18
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-B3a5MhON.js → deploy-engine-CU47_nau.js} +216 -25
- package/dist/deploy-engine-CU47_nau.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-B3a5MhON.js.map +0 -1
|
@@ -10278,15 +10278,28 @@ var WAFv2WebACLProvider = class {
|
|
|
10278
10278
|
const AWS_NO_VALUE = Symbol("AWS::NoValue");
|
|
10279
10279
|
/**
|
|
10280
10280
|
* Resource types whose CloudFormation `Ref` returns the segment AFTER the LAST
|
|
10281
|
-
* pipe in cdkd's compound
|
|
10282
|
-
*
|
|
10283
|
-
*
|
|
10284
|
-
*
|
|
10285
|
-
*
|
|
10286
|
-
*
|
|
10287
|
-
*
|
|
10288
|
-
*
|
|
10289
|
-
* the
|
|
10281
|
+
* pipe in cdkd's compound physical id (rather than the whole physical id).
|
|
10282
|
+
*
|
|
10283
|
+
* Membership is decided by the ID SHAPE, not by the routing layer, and the Set
|
|
10284
|
+
* carries BOTH kinds:
|
|
10285
|
+
*
|
|
10286
|
+
* - **Cloud-Control-provisioned** (the original and still the majority) —
|
|
10287
|
+
* either the type has no SDK provider, or the #614 silent-drop routing sent
|
|
10288
|
+
* an SDK-backed type through CC (e.g. an ApiGateway Stage carrying
|
|
10289
|
+
* `MethodSettings`, which the SDK provider does not wire — issue #963). CC's
|
|
10290
|
+
* primaryIdentifier is compound (`<parentId>|<ref>`, or `<a>|<b>|<ref>` for
|
|
10291
|
+
* triple-segment AppConfig children) while CFn's `Ref` returns only the
|
|
10292
|
+
* trailing `<ref>`. For SDK-provisioned instances of these types the stored
|
|
10293
|
+
* id has no pipe, so the extraction is a no-op.
|
|
10294
|
+
* - **SDK-provisioned, provider-built compound** — the SDK provider ITSELF
|
|
10295
|
+
* packs the segments because its CRUD calls need all of them while
|
|
10296
|
+
* `ResourceProvider`'s read-side methods receive a single string. Here the
|
|
10297
|
+
* extraction is load-bearing on the ORDINARY deploy path and there is no
|
|
10298
|
+
* pipe-free variant to fall through. `AWS::S3Tables::Namespace` /
|
|
10299
|
+
* `::Table` (whose per-routing split is spelled out at their entry) and
|
|
10300
|
+
* `AWS::Glue::Table` (issue #1667) are these.
|
|
10301
|
+
*
|
|
10302
|
+
* The per-type map:
|
|
10290
10303
|
* - AWS::ApiGateway::Model `<restApiId>|<modelName>` -> model name
|
|
10291
10304
|
* - AWS::ApiGateway::RequestValidator `<restApiId>|<validatorId>` -> validator id
|
|
10292
10305
|
* - AWS::ApiGateway::Stage `<restApiId>|<stageName>` -> stage name
|
|
@@ -10309,6 +10322,7 @@ const AWS_NO_VALUE = Symbol("AWS::NoValue");
|
|
|
10309
10322
|
* - AWS::AppConfig::ConfigurationProfile `<appId>|<profileId>` -> profile id
|
|
10310
10323
|
* - AWS::AppConfig::HostedConfigurationVersion `<appId>|<profileId>|<ver>` -> version number
|
|
10311
10324
|
* - AWS::AppConfig::Deployment `<appId>|<envId>|<deployNum>` -> deployment number
|
|
10325
|
+
* - AWS::Glue::Table `<databaseName>|<tableName>` -> table name
|
|
10312
10326
|
*
|
|
10313
10327
|
* The extraction takes the segment after the LAST pipe (see
|
|
10314
10328
|
* {@link IntrinsicFunctionResolver.resolveRefValue}) so it is correct for both
|
|
@@ -10348,6 +10362,31 @@ const AWS_NO_VALUE = Symbol("AWS::NoValue");
|
|
|
10348
10362
|
* {@link REF_RETURNS_SEGMENT_BEFORE_FIRST_PIPE} instead of this Set: the
|
|
10349
10363
|
* after-last-pipe extraction would return the PARENT id for them.
|
|
10350
10364
|
* 5. Pin each addition with a unit test in intrinsic-functions.test.ts.
|
|
10365
|
+
*
|
|
10366
|
+
* AUDIT RECORD (2026-08-12, issue #1667) — every type in the composite-id table
|
|
10367
|
+
* of `docs/state-management.md` was re-checked against its docs-verified `Ref`,
|
|
10368
|
+
* plus the two types that table lists as ACCEPTING a composite without
|
|
10369
|
+
* producing one. `AWS::Glue::Table` was the one this Set could fix and is added
|
|
10370
|
+
* below. The types deliberately NOT in either Set, with the reason:
|
|
10371
|
+
* - correct to exclude, `Ref` is a synthetic / AWS-generated id no segment
|
|
10372
|
+
* reconstructs: ApiGateway::Method, EC2::NetworkAclEntry ("the ID of the
|
|
10373
|
+
* network ACL entry"), EC2::Route, EC2::VPCGatewayAttachment,
|
|
10374
|
+
* Lambda::EventInvokeConfig;
|
|
10375
|
+
* - correct to exclude, the docs page documents NO `Ref` return value at all:
|
|
10376
|
+
* EC2::SecurityGroupIngress (and its Egress sibling, same id shape), and
|
|
10377
|
+
* Lambda::Permission — cdkd stores the bare statement id, but state written
|
|
10378
|
+
* by the older Cloud Control path can hold `<functionArn>|<statementId>`,
|
|
10379
|
+
* and with no documented contract the raw id is the least-surprising value
|
|
10380
|
+
* (the same call the maintenance note above already records for it);
|
|
10381
|
+
* - already correct, no change needed: EC2::EIP (before-first-pipe),
|
|
10382
|
+
* S3Tables::Namespace / ::Table (added by the 2026-07-03 close-out audit),
|
|
10383
|
+
* ECS::Service (before-first-pipe; stores the bare ARN on the SDK path);
|
|
10384
|
+
* - KNOWN WRONG and NOT fixable by a Set entry, tracked in issue #1681:
|
|
10385
|
+
* AppSync::ApiKey / ::DataSource / ::Resolver (`Ref` returns the resource
|
|
10386
|
+
* ARN, so the value must be RECONSTRUCTED like the WAFv2::WebACL case
|
|
10387
|
+
* below, not extracted) and Route53::RecordSet (`Ref` returns "the name of
|
|
10388
|
+
* the record" — the MIDDLE segment of `<hostedZoneId>|<name>|<type>`, which
|
|
10389
|
+
* neither after-LAST-pipe nor before-FIRST-pipe yields).
|
|
10351
10390
|
*/
|
|
10352
10391
|
const REF_RETURNS_SEGMENT_AFTER_PIPE = /* @__PURE__ */ new Set([
|
|
10353
10392
|
"AWS::ApiGateway::Model",
|
|
@@ -10373,7 +10412,8 @@ const REF_RETURNS_SEGMENT_AFTER_PIPE = /* @__PURE__ */ new Set([
|
|
|
10373
10412
|
"AWS::AppConfig::HostedConfigurationVersion",
|
|
10374
10413
|
"AWS::AppConfig::Deployment",
|
|
10375
10414
|
"AWS::S3Tables::Namespace",
|
|
10376
|
-
"AWS::S3Tables::Table"
|
|
10415
|
+
"AWS::S3Tables::Table",
|
|
10416
|
+
"AWS::Glue::Table"
|
|
10377
10417
|
]);
|
|
10378
10418
|
/**
|
|
10379
10419
|
* Sibling of {@link REF_RETURNS_SEGMENT_AFTER_PIPE} for compound-id types
|
|
@@ -10869,11 +10909,14 @@ var IntrinsicFunctionResolver = class {
|
|
|
10869
10909
|
* stores. But for a few types CFn's `Ref` returns a sub-component of the
|
|
10870
10910
|
* physical id, and returning the raw physical id breaks downstream consumers.
|
|
10871
10911
|
*
|
|
10872
|
-
* The {@link REF_RETURNS_SEGMENT_AFTER_PIPE} types
|
|
10873
|
-
*
|
|
10874
|
-
*
|
|
10875
|
-
*
|
|
10876
|
-
*
|
|
10912
|
+
* The {@link REF_RETURNS_SEGMENT_AFTER_PIPE} types store a COMPOUND physical
|
|
10913
|
+
* id `<parentId>|<ref>` while CFn's `Ref` returns only the trailing `<ref>`
|
|
10914
|
+
* segment. Most are compound because Cloud Control provisions them (either
|
|
10915
|
+
* they have no SDK provider, or the #614 silent-drop routing sent an
|
|
10916
|
+
* SDK-backed type through CC) and its primaryIdentifier is compound; the rest
|
|
10917
|
+
* — `AWS::S3Tables::Namespace` / `::Table` and `AWS::Glue::Table` — are
|
|
10918
|
+
* compound because their own SDK provider packs the segments. The Set's
|
|
10919
|
+
* header records the split per type; examples:
|
|
10877
10920
|
* - `AWS::ApiGateway::Model` → Ref is the model NAME; physical id is
|
|
10878
10921
|
* `<restApiId>|<modelName>`. A method wiring
|
|
10879
10922
|
* `RequestModels: { "application/json": { "Ref": <Model> } }` would
|
|
@@ -12872,7 +12915,7 @@ var CloudControlProvider = class {
|
|
|
12872
12915
|
if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
|
|
12873
12916
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
12874
12917
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
12875
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
12918
|
+
const { ASGProvider } = await import("./asg-provider-BOGjxxxb.js").then((n) => n.n);
|
|
12876
12919
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
12877
12920
|
return;
|
|
12878
12921
|
}
|
|
@@ -13522,6 +13565,25 @@ function parseLambdaPayload(payloadBytes) {
|
|
|
13522
13565
|
return parsed;
|
|
13523
13566
|
}
|
|
13524
13567
|
/**
|
|
13568
|
+
* Decode the base64 `LogResult` a `LogType: 'Tail'` invoke returns into the
|
|
13569
|
+
* backing function's log tail (issue #1674).
|
|
13570
|
+
*
|
|
13571
|
+
* Best-effort by design: this only ever feeds diagnostics and the retry
|
|
13572
|
+
* classifier, so nothing here may fail a deploy that is otherwise fine. Node's
|
|
13573
|
+
* base64 decoder is LENIENT — it drops invalid characters rather than throwing —
|
|
13574
|
+
* so a malformed value decodes to garbage, which simply matches no signal; the
|
|
13575
|
+
* `catch` is belt-and-braces for that contract changing, not a live path.
|
|
13576
|
+
*/
|
|
13577
|
+
function decodeInvokeLogTail(logResult) {
|
|
13578
|
+
if (!logResult) return void 0;
|
|
13579
|
+
try {
|
|
13580
|
+
const decoded = Buffer.from(logResult, "base64").toString("utf8");
|
|
13581
|
+
return decoded.length > 0 ? decoded : void 0;
|
|
13582
|
+
} catch {
|
|
13583
|
+
return;
|
|
13584
|
+
}
|
|
13585
|
+
}
|
|
13586
|
+
/**
|
|
13525
13587
|
* IAM-authorization-propagation signals in a custom resource FAILED reason that
|
|
13526
13588
|
* indicate the backing Lambda's freshly-attached execution-role policy has not
|
|
13527
13589
|
* yet taken effect for its assumed-role session (so a recycle + retry will
|
|
@@ -13538,6 +13600,67 @@ const CR_TRANSIENT_AUTHZ_SIGNALS = [
|
|
|
13538
13600
|
"is unable to assume"
|
|
13539
13601
|
];
|
|
13540
13602
|
/**
|
|
13603
|
+
* The same IAM-authorization-propagation signals, matched against the backing
|
|
13604
|
+
* function's INVOCATION LOG TAIL rather than the FAILED reason (issue #1674).
|
|
13605
|
+
*
|
|
13606
|
+
* Why a second set is needed at all: the reason string is written by the
|
|
13607
|
+
* handler, and a handler that wraps an SDK / CLI failure in its own message —
|
|
13608
|
+
* normal handler hygiene — erases every authz phrase before cdkd ever sees it.
|
|
13609
|
+
* CDK's `BucketDeployment` is the widely-used instance: `aws_command()` lets
|
|
13610
|
+
* `subprocess.check_call` raise, and `str(CalledProcessError)` is only
|
|
13611
|
+
* `Command '[...]' returned non-zero exit status 1.`, so the 403 on the asset
|
|
13612
|
+
* object reaches cdkd with no authz wording at all and the retry above never
|
|
13613
|
+
* fires — on a resource GUARANTEED to race, since CDK generates the handler
|
|
13614
|
+
* role, its inline policy and the custom resource in the same stack.
|
|
13615
|
+
*
|
|
13616
|
+
* The 403 does survive in the function's own log, which a `LogType: 'Tail'`
|
|
13617
|
+
* invoke returns inline (no CloudWatch Logs dependency, no extra API call, no
|
|
13618
|
+
* additional IAM permission — see `invokeLambda`). So this set adds the
|
|
13619
|
+
* CLI / SDK-level spellings of the SAME denial that the handler swallowed:
|
|
13620
|
+
* botocore's `An error occurred (403) when calling the HeadObject operation`
|
|
13621
|
+
* and the `AccessDenied` / `AccessDeniedException` error codes.
|
|
13622
|
+
*
|
|
13623
|
+
* Deliberately still NARROW, for the reason `isTransientAuthzFailure`
|
|
13624
|
+
* documents: a bare `403` / `forbidden` would match a handler legitimately
|
|
13625
|
+
* logging an unrelated downstream denial, and generic transient errors
|
|
13626
|
+
* (throttling / timeouts) must not trigger a CR re-invoke at all.
|
|
13627
|
+
*
|
|
13628
|
+
* ACCEPTED COST, stated because a log surface is noisier than a
|
|
13629
|
+
* handler-authored reason: a handler that merely LOGS a genuine, permanent
|
|
13630
|
+
* `AccessDenied` — one it caught, or one no propagation will fix — now buys
|
|
13631
|
+
* `transientAuthzMaxRetries` extra invokes plus a
|
|
13632
|
+
* `recycleBackingFunctionExecEnv` each. Bounded (2 by default; set
|
|
13633
|
+
* `CDKD_CR_AUTHZ_MAX_RETRIES=0` to disable the RETRY — the log-tail scan and the
|
|
13634
|
+
* reason annotation below still run, since they only describe the failure), and
|
|
13635
|
+
* the original failure still surfaces afterwards, so the failure is DELAYED,
|
|
13636
|
+
* never masked.
|
|
13637
|
+
*
|
|
13638
|
+
* The cost is NOT purely wasted time, and saying only "delayed, never masked"
|
|
13639
|
+
* would undersell it: a re-invoke re-runs the handler's `Create`, so a handler
|
|
13640
|
+
* that is not idempotent and had already done partial work repeats that work —
|
|
13641
|
+
* and under the CDK Provider framework the re-invoked `onEvent` can create a
|
|
13642
|
+
* SECOND physical resource, orphaning the first. That exposure is not new (the
|
|
13643
|
+
* pre-existing reason-string match has always been able to retry), but the log
|
|
13644
|
+
* signal widens what reaches it. The trade is still deliberate: the alternative
|
|
13645
|
+
* is the pre-#1674 behavior, where the propagation race this whole mechanism
|
|
13646
|
+
* exists for fails the deploy on its first attempt, every time.
|
|
13647
|
+
*/
|
|
13648
|
+
const CR_TRANSIENT_AUTHZ_LOG_SIGNALS = [
|
|
13649
|
+
...CR_TRANSIENT_AUTHZ_SIGNALS,
|
|
13650
|
+
"an error occurred (403)",
|
|
13651
|
+
"accessdenied",
|
|
13652
|
+
"access denied"
|
|
13653
|
+
];
|
|
13654
|
+
/**
|
|
13655
|
+
* Cap for the backing function's log tail when it is surfaced in an EPHEMERAL
|
|
13656
|
+
* warning (the `FunctionError` arm). Deliberately far larger than
|
|
13657
|
+
* `truncateReason`'s 200-char default — a crashed handler's real cause is often
|
|
13658
|
+
* several lines above the last one (a Python traceback) — but not unbounded:
|
|
13659
|
+
* this lands in CI logs and terminal scrollback. Lambda caps the tail at 4 KB
|
|
13660
|
+
* regardless, so this only trims the extreme case.
|
|
13661
|
+
*/
|
|
13662
|
+
const CR_LOG_TAIL_WARN_MAX_CHARS = 2e3;
|
|
13663
|
+
/**
|
|
13541
13664
|
* Custom Resource Provider
|
|
13542
13665
|
*
|
|
13543
13666
|
* Implements Lambda-backed custom resources by invoking the Lambda function
|
|
@@ -13634,7 +13757,9 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
13634
13757
|
* `disableOuterRetry` to avoid stranding a pre-signed response URL — so we
|
|
13635
13758
|
* retry HERE instead, deriving a fresh response URL + RequestId per attempt
|
|
13636
13759
|
* and recycling the backing function's execution environment between tries).
|
|
13637
|
-
* Override via `CDKD_CR_AUTHZ_MAX_RETRIES`
|
|
13760
|
+
* Override via `CDKD_CR_AUTHZ_MAX_RETRIES`. `0` disables the RETRY only —
|
|
13761
|
+
* the issue-#1674 log-tail scan and the reason annotation it produces still
|
|
13762
|
+
* run, because they describe the failure rather than react to it.
|
|
13638
13763
|
*/
|
|
13639
13764
|
transientAuthzMaxRetries = (() => {
|
|
13640
13765
|
const raw = process.env["CDKD_CR_AUTHZ_MAX_RETRIES"];
|
|
@@ -13900,12 +14025,18 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
13900
14025
|
const invocation = await this.prepareInvocation();
|
|
13901
14026
|
const request = buildRequest(invocation);
|
|
13902
14027
|
this.logger.debug(`Sending custom resource ${operation.toLowerCase()} request: ${serviceToken}`);
|
|
13903
|
-
const cfnResponse = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, operation);
|
|
13904
|
-
|
|
13905
|
-
|
|
14028
|
+
const { response: cfnResponse, logResult } = await this.sendRequest(serviceToken, request, invocation.responseKey, logicalId, operation);
|
|
14029
|
+
const reasonIsAuthz = cfnResponse.Status === "FAILED" && this.isTransientAuthzFailure(cfnResponse.Reason);
|
|
14030
|
+
const logAuthzMatch = cfnResponse.Status === "FAILED" && !reasonIsAuthz ? this.findTransientAuthzLogLine(decodeInvokeLogTail(logResult)) : void 0;
|
|
14031
|
+
if (cfnResponse.Status === "FAILED" && attempt < this.transientAuthzMaxRetries && (reasonIsAuthz || logAuthzMatch !== void 0)) {
|
|
14032
|
+
this.logger.warn(`Custom resource ${operation} for ${logicalId} returned a transient IAM-authorization FAILED (attempt ${attempt + 1}/${this.transientAuthzMaxRetries + 1}): ${this.truncateReason(cfnResponse.Reason)}. ` + (logAuthzMatch === void 0 ? "" : `The handler's reason carried no authorization wording; the denial was found in the backing function's log: ${this.truncateReason(logAuthzMatch.line)}. `) + `Recycling the backing function's execution environment and retrying so its next cold start picks up the propagated policy.`);
|
|
13906
14033
|
await this.recycleBackingFunctionExecEnv(serviceToken, logicalId);
|
|
13907
14034
|
continue;
|
|
13908
14035
|
}
|
|
14036
|
+
if (cfnResponse.Status === "FAILED" && logAuthzMatch !== void 0) return {
|
|
14037
|
+
...cfnResponse,
|
|
14038
|
+
Reason: `${cfnResponse.Reason ?? "Unknown reason"} [cdkd: the reason carried no authorization wording, but the backing function's invocation log matched the IAM-authorization signal "${logAuthzMatch.signal}" — see the cdkd warning for the log line, or the function's CloudWatch log group]`
|
|
14039
|
+
};
|
|
13909
14040
|
return cfnResponse;
|
|
13910
14041
|
}
|
|
13911
14042
|
}
|
|
@@ -13927,6 +14058,45 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
13927
14058
|
const lower = reason.toLowerCase();
|
|
13928
14059
|
return CR_TRANSIENT_AUTHZ_SIGNALS.some((p) => lower.includes(p));
|
|
13929
14060
|
}
|
|
14061
|
+
/**
|
|
14062
|
+
* Find the IAM-authorization denial inside a backing function's invocation
|
|
14063
|
+
* log tail, for the case the FAILED reason itself carries none (issue #1674).
|
|
14064
|
+
*
|
|
14065
|
+
* Returns the FIRST matching log LINE rather than a boolean, so the caller can
|
|
14066
|
+
* put the denial the handler swallowed into its message — today the user has
|
|
14067
|
+
* to open CloudWatch to discover that `returned non-zero exit status 1` was a
|
|
14068
|
+
* 403 on the asset object. The line is returned verbatim (the caller
|
|
14069
|
+
* truncates); only the MATCH is case-insensitive.
|
|
14070
|
+
*
|
|
14071
|
+
* Absent for an SNS-backed custom resource — no Lambda invoke, so no tail.
|
|
14072
|
+
*
|
|
14073
|
+
* **Known bound.** The tail always belongs to the DISPATCH invoke. For a
|
|
14074
|
+
* handler that does its work inline and PUTs the cfn-response itself (CDK's
|
|
14075
|
+
* `BucketDeployment` — the case this exists for), that IS where the failure
|
|
14076
|
+
* happened. For the CDK Provider framework's genuinely async pattern it is
|
|
14077
|
+
* NOT: the failure happens later, in a Step-Functions-driven handler, so a
|
|
14078
|
+
* stray denial logged by the `onEvent` wrapper can buy a bounded extra retry
|
|
14079
|
+
* with a message pointing at the wrong execution. That path is otherwise
|
|
14080
|
+
* covered by the reason string, which the framework populates from the
|
|
14081
|
+
* underlying error, so the cost is redundancy rather than a wrong answer.
|
|
14082
|
+
*
|
|
14083
|
+
* Suppressing the tail for the async pattern is NOT the fix, and the reason
|
|
14084
|
+
* is worth recording: `isAsyncPattern` in `getCustomResourceResponse` means
|
|
14085
|
+
* only "the invoke returned no direct payload", which is equally true of
|
|
14086
|
+
* `BucketDeployment` — its Python handler returns `None`. Gating on it would
|
|
14087
|
+
* switch off exactly the case this feature exists for.
|
|
14088
|
+
*/
|
|
14089
|
+
findTransientAuthzLogLine(logTail) {
|
|
14090
|
+
if (!logTail) return void 0;
|
|
14091
|
+
for (const line of logTail.split("\n")) {
|
|
14092
|
+
const lower = line.toLowerCase();
|
|
14093
|
+
const signal = CR_TRANSIENT_AUTHZ_LOG_SIGNALS.find((p) => lower.includes(p));
|
|
14094
|
+
if (signal !== void 0) return {
|
|
14095
|
+
line: line.trim(),
|
|
14096
|
+
signal
|
|
14097
|
+
};
|
|
14098
|
+
}
|
|
14099
|
+
}
|
|
13930
14100
|
/** Truncate a CR FAILED reason for log readability. */
|
|
13931
14101
|
truncateReason(reason, max = 200) {
|
|
13932
14102
|
const r = reason ?? "Unknown reason";
|
|
@@ -13968,16 +14138,25 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
13968
14138
|
* Send custom resource request via the appropriate service (Lambda or SNS)
|
|
13969
14139
|
* For Lambda: invokes synchronously and returns the response
|
|
13970
14140
|
* For SNS: publishes to topic and polls S3 for response
|
|
14141
|
+
*
|
|
14142
|
+
* Also returns the backing function's raw invocation `LogResult` when there
|
|
14143
|
+
* is one, so the caller can recover an authorization denial the handler
|
|
14144
|
+
* erased from the FAILED reason (issue #1674). Returned UNDECODED so the
|
|
14145
|
+
* happy path pays nothing — only a FAILED whose reason missed decodes it.
|
|
14146
|
+
* Absent on the SNS path: there is no Lambda invoke to attach a log to.
|
|
13971
14147
|
*/
|
|
13972
14148
|
async sendRequest(serviceToken, request, responseKey, logicalId, operation) {
|
|
13973
14149
|
if (this.isSnsServiceToken(serviceToken)) {
|
|
13974
14150
|
this.logger.debug(`ServiceToken is SNS topic, publishing to: ${serviceToken}`);
|
|
13975
14151
|
await this.publishToSns(serviceToken, request);
|
|
13976
|
-
return await this.pollS3Response(responseKey, logicalId, operation);
|
|
14152
|
+
return { response: await this.pollS3Response(responseKey, logicalId, operation) };
|
|
13977
14153
|
}
|
|
13978
14154
|
await this.waitForBackingLambdaReady(serviceToken, logicalId);
|
|
13979
|
-
const
|
|
13980
|
-
return
|
|
14155
|
+
const invokeResponse = await this.invokeLambda(serviceToken, request);
|
|
14156
|
+
return {
|
|
14157
|
+
response: await this.getCustomResourceResponse(invokeResponse, responseKey, logicalId, operation),
|
|
14158
|
+
...invokeResponse.LogResult === void 0 ? {} : { logResult: invokeResponse.LogResult }
|
|
14159
|
+
};
|
|
13981
14160
|
}
|
|
13982
14161
|
/**
|
|
13983
14162
|
* Block until the backing Lambda function for a Custom Resource is in a
|
|
@@ -14036,11 +14215,21 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
14036
14215
|
}
|
|
14037
14216
|
/**
|
|
14038
14217
|
* Invoke Lambda function synchronously
|
|
14218
|
+
*
|
|
14219
|
+
* `LogType: 'Tail'` makes Lambda return the last 4 KB of THIS invocation's
|
|
14220
|
+
* log, base64-encoded, in `LogResult` (issue #1674). It is only valid with
|
|
14221
|
+
* `RequestResponse`, which is what this path always uses. This is deliberately
|
|
14222
|
+
* preferred over reading the function's CloudWatch log group after the fact:
|
|
14223
|
+
* it needs no CloudWatch Logs client, no `logs:GetLogEvents` on cdkd's own
|
|
14224
|
+
* credentials, and no extra API call — the tail rides the invoke response
|
|
14225
|
+
* cdkd already waits for, and is scoped to the exact invocation that failed
|
|
14226
|
+
* rather than to whatever happens to be latest in the log stream.
|
|
14039
14227
|
*/
|
|
14040
14228
|
async invokeLambda(serviceToken, request) {
|
|
14041
14229
|
return await this.lambdaClient.send(new InvokeCommand({
|
|
14042
14230
|
FunctionName: serviceToken,
|
|
14043
14231
|
InvocationType: "RequestResponse",
|
|
14232
|
+
LogType: "Tail",
|
|
14044
14233
|
Payload: Buffer.from(JSON.stringify(request))
|
|
14045
14234
|
}));
|
|
14046
14235
|
}
|
|
@@ -14055,6 +14244,8 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
14055
14244
|
async getCustomResourceResponse(lambdaResponse, responseKey, logicalId, operation) {
|
|
14056
14245
|
if (lambdaResponse.FunctionError) {
|
|
14057
14246
|
const errorPayload = lambdaResponse.Payload ? Buffer.from(lambdaResponse.Payload).toString() : "Unknown";
|
|
14247
|
+
const logTail = decodeInvokeLogTail(lambdaResponse.LogResult);
|
|
14248
|
+
if (logTail !== void 0) this.logger.warn(`Backing function log tail for ${logicalId} (${operation}):\n` + this.truncateReason(logTail, CR_LOG_TAIL_WARN_MAX_CHARS));
|
|
14058
14249
|
throw new Error(`Lambda function error (${lambdaResponse.FunctionError}): ${errorPayload}`);
|
|
14059
14250
|
}
|
|
14060
14251
|
let hasDirectPayload = false;
|
|
@@ -19541,7 +19732,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
19541
19732
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
19542
19733
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
19543
19734
|
function getCdkdVersion() {
|
|
19544
|
-
return "0.280.
|
|
19735
|
+
return "0.280.40";
|
|
19545
19736
|
}
|
|
19546
19737
|
/**
|
|
19547
19738
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -21707,4 +21898,4 @@ var DeployEngine = class {
|
|
|
21707
21898
|
|
|
21708
21899
|
//#endregion
|
|
21709
21900
|
export { requireConfigObject as $, AssemblyReader as $t, green as A, __exportAll as An, runDockerForeground as At, disableInstanceApiTermination as B, resolveCaptureObservedState as Bt, MULTI_REGION_RECREATE_BLOCKED_TYPES as C, StackTerminationProtectionError as Cn, getBootstrapMarkerKey as Ct, bold as D, isCdkdError as Dn, buildDockerImage as Dt, formatResourceLine as E, formatError as En, validateContainerRepoName as Et, clearOnUpdateRemoval as F, synthesisStatusMessage as Ft, WAFv2WebACLProvider as G, stateBucketExistenceConfirmed as Gt, IntrinsicFunctionResolver as H, resolveStateBucketWithDefault as Ht, ProviderRegistry as I, getDefaultStateBucketName as It, assertRegionMatch as J, CFN_TEMPLATE_URL_LIMIT as Jt, normalizeAwsTagsToCfn as K, warnDeprecatedNoPrefixCliFlag as Kt, findActionableSilentDrops as L, getLegacyStateBucketName as Lt, yellow as M, AssetManifestLoader as Mt, IAMRoleProvider as N, getDockerImageBySourceHash as Nt, cyan as O, normalizeAwsError as On, formatDockerLoginError as Ot, collectInlinePolicyNamesManagedBySiblings as P, Synthesizer as Pt, requireConfigArray as Q, expectedOwnerParam as Qt, CloudControlProvider as R, resolveApp as Rt, extractDeploymentEventError as S, StackHasActiveImportsError as Sn, ensureAssetStorage as St, renderStatefulReason as T, SynthesisError as Tn, validateAssetBucketName as Tt, cfnRefValueFromPhysicalId as U, resolveStateBucketWithDefaultAndSource as Ut, isTerminationProtectionPropagationError as V, resolveSkipPrefix as Vt, refStateLookupFromResource as W, resolveUseCdkBootstrapAssets as Wt, readConfigString as X, findLargeInlineResources as Xt, configStringRefusal as Y, MIGRATE_TMP_PREFIX as Yt, replayWarn as Z, uploadCfnTemplate as Zt, createPreDeleteFinalSnapshot as _, NestedStackChildDirectDestroyError as _n, createAssetRedirectResolver as _t, DeploymentEventsStore as a, resetAwsClients as an, isRetryableTransientError as at, unsupportedFinalSnapshotError as b, ResourceTimeoutError as bn, AssetModeResolver as bt, replayFailedOperations as c, CdkdError as cn, TemplateParser as ct, IMPLICIT_DELETE_DEPENDENCIES as d, DeployCancelledError as dn, rebuildClientForBucketRegion as dt, processStackMessages as en, requireConfigString as et, computeImplicitDeleteEdges as f, LocalInvokeBuildError as fn, shouldRetainResource as ft, ccRoutedFinalSnapshotError as g, MissingCdkCliError as gn, buildAssetRedirectMap as gt, buildFinalSnapshotIdentifier as h, LockError as hn, WorkGraph as ht, DeploymentEventsReader as i, getAwsClients as in, withRetry as it, red as j, runDockerStreaming as jt, gray as k, withErrorHandling as kn, getDockerCmd as kt, replayRollback as l, ConfigError as ln, LockManager as lt, PRE_DELETE_SNAPSHOT_TYPES as m, LocalStartServiceError as mn, stringifyValue as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveBucketRegion as nn, DiffCalculator as nt, planFailedOps as o, setAwsClients as on, isThrottlingError as ot, ATOMIC_FINAL_SNAPSHOT_TYPES as p, LocalMigrateError as pn, AssetPublisher as pt, resolveExplicitPhysicalId as q, CFN_TEMPLATE_BODY_LIMIT as qt, DeployEngine as r, AwsClients as rn, describeTypeWithThrottleRetry as rt, planRollback as s, AssetError as sn, DagBuilder as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, clearBucketRegionCache as tn, applyRoleArnIfSet as tt, withResourceDeadline as u, DependencyError as un, S3StateBackend as ut, isFinalSnapshotError as v, PartialFailureError as vn, loadPublishableAssetManifest as vt, isStatefulRecreateTargetSync as w, StateError as wn, parseBootstrapMarker as wt, makeCanonicalizePropertiesFn as x, ResourceUpdateNotSupportedError as xn, BOOTSTRAP_MARKER_PREFIX as xt, refusesFinalSnapshot as y, ProvisioningError as yn, rewriteTemplateAssetReferences as yt, slowCcOperationTimeoutMs as z, resolveAutoAssetStorage as zt };
|
|
21710
|
-
//# sourceMappingURL=deploy-engine-
|
|
21901
|
+
//# sourceMappingURL=deploy-engine-CU47_nau.js.map
|