@go-to-k/cdkd 0.221.8 → 0.221.9

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.
@@ -7,7 +7,7 @@ import { AttachRolePolicyCommand, CreateRoleCommand, DeleteRoleCommand, DeleteRo
7
7
  import { PublishCommand, SNSClient } from "@aws-sdk/client-sns";
8
8
  import { GetFunctionCommand, GetFunctionUrlConfigCommand, InvokeCommand, LambdaClient, UpdateFunctionConfigurationCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
9
9
  import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
10
- import { DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeLaunchTemplatesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, EC2Client } from "@aws-sdk/client-ec2";
10
+ import { DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeInstancesCommand, DescribeLaunchTemplatesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, EC2Client } from "@aws-sdk/client-ec2";
11
11
  import { DescribeTableCommand } from "@aws-sdk/client-dynamodb";
12
12
  import { CloudFormationClient, CreateChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeTypeCommand, GetTemplateCommand, waitUntilChangeSetCreateComplete } from "@aws-sdk/client-cloudformation";
13
13
  import { GetRestApiCommand } from "@aws-sdk/client-api-gateway";
@@ -5463,6 +5463,15 @@ const cachedAvailabilityZones = {};
5463
5463
  */
5464
5464
  const cachedDynamicReferences = {};
5465
5465
  /**
5466
+ * Cache for EC2 instance attributes that require a live DescribeInstances
5467
+ * lookup (PrivateIp / PublicIp / PrivateDnsName / PublicDnsName /
5468
+ * AvailabilityZone). Keyed by `${physicalId}#${attributeName}`. The IP /
5469
+ * DNS attributes are not derivable from the instance id, so they are read
5470
+ * back from AWS once per (instance, attribute) and memoized for the deploy
5471
+ * lifetime.
5472
+ */
5473
+ const cachedEc2InstanceAttributes = {};
5474
+ /**
5466
5475
  * Get AWS account information from STS
5467
5476
  */
5468
5477
  async function getAccountInfo(overrideRegion) {
@@ -5859,6 +5868,10 @@ var IntrinsicFunctionResolver = class {
5859
5868
  case "Arn": return `arn:${partition}:cloudwatch:${region}:${accountId}:alarm:${physicalId}`;
5860
5869
  default: return physicalId;
5861
5870
  }
5871
+ if (resourceType === "AWS::CloudWatch::CompositeAlarm") switch (attributeName) {
5872
+ case "Arn": return `arn:${partition}:cloudwatch:${region}:${accountId}:alarm:${physicalId}`;
5873
+ default: return physicalId;
5874
+ }
5862
5875
  if (resourceType === "AWS::RDS::DBInstance" || resourceType === "AWS::DocDB::DBInstance" || resourceType === "AWS::Neptune::DBInstance") switch (attributeName) {
5863
5876
  case "DBInstanceArn":
5864
5877
  case "Arn": return `arn:${partition}:rds:${region}:${accountId}:db:${physicalId}`;
@@ -5929,6 +5942,47 @@ var IntrinsicFunctionResolver = class {
5929
5942
  case "SubnetId": return physicalId;
5930
5943
  default: return physicalId;
5931
5944
  }
5945
+ if (resourceType === "AWS::EC2::Instance") switch (attributeName) {
5946
+ case "PrivateIp":
5947
+ case "PublicIp":
5948
+ case "PrivateDnsName":
5949
+ case "PublicDnsName":
5950
+ case "AvailabilityZone": {
5951
+ const cacheKey = `${physicalId}#${attributeName}`;
5952
+ const cached = cachedEc2InstanceAttributes[cacheKey];
5953
+ if (cached !== void 0) return cached;
5954
+ try {
5955
+ const instance = (await getAwsClients().ec2.send(new DescribeInstancesCommand({ InstanceIds: [physicalId] }))).Reservations?.[0]?.Instances?.[0];
5956
+ let value;
5957
+ switch (attributeName) {
5958
+ case "PrivateIp":
5959
+ value = instance?.PrivateIpAddress;
5960
+ break;
5961
+ case "PublicIp":
5962
+ value = instance?.PublicIpAddress;
5963
+ break;
5964
+ case "PrivateDnsName":
5965
+ value = instance?.PrivateDnsName;
5966
+ break;
5967
+ case "PublicDnsName":
5968
+ value = instance?.PublicDnsName;
5969
+ break;
5970
+ case "AvailabilityZone":
5971
+ value = instance?.Placement?.AvailabilityZone;
5972
+ break;
5973
+ }
5974
+ if (value !== void 0 && value !== null && value !== "") {
5975
+ cachedEc2InstanceAttributes[cacheKey] = value;
5976
+ return value;
5977
+ }
5978
+ this.logger.warn(`DescribeInstances(${physicalId}) returned no ${attributeName}; returning physical ID`);
5979
+ } catch (err) {
5980
+ this.logger.warn(`DescribeInstances(${physicalId}) failed for ${attributeName}: ${err instanceof Error ? err.message : String(err)}`);
5981
+ }
5982
+ return physicalId;
5983
+ }
5984
+ default: return physicalId;
5985
+ }
5932
5986
  if (resourceType === "AWS::EC2::LaunchTemplate") {
5933
5987
  if (attributeName === "LatestVersionNumber" || attributeName === "DefaultVersionNumber") {
5934
5988
  try {
@@ -11435,6 +11489,84 @@ const IMPLICIT_DELETE_DEPENDENCIES = {
11435
11489
  "AWS::Lambda::Function"
11436
11490
  ]
11437
11491
  };
11492
+ /**
11493
+ * Matches one alarm-state function token in a CompositeAlarm `AlarmRule`:
11494
+ * ALARM("name") | OK('name') | INSUFFICIENT_DATA(name)
11495
+ * The argument is captured raw (with any surrounding quotes) and trimmed /
11496
+ * unquoted by {@link extractReferencedAlarmNames}. CloudWatch also accepts the
11497
+ * boolean literals TRUE / FALSE which carry no argument, so they never match.
11498
+ */
11499
+ const ALARM_RULE_FUNCTION_REGEX = /\b(?:ALARM|OK|INSUFFICIENT_DATA)\s*\(\s*([^)]*?)\s*\)/gi;
11500
+ /**
11501
+ * Extract every alarm NAME (or ARN) referenced by a CompositeAlarm `AlarmRule`
11502
+ * string. The rule references its child alarms by NAME (or ARN) as a plain
11503
+ * string — there is no `Ref` / `Fn::GetAtt`, so cdkd's DAG sees no dependency
11504
+ * edge from these references. We parse them out so a delete-ordering edge can be
11505
+ * synthesized (CloudWatch refuses to delete a metric alarm while a composite
11506
+ * alarm still references it).
11507
+ *
11508
+ * Handles the three alarm-state functions (`ALARM` / `OK` / `INSUFFICIENT_DATA`)
11509
+ * and both the bare-name and quoted-name forms. An ARN argument
11510
+ * (`arn:aws:cloudwatch:...:alarm:<name>`) is reduced to its trailing `<name>`
11511
+ * so it can be matched against a referenced alarm's `AlarmName` / physical id
11512
+ * the same way a bare name is.
11513
+ */
11514
+ function extractReferencedAlarmNames(alarmRule) {
11515
+ const names = /* @__PURE__ */ new Set();
11516
+ for (const match of alarmRule.matchAll(ALARM_RULE_FUNCTION_REGEX)) {
11517
+ let arg = (match[1] ?? "").trim();
11518
+ if (arg.length === 0) continue;
11519
+ if (arg.startsWith("\"") && arg.endsWith("\"") || arg.startsWith("'") && arg.endsWith("'")) arg = arg.slice(1, -1);
11520
+ if (arg.length === 0) continue;
11521
+ const arnAlarmMatch = /:alarm:(.+)$/.exec(arg);
11522
+ if (arnAlarmMatch?.[1]) names.add(arnAlarmMatch[1]);
11523
+ else names.add(arg);
11524
+ }
11525
+ return [...names];
11526
+ }
11527
+ /**
11528
+ * Compute per-resource delete-ordering edges that cannot be inferred from
11529
+ * Ref / Fn::GetAtt edges or from the type-pair {@link IMPLICIT_DELETE_DEPENDENCIES}
11530
+ * table.
11531
+ *
11532
+ * Currently this synthesizes edges for `AWS::CloudWatch::CompositeAlarm`: a
11533
+ * composite alarm references its child alarms (metric `AWS::CloudWatch::Alarm`
11534
+ * or other composite alarms) by NAME inside its `AlarmRule` string. Because the
11535
+ * reference is a plain string (no `Ref` / `Fn::GetAtt`), cdkd's DAG sees no
11536
+ * dependency edge, so without this the metric alarm can be scheduled for
11537
+ * deletion while the composite still exists — and CloudWatch rejects that with
11538
+ * `Cannot delete <alarm> as there are composite alarm(s) depending on it.`
11539
+ * We therefore emit an edge making the composite alarm delete BEFORE every
11540
+ * alarm its `AlarmRule` references (handling composite-of-composite too).
11541
+ *
11542
+ * @param resources logical id -> resource (the subset of resources participating
11543
+ * in the delete). Only entries whose logical id is a key in this record are
11544
+ * considered as edge endpoints.
11545
+ */
11546
+ function computeImplicitDeleteEdges(resources) {
11547
+ const edges = [];
11548
+ const alarmTypes = new Set(["AWS::CloudWatch::Alarm", "AWS::CloudWatch::CompositeAlarm"]);
11549
+ const nameToLogicalId = /* @__PURE__ */ new Map();
11550
+ for (const [logicalId, resource] of Object.entries(resources)) {
11551
+ if (!alarmTypes.has(resource.resourceType)) continue;
11552
+ const alarmName = typeof resource.properties?.["AlarmName"] === "string" ? resource.properties["AlarmName"] : resource.physicalId;
11553
+ if (alarmName) nameToLogicalId.set(alarmName, logicalId);
11554
+ }
11555
+ for (const [logicalId, resource] of Object.entries(resources)) {
11556
+ if (resource.resourceType !== "AWS::CloudWatch::CompositeAlarm") continue;
11557
+ const alarmRule = resource.properties?.["AlarmRule"];
11558
+ if (typeof alarmRule !== "string") continue;
11559
+ for (const referencedName of extractReferencedAlarmNames(alarmRule)) {
11560
+ const referencedLogicalId = nameToLogicalId.get(referencedName);
11561
+ if (!referencedLogicalId || referencedLogicalId === logicalId) continue;
11562
+ edges.push({
11563
+ before: logicalId,
11564
+ after: referencedLogicalId
11565
+ });
11566
+ }
11567
+ }
11568
+ return edges;
11569
+ }
11438
11570
 
11439
11571
  //#endregion
11440
11572
  //#region src/deployment/retryable-errors.ts
@@ -12850,6 +12982,18 @@ var DeployEngine = class {
12850
12982
  }
12851
12983
  }
12852
12984
  }
12985
+ const scoped = {};
12986
+ for (const id of deleteIds) {
12987
+ const resource = state.resources[id];
12988
+ if (resource) scoped[id] = resource;
12989
+ }
12990
+ for (const { before, after } of computeImplicitDeleteEdges(scoped)) {
12991
+ if (!dependedBy.has(after)) dependedBy.set(after, /* @__PURE__ */ new Set());
12992
+ if (!dependedBy.get(after).has(before)) {
12993
+ dependedBy.get(after).add(before);
12994
+ this.logger.debug(`Implicit delete dependency: ${before} (${scoped[before]?.resourceType}) must be deleted before ${after} (${scoped[after]?.resourceType})`);
12995
+ }
12996
+ }
12853
12997
  }
12854
12998
  /**
12855
12999
  * Prepare a property map for a Cloud Control API call. When a Tier 1
@@ -12933,5 +13077,5 @@ var DeployEngine = class {
12933
13077
  };
12934
13078
 
12935
13079
  //#endregion
12936
- export { clearBucketRegionCache as $, AssetPublisher as A, getLegacyStateBucketName as B, DiffCalculator as C, S3StateBackend as D, LockManager as E, getDockerCmd as F, resolveStateBucketWithDefaultAndSource as G, resolveCaptureObservedState as H, runDockerForeground as I, CFN_TEMPLATE_URL_LIMIT as J, warnDeprecatedNoPrefixCliFlag as K, runDockerStreaming as L, WorkGraph as M, buildDockerImage as N, rebuildClientForBucketRegion as O, formatDockerLoginError as P, AssemblyReader as Q, Synthesizer as R, applyRoleArnIfSet as S, TemplateParser as T, resolveSkipPrefix as U, resolveApp as V, resolveStateBucketWithDefault as W, findLargeInlineResources as X, MIGRATE_TMP_PREFIX as Y, uploadCfnTemplate as Z, collectInlinePolicyNamesManagedBySiblings as _, withRetry as a, CloudControlProvider as b, extractDeploymentEventError as c, cyan as d, resolveBucketRegion as et, gray as f, IAMRoleProvider as g, yellow as h, withResourceDeadline as i, stringifyValue as j, shouldRetainResource as k, formatResourceLine as l, red as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, green as p, CFN_TEMPLATE_BODY_LIMIT as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, bold as u, ProviderRegistry as v, DagBuilder as w, IntrinsicFunctionResolver as x, findActionableSilentDrops as y, getDefaultStateBucketName as z };
12937
- //# sourceMappingURL=deploy-engine-DCkhw7i7.js.map
13080
+ export { AssemblyReader as $, shouldRetainResource as A, getDefaultStateBucketName as B, applyRoleArnIfSet as C, LockManager as D, TemplateParser as E, formatDockerLoginError as F, resolveStateBucketWithDefault as G, resolveApp as H, getDockerCmd as I, CFN_TEMPLATE_BODY_LIMIT as J, resolveStateBucketWithDefaultAndSource as K, runDockerForeground as L, stringifyValue as M, WorkGraph as N, S3StateBackend as O, buildDockerImage as P, uploadCfnTemplate as Q, runDockerStreaming as R, IntrinsicFunctionResolver as S, DagBuilder as T, resolveCaptureObservedState as U, getLegacyStateBucketName as V, resolveSkipPrefix as W, MIGRATE_TMP_PREFIX as X, CFN_TEMPLATE_URL_LIMIT as Y, findLargeInlineResources as Z, IAMRoleProvider as _, withRetry as a, findActionableSilentDrops as b, computeImplicitDeleteEdges as c, bold as d, clearBucketRegionCache as et, cyan as f, yellow as g, red as h, withResourceDeadline as i, AssetPublisher as j, rebuildClientForBucketRegion as k, extractDeploymentEventError as l, green as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, gray as p, warnDeprecatedNoPrefixCliFlag as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveBucketRegion as tt, formatResourceLine as u, collectInlinePolicyNamesManagedBySiblings as v, DiffCalculator as w, CloudControlProvider as x, ProviderRegistry as y, Synthesizer as z };
13081
+ //# sourceMappingURL=deploy-engine-alFrHerE.js.map