@go-to-k/cdkd 0.30.0 → 0.30.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/cli.js CHANGED
@@ -7506,7 +7506,11 @@ Error: ${err.message || "Unknown error"}`,
7506
7506
  };
7507
7507
 
7508
7508
  // src/provisioning/providers/custom-resource-provider.ts
7509
- import { InvokeCommand } from "@aws-sdk/client-lambda";
7509
+ import {
7510
+ InvokeCommand,
7511
+ waitUntilFunctionActiveV2,
7512
+ waitUntilFunctionUpdatedV2
7513
+ } from "@aws-sdk/client-lambda";
7510
7514
  import { PublishCommand } from "@aws-sdk/client-sns";
7511
7515
  import {
7512
7516
  S3Client as S3Client6,
@@ -7790,9 +7794,54 @@ var CustomResourceProvider = class _CustomResourceProvider {
7790
7794
  await this.publishToSns(serviceToken, request);
7791
7795
  return await this.pollS3Response(responseKey, logicalId, operation);
7792
7796
  }
7797
+ await this.waitForBackingLambdaReady(serviceToken, logicalId);
7793
7798
  const response = await this.invokeLambda(serviceToken, request);
7794
7799
  return await this.getCustomResourceResponse(response, responseKey, logicalId, operation);
7795
7800
  }
7801
+ /**
7802
+ * Block until the backing Lambda function for a Custom Resource is in a
7803
+ * state that accepts a synchronous Invoke.
7804
+ *
7805
+ * Two sequential waiters:
7806
+ * 1. `waitUntilFunctionActiveV2` — handles the post-CreateFunction
7807
+ * `Pending` window (image pull, VPC ENI attachment, layer init).
7808
+ * 2. `waitUntilFunctionUpdatedV2` — handles the post-Update
7809
+ * `InProgress` window (configuration / code swap settling).
7810
+ * Together they cover the only two transient states that reject
7811
+ * synchronous Invokes.
7812
+ *
7813
+ * In the common case (Lambda has been Active for a while, no in-flight
7814
+ * Update), both waiters return on first poll → ~2 GetFunction calls →
7815
+ * ~200ms overhead. That's the price for correctness; the alternative
7816
+ * (whole-stack Active wait at Lambda CREATE) is ~5–10 minutes per
7817
+ * VPC-attached function.
7818
+ *
7819
+ * `serviceToken` is the Lambda function ARN; the Lambda SDK accepts
7820
+ * both name and ARN as `FunctionName`, so we pass the ARN through
7821
+ * unchanged.
7822
+ *
7823
+ * `maxWaitTime` is set generously (10 min) because VPC ENI attachment
7824
+ * has been observed to take 8+ minutes in pathological cases. The
7825
+ * deploy engine's per-resource `--resource-timeout` (default 30 min)
7826
+ * still bounds the outer Custom Resource provisioning attempt, so
7827
+ * this waiter cap is layered defense, not the only timeout.
7828
+ */
7829
+ async waitForBackingLambdaReady(serviceToken, logicalId) {
7830
+ try {
7831
+ await waitUntilFunctionActiveV2(
7832
+ { client: this.lambdaClient, maxWaitTime: 600 },
7833
+ { FunctionName: serviceToken }
7834
+ );
7835
+ await waitUntilFunctionUpdatedV2(
7836
+ { client: this.lambdaClient, maxWaitTime: 600 },
7837
+ { FunctionName: serviceToken }
7838
+ );
7839
+ } catch (error) {
7840
+ throw new Error(
7841
+ `Lambda backing custom resource ${logicalId} (${serviceToken}) did not reach a ready state for Invoke: ${error instanceof Error ? error.message : String(error)}`
7842
+ );
7843
+ }
7844
+ }
7796
7845
  /**
7797
7846
  * Publish custom resource request to an SNS topic
7798
7847
  */
@@ -12966,7 +13015,8 @@ import {
12966
13015
  GetFunctionCommand,
12967
13016
  ListFunctionsCommand,
12968
13017
  ListTagsCommand,
12969
- ResourceNotFoundException
13018
+ ResourceNotFoundException,
13019
+ waitUntilFunctionUpdatedV2 as waitUntilFunctionUpdatedV22
12970
13020
  } from "@aws-sdk/client-lambda";
12971
13021
  import {
12972
13022
  DescribeNetworkInterfacesCommand,
@@ -13009,6 +13059,20 @@ var LambdaFunctionProvider = class {
13009
13059
  eniWaitTimeoutMs = 10 * 60 * 1e3;
13010
13060
  eniWaitInitialDelayMs = 1e4;
13011
13061
  eniWaitMaxDelayMs = 3e4;
13062
+ // Budget for the post-Update wait that blocks until LastUpdateStatus
13063
+ // === 'Successful'. Required to prevent the SECOND in-flight call (e.g.
13064
+ // UpdateFunctionCode immediately after UpdateFunctionConfiguration)
13065
+ // from racing the first with "function is currently in the following
13066
+ // state: InProgress". Update typically settles in seconds; the 10-min
13067
+ // cap is generous slack for layer-update / VPC-detach edge cases.
13068
+ // Seconds (the SDK waiter contract is seconds, not ms).
13069
+ //
13070
+ // The post-CreateFunction `State=Active` wait used to live here too
13071
+ // (PR #121) but doubled deploy time on benchmark stacks because every
13072
+ // Lambda paid the cost regardless of whether anything synchronously
13073
+ // invoked it. The Active wait now lives in `CustomResourceProvider`
13074
+ // (the only deploy-time consumer that breaks against Pending).
13075
+ functionUpdateMaxWaitSeconds = 10 * 60;
13012
13076
  // delstack-style ENI cleanup tunables.
13013
13077
  // - initial sleep: gives AWS time to publish post-detach ENI state via
13014
13078
  // DescribeNetworkInterfaces (right after the update, the API can return
@@ -13142,6 +13206,7 @@ var LambdaFunctionProvider = class {
13142
13206
  };
13143
13207
  await this.lambdaClient.send(new UpdateFunctionConfigurationCommand(configParams));
13144
13208
  this.logger.debug(`Updated configuration for Lambda function ${physicalId}`);
13209
+ await this.waitForFunctionUpdated(logicalId, resourceType, physicalId);
13145
13210
  }
13146
13211
  const newCode = properties["Code"];
13147
13212
  const oldCode = previousProperties["Code"];
@@ -13157,6 +13222,7 @@ var LambdaFunctionProvider = class {
13157
13222
  };
13158
13223
  await this.lambdaClient.send(new UpdateFunctionCodeCommand(codeParams));
13159
13224
  this.logger.debug(`Updated code for Lambda function ${physicalId}`);
13225
+ await this.waitForFunctionUpdated(logicalId, resourceType, physicalId);
13160
13226
  }
13161
13227
  const getResponse = await this.lambdaClient.send(
13162
13228
  new GetFunctionCommand({ FunctionName: physicalId })
@@ -13170,6 +13236,9 @@ var LambdaFunctionProvider = class {
13170
13236
  }
13171
13237
  };
13172
13238
  } catch (error) {
13239
+ if (error instanceof ProvisioningError) {
13240
+ throw error;
13241
+ }
13173
13242
  const cause = error instanceof Error ? error : void 0;
13174
13243
  throw new ProvisioningError(
13175
13244
  `Failed to update Lambda function ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
@@ -13336,6 +13405,41 @@ var LambdaFunctionProvider = class {
13336
13405
  * Timeout is a soft warning — downstream Subnet/SG deletion has its own
13337
13406
  * retries.
13338
13407
  */
13408
+ /**
13409
+ * Block until the function's LastUpdateStatus === 'Successful'.
13410
+ *
13411
+ * Used after UpdateFunctionConfiguration / UpdateFunctionCode. Wraps the
13412
+ * SDK's `waitUntilFunctionUpdatedV2` (acceptors: SUCCESS=Successful,
13413
+ * FAILURE=Failed, RETRY=InProgress). Errors are surfaced as
13414
+ * `ProvisioningError` so the deploy engine's per-resource error
13415
+ * handling treats them identically to an Update API failure.
13416
+ *
13417
+ * NOTE: post-CreateFunction `State=Active` wait was deliberately moved
13418
+ * out of this provider in favor of an on-demand wait inside
13419
+ * `CustomResourceProvider.sendRequest` (the only deploy-time consumer
13420
+ * that breaks against a Pending Lambda). Blocking the entire deploy
13421
+ * DAG behind every Lambda's Active transition more than doubled
13422
+ * deploy time on benchmark stacks; the on-demand wait scoped to the
13423
+ * one resource type that actually needs it preserves the bug fix
13424
+ * without paying the whole-stack tax.
13425
+ */
13426
+ async waitForFunctionUpdated(logicalId, resourceType, functionName) {
13427
+ try {
13428
+ await waitUntilFunctionUpdatedV22(
13429
+ { client: this.lambdaClient, maxWaitTime: this.functionUpdateMaxWaitSeconds },
13430
+ { FunctionName: functionName }
13431
+ );
13432
+ } catch (error) {
13433
+ const cause = error instanceof Error ? error : void 0;
13434
+ throw new ProvisioningError(
13435
+ `Lambda function ${logicalId} update did not complete: ${error instanceof Error ? error.message : String(error)}`,
13436
+ resourceType,
13437
+ logicalId,
13438
+ functionName,
13439
+ cause
13440
+ );
13441
+ }
13442
+ }
13339
13443
  /**
13340
13444
  * Poll GetFunction until LastUpdateStatus is no longer `InProgress`.
13341
13445
  *
@@ -13348,6 +13452,12 @@ var LambdaFunctionProvider = class {
13348
13452
  * Bounded by eniWaitTimeoutMs (10min) and treated as a soft warning on
13349
13453
  * timeout: the subsequent ENI cleanup loop and downstream retries cover
13350
13454
  * the residual edge case.
13455
+ *
13456
+ * NOTE: deliberately separate from `waitForFunctionUpdated` (which uses
13457
+ * the SDK's `waitUntilFunctionUpdatedV2` and throws on FAILURE). The
13458
+ * pre-delete path needs a more lenient acceptor: if a prior update
13459
+ * failed, we still want to proceed with DeleteFunction rather than
13460
+ * abort, because the function is going away anyway.
13351
13461
  */
13352
13462
  async waitForLambdaUpdateCompleted(functionName) {
13353
13463
  const start = Date.now();
@@ -36062,7 +36172,7 @@ function reorderArgs(argv) {
36062
36172
  }
36063
36173
  async function main() {
36064
36174
  const program = new Command13();
36065
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.30.0");
36175
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.30.2");
36066
36176
  program.addCommand(createBootstrapCommand());
36067
36177
  program.addCommand(createSynthCommand());
36068
36178
  program.addCommand(createListCommand());