@go-to-k/cdkd 0.219.1 → 0.219.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.
package/dist/cli.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-DWUnLza1.js";
3
- import { $ as CFN_TEMPLATE_URL_LIMIT, A as DagBuilder, B as getDockerCmd, C as CloudControlProvider, D as IntrinsicFunctionResolver, Dt as withErrorHandling, E as isTerminationProtectionPropagationError, Et as normalizeAwsError, F as AssetPublisher, Ft as generateResourceName, G as getLegacyStateBucketName, H as runDockerStreaming, I as stringifyValue, It as generateResourceNameWithFallback, J as resolveSkipPrefix, K as resolveApp, L as WorkGraph, Lt as withSkipPrefix, M as LockManager, Mt as getLiveRenderer, N as S3StateBackend, Nt as PATTERN_B_NAME_PROPERTIES, O as applyRoleArnIfSet, P as shouldRetainResource, Pt as PATTERN_B_RESOURCE_TYPES, Q as CFN_TEMPLATE_BODY_LIMIT, R as buildDockerImage, Rt as withStackName, S as findActionableSilentDrops, T as disableInstanceApiTermination, U as Synthesizer, V as runDockerForeground, W as getDefaultStateBucketName, X as resolveStateBucketWithDefaultAndSource, Y as resolveStateBucketWithDefault, Z as warnDeprecatedNoPrefixCliFlag, _ as CDK_PATH_TAG, _t as ProvisioningError, a as withRetry, at as resolveBucketRegion, b as resolveExplicitPhysicalId, bt as StackHasActiveImportsError, c as formatResourceLine, d as gray, dt as LocalMigrateError, et as MIGRATE_TMP_PREFIX, f as green, ft as LocalStartServiceError, g as collectInlinePolicyNamesManagedBySiblings, gt as PartialFailureError, h as IAMRoleProvider, ht as NestedStackChildDirectDestroyError, i as withResourceDeadline, j as TemplateParser, jt as runStackBuffered, k as DiffCalculator, kt as getLogger, l as bold, m as yellow, mt as MissingCdkCliError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as uploadCfnTemplate, o as isRetryableTransientError, p as red, q as resolveCaptureObservedState, r as DeployEngine, rt as AssemblyReader, s as IMPLICIT_DELETE_DEPENDENCIES, st as CdkdError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as findLargeInlineResources, u as cyan, ut as LocalInvokeBuildError$1, v as matchesCdkPath, vt as ResourceTimeoutError, w as assertRegionMatch, x as ProviderRegistry, xt as StackTerminationProtectionError, y as normalizeAwsTagsToCfn, yt as ResourceUpdateNotSupportedError, z as formatDockerLoginError } from "./deploy-engine-B6CuzOHi.js";
2
+ import { A as withErrorHandling, B as generateResourceNameWithFallback, C as StackHasActiveImportsError, F as getLiveRenderer, H as withStackName, I as PATTERN_B_NAME_PROPERTIES, L as PATTERN_B_RESOURCE_TYPES, M as getLogger, P as runStackBuffered, S as ResourceUpdateNotSupportedError, V as withSkipPrefix, _ as MissingCdkCliError, a as assertRegionMatch, b as ProvisioningError, f as LocalInvokeBuildError$1, i as resolveExplicitPhysicalId, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as matchesCdkPath, o as disableInstanceApiTermination, p as LocalMigrateError, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, t as CDK_PATH_TAG, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError, z as generateResourceName } from "./import-helpers-wLipXr5g.js";
3
+ import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-pjPwZz1r.js";
4
+ import { A as WorkGraph, B as resolveCaptureObservedState, C as DagBuilder, D as shouldRetainResource, E as S3StateBackend, F as runDockerStreaming, G as CFN_TEMPLATE_BODY_LIMIT, H as resolveStateBucketWithDefault, I as Synthesizer, J as findLargeInlineResources, K as CFN_TEMPLATE_URL_LIMIT, L as getDefaultStateBucketName, M as formatDockerLoginError, N as getDockerCmd, O as AssetPublisher, P as runDockerForeground, Q as resolveBucketRegion, R as getLegacyStateBucketName, S as DiffCalculator, T as LockManager, U as resolveStateBucketWithDefaultAndSource, V as resolveSkipPrefix, W as warnDeprecatedNoPrefixCliFlag, X as AssemblyReader, Y as uploadCfnTemplate, _ as ProviderRegistry, a as withRetry, b as IntrinsicFunctionResolver, c as formatResourceLine, d as gray, f as green, g as collectInlinePolicyNamesManagedBySiblings, h as IAMRoleProvider, i as withResourceDeadline, j as buildDockerImage, k as stringifyValue, l as bold, m as yellow, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as red, q as MIGRATE_TMP_PREFIX, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as cyan, v as findActionableSilentDrops, w as TemplateParser, x as applyRoleArnIfSet, y as CloudControlProvider, z as resolveApp } from "./deploy-engine-Drw_e42s.js";
5
+ import { t as ASGProvider } from "./asg-provider-B_hrCxRx.js";
4
6
  import { AsyncLocalStorage } from "node:async_hooks";
5
7
  import { randomBytes, randomUUID } from "node:crypto";
6
8
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -59,7 +61,6 @@ import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailComman
59
61
  import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$10, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
60
62
  import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, ListVectorBucketsCommand, S3VectorsClient } from "@aws-sdk/client-s3vectors";
61
63
  import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand as ListTablesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$19, NotFoundException as NotFoundException$5, S3TablesClient, TagResourceCommand as TagResourceCommand$16, UntagResourceCommand as UntagResourceCommand$15 } from "@aws-sdk/client-s3tables";
62
- import { AttachLoadBalancerTargetGroupsCommand, AttachLoadBalancersCommand, AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, CreateOrUpdateTagsCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DeleteTagsCommand as DeleteTagsCommand$1, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachLoadBalancerTargetGroupsCommand, DetachLoadBalancersCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
63
64
  import { Document, Pair, Scalar, YAMLMap, YAMLSeq, parse as parse$1, stringify } from "yaml";
64
65
  import { createLocalStartAgentCoreCommand, createLocalStartCloudFrontCommand, createLocalStateProvider, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resolveCfnFallbackRegion, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
65
66
  import { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_MCP_PROTOCOL, ConnectionRegistry, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, MCP_CONTAINER_PORT, MCP_PATH, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addStartServiceSpecificOptions, albStrategy, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, classifySourceChange, createAuthorizerCache, createFileWatcher, createFileWatcher as createFileWatcher$1, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, discoverRoutes, discoverWebSocketApis, downloadAndExtractS3Bundle, filterRoutesByApiIdentifier, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, materializeLayerFromArn, mcpInvokeOnce, parseConnectionsPath, parseSelectionExpressionPath, pickAgentCoreCandidateStack, pickAgentCoreCandidateStack as pickAgentCoreCandidateStack$1, probeHostGatewaySupport, readMtlsMaterialsFromDisk, resolveAgentCoreTarget, resolveEnvVars, resolveHostGatewayExtraHosts, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, signAgentCoreInvocation, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin, verifyJwtViaDiscovery, waitForAgentCorePing } from "cdk-local/internal";
@@ -33664,757 +33665,6 @@ var ECRProvider = class {
33664
33665
  }
33665
33666
  };
33666
33667
 
33667
- //#endregion
33668
- //#region src/provisioning/providers/asg-provider.ts
33669
- /**
33670
- * AWS Auto Scaling Provider
33671
- *
33672
- * Implements resource provisioning for `AWS::AutoScaling::AutoScalingGroup`.
33673
- *
33674
- * WHY a dedicated SDK provider (instead of CC API fallback):
33675
- * 1. Owns the `--remove-protection` flip-off: ASG protection has three
33676
- * levels (`none` / `prevent-force-deletion` / `prevent-all-deletion`)
33677
- * and the destroy path needs to (a) clear it via `UpdateAutoScalingGroup
33678
- * ({DeletionProtection: 'none'})` before the actual delete and (b) set
33679
- * `ForceDelete: true` on `DeleteAutoScalingGroup` so AWS terminates any
33680
- * running instances as part of the delete (matches the user's "I know
33681
- * what I'm doing" intent).
33682
- * 2. Faster than CC API for the common case — direct Create/Update calls
33683
- * with no eventual-consistency polling beyond what `DescribeAutoScaling
33684
- * Groups` already provides.
33685
- *
33686
- * Update has narrower coverage than create: AWS does not support modifying
33687
- * `AutoScalingGroupName` (immutable) — that diff still surfaces
33688
- * `ResourceUpdateNotSupportedError` so the caller can `cdkd deploy
33689
- * --replace`. The mutable fields handled in-place via
33690
- * `UpdateAutoScalingGroup` include MinSize / MaxSize / DesiredCapacity /
33691
- * VPCZoneIdentifier / HealthCheckType / HealthCheckGracePeriod /
33692
- * DefaultCooldown / Cooldown / NewInstancesProtectedFromScaleIn /
33693
- * MaxInstanceLifetime / TerminationPolicies / CapacityRebalance /
33694
- * ServiceLinkedRoleARN / Context / DesiredCapacityType /
33695
- * DefaultInstanceWarmup / AvailabilityZones / AvailabilityZoneDistribution
33696
- * / AvailabilityZoneImpairmentPolicy / SkipZonalShiftValidation /
33697
- * CapacityReservationSpecification / InstanceMaintenancePolicy /
33698
- * DeletionProtection / MixedInstancesPolicy / LaunchTemplate.
33699
- *
33700
- * Sub-shape diffs are applied via dedicated AWS APIs before the main
33701
- * `UpdateAutoScalingGroup` call:
33702
- * - `Tags` → `CreateOrUpdateTags` / `DeleteTags` (#475)
33703
- * - `LoadBalancerNames` → `AttachLoadBalancers` /
33704
- * `DetachLoadBalancers` (#476)
33705
- * - `TargetGroupARNs` → `AttachLoadBalancerTargetGroups` /
33706
- * `DetachLoadBalancerTargetGroups` (#476)
33707
- * - `MetricsCollection` → `EnableMetricsCollection` /
33708
- * `DisableMetricsCollection`
33709
- * - `LifecycleHookSpecificationList` → per-entry `PutLifecycleHook` /
33710
- * `DeleteLifecycleHook`
33711
- * - `TrafficSources` → `AttachTrafficSources` /
33712
- * `DetachTrafficSources`
33713
- * - `NotificationConfigurations` → per-topic
33714
- * `PutNotificationConfiguration` /
33715
- * `DeleteNotificationConfiguration`
33716
- *
33717
- * Each helper is a no-op when the before/after JSON is identical.
33718
- */
33719
- var ASGProvider = class ASGProvider {
33720
- asgClient;
33721
- providerRegion = process.env["AWS_REGION"];
33722
- logger = getLogger().child("ASGProvider");
33723
- handledProperties = new Map([["AWS::AutoScaling::AutoScalingGroup", new Set([
33724
- "AutoScalingGroupName",
33725
- "LaunchTemplate",
33726
- "MinSize",
33727
- "MaxSize",
33728
- "DesiredCapacity",
33729
- "VPCZoneIdentifier",
33730
- "AvailabilityZones",
33731
- "HealthCheckType",
33732
- "HealthCheckGracePeriod",
33733
- "Cooldown",
33734
- "DefaultCooldown",
33735
- "Tags",
33736
- "TerminationPolicies",
33737
- "NewInstancesProtectedFromScaleIn",
33738
- "CapacityRebalance",
33739
- "ServiceLinkedRoleARN",
33740
- "MaxInstanceLifetime",
33741
- "LoadBalancerNames",
33742
- "TargetGroupARNs",
33743
- "MetricsCollection",
33744
- "LifecycleHookSpecificationList",
33745
- "MixedInstancesPolicy",
33746
- "Context",
33747
- "DesiredCapacityType",
33748
- "DefaultInstanceWarmup",
33749
- "TrafficSources",
33750
- "NotificationConfigurations",
33751
- "AvailabilityZoneDistribution",
33752
- "AvailabilityZoneImpairmentPolicy",
33753
- "SkipZonalShiftValidation",
33754
- "CapacityReservationSpecification",
33755
- "InstanceMaintenancePolicy",
33756
- "DeletionProtection"
33757
- ])]]);
33758
- unhandledByDesign = new Map([["AWS::AutoScaling::AutoScalingGroup", new Map([["LaunchConfigurationName", "AWS Launch Configurations end-of-life 2024-10; use LaunchTemplate instead"], ["NotificationConfiguration", "Legacy singular form; use NotificationConfigurations (plural) which cdkd already wires"]])]]);
33759
- getClient() {
33760
- if (!this.asgClient) this.asgClient = new AutoScalingClient(this.providerRegion ? { region: this.providerRegion } : {});
33761
- return this.asgClient;
33762
- }
33763
- async create(logicalId, resourceType, properties) {
33764
- if (resourceType !== "AWS::AutoScaling::AutoScalingGroup") throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
33765
- const groupName = properties["AutoScalingGroupName"] || generateResourceName(logicalId, { maxLength: 255 });
33766
- this.logger.debug(`Creating AutoScalingGroup ${logicalId}: ${groupName}`);
33767
- try {
33768
- const launchTemplate = this.buildLaunchTemplate(properties);
33769
- const tags = this.buildTags(groupName, properties);
33770
- const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties["VPCZoneIdentifier"]);
33771
- const minSize = properties["MinSize"] != null ? Number(properties["MinSize"]) : 0;
33772
- const maxSize = properties["MaxSize"] != null ? Number(properties["MaxSize"]) : minSize;
33773
- await this.getClient().send(new CreateAutoScalingGroupCommand({
33774
- AutoScalingGroupName: groupName,
33775
- MinSize: minSize,
33776
- MaxSize: maxSize,
33777
- ...properties["DesiredCapacity"] != null && { DesiredCapacity: Number(properties["DesiredCapacity"]) },
33778
- ...launchTemplate && { LaunchTemplate: launchTemplate },
33779
- ...properties["MixedInstancesPolicy"] !== void 0 && { MixedInstancesPolicy: properties["MixedInstancesPolicy"] },
33780
- ...vpcZoneIdentifier !== void 0 && { VPCZoneIdentifier: vpcZoneIdentifier },
33781
- ...properties["AvailabilityZones"] !== void 0 && { AvailabilityZones: properties["AvailabilityZones"] },
33782
- ...properties["HealthCheckType"] !== void 0 && { HealthCheckType: properties["HealthCheckType"] },
33783
- ...properties["HealthCheckGracePeriod"] != null && { HealthCheckGracePeriod: Number(properties["HealthCheckGracePeriod"]) },
33784
- ...properties["Cooldown"] != null && { DefaultCooldown: Number(properties["Cooldown"]) },
33785
- ...properties["DefaultCooldown"] != null && { DefaultCooldown: Number(properties["DefaultCooldown"]) },
33786
- ...properties["TerminationPolicies"] !== void 0 && { TerminationPolicies: properties["TerminationPolicies"] },
33787
- ...properties["NewInstancesProtectedFromScaleIn"] !== void 0 && { NewInstancesProtectedFromScaleIn: properties["NewInstancesProtectedFromScaleIn"] },
33788
- ...properties["CapacityRebalance"] !== void 0 && { CapacityRebalance: properties["CapacityRebalance"] },
33789
- ...properties["ServiceLinkedRoleARN"] !== void 0 && { ServiceLinkedRoleARN: properties["ServiceLinkedRoleARN"] },
33790
- ...properties["MaxInstanceLifetime"] != null && { MaxInstanceLifetime: Number(properties["MaxInstanceLifetime"]) },
33791
- ...properties["LoadBalancerNames"] !== void 0 && { LoadBalancerNames: properties["LoadBalancerNames"] },
33792
- ...properties["TargetGroupARNs"] !== void 0 && { TargetGroupARNs: properties["TargetGroupARNs"] },
33793
- ...properties["Context"] !== void 0 && { Context: properties["Context"] },
33794
- ...properties["DesiredCapacityType"] !== void 0 && { DesiredCapacityType: properties["DesiredCapacityType"] },
33795
- ...properties["DefaultInstanceWarmup"] != null && { DefaultInstanceWarmup: Number(properties["DefaultInstanceWarmup"]) },
33796
- ...properties["LifecycleHookSpecificationList"] !== void 0 && { LifecycleHookSpecificationList: properties["LifecycleHookSpecificationList"] },
33797
- ...properties["TrafficSources"] !== void 0 && { TrafficSources: properties["TrafficSources"] },
33798
- ...properties["AvailabilityZoneDistribution"] !== void 0 && { AvailabilityZoneDistribution: properties["AvailabilityZoneDistribution"] },
33799
- ...properties["AvailabilityZoneImpairmentPolicy"] !== void 0 && { AvailabilityZoneImpairmentPolicy: properties["AvailabilityZoneImpairmentPolicy"] },
33800
- ...properties["SkipZonalShiftValidation"] !== void 0 && { SkipZonalShiftValidation: properties["SkipZonalShiftValidation"] },
33801
- ...properties["CapacityReservationSpecification"] !== void 0 && { CapacityReservationSpecification: properties["CapacityReservationSpecification"] },
33802
- ...properties["InstanceMaintenancePolicy"] !== void 0 && { InstanceMaintenancePolicy: properties["InstanceMaintenancePolicy"] },
33803
- ...properties["DeletionProtection"] !== void 0 && { DeletionProtection: properties["DeletionProtection"] },
33804
- ...tags.length > 0 && { Tags: tags }
33805
- }));
33806
- this.logger.debug(`Successfully created AutoScalingGroup ${logicalId}: ${groupName}`);
33807
- const arn = await this.fetchArn(groupName);
33808
- const attributes = {};
33809
- if (arn) attributes["Arn"] = arn;
33810
- if (launchTemplate?.LaunchTemplateId) attributes["LaunchTemplateID"] = launchTemplate.LaunchTemplateId;
33811
- return {
33812
- physicalId: groupName,
33813
- attributes
33814
- };
33815
- } catch (error) {
33816
- const cause = error instanceof Error ? error : void 0;
33817
- throw new ProvisioningError(`Failed to create AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, groupName, cause);
33818
- }
33819
- }
33820
- async update(logicalId, physicalId, resourceType, properties, previousProperties) {
33821
- if (resourceType !== "AWS::AutoScaling::AutoScalingGroup") throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
33822
- this.logger.debug(`Updating AutoScalingGroup ${logicalId}: ${physicalId}`);
33823
- const stringEq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
33824
- if (!stringEq(properties["AutoScalingGroupName"], previousProperties["AutoScalingGroupName"])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AutoScalingGroupName is immutable on AWS — UpdateAutoScalingGroup does not accept a new name; the name is fixed at creation. Use cdkd deploy --replace to replace the group.");
33825
- try {
33826
- await this.applyTagsDiff(physicalId, properties["Tags"], previousProperties["Tags"]);
33827
- await this.applyLoadBalancerNamesDiff(physicalId, properties["LoadBalancerNames"], previousProperties["LoadBalancerNames"]);
33828
- await this.applyTargetGroupArnsDiff(physicalId, properties["TargetGroupARNs"], previousProperties["TargetGroupARNs"]);
33829
- await this.applyMetricsCollectionDiff(physicalId, properties["MetricsCollection"], previousProperties["MetricsCollection"]);
33830
- await this.applyLifecycleHooksDiff(physicalId, properties["LifecycleHookSpecificationList"], previousProperties["LifecycleHookSpecificationList"]);
33831
- await this.applyTrafficSourcesDiff(physicalId, properties["TrafficSources"], previousProperties["TrafficSources"]);
33832
- await this.applyNotificationConfigurationsDiff(physicalId, properties["NotificationConfigurations"], previousProperties["NotificationConfigurations"]);
33833
- const launchTemplate = this.buildLaunchTemplate(properties);
33834
- const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties["VPCZoneIdentifier"]);
33835
- await this.getClient().send(new UpdateAutoScalingGroupCommand({
33836
- AutoScalingGroupName: physicalId,
33837
- ...properties["MinSize"] != null && { MinSize: Number(properties["MinSize"]) },
33838
- ...properties["MaxSize"] != null && { MaxSize: Number(properties["MaxSize"]) },
33839
- ...properties["DesiredCapacity"] != null && { DesiredCapacity: Number(properties["DesiredCapacity"]) },
33840
- ...launchTemplate && { LaunchTemplate: launchTemplate },
33841
- ...properties["MixedInstancesPolicy"] !== void 0 && { MixedInstancesPolicy: properties["MixedInstancesPolicy"] },
33842
- ...vpcZoneIdentifier !== void 0 && { VPCZoneIdentifier: vpcZoneIdentifier },
33843
- ...properties["AvailabilityZones"] !== void 0 && { AvailabilityZones: properties["AvailabilityZones"] },
33844
- ...properties["HealthCheckType"] !== void 0 && { HealthCheckType: properties["HealthCheckType"] },
33845
- ...properties["HealthCheckGracePeriod"] != null && { HealthCheckGracePeriod: Number(properties["HealthCheckGracePeriod"]) },
33846
- ...properties["Cooldown"] != null && { DefaultCooldown: Number(properties["Cooldown"]) },
33847
- ...properties["DefaultCooldown"] != null && { DefaultCooldown: Number(properties["DefaultCooldown"]) },
33848
- ...properties["TerminationPolicies"] !== void 0 && { TerminationPolicies: properties["TerminationPolicies"] },
33849
- ...properties["NewInstancesProtectedFromScaleIn"] !== void 0 && { NewInstancesProtectedFromScaleIn: properties["NewInstancesProtectedFromScaleIn"] },
33850
- ...properties["CapacityRebalance"] !== void 0 && { CapacityRebalance: properties["CapacityRebalance"] },
33851
- ...properties["ServiceLinkedRoleARN"] !== void 0 && { ServiceLinkedRoleARN: properties["ServiceLinkedRoleARN"] },
33852
- ...properties["MaxInstanceLifetime"] != null && { MaxInstanceLifetime: Number(properties["MaxInstanceLifetime"]) },
33853
- ...properties["Context"] !== void 0 && { Context: properties["Context"] },
33854
- ...properties["DesiredCapacityType"] !== void 0 && { DesiredCapacityType: properties["DesiredCapacityType"] },
33855
- ...properties["DefaultInstanceWarmup"] != null && { DefaultInstanceWarmup: Number(properties["DefaultInstanceWarmup"]) },
33856
- ...properties["AvailabilityZoneDistribution"] !== void 0 && { AvailabilityZoneDistribution: properties["AvailabilityZoneDistribution"] },
33857
- ...properties["AvailabilityZoneImpairmentPolicy"] !== void 0 && { AvailabilityZoneImpairmentPolicy: properties["AvailabilityZoneImpairmentPolicy"] },
33858
- ...properties["SkipZonalShiftValidation"] !== void 0 && { SkipZonalShiftValidation: properties["SkipZonalShiftValidation"] },
33859
- ...properties["CapacityReservationSpecification"] !== void 0 && { CapacityReservationSpecification: properties["CapacityReservationSpecification"] },
33860
- ...properties["InstanceMaintenancePolicy"] !== void 0 && { InstanceMaintenancePolicy: properties["InstanceMaintenancePolicy"] },
33861
- ...properties["DeletionProtection"] !== void 0 && { DeletionProtection: properties["DeletionProtection"] }
33862
- }));
33863
- this.logger.debug(`Successfully updated AutoScalingGroup ${logicalId}`);
33864
- const arn = await this.fetchArn(physicalId);
33865
- const attributes = {};
33866
- if (arn) attributes["Arn"] = arn;
33867
- if (launchTemplate?.LaunchTemplateId) attributes["LaunchTemplateID"] = launchTemplate.LaunchTemplateId;
33868
- return {
33869
- physicalId,
33870
- wasReplaced: false,
33871
- attributes
33872
- };
33873
- } catch (error) {
33874
- if (error instanceof ResourceUpdateNotSupportedError) throw error;
33875
- const cause = error instanceof Error ? error : void 0;
33876
- throw new ProvisioningError(`Failed to update AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
33877
- }
33878
- }
33879
- async delete(logicalId, physicalId, resourceType, _properties, context) {
33880
- this.logger.debug(`Deleting AutoScalingGroup ${logicalId}: ${physicalId}`);
33881
- if (context?.removeProtection === true) try {
33882
- await this.getClient().send(new UpdateAutoScalingGroupCommand({
33883
- AutoScalingGroupName: physicalId,
33884
- DeletionProtection: "none"
33885
- }));
33886
- this.logger.debug(`Disabled DeletionProtection on AutoScalingGroup ${logicalId} before delete`);
33887
- } catch (flipError) {
33888
- this.logger.debug(`Could not disable DeletionProtection on ${physicalId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`);
33889
- }
33890
- try {
33891
- await this.getClient().send(new DeleteAutoScalingGroupCommand({
33892
- AutoScalingGroupName: physicalId,
33893
- ForceDelete: context?.removeProtection === true
33894
- }));
33895
- this.logger.debug(`Successfully initiated deletion of AutoScalingGroup ${logicalId}`);
33896
- await this.waitForGroupDeleted(physicalId);
33897
- } catch (error) {
33898
- if (this.isNotFoundError(error)) {
33899
- assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
33900
- this.logger.debug(`AutoScalingGroup ${physicalId} does not exist, skipping deletion`);
33901
- return;
33902
- }
33903
- const cause = error instanceof Error ? error : void 0;
33904
- throw new ProvisioningError(`Failed to delete AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
33905
- }
33906
- }
33907
- async getAttribute(physicalId, _resourceType, attributeName) {
33908
- const group = await this.describeGroup(physicalId);
33909
- if (!group) throw new ProvisioningError(`AutoScalingGroup ${physicalId} not found while resolving attribute ${attributeName}`, "AWS::AutoScaling::AutoScalingGroup", physicalId, physicalId);
33910
- switch (attributeName) {
33911
- case "Arn":
33912
- case "AutoScalingGroupARN": return group.AutoScalingGroupARN ?? "";
33913
- case "LaunchConfigurationName": return group.LaunchConfigurationName ?? "";
33914
- case "LaunchTemplateID":
33915
- case "LaunchTemplateId": return group.LaunchTemplate?.LaunchTemplateId ?? "";
33916
- default: return "";
33917
- }
33918
- }
33919
- /**
33920
- * Read the AWS-current AutoScalingGroup configuration in CFn-property shape.
33921
- *
33922
- * Surfaces the user-controllable subset of `DescribeAutoScalingGroups`,
33923
- * with always-emit placeholders on user-controllable top-level keys per
33924
- * the cdkd PR #145 always-emit convention so that v3 `observedProperties`
33925
- * baseline catches console-side ADDs to fields a clean deploy did not
33926
- * template (e.g. a console-set `DeletionProtection: 'prevent-force-deletion'`
33927
- * on a group originally created without it).
33928
- *
33929
- * Sub-shapes (LifecycleHookSpecificationList / TrafficSources /
33930
- * NotificationConfigurations) are surfaced via three parallel Describe
33931
- * calls fired alongside the primary `DescribeAutoScalingGroups`. Each is
33932
- * best-effort: a per-call failure (e.g. permissions gap on
33933
- * `autoscaling:DescribeLifecycleHooks`) is logged at debug and the
33934
- * matching key falls back to its always-emit `[]` placeholder rather
33935
- * than aborting the whole drift read.
33936
- *
33937
- * `MetricsCollection` is reverse-mapped from `EnabledMetrics` (already
33938
- * present on the primary `DescribeAutoScalingGroups` response, so no
33939
- * extra call is needed).
33940
- *
33941
- * Returns `undefined` when the group is gone.
33942
- */
33943
- async readCurrentState(physicalId, _logicalId, _resourceType) {
33944
- const groupPromise = (async () => {
33945
- try {
33946
- return await this.describeGroup(physicalId);
33947
- } catch (err) {
33948
- if (this.isNotFoundError(err)) return void 0;
33949
- throw err;
33950
- }
33951
- })();
33952
- const lifecycleHooksPromise = this.getClient().send(new DescribeLifecycleHooksCommand({ AutoScalingGroupName: physicalId })).then((r) => r.LifecycleHooks ?? []).catch((err) => {
33953
- this.logger.debug(`DescribeLifecycleHooks(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
33954
- return [];
33955
- });
33956
- const trafficSourcesPromise = this.getClient().send(new DescribeTrafficSourcesCommand({ AutoScalingGroupName: physicalId })).then((r) => r.TrafficSources ?? []).catch((err) => {
33957
- this.logger.debug(`DescribeTrafficSources(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
33958
- return [];
33959
- });
33960
- const notificationsPromise = this.getClient().send(new DescribeNotificationConfigurationsCommand({ AutoScalingGroupNames: [physicalId] })).then((r) => r.NotificationConfigurations ?? []).catch((err) => {
33961
- this.logger.debug(`DescribeNotificationConfigurations(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
33962
- return [];
33963
- });
33964
- const [group, lifecycleHooks, trafficSources, notifications] = await Promise.all([
33965
- groupPromise,
33966
- lifecycleHooksPromise,
33967
- trafficSourcesPromise,
33968
- notificationsPromise
33969
- ]);
33970
- if (!group) return void 0;
33971
- const result = {};
33972
- if (group.AutoScalingGroupName !== void 0) result["AutoScalingGroupName"] = group.AutoScalingGroupName;
33973
- if (group.LaunchTemplate) {
33974
- const lt = {};
33975
- if (group.LaunchTemplate.LaunchTemplateId !== void 0) lt["LaunchTemplateId"] = group.LaunchTemplate.LaunchTemplateId;
33976
- if (group.LaunchTemplate.LaunchTemplateName !== void 0) lt["LaunchTemplateName"] = group.LaunchTemplate.LaunchTemplateName;
33977
- if (group.LaunchTemplate.Version !== void 0) lt["Version"] = group.LaunchTemplate.Version;
33978
- result["LaunchTemplate"] = lt;
33979
- }
33980
- result["MinSize"] = group.MinSize ?? 0;
33981
- result["MaxSize"] = group.MaxSize ?? 0;
33982
- if (group.DesiredCapacity !== void 0) result["DesiredCapacity"] = group.DesiredCapacity;
33983
- if (group.VPCZoneIdentifier !== void 0 && group.VPCZoneIdentifier !== "") result["VPCZoneIdentifier"] = group.VPCZoneIdentifier.split(",").map((s) => s.trim());
33984
- else result["VPCZoneIdentifier"] = [];
33985
- result["AvailabilityZones"] = group.AvailabilityZones ?? [];
33986
- if (group.HealthCheckType !== void 0) result["HealthCheckType"] = group.HealthCheckType;
33987
- if (group.HealthCheckGracePeriod !== void 0) result["HealthCheckGracePeriod"] = group.HealthCheckGracePeriod;
33988
- if (group.DefaultCooldown !== void 0) result["Cooldown"] = group.DefaultCooldown;
33989
- result["NewInstancesProtectedFromScaleIn"] = group.NewInstancesProtectedFromScaleIn ?? false;
33990
- result["TerminationPolicies"] = group.TerminationPolicies ?? [];
33991
- result["CapacityRebalance"] = group.CapacityRebalance ?? false;
33992
- if (group.ServiceLinkedRoleARN !== void 0) result["ServiceLinkedRoleARN"] = group.ServiceLinkedRoleARN;
33993
- if (group.MaxInstanceLifetime !== void 0) result["MaxInstanceLifetime"] = group.MaxInstanceLifetime;
33994
- result["LoadBalancerNames"] = group.LoadBalancerNames ?? [];
33995
- result["TargetGroupARNs"] = group.TargetGroupARNs ?? [];
33996
- if (group.Context !== void 0) result["Context"] = group.Context;
33997
- if (group.DesiredCapacityType !== void 0) result["DesiredCapacityType"] = group.DesiredCapacityType;
33998
- if (group.DefaultInstanceWarmup !== void 0) result["DefaultInstanceWarmup"] = group.DefaultInstanceWarmup;
33999
- if (group.MixedInstancesPolicy !== void 0) result["MixedInstancesPolicy"] = group.MixedInstancesPolicy;
34000
- if (group.AvailabilityZoneDistribution !== void 0) result["AvailabilityZoneDistribution"] = group.AvailabilityZoneDistribution;
34001
- if (group.AvailabilityZoneImpairmentPolicy !== void 0) result["AvailabilityZoneImpairmentPolicy"] = group.AvailabilityZoneImpairmentPolicy;
34002
- if (group.CapacityReservationSpecification !== void 0) result["CapacityReservationSpecification"] = group.CapacityReservationSpecification;
34003
- if (group.InstanceMaintenancePolicy !== void 0) result["InstanceMaintenancePolicy"] = group.InstanceMaintenancePolicy;
34004
- if (group.DeletionProtection !== void 0) result["DeletionProtection"] = group.DeletionProtection;
34005
- else result["DeletionProtection"] = "none";
34006
- result["Tags"] = normalizeAwsTagsToCfn(group.Tags);
34007
- result["MetricsCollection"] = mapEnabledMetricsToCfn(group.EnabledMetrics);
34008
- result["LifecycleHookSpecificationList"] = mapLifecycleHooksToCfn(lifecycleHooks);
34009
- result["TrafficSources"] = mapTrafficSourcesToCfn(trafficSources.filter((t) => {
34010
- if (t.Identifier === void 0) return false;
34011
- if (t.Type === "elbv2" || t.Type === "elb") return false;
34012
- return true;
34013
- }));
34014
- result["NotificationConfigurations"] = mapNotificationsToCfn(notifications);
34015
- return result;
34016
- }
34017
- buildLaunchTemplate(properties) {
34018
- const lt = properties["LaunchTemplate"];
34019
- if (!lt) return void 0;
34020
- const out = {};
34021
- if (lt.LaunchTemplateId !== void 0) {
34022
- out.LaunchTemplateId = lt.LaunchTemplateId;
34023
- if (lt.LaunchTemplateName !== void 0) this.logger.debug(`buildLaunchTemplate: both LaunchTemplateId (${lt.LaunchTemplateId}) and LaunchTemplateName (${lt.LaunchTemplateName}) templated; dropping Name (#551)`);
34024
- } else if (lt.LaunchTemplateName !== void 0) out.LaunchTemplateName = lt.LaunchTemplateName;
34025
- if (lt.Version !== void 0) out.Version = String(lt.Version);
34026
- if (out.LaunchTemplateId === void 0 && out.LaunchTemplateName === void 0) return;
34027
- return out;
34028
- }
34029
- /**
34030
- * CFn `Tags` is `[{Key, Value, PropagateAtLaunch?}]`. AWS expects each
34031
- * tag to also carry `ResourceId: <groupName>` and `ResourceType:
34032
- * 'auto-scaling-group'`. We tack those on at create time so the SDK
34033
- * input shape matches without forcing the user to template them.
34034
- */
34035
- buildTags(groupName, properties) {
34036
- const raw = properties["Tags"];
34037
- if (!raw) return [];
34038
- return raw.filter((t) => t.Key !== void 0).map((t) => ({
34039
- ResourceId: groupName,
34040
- ResourceType: "auto-scaling-group",
34041
- Key: t.Key,
34042
- Value: t.Value ?? "",
34043
- PropagateAtLaunch: t.PropagateAtLaunch ?? false
34044
- }));
34045
- }
34046
- /**
34047
- * CFn `VPCZoneIdentifier` is a list of subnet ids; the AWS SDK input
34048
- * field is a comma-joined string.
34049
- */
34050
- joinVpcZoneIdentifier(value) {
34051
- if (value === void 0 || value === null) return void 0;
34052
- if (Array.isArray(value)) {
34053
- const cleaned = value.map((v) => String(v).trim()).filter((v) => v.length > 0);
34054
- if (cleaned.length === 0) return void 0;
34055
- return cleaned.join(",");
34056
- }
34057
- if (typeof value === "string") return value;
34058
- }
34059
- async describeGroup(groupName) {
34060
- return (await this.getClient().send(new DescribeAutoScalingGroupsCommand({ AutoScalingGroupNames: [groupName] }))).AutoScalingGroups?.[0];
34061
- }
34062
- async fetchArn(groupName) {
34063
- try {
34064
- return (await this.describeGroup(groupName))?.AutoScalingGroupARN;
34065
- } catch (err) {
34066
- this.logger.debug(`DescribeAutoScalingGroups(${groupName}) failed: ${err instanceof Error ? err.message : String(err)}`);
34067
- return;
34068
- }
34069
- }
34070
- isNotFoundError(error) {
34071
- if (!(error instanceof Error)) return false;
34072
- const name = error.name ?? "";
34073
- const message = error.message.toLowerCase();
34074
- return name === "ValidationError" && (message.includes("autoscalinggroup name not found") || message.includes("not found") || message.includes("does not exist"));
34075
- }
34076
- async waitForGroupDeleted(groupName, maxWaitMs = 9e5) {
34077
- const startTime = Date.now();
34078
- let delay = 5e3;
34079
- while (Date.now() - startTime < maxWaitMs) {
34080
- try {
34081
- if (!await this.describeGroup(groupName)) return;
34082
- } catch (error) {
34083
- if (this.isNotFoundError(error)) return;
34084
- throw error;
34085
- }
34086
- await this.sleep(delay);
34087
- delay = Math.min(delay * 2, 3e4);
34088
- }
34089
- throw new Error(`Timed out waiting for AutoScalingGroup ${groupName} to be deleted (15 minute cap)`);
34090
- }
34091
- sleep(ms) {
34092
- return new Promise((resolve) => setTimeout(resolve, ms));
34093
- }
34094
- /**
34095
- * Diff and apply changes to the ASG's `Tags` property via the
34096
- * `CreateOrUpdateTags` / `DeleteTags` AWS APIs (#475). CFn Tags shape is
34097
- * `[{Key, Value, PropagateAtLaunch}]`; AWS Tag input adds `ResourceId`
34098
- * (= the ASG name) and `ResourceType: 'auto-scaling-group'`.
34099
- *
34100
- * Diff semantics:
34101
- * - Removed keys → `DeleteTags`.
34102
- * - Added keys → `CreateOrUpdateTags`.
34103
- * - Modified value or `PropagateAtLaunch` flag → `CreateOrUpdateTags`
34104
- * (the AWS API upserts by `(ResourceId, ResourceType, Key)` tuple, so
34105
- * a single upsert call replaces the old value).
34106
- *
34107
- * No-op when before/after JSON is identical.
34108
- */
34109
- async applyTagsDiff(physicalId, next, prev) {
34110
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34111
- const nextEntries = Array.isArray(next) ? next : [];
34112
- const prevEntries = Array.isArray(prev) ? prev : [];
34113
- const nextByKey = /* @__PURE__ */ new Map();
34114
- for (const t of nextEntries) if (t.Key) nextByKey.set(t.Key, t);
34115
- const prevByKey = /* @__PURE__ */ new Map();
34116
- for (const t of prevEntries) if (t.Key) prevByKey.set(t.Key, t);
34117
- const toDelete = [];
34118
- for (const [key, tag] of prevByKey) if (!nextByKey.has(key)) toDelete.push(tag);
34119
- if (toDelete.length > 0) await this.getClient().send(new DeleteTagsCommand$1({ Tags: toDelete.map((t) => ({
34120
- ResourceId: physicalId,
34121
- ResourceType: "auto-scaling-group",
34122
- Key: t.Key
34123
- })) }));
34124
- const toUpsert = [];
34125
- for (const [key, tag] of nextByKey) {
34126
- const before = prevByKey.get(key);
34127
- if (JSON.stringify(before) === JSON.stringify(tag)) continue;
34128
- toUpsert.push(tag);
34129
- }
34130
- if (toUpsert.length > 0) await this.getClient().send(new CreateOrUpdateTagsCommand({ Tags: toUpsert.map((t) => ({
34131
- ResourceId: physicalId,
34132
- ResourceType: "auto-scaling-group",
34133
- Key: t.Key,
34134
- ...t.Value !== void 0 && { Value: t.Value },
34135
- ...t.PropagateAtLaunch !== void 0 && { PropagateAtLaunch: t.PropagateAtLaunch }
34136
- })) }));
34137
- }
34138
- /**
34139
- * Diff `LoadBalancerNames` (Classic Load Balancers) and issue
34140
- * `AttachLoadBalancers` / `DetachLoadBalancers` for the delta (#476).
34141
- * Names are opaque strings; AWS allows N attached LBs per ASG so this
34142
- * helper batches every add into one Attach call and every remove into
34143
- * one Detach call. No-op when before/after JSON is identical.
34144
- */
34145
- async applyLoadBalancerNamesDiff(physicalId, next, prev) {
34146
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34147
- const nextNames = (Array.isArray(next) ? next : []).filter((n) => typeof n === "string");
34148
- const prevNames = (Array.isArray(prev) ? prev : []).filter((n) => typeof n === "string");
34149
- const nextSet = new Set(nextNames);
34150
- const prevSet = new Set(prevNames);
34151
- const toAttach = nextNames.filter((n) => !prevSet.has(n));
34152
- const toDetach = prevNames.filter((n) => !nextSet.has(n));
34153
- if (toDetach.length > 0) await this.getClient().send(new DetachLoadBalancersCommand({
34154
- AutoScalingGroupName: physicalId,
34155
- LoadBalancerNames: toDetach
34156
- }));
34157
- if (toAttach.length > 0) await this.getClient().send(new AttachLoadBalancersCommand({
34158
- AutoScalingGroupName: physicalId,
34159
- LoadBalancerNames: toAttach
34160
- }));
34161
- }
34162
- /**
34163
- * Diff `TargetGroupARNs` (ALB / NLB target groups) and issue
34164
- * `AttachLoadBalancerTargetGroups` /
34165
- * `DetachLoadBalancerTargetGroups` for the delta (#476). Target-group
34166
- * ARNs are opaque strings; same per-call batching pattern as
34167
- * `applyLoadBalancerNamesDiff`. No-op when before/after JSON is
34168
- * identical.
34169
- */
34170
- async applyTargetGroupArnsDiff(physicalId, next, prev) {
34171
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34172
- const nextArns = (Array.isArray(next) ? next : []).filter((a) => typeof a === "string");
34173
- const prevArns = (Array.isArray(prev) ? prev : []).filter((a) => typeof a === "string");
34174
- const nextSet = new Set(nextArns);
34175
- const prevSet = new Set(prevArns);
34176
- const toAttach = nextArns.filter((a) => !prevSet.has(a));
34177
- const toDetach = prevArns.filter((a) => !nextSet.has(a));
34178
- if (toDetach.length > 0) await this.getClient().send(new DetachLoadBalancerTargetGroupsCommand({
34179
- AutoScalingGroupName: physicalId,
34180
- TargetGroupARNs: toDetach
34181
- }));
34182
- if (toAttach.length > 0) await this.getClient().send(new AttachLoadBalancerTargetGroupsCommand({
34183
- AutoScalingGroupName: physicalId,
34184
- TargetGroupARNs: toAttach
34185
- }));
34186
- if (toDetach.length > 0 || toAttach.length > 0) await this.waitForTargetGroupArnsConvergence(physicalId, new Set(nextArns));
34187
- }
34188
- static TG_CONVERGENCE_TIMEOUT_MS = 3e4;
34189
- static TG_CONVERGENCE_POLL_INTERVAL_MS = 1e3;
34190
- async waitForTargetGroupArnsConvergence(physicalId, expected) {
34191
- const deadlineMs = Date.now() + ASGProvider.TG_CONVERGENCE_TIMEOUT_MS;
34192
- let lastObserved = /* @__PURE__ */ new Set();
34193
- while (Date.now() < deadlineMs) {
34194
- let resp;
34195
- try {
34196
- resp = await this.getClient().send(new DescribeAutoScalingGroupsCommand({ AutoScalingGroupNames: [physicalId] }));
34197
- } catch (err) {
34198
- this.logger.debug(`applyTargetGroupArnsDiff convergence poll: transient error, retrying — ${err instanceof Error ? err.message : String(err)}`);
34199
- await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));
34200
- continue;
34201
- }
34202
- lastObserved = new Set(resp.AutoScalingGroups?.[0]?.TargetGroupARNs ?? []);
34203
- if (lastObserved.size === expected.size && [...expected].every((a) => lastObserved.has(a))) return;
34204
- await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));
34205
- }
34206
- const expectedSorted = [...expected].sort();
34207
- const observedSorted = [...lastObserved].sort();
34208
- this.logger.warn(`applyTargetGroupArnsDiff: TG set did not converge within ${ASGProvider.TG_CONVERGENCE_TIMEOUT_MS}ms for ASG ${physicalId}. expected=${JSON.stringify(expectedSorted)} observed=${JSON.stringify(observedSorted)}`);
34209
- }
34210
- async applyMetricsCollectionDiff(physicalId, next, prev) {
34211
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34212
- const nextEntries = Array.isArray(next) ? next : [];
34213
- const prevEntries = Array.isArray(prev) ? prev : [];
34214
- const prevByGranularity = /* @__PURE__ */ new Map();
34215
- for (const e of prevEntries) if (e.Granularity) prevByGranularity.set(e.Granularity, e.Metrics);
34216
- const nextByGranularity = /* @__PURE__ */ new Map();
34217
- for (const e of nextEntries) if (e.Granularity) nextByGranularity.set(e.Granularity, e.Metrics);
34218
- for (const [granularity, metrics] of prevByGranularity) if (!nextByGranularity.has(granularity)) await this.getClient().send(new DisableMetricsCollectionCommand({
34219
- AutoScalingGroupName: physicalId,
34220
- ...metrics && metrics.length > 0 ? { Metrics: metrics } : {}
34221
- }));
34222
- for (const [granularity, metrics] of nextByGranularity) {
34223
- const before = prevByGranularity.get(granularity);
34224
- if (JSON.stringify(before ?? null) === JSON.stringify(metrics ?? null)) continue;
34225
- if (before && before.length > 0) {
34226
- const removed = metrics ? before.filter((m) => !metrics.includes(m)) : [];
34227
- if (removed.length > 0) await this.getClient().send(new DisableMetricsCollectionCommand({
34228
- AutoScalingGroupName: physicalId,
34229
- Metrics: removed
34230
- }));
34231
- }
34232
- await this.getClient().send(new EnableMetricsCollectionCommand({
34233
- AutoScalingGroupName: physicalId,
34234
- Granularity: granularity,
34235
- ...metrics && metrics.length > 0 ? { Metrics: metrics } : {}
34236
- }));
34237
- }
34238
- }
34239
- async applyLifecycleHooksDiff(physicalId, next, prev) {
34240
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34241
- const nextEntries = Array.isArray(next) ? next : [];
34242
- const prevEntries = Array.isArray(prev) ? prev : [];
34243
- const nextNames = new Set(nextEntries.map((e) => e.LifecycleHookName).filter((n) => !!n));
34244
- for (const e of prevEntries) if (e.LifecycleHookName && !nextNames.has(e.LifecycleHookName)) await this.getClient().send(new DeleteLifecycleHookCommand({
34245
- AutoScalingGroupName: physicalId,
34246
- LifecycleHookName: e.LifecycleHookName
34247
- }));
34248
- const prevByName = /* @__PURE__ */ new Map();
34249
- for (const e of prevEntries) if (e.LifecycleHookName) prevByName.set(e.LifecycleHookName, e);
34250
- for (const e of nextEntries) {
34251
- if (!e.LifecycleHookName) continue;
34252
- const prevHook = prevByName.get(e.LifecycleHookName);
34253
- if (JSON.stringify(prevHook) === JSON.stringify(e)) continue;
34254
- await this.getClient().send(new PutLifecycleHookCommand({
34255
- AutoScalingGroupName: physicalId,
34256
- LifecycleHookName: e.LifecycleHookName,
34257
- ...e.LifecycleTransition !== void 0 && { LifecycleTransition: e.LifecycleTransition },
34258
- ...e.RoleARN !== void 0 && { RoleARN: e.RoleARN },
34259
- ...e.NotificationTargetARN !== void 0 && { NotificationTargetARN: e.NotificationTargetARN },
34260
- ...e.NotificationMetadata !== void 0 && { NotificationMetadata: e.NotificationMetadata },
34261
- ...e.HeartbeatTimeout !== void 0 && { HeartbeatTimeout: e.HeartbeatTimeout },
34262
- ...e.DefaultResult !== void 0 && { DefaultResult: e.DefaultResult }
34263
- }));
34264
- }
34265
- }
34266
- async applyTrafficSourcesDiff(physicalId, next, prev) {
34267
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34268
- const nextEntries = Array.isArray(next) ? next : [];
34269
- const prevEntries = Array.isArray(prev) ? prev : [];
34270
- const nextIds = new Set(nextEntries.map((e) => e.Identifier).filter((i) => !!i));
34271
- const prevIds = new Set(prevEntries.map((e) => e.Identifier).filter((i) => !!i));
34272
- const toDetach = prevEntries.filter((e) => e.Identifier && !nextIds.has(e.Identifier));
34273
- const toAttach = nextEntries.filter((e) => e.Identifier && !prevIds.has(e.Identifier));
34274
- if (toDetach.length > 0) await this.getClient().send(new DetachTrafficSourcesCommand({
34275
- AutoScalingGroupName: physicalId,
34276
- TrafficSources: toDetach.map((e) => ({
34277
- Identifier: e.Identifier,
34278
- ...e.Type !== void 0 && { Type: e.Type }
34279
- }))
34280
- }));
34281
- if (toAttach.length > 0) await this.getClient().send(new AttachTrafficSourcesCommand({
34282
- AutoScalingGroupName: physicalId,
34283
- TrafficSources: toAttach.map((e) => ({
34284
- Identifier: e.Identifier,
34285
- ...e.Type !== void 0 && { Type: e.Type }
34286
- }))
34287
- }));
34288
- }
34289
- async applyNotificationConfigurationsDiff(physicalId, next, prev) {
34290
- if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
34291
- const nextEntries = Array.isArray(next) ? next : [];
34292
- const prevEntries = Array.isArray(prev) ? prev : [];
34293
- const nextByTopic = /* @__PURE__ */ new Map();
34294
- for (const e of nextEntries) if (e.TopicARN) nextByTopic.set(e.TopicARN, e.NotificationTypes);
34295
- const prevByTopic = /* @__PURE__ */ new Map();
34296
- for (const e of prevEntries) if (e.TopicARN) prevByTopic.set(e.TopicARN, e.NotificationTypes);
34297
- for (const topic of prevByTopic.keys()) if (!nextByTopic.has(topic)) await this.getClient().send(new DeleteNotificationConfigurationCommand({
34298
- AutoScalingGroupName: physicalId,
34299
- TopicARN: topic
34300
- }));
34301
- for (const [topic, types] of nextByTopic) {
34302
- const before = prevByTopic.get(topic);
34303
- if (JSON.stringify(before ?? null) === JSON.stringify(types ?? null)) continue;
34304
- await this.getClient().send(new PutNotificationConfigurationCommand({
34305
- AutoScalingGroupName: physicalId,
34306
- TopicARN: topic,
34307
- NotificationTypes: types ?? []
34308
- }));
34309
- }
34310
- }
34311
- };
34312
- /**
34313
- * Reverse-map AWS `EnabledMetrics: [{Metric, Granularity}]` (flat list,
34314
- * one row per enabled metric) back to the CFn array shape
34315
- * `[{Granularity, Metrics?[]}]`. Metrics with the same Granularity are
34316
- * grouped together; the resulting Metrics list is sorted alphabetically
34317
- * for stable positional compare in the drift comparator.
34318
- *
34319
- * Always returns a placeholder `[]` per the cdkd PR #145 always-emit
34320
- * convention so a console-side EnableMetricsCollection on a previously-
34321
- * empty group surfaces as drift on the v3 `observedProperties` baseline.
34322
- */
34323
- function mapEnabledMetricsToCfn(enabledMetrics) {
34324
- if (!enabledMetrics || enabledMetrics.length === 0) return [];
34325
- const byGranularity = /* @__PURE__ */ new Map();
34326
- for (const e of enabledMetrics) {
34327
- const g = e.Granularity;
34328
- if (!g) continue;
34329
- let set = byGranularity.get(g);
34330
- if (!set) {
34331
- set = /* @__PURE__ */ new Set();
34332
- byGranularity.set(g, set);
34333
- }
34334
- if (e.Metric) set.add(e.Metric);
34335
- }
34336
- const result = [];
34337
- for (const granularity of Array.from(byGranularity.keys()).sort()) {
34338
- const metrics = Array.from(byGranularity.get(granularity) ?? []).sort();
34339
- result.push(metrics.length > 0 ? {
34340
- Granularity: granularity,
34341
- Metrics: metrics
34342
- } : { Granularity: granularity });
34343
- }
34344
- return result;
34345
- }
34346
- /**
34347
- * Reverse-map AWS `DescribeLifecycleHooks` response to the CFn
34348
- * `LifecycleHookSpecificationList` shape. Each hook is surfaced under the
34349
- * exact CFn property name. AWS-side fields cdkd state never carried
34350
- * (`AutoScalingGroupName` — duplicated on every hook by AWS,
34351
- * `GlobalTimeout` — AWS-derived) are filtered out. Sorted by
34352
- * LifecycleHookName for stable positional compare.
34353
- */
34354
- function mapLifecycleHooksToCfn(hooks) {
34355
- if (!hooks || hooks.length === 0) return [];
34356
- const result = [];
34357
- for (const h of hooks) {
34358
- if (!h.LifecycleHookName) continue;
34359
- const entry = { LifecycleHookName: h.LifecycleHookName };
34360
- if (h.LifecycleTransition !== void 0) entry["LifecycleTransition"] = h.LifecycleTransition;
34361
- if (h.RoleARN !== void 0) entry["RoleARN"] = h.RoleARN;
34362
- if (h.NotificationTargetARN !== void 0) entry["NotificationTargetARN"] = h.NotificationTargetARN;
34363
- if (h.NotificationMetadata !== void 0) entry["NotificationMetadata"] = h.NotificationMetadata;
34364
- if (h.HeartbeatTimeout !== void 0) entry["HeartbeatTimeout"] = h.HeartbeatTimeout;
34365
- if (h.DefaultResult !== void 0) entry["DefaultResult"] = h.DefaultResult;
34366
- result.push(entry);
34367
- }
34368
- result.sort((a, b) => String(a["LifecycleHookName"]).localeCompare(String(b["LifecycleHookName"])));
34369
- return result;
34370
- }
34371
- /**
34372
- * Reverse-map AWS `DescribeTrafficSources` response to the CFn
34373
- * `TrafficSources` shape `[{Identifier, Type?}]`. AWS-side runtime fields
34374
- * (`State`, the deprecated `TrafficSource` alias) are filtered out.
34375
- * Sorted by Identifier for stable positional compare.
34376
- */
34377
- function mapTrafficSourcesToCfn(trafficSources) {
34378
- if (!trafficSources || trafficSources.length === 0) return [];
34379
- const result = [];
34380
- for (const t of trafficSources) {
34381
- if (!t.Identifier) continue;
34382
- const entry = { Identifier: t.Identifier };
34383
- if (t.Type !== void 0) entry["Type"] = t.Type;
34384
- result.push(entry);
34385
- }
34386
- result.sort((a, b) => String(a["Identifier"]).localeCompare(String(b["Identifier"])));
34387
- return result;
34388
- }
34389
- /**
34390
- * Reverse-map AWS `DescribeNotificationConfigurations` (a flat list, one
34391
- * row per `(topicArn, notificationType)`) into the CFn shape
34392
- * `[{TopicARN, NotificationTypes[]}]`. NotificationTypes are grouped per
34393
- * TopicARN and sorted alphabetically for stable positional compare.
34394
- */
34395
- function mapNotificationsToCfn(configurations) {
34396
- if (!configurations || configurations.length === 0) return [];
34397
- const byTopic = /* @__PURE__ */ new Map();
34398
- for (const c of configurations) {
34399
- if (!c.TopicARN) continue;
34400
- let set = byTopic.get(c.TopicARN);
34401
- if (!set) {
34402
- set = /* @__PURE__ */ new Set();
34403
- byTopic.set(c.TopicARN, set);
34404
- }
34405
- if (c.NotificationType) set.add(c.NotificationType);
34406
- }
34407
- const result = [];
34408
- for (const topic of Array.from(byTopic.keys()).sort()) {
34409
- const types = Array.from(byTopic.get(topic) ?? []).sort();
34410
- result.push({
34411
- TopicARN: topic,
34412
- NotificationTypes: types
34413
- });
34414
- }
34415
- return result;
34416
- }
34417
-
34418
33668
  //#endregion
34419
33669
  //#region src/cli/commands/destroy-runner.ts
34420
33670
  /**
@@ -53522,7 +52772,7 @@ function reorderArgs(argv) {
53522
52772
  async function main() {
53523
52773
  installPipeCloseHandler();
53524
52774
  const program = new Command();
53525
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.219.1");
52775
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.219.3");
53526
52776
  program.addCommand(createBootstrapCommand());
53527
52777
  program.addCommand(createSynthCommand());
53528
52778
  program.addCommand(createListCommand());