@go-to-k/cdkd 0.156.0 → 0.158.0
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/README.md +9 -3
- package/dist/{aws-clients-BF03Alpe.js → aws-clients-B15NAPbL.js} +24 -1
- package/dist/aws-clients-B15NAPbL.js.map +1 -0
- package/dist/cli.js +886 -152
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-YQwoPaCE.js → deploy-engine-UmoqjtWH.js} +19 -6
- package/dist/deploy-engine-UmoqjtWH.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +2 -1
- package/dist/aws-clients-BF03Alpe.js.map +0 -1
- package/dist/deploy-engine-YQwoPaCE.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { _ as withSkipPrefix, a as runDockerStreaming, c as getLogger, d as getLiveRenderer, f as PATTERN_B_NAME_PROPERTIES, g as generateResourceNameWithFallback, h as generateResourceName, i as runDockerForeground, n as formatDockerLoginError, p as PATTERN_B_RESOURCE_TYPES, r as getDockerCmd, u as runStackBuffered, v as withStackName } from "./docker-cmd-iDMcWcre.js";
|
|
3
|
-
import { $ as CdkdError, A as shouldRetainResource, B as resolveSkipPrefix, C as IntrinsicFunctionResolver, D as TemplateParser, E as DagBuilder, F as Synthesizer, G as CFN_TEMPLATE_URL_LIMIT, H as resolveStateBucketWithDefaultAndSource, I as getDefaultStateBucketName, J as uploadCfnTemplate, K as MIGRATE_TMP_PREFIX, L as getLegacyStateBucketName, M as stringifyValue, N as WorkGraph, O as LockManager, P as buildDockerImage, R as resolveApp, S as assertRegionMatch, T as DiffCalculator, U as warnDeprecatedNoPrefixCliFlag, V as resolveStateBucketWithDefault, W as CFN_TEMPLATE_BODY_LIMIT, Y as AssemblyReader, Z as resolveBucketRegion, _ as matchesCdkPath, a as withRetry, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as PartialFailureError, d as green, dt as ResourceUpdateNotSupportedError, f as red, ft as RouteDiscoveryError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalStartServiceError, j as AssetPublisher, k as S3StateBackend, l as cyan, lt as ProvisioningError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LocalInvokeBuildError, o as IMPLICIT_DELETE_DEPENDENCIES, ot as MissingCdkCliError, p as yellow, pt as StackHasActiveImportsError, q as findLargeInlineResources, r as DeployEngine, rt as LocalMigrateError, s as formatResourceLine, st as NestedStackChildDirectDestroyError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ResourceTimeoutError, v as normalizeAwsTagsToCfn, w as applyRoleArnIfSet, x as CloudControlProvider, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveCaptureObservedState } from "./deploy-engine-
|
|
4
|
-
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-
|
|
3
|
+
import { $ as CdkdError, A as shouldRetainResource, B as resolveSkipPrefix, C as IntrinsicFunctionResolver, D as TemplateParser, E as DagBuilder, F as Synthesizer, G as CFN_TEMPLATE_URL_LIMIT, H as resolveStateBucketWithDefaultAndSource, I as getDefaultStateBucketName, J as uploadCfnTemplate, K as MIGRATE_TMP_PREFIX, L as getLegacyStateBucketName, M as stringifyValue, N as WorkGraph, O as LockManager, P as buildDockerImage, R as resolveApp, S as assertRegionMatch, T as DiffCalculator, U as warnDeprecatedNoPrefixCliFlag, V as resolveStateBucketWithDefault, W as CFN_TEMPLATE_BODY_LIMIT, Y as AssemblyReader, Z as resolveBucketRegion, _ as matchesCdkPath, a as withRetry, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as PartialFailureError, d as green, dt as ResourceUpdateNotSupportedError, f as red, ft as RouteDiscoveryError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalStartServiceError, j as AssetPublisher, k as S3StateBackend, l as cyan, lt as ProvisioningError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LocalInvokeBuildError, o as IMPLICIT_DELETE_DEPENDENCIES, ot as MissingCdkCliError, p as yellow, pt as StackHasActiveImportsError, q as findLargeInlineResources, r as DeployEngine, rt as LocalMigrateError, s as formatResourceLine, st as NestedStackChildDirectDestroyError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ResourceTimeoutError, v as normalizeAwsTagsToCfn, w as applyRoleArnIfSet, x as CloudControlProvider, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveCaptureObservedState } from "./deploy-engine-UmoqjtWH.js";
|
|
4
|
+
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
7
7
|
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";
|
|
@@ -12,7 +12,7 @@ import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCo
|
|
|
12
12
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
13
13
|
import { AssociateRouteTableCommand, AttachInternetGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAvailabilityZonesCommand, DescribeInstanceAttributeCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, ReplaceNetworkAclAssociationCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
|
|
14
14
|
import { CreateTableCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
|
|
15
|
-
import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
15
|
+
import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, ListExportsCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
16
16
|
import { APIGatewayClient, CreateAuthorizerCommand, CreateDeploymentCommand, CreateResourceCommand, CreateStageCommand, DeleteAuthorizerCommand, DeleteDeploymentCommand, DeleteMethodCommand, DeleteResourceCommand, DeleteStageCommand, GetAccountCommand, GetAuthorizerCommand, GetDeploymentCommand, GetMethodCommand, GetResourceCommand, GetStageCommand, NotFoundException as NotFoundException$1, PutIntegrationCommand, PutIntegrationResponseCommand, PutMethodCommand, PutMethodResponseCommand, TagResourceCommand as TagResourceCommand$3, UntagResourceCommand as UntagResourceCommand$3, UpdateAccountCommand, UpdateAuthorizerCommand, UpdateMethodCommand, UpdateStageCommand } from "@aws-sdk/client-api-gateway";
|
|
17
17
|
import { CreateEventBusCommand, DeleteEventBusCommand, DeleteRuleCommand, DescribeEventBusCommand, DescribeRuleCommand, EventBridgeClient, ListEventBusesCommand, ListRulesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$1, ListTargetsByRuleCommand, PutRuleCommand, PutTargetsCommand, RemoveTargetsCommand, ResourceNotFoundException as ResourceNotFoundException$2, TagResourceCommand as TagResourceCommand$4, UntagResourceCommand as UntagResourceCommand$4, UpdateEventBusCommand } from "@aws-sdk/client-eventbridge";
|
|
18
18
|
import { CreateSecretCommand, DeleteSecretCommand, DescribeSecretCommand, GetSecretValueCommand, ListSecretsCommand, RemoveRegionsFromReplicationCommand, ReplicateSecretToRegionsCommand, ResourceNotFoundException as ResourceNotFoundException$3, SecretsManagerClient, TagResourceCommand as TagResourceCommand$5, UntagResourceCommand as UntagResourceCommand$5, UpdateSecretCommand } from "@aws-sdk/client-secrets-manager";
|
|
@@ -21,6 +21,7 @@ import { CloudFrontClient, CreateCloudFrontOriginAccessIdentityCommand, CreateDi
|
|
|
21
21
|
import { CloudWatchClient, DeleteAlarmsCommand, DescribeAlarmsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$4, PutMetricAlarmCommand, TagResourceCommand as TagResourceCommand$6, UntagResourceCommand as UntagResourceCommand$6 } from "@aws-sdk/client-cloudwatch";
|
|
22
22
|
import { CloudWatchLogsClient, CreateLogGroupCommand, DeleteDataProtectionPolicyCommand, DeleteIndexPolicyCommand, DeleteLogGroupCommand, DeleteRetentionPolicyCommand, DescribeIndexPoliciesCommand, DescribeLogGroupsCommand, GetDataProtectionPolicyCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$5, PutBearerTokenAuthenticationCommand, PutDataProtectionPolicyCommand, PutIndexPolicyCommand, PutLogGroupDeletionProtectionCommand, PutRetentionPolicyCommand, ResourceAlreadyExistsException, ResourceNotFoundException as ResourceNotFoundException$4, TagResourceCommand as TagResourceCommand$7, UntagResourceCommand as UntagResourceCommand$7 } from "@aws-sdk/client-cloudwatch-logs";
|
|
23
23
|
import { BedrockAgentCoreControlClient, CreateAgentRuntimeCommand, DeleteAgentRuntimeCommand, GetAgentRuntimeCommand, ResourceNotFoundException as ResourceNotFoundException$5, UpdateAgentRuntimeCommand } from "@aws-sdk/client-bedrock-agentcore-control";
|
|
24
|
+
import { ACMClient, AddTagsToCertificateCommand, DeleteCertificateCommand, DescribeCertificateCommand, ListCertificatesCommand, ListTagsForCertificateCommand, RemoveTagsFromCertificateCommand, RequestCertificateCommand, ResourceNotFoundException as ResourceNotFoundException$6, UpdateCertificateOptionsCommand } from "@aws-sdk/client-acm";
|
|
24
25
|
import * as fs from "node:fs";
|
|
25
26
|
import { cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
26
27
|
import * as path from "node:path";
|
|
@@ -44,17 +45,17 @@ import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, Delet
|
|
|
44
45
|
import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateDBClusterCommand as CreateDBClusterCommand$1, CreateDBInstanceCommand as CreateDBInstanceCommand$1, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$1, DeleteDBClusterCommand as DeleteDBClusterCommand$1, DeleteDBInstanceCommand as DeleteDBInstanceCommand$1, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$1, DescribeDBClustersCommand as DescribeDBClustersCommand$1, DescribeDBInstancesCommand as DescribeDBInstancesCommand$1, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$1, DocDBClient, ListTagsForResourceCommand as ListTagsForResourceCommand$11, ModifyDBClusterCommand as ModifyDBClusterCommand$1, ModifyDBInstanceCommand as ModifyDBInstanceCommand$1, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$1, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$2 } from "@aws-sdk/client-docdb";
|
|
45
46
|
import { AddTagsToResourceCommand as AddTagsToResourceCommand$3, CreateDBClusterCommand as CreateDBClusterCommand$2, CreateDBInstanceCommand as CreateDBInstanceCommand$2, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$2, DeleteDBClusterCommand as DeleteDBClusterCommand$2, DeleteDBInstanceCommand as DeleteDBInstanceCommand$2, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$2, DescribeDBClustersCommand as DescribeDBClustersCommand$2, DescribeDBInstancesCommand as DescribeDBInstancesCommand$2, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$2, ListTagsForResourceCommand as ListTagsForResourceCommand$12, ModifyDBClusterCommand as ModifyDBClusterCommand$2, ModifyDBInstanceCommand as ModifyDBInstanceCommand$2, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$2, NeptuneClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$3 } from "@aws-sdk/client-neptune";
|
|
46
47
|
import { CreateWebACLCommand, DeleteWebACLCommand, GetWebACLCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$13, ListWebACLsCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$11, UpdateWebACLCommand, WAFNonexistentItemException, WAFV2Client } from "@aws-sdk/client-wafv2";
|
|
47
|
-
import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$
|
|
48
|
+
import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$7, UpdateUserPoolCommand } from "@aws-sdk/client-cognito-identity-provider";
|
|
48
49
|
import { AddTagsToResourceCommand as AddTagsToResourceCommand$4, CreateCacheClusterCommand, CreateCacheSubnetGroupCommand, DeleteCacheClusterCommand, DeleteCacheSubnetGroupCommand, DescribeCacheClustersCommand, DescribeCacheSubnetGroupsCommand, ElastiCacheClient, ListTagsForResourceCommand as ListTagsForResourceCommand$15, ModifyCacheClusterCommand, ModifyCacheSubnetGroupCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$4 } from "@aws-sdk/client-elasticache";
|
|
49
50
|
import { CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateServiceCommand$1, DeleteNamespaceCommand, DeleteServiceCommand as DeleteServiceCommand$1, GetNamespaceCommand, GetOperationCommand, GetServiceCommand, ListNamespacesCommand, ListServicesCommand as ListServicesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$16, NamespaceNotFound, ServiceDiscoveryClient, ServiceNotFound, UpdatePrivateDnsNamespaceCommand, UpdateServiceCommand as UpdateServiceCommand$1 } from "@aws-sdk/client-servicediscovery";
|
|
50
51
|
import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$12, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
|
|
51
52
|
import { parse, print } from "graphql";
|
|
52
53
|
import { CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
|
|
53
|
-
import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$
|
|
54
|
+
import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$13, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
|
|
54
55
|
import { AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, UpdateFileSystemCommand } from "@aws-sdk/client-efs";
|
|
55
|
-
import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$
|
|
56
|
+
import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$9, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
|
|
56
57
|
import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$1, ListTrailsCommand, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$1, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
|
|
57
|
-
import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$
|
|
58
|
+
import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$10, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
|
|
58
59
|
import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, ListVectorBucketsCommand, S3VectorsClient } from "@aws-sdk/client-s3vectors";
|
|
59
60
|
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 } from "@aws-sdk/client-s3tables";
|
|
60
61
|
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";
|
|
@@ -22643,7 +22644,7 @@ var CognitoUserPoolProvider = class {
|
|
|
22643
22644
|
if (!templatedActive) try {
|
|
22644
22645
|
needsFlip = (await this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId }))).UserPool?.DeletionProtection === "ACTIVE";
|
|
22645
22646
|
} catch (descError) {
|
|
22646
|
-
if (descError instanceof ResourceNotFoundException$
|
|
22647
|
+
if (descError instanceof ResourceNotFoundException$7) {
|
|
22647
22648
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
22648
22649
|
this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
|
|
22649
22650
|
return;
|
|
@@ -22666,7 +22667,7 @@ var CognitoUserPoolProvider = class {
|
|
|
22666
22667
|
await this.getClient().send(new DeleteUserPoolCommand({ UserPoolId: physicalId }));
|
|
22667
22668
|
this.logger.debug(`Successfully deleted Cognito User Pool ${logicalId}`);
|
|
22668
22669
|
} catch (error) {
|
|
22669
|
-
if (error instanceof ResourceNotFoundException$
|
|
22670
|
+
if (error instanceof ResourceNotFoundException$7) {
|
|
22670
22671
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
22671
22672
|
this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
|
|
22672
22673
|
return;
|
|
@@ -22699,7 +22700,7 @@ var CognitoUserPoolProvider = class {
|
|
|
22699
22700
|
try {
|
|
22700
22701
|
resp = await this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId }));
|
|
22701
22702
|
} catch (err) {
|
|
22702
|
-
if (err instanceof ResourceNotFoundException$
|
|
22703
|
+
if (err instanceof ResourceNotFoundException$7) return void 0;
|
|
22703
22704
|
throw err;
|
|
22704
22705
|
}
|
|
22705
22706
|
const pool = resp.UserPool;
|
|
@@ -22754,7 +22755,7 @@ var CognitoUserPoolProvider = class {
|
|
|
22754
22755
|
attributes: {}
|
|
22755
22756
|
};
|
|
22756
22757
|
} catch (err) {
|
|
22757
|
-
if (err instanceof ResourceNotFoundException$
|
|
22758
|
+
if (err instanceof ResourceNotFoundException$7) return null;
|
|
22758
22759
|
throw err;
|
|
22759
22760
|
}
|
|
22760
22761
|
const desiredName = typeof input.properties?.["UserPoolName"] === "string" ? input.properties["UserPoolName"] : void 0;
|
|
@@ -22778,7 +22779,7 @@ var CognitoUserPoolProvider = class {
|
|
|
22778
22779
|
attributes: {}
|
|
22779
22780
|
};
|
|
22780
22781
|
} catch (err) {
|
|
22781
|
-
if (err instanceof ResourceNotFoundException$
|
|
22782
|
+
if (err instanceof ResourceNotFoundException$7) continue;
|
|
22782
22783
|
throw err;
|
|
22783
22784
|
}
|
|
22784
22785
|
}
|
|
@@ -27008,7 +27009,7 @@ var KinesisStreamProvider = class {
|
|
|
27008
27009
|
}));
|
|
27009
27010
|
this.logger.debug(`Successfully deleted Kinesis stream ${logicalId}`);
|
|
27010
27011
|
} catch (error) {
|
|
27011
|
-
if (error instanceof ResourceNotFoundException$
|
|
27012
|
+
if (error instanceof ResourceNotFoundException$8) {
|
|
27012
27013
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
27013
27014
|
this.logger.debug(`Kinesis stream ${physicalId} does not exist, skipping deletion`);
|
|
27014
27015
|
return;
|
|
@@ -27088,7 +27089,7 @@ var KinesisStreamProvider = class {
|
|
|
27088
27089
|
try {
|
|
27089
27090
|
stream = (await this.getClient().send(new DescribeStreamCommand({ StreamName: physicalId }))).StreamDescription;
|
|
27090
27091
|
} catch (err) {
|
|
27091
|
-
if (err instanceof ResourceNotFoundException$
|
|
27092
|
+
if (err instanceof ResourceNotFoundException$8) return void 0;
|
|
27092
27093
|
throw err;
|
|
27093
27094
|
}
|
|
27094
27095
|
if (!stream) return void 0;
|
|
@@ -27106,7 +27107,7 @@ var KinesisStreamProvider = class {
|
|
|
27106
27107
|
try {
|
|
27107
27108
|
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForStreamCommand({ StreamName: physicalId }))).Tags);
|
|
27108
27109
|
} catch (err) {
|
|
27109
|
-
if (err instanceof ResourceNotFoundException$
|
|
27110
|
+
if (err instanceof ResourceNotFoundException$8) return void 0;
|
|
27110
27111
|
this.logger.debug(`Kinesis ListTagsForStream(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
27111
27112
|
}
|
|
27112
27113
|
return result;
|
|
@@ -27120,7 +27121,7 @@ var KinesisStreamProvider = class {
|
|
|
27120
27121
|
attributes: {}
|
|
27121
27122
|
};
|
|
27122
27123
|
} catch (err) {
|
|
27123
|
-
if (err instanceof ResourceNotFoundException$
|
|
27124
|
+
if (err instanceof ResourceNotFoundException$8) return null;
|
|
27124
27125
|
throw err;
|
|
27125
27126
|
}
|
|
27126
27127
|
if (!input.cdkPath) return null;
|
|
@@ -27291,7 +27292,7 @@ var KinesisStreamConsumerProvider = class {
|
|
|
27291
27292
|
await this.getClient().send(new DeregisterStreamConsumerCommand({ ConsumerARN: physicalId }));
|
|
27292
27293
|
this.logger.debug(`Successfully deregistered Kinesis stream consumer ${logicalId}`);
|
|
27293
27294
|
} catch (error) {
|
|
27294
|
-
if (error instanceof ResourceNotFoundException$
|
|
27295
|
+
if (error instanceof ResourceNotFoundException$8) {
|
|
27295
27296
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
27296
27297
|
this.logger.debug(`Kinesis stream consumer ${physicalId} not found, skipping`);
|
|
27297
27298
|
return;
|
|
@@ -27337,7 +27338,7 @@ var KinesisStreamConsumerProvider = class {
|
|
|
27337
27338
|
try {
|
|
27338
27339
|
desc = (await this.getClient().send(new DescribeStreamConsumerCommand({ ConsumerARN: physicalId }))).ConsumerDescription;
|
|
27339
27340
|
} catch (err) {
|
|
27340
|
-
if (err instanceof ResourceNotFoundException$
|
|
27341
|
+
if (err instanceof ResourceNotFoundException$8) return void 0;
|
|
27341
27342
|
throw err;
|
|
27342
27343
|
}
|
|
27343
27344
|
if (!desc) return void 0;
|
|
@@ -27347,7 +27348,7 @@ var KinesisStreamConsumerProvider = class {
|
|
|
27347
27348
|
try {
|
|
27348
27349
|
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$17({ ResourceARN: physicalId }))).Tags);
|
|
27349
27350
|
} catch (err) {
|
|
27350
|
-
if (err instanceof ResourceNotFoundException$
|
|
27351
|
+
if (err instanceof ResourceNotFoundException$8) return void 0;
|
|
27351
27352
|
this.logger.debug(`ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
27352
27353
|
result["Tags"] = [];
|
|
27353
27354
|
}
|
|
@@ -28414,7 +28415,7 @@ var FirehoseProvider = class {
|
|
|
28414
28415
|
await this.getClient().send(new DeleteDeliveryStreamCommand({ DeliveryStreamName: physicalId }));
|
|
28415
28416
|
this.logger.debug(`Successfully deleted Firehose delivery stream ${logicalId}`);
|
|
28416
28417
|
} catch (error) {
|
|
28417
|
-
if (error instanceof ResourceNotFoundException$
|
|
28418
|
+
if (error instanceof ResourceNotFoundException$9) {
|
|
28418
28419
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
28419
28420
|
this.logger.debug(`Firehose delivery stream ${physicalId} does not exist, skipping deletion`);
|
|
28420
28421
|
return;
|
|
@@ -28951,7 +28952,7 @@ var FirehoseProvider = class {
|
|
|
28951
28952
|
try {
|
|
28952
28953
|
desc = (await this.getClient().send(new DescribeDeliveryStreamCommand({ DeliveryStreamName: physicalId }))).DeliveryStreamDescription;
|
|
28953
28954
|
} catch (err) {
|
|
28954
|
-
if (err instanceof ResourceNotFoundException$
|
|
28955
|
+
if (err instanceof ResourceNotFoundException$9) return void 0;
|
|
28955
28956
|
throw err;
|
|
28956
28957
|
}
|
|
28957
28958
|
if (!desc) return void 0;
|
|
@@ -28982,7 +28983,7 @@ var FirehoseProvider = class {
|
|
|
28982
28983
|
try {
|
|
28983
28984
|
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForDeliveryStreamCommand({ DeliveryStreamName: physicalId }))).Tags);
|
|
28984
28985
|
} catch (err) {
|
|
28985
|
-
if (err instanceof ResourceNotFoundException$
|
|
28986
|
+
if (err instanceof ResourceNotFoundException$9) return void 0;
|
|
28986
28987
|
this.logger.debug(`Firehose ListTagsForDeliveryStream(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
28987
28988
|
result["Tags"] = [];
|
|
28988
28989
|
}
|
|
@@ -29019,7 +29020,7 @@ var FirehoseProvider = class {
|
|
|
29019
29020
|
attributes: {}
|
|
29020
29021
|
};
|
|
29021
29022
|
} catch (err) {
|
|
29022
|
-
if (err instanceof ResourceNotFoundException$
|
|
29023
|
+
if (err instanceof ResourceNotFoundException$9) return null;
|
|
29023
29024
|
throw err;
|
|
29024
29025
|
}
|
|
29025
29026
|
if (!input.cdkPath) return null;
|
|
@@ -29790,7 +29791,7 @@ var CodeBuildProvider = class {
|
|
|
29790
29791
|
await this.getClient().send(new DeleteProjectCommand({ name: physicalId }));
|
|
29791
29792
|
this.logger.debug(`Successfully deleted CodeBuild Project ${logicalId}`);
|
|
29792
29793
|
} catch (error) {
|
|
29793
|
-
if (error instanceof ResourceNotFoundException$
|
|
29794
|
+
if (error instanceof ResourceNotFoundException$10) {
|
|
29794
29795
|
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
29795
29796
|
this.logger.debug(`CodeBuild Project ${physicalId} does not exist, skipping deletion`);
|
|
29796
29797
|
return;
|
|
@@ -29845,7 +29846,7 @@ var CodeBuildProvider = class {
|
|
|
29845
29846
|
try {
|
|
29846
29847
|
project = (await this.getClient().send(new BatchGetProjectsCommand({ names: [physicalId] }))).projects?.[0];
|
|
29847
29848
|
} catch (err) {
|
|
29848
|
-
if (err instanceof ResourceNotFoundException$
|
|
29849
|
+
if (err instanceof ResourceNotFoundException$10) return void 0;
|
|
29849
29850
|
throw err;
|
|
29850
29851
|
}
|
|
29851
29852
|
if (!project) return void 0;
|
|
@@ -29989,7 +29990,7 @@ var CodeBuildProvider = class {
|
|
|
29989
29990
|
attributes: {}
|
|
29990
29991
|
} : null;
|
|
29991
29992
|
} catch (err) {
|
|
29992
|
-
if (err instanceof ResourceNotFoundException$
|
|
29993
|
+
if (err instanceof ResourceNotFoundException$10) return null;
|
|
29993
29994
|
throw err;
|
|
29994
29995
|
}
|
|
29995
29996
|
if (!input.cdkPath) return null;
|
|
@@ -32525,6 +32526,350 @@ var NestedStackProvider = class {
|
|
|
32525
32526
|
}
|
|
32526
32527
|
};
|
|
32527
32528
|
|
|
32529
|
+
//#endregion
|
|
32530
|
+
//#region src/provisioning/providers/acm-certificate-provider.ts
|
|
32531
|
+
/**
|
|
32532
|
+
* AWS ACM Certificate Provider
|
|
32533
|
+
*
|
|
32534
|
+
* Implements `AWS::CertificateManager::Certificate` using the ACM SDK.
|
|
32535
|
+
*
|
|
32536
|
+
* **DNS / EMAIL validation is asynchronous.** `RequestCertificate` returns
|
|
32537
|
+
* immediately with status `PENDING_VALIDATION`; the certificate only reaches
|
|
32538
|
+
* `ISSUED` once AWS has confirmed the DNS records (or the email click).
|
|
32539
|
+
*
|
|
32540
|
+
* `create()` polls `DescribeCertificate` until status flips to `ISSUED`. On
|
|
32541
|
+
* the first poll that returns PENDING_VALIDATION, the provider logs the
|
|
32542
|
+
* `DomainValidationOptions` AWS posted so the user knows which CNAME records
|
|
32543
|
+
* to add to their DNS zone. `CDKD_NO_WAIT=true` (or `cdkd deploy --no-wait`)
|
|
32544
|
+
* short-circuits the loop and returns immediately with the ARN — downstream
|
|
32545
|
+
* consumers (CloudFront, ALB) will fail to start if they reach the cert
|
|
32546
|
+
* before it issues, but that's the documented trade-off.
|
|
32547
|
+
*
|
|
32548
|
+
* **CloudFront cross-region note**: ACM certificates referenced by a
|
|
32549
|
+
* CloudFront Distribution MUST live in `us-east-1`. cdkd does not enforce
|
|
32550
|
+
* this — it's the developer's responsibility to deploy the certificate
|
|
32551
|
+
* stack to `us-east-1`. The provider uses the single ACMClient configured
|
|
32552
|
+
* in `aws-clients.ts` (region = stack's region) and does NOT override.
|
|
32553
|
+
*
|
|
32554
|
+
* Physical id is the certificate ARN. CFn exposes only `Ref` (returns the
|
|
32555
|
+
* ARN); `getAttribute('Arn')` / `getAttribute('CertificateArn')` also
|
|
32556
|
+
* return the ARN for any defensive call site.
|
|
32557
|
+
*/
|
|
32558
|
+
var ACMCertificateProvider = class {
|
|
32559
|
+
acmClient;
|
|
32560
|
+
logger = getLogger().child("ACMCertificateProvider");
|
|
32561
|
+
maxPollAttempts = Number(process.env["CDKD_ACM_POLL_ATTEMPTS"] ?? 60);
|
|
32562
|
+
pollIntervalMs = Number(process.env["CDKD_ACM_POLL_INTERVAL_MS"] ?? 1e4);
|
|
32563
|
+
constructor() {
|
|
32564
|
+
this.acmClient = getAwsClients().acm;
|
|
32565
|
+
}
|
|
32566
|
+
handledProperties = new Map([["AWS::CertificateManager::Certificate", new Set([
|
|
32567
|
+
"DomainName",
|
|
32568
|
+
"ValidationMethod",
|
|
32569
|
+
"SubjectAlternativeNames",
|
|
32570
|
+
"DomainValidationOptions",
|
|
32571
|
+
"CertificateAuthorityArn",
|
|
32572
|
+
"CertificateTransparencyLoggingPreference",
|
|
32573
|
+
"CertificateExport",
|
|
32574
|
+
"KeyAlgorithm",
|
|
32575
|
+
"Tags"
|
|
32576
|
+
])]]);
|
|
32577
|
+
async create(logicalId, resourceType, properties) {
|
|
32578
|
+
this.logger.debug(`Requesting ACM certificate ${logicalId}`);
|
|
32579
|
+
const domainName = properties["DomainName"];
|
|
32580
|
+
if (!domainName) throw new ProvisioningError(`DomainName is required for ACM certificate ${logicalId}`, resourceType, logicalId);
|
|
32581
|
+
const input = { DomainName: domainName };
|
|
32582
|
+
if (properties["ValidationMethod"]) input["ValidationMethod"] = properties["ValidationMethod"];
|
|
32583
|
+
if (Array.isArray(properties["SubjectAlternativeNames"])) input["SubjectAlternativeNames"] = properties["SubjectAlternativeNames"];
|
|
32584
|
+
if (Array.isArray(properties["DomainValidationOptions"])) input["DomainValidationOptions"] = properties["DomainValidationOptions"].map((opt) => {
|
|
32585
|
+
const cleaned = { DomainName: opt["DomainName"] };
|
|
32586
|
+
if (opt["ValidationDomain"]) cleaned["ValidationDomain"] = opt["ValidationDomain"];
|
|
32587
|
+
return cleaned;
|
|
32588
|
+
}).filter((opt) => opt["DomainName"]);
|
|
32589
|
+
if (properties["CertificateAuthorityArn"]) input["CertificateAuthorityArn"] = properties["CertificateAuthorityArn"];
|
|
32590
|
+
if (properties["KeyAlgorithm"]) input["KeyAlgorithm"] = properties["KeyAlgorithm"];
|
|
32591
|
+
const options = {};
|
|
32592
|
+
if (properties["CertificateTransparencyLoggingPreference"]) options["CertificateTransparencyLoggingPreference"] = properties["CertificateTransparencyLoggingPreference"];
|
|
32593
|
+
if (properties["CertificateExport"]) options["Export"] = properties["CertificateExport"];
|
|
32594
|
+
if (Object.keys(options).length > 0) input["Options"] = options;
|
|
32595
|
+
const tags = properties["Tags"];
|
|
32596
|
+
if (tags && Array.isArray(tags) && tags.length > 0) input["Tags"] = tags;
|
|
32597
|
+
try {
|
|
32598
|
+
const certificateArn = (await this.acmClient.send(new RequestCertificateCommand(input))).CertificateArn;
|
|
32599
|
+
if (!certificateArn) throw new ProvisioningError(`RequestCertificate succeeded but no CertificateArn returned for ${logicalId}`, resourceType, logicalId);
|
|
32600
|
+
this.logger.debug(`Requested ACM certificate: ${certificateArn}`);
|
|
32601
|
+
if (!(process.env["CDKD_NO_WAIT"] === "true")) await this.waitForCertificateIssued(certificateArn, logicalId);
|
|
32602
|
+
else this.logger.warn(`Skipping wait for ACM certificate ${logicalId} (CDKD_NO_WAIT=true). Downstream consumers (CloudFront / ALB) will fail until the cert reaches ISSUED.`);
|
|
32603
|
+
return {
|
|
32604
|
+
physicalId: certificateArn,
|
|
32605
|
+
attributes: {
|
|
32606
|
+
Arn: certificateArn,
|
|
32607
|
+
CertificateArn: certificateArn
|
|
32608
|
+
}
|
|
32609
|
+
};
|
|
32610
|
+
} catch (error) {
|
|
32611
|
+
const cause = error instanceof Error ? error : void 0;
|
|
32612
|
+
throw new ProvisioningError(`Failed to create ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
32613
|
+
}
|
|
32614
|
+
}
|
|
32615
|
+
async update(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
32616
|
+
this.logger.debug(`Updating ACM certificate ${logicalId}: ${physicalId}`);
|
|
32617
|
+
const changedImmutable = [
|
|
32618
|
+
"DomainName",
|
|
32619
|
+
"ValidationMethod",
|
|
32620
|
+
"SubjectAlternativeNames",
|
|
32621
|
+
"DomainValidationOptions",
|
|
32622
|
+
"CertificateAuthorityArn",
|
|
32623
|
+
"KeyAlgorithm"
|
|
32624
|
+
].find((k) => JSON.stringify(properties[k]) !== JSON.stringify(previousProperties[k]));
|
|
32625
|
+
if (changedImmutable) {
|
|
32626
|
+
this.logger.debug(`${changedImmutable} changed, replacing ACM certificate: ${physicalId}`);
|
|
32627
|
+
const createResult = await this.create(logicalId, resourceType, properties);
|
|
32628
|
+
try {
|
|
32629
|
+
await this.delete(logicalId, physicalId, resourceType, previousProperties);
|
|
32630
|
+
} catch (error) {
|
|
32631
|
+
this.logger.warn(`Failed to delete old ACM certificate ${physicalId} during replacement: ${String(error)}. The old certificate may be orphaned and require manual cleanup.`);
|
|
32632
|
+
}
|
|
32633
|
+
const result = {
|
|
32634
|
+
physicalId: createResult.physicalId,
|
|
32635
|
+
wasReplaced: true
|
|
32636
|
+
};
|
|
32637
|
+
if (createResult.attributes) result.attributes = createResult.attributes;
|
|
32638
|
+
return result;
|
|
32639
|
+
}
|
|
32640
|
+
try {
|
|
32641
|
+
const newCt = properties["CertificateTransparencyLoggingPreference"];
|
|
32642
|
+
const oldCt = previousProperties["CertificateTransparencyLoggingPreference"];
|
|
32643
|
+
const newExport = properties["CertificateExport"];
|
|
32644
|
+
const oldExport = previousProperties["CertificateExport"];
|
|
32645
|
+
if (newCt !== oldCt || newExport !== oldExport) {
|
|
32646
|
+
const options = {};
|
|
32647
|
+
if (newCt) options["CertificateTransparencyLoggingPreference"] = newCt;
|
|
32648
|
+
if (newExport) options["Export"] = newExport;
|
|
32649
|
+
if (Object.keys(options).length > 0) {
|
|
32650
|
+
await this.acmClient.send(new UpdateCertificateOptionsCommand({
|
|
32651
|
+
CertificateArn: physicalId,
|
|
32652
|
+
Options: options
|
|
32653
|
+
}));
|
|
32654
|
+
this.logger.debug(`Updated certificate Options on ${physicalId}`);
|
|
32655
|
+
}
|
|
32656
|
+
}
|
|
32657
|
+
await this.updateTags(physicalId, properties["Tags"], previousProperties["Tags"]);
|
|
32658
|
+
return {
|
|
32659
|
+
physicalId,
|
|
32660
|
+
wasReplaced: false,
|
|
32661
|
+
attributes: {
|
|
32662
|
+
Arn: physicalId,
|
|
32663
|
+
CertificateArn: physicalId
|
|
32664
|
+
}
|
|
32665
|
+
};
|
|
32666
|
+
} catch (error) {
|
|
32667
|
+
const cause = error instanceof Error ? error : void 0;
|
|
32668
|
+
throw new ProvisioningError(`Failed to update ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
32669
|
+
}
|
|
32670
|
+
}
|
|
32671
|
+
async delete(logicalId, physicalId, resourceType, _properties, context) {
|
|
32672
|
+
this.logger.debug(`Deleting ACM certificate ${logicalId}: ${physicalId}`);
|
|
32673
|
+
try {
|
|
32674
|
+
try {
|
|
32675
|
+
await this.acmClient.send(new DeleteCertificateCommand({ CertificateArn: physicalId }));
|
|
32676
|
+
} catch (error) {
|
|
32677
|
+
if (error instanceof ResourceNotFoundException$6) {
|
|
32678
|
+
assertRegionMatch(await this.acmClient.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
32679
|
+
this.logger.debug(`Certificate ${physicalId} does not exist, skipping deletion`);
|
|
32680
|
+
return;
|
|
32681
|
+
}
|
|
32682
|
+
throw error;
|
|
32683
|
+
}
|
|
32684
|
+
this.logger.debug(`Successfully deleted ACM certificate ${logicalId}`);
|
|
32685
|
+
} catch (error) {
|
|
32686
|
+
const cause = error instanceof Error ? error : void 0;
|
|
32687
|
+
throw new ProvisioningError(`Failed to delete ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
32688
|
+
}
|
|
32689
|
+
}
|
|
32690
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
32691
|
+
if (attributeName === "Arn" || attributeName === "CertificateArn") return physicalId;
|
|
32692
|
+
}
|
|
32693
|
+
/**
|
|
32694
|
+
* Read the AWS-current certificate properties in CFn-property shape.
|
|
32695
|
+
*
|
|
32696
|
+
* Coverage:
|
|
32697
|
+
* - `DomainName`, `SubjectAlternativeNames`, `KeyAlgorithm` straight from
|
|
32698
|
+
* `DescribeCertificate.Certificate.*`.
|
|
32699
|
+
* - `CertificateTransparencyLoggingPreference` extracted from the nested
|
|
32700
|
+
* `Options` field and flattened to match CFn shape.
|
|
32701
|
+
* - `Tags` via `ListTagsForCertificate`, with the `aws:cdk:path` etc.
|
|
32702
|
+
* auto-tags filtered out by `normalizeAwsTagsToCfn`.
|
|
32703
|
+
* - `ValidationMethod` / `DomainValidationOptions` are intentionally NOT
|
|
32704
|
+
* surfaced — the deployed cert's validation state is observation-only;
|
|
32705
|
+
* cdkd state stores the request-time input, which can legitimately
|
|
32706
|
+
* diverge from the observed state without indicating drift.
|
|
32707
|
+
*
|
|
32708
|
+
* Returns `undefined` when the cert is gone (`ResourceNotFoundException`).
|
|
32709
|
+
*/
|
|
32710
|
+
async readCurrentState(physicalId, _logicalId, _resourceType, _properties) {
|
|
32711
|
+
let cert;
|
|
32712
|
+
try {
|
|
32713
|
+
cert = (await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: physicalId }))).Certificate;
|
|
32714
|
+
} catch (err) {
|
|
32715
|
+
if (err instanceof ResourceNotFoundException$6) return void 0;
|
|
32716
|
+
throw err;
|
|
32717
|
+
}
|
|
32718
|
+
if (!cert) return void 0;
|
|
32719
|
+
const result = {};
|
|
32720
|
+
if (cert.DomainName !== void 0) result["DomainName"] = cert.DomainName;
|
|
32721
|
+
if (Array.isArray(cert.SubjectAlternativeNames)) result["SubjectAlternativeNames"] = cert.SubjectAlternativeNames;
|
|
32722
|
+
if (cert.KeyAlgorithm !== void 0) result["KeyAlgorithm"] = cert.KeyAlgorithm;
|
|
32723
|
+
if (cert.CertificateAuthorityArn !== void 0) result["CertificateAuthorityArn"] = cert.CertificateAuthorityArn;
|
|
32724
|
+
if (cert.Options?.CertificateTransparencyLoggingPreference !== void 0) result["CertificateTransparencyLoggingPreference"] = cert.Options.CertificateTransparencyLoggingPreference;
|
|
32725
|
+
if (cert.Options?.Export !== void 0) result["CertificateExport"] = cert.Options.Export;
|
|
32726
|
+
try {
|
|
32727
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.acmClient.send(new ListTagsForCertificateCommand({ CertificateArn: physicalId }))).Tags);
|
|
32728
|
+
} catch (err) {
|
|
32729
|
+
if (!(err instanceof ResourceNotFoundException$6)) throw err;
|
|
32730
|
+
}
|
|
32731
|
+
return result;
|
|
32732
|
+
}
|
|
32733
|
+
/**
|
|
32734
|
+
* Path the deploy engine queries to compare drift snapshots — paths
|
|
32735
|
+
* `readCurrentState` deliberately does NOT round-trip.
|
|
32736
|
+
*/
|
|
32737
|
+
getDriftUnknownPaths(_resourceType) {
|
|
32738
|
+
return ["ValidationMethod", "DomainValidationOptions"];
|
|
32739
|
+
}
|
|
32740
|
+
/**
|
|
32741
|
+
* Adopt an existing certificate into cdkd state.
|
|
32742
|
+
*
|
|
32743
|
+
* Lookup order:
|
|
32744
|
+
* 1. `--resource` override (must be an ARN — ACM has no other unique id).
|
|
32745
|
+
* 2. Tag-based `aws:cdk:path` match across `ListCertificates` +
|
|
32746
|
+
* `ListTagsForCertificate`. NOTE: ACM has no `Scope: Local` filter —
|
|
32747
|
+
* `ListCertificates` returns customer-managed certs only (AWS-managed
|
|
32748
|
+
* certs are not surfaced via this API), so no extra guard is needed.
|
|
32749
|
+
*/
|
|
32750
|
+
async import(input) {
|
|
32751
|
+
if (input.knownPhysicalId) {
|
|
32752
|
+
const arn = input.knownPhysicalId;
|
|
32753
|
+
if (!arn.startsWith("arn:")) throw new Error(`--resource override for ${input.logicalId} must be an ARN (got '${arn}'). ACM certificates have no human-readable physical id.`);
|
|
32754
|
+
try {
|
|
32755
|
+
await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: arn }));
|
|
32756
|
+
return {
|
|
32757
|
+
physicalId: arn,
|
|
32758
|
+
attributes: {
|
|
32759
|
+
Arn: arn,
|
|
32760
|
+
CertificateArn: arn
|
|
32761
|
+
}
|
|
32762
|
+
};
|
|
32763
|
+
} catch (err) {
|
|
32764
|
+
if (err instanceof ResourceNotFoundException$6) return null;
|
|
32765
|
+
throw err;
|
|
32766
|
+
}
|
|
32767
|
+
}
|
|
32768
|
+
if (!input.cdkPath) return null;
|
|
32769
|
+
let nextToken;
|
|
32770
|
+
do {
|
|
32771
|
+
const list = await this.acmClient.send(new ListCertificatesCommand({ ...nextToken ? { NextToken: nextToken } : {} }));
|
|
32772
|
+
for (const summary of list.CertificateSummaryList ?? []) {
|
|
32773
|
+
if (!summary.CertificateArn) continue;
|
|
32774
|
+
try {
|
|
32775
|
+
if (matchesCdkPath((await this.acmClient.send(new ListTagsForCertificateCommand({ CertificateArn: summary.CertificateArn }))).Tags, input.cdkPath)) return {
|
|
32776
|
+
physicalId: summary.CertificateArn,
|
|
32777
|
+
attributes: {
|
|
32778
|
+
Arn: summary.CertificateArn,
|
|
32779
|
+
CertificateArn: summary.CertificateArn
|
|
32780
|
+
}
|
|
32781
|
+
};
|
|
32782
|
+
} catch (err) {
|
|
32783
|
+
if (err instanceof ResourceNotFoundException$6) continue;
|
|
32784
|
+
throw err;
|
|
32785
|
+
}
|
|
32786
|
+
}
|
|
32787
|
+
nextToken = list.NextToken;
|
|
32788
|
+
} while (nextToken);
|
|
32789
|
+
return null;
|
|
32790
|
+
}
|
|
32791
|
+
/**
|
|
32792
|
+
* Poll `DescribeCertificate` until status === `ISSUED`. On the FIRST poll
|
|
32793
|
+
* that returns PENDING_VALIDATION, log the DomainValidationOptions AWS
|
|
32794
|
+
* posted so the user knows which CNAME records to add to their DNS zone.
|
|
32795
|
+
*
|
|
32796
|
+
* Throws on `VALIDATION_TIMED_OUT` / `FAILED` (terminal failures) and on
|
|
32797
|
+
* polling-cap exhaustion (treated as timeout). SIGINT short-circuits the
|
|
32798
|
+
* loop and returns control to the deploy engine's cleanup path.
|
|
32799
|
+
*/
|
|
32800
|
+
async waitForCertificateIssued(certificateArn, logicalId) {
|
|
32801
|
+
this.logger.debug(`Waiting for ACM certificate ${certificateArn} to reach ISSUED status...`);
|
|
32802
|
+
let interrupted = false;
|
|
32803
|
+
let validationOptionsLogged = false;
|
|
32804
|
+
const sigintHandler = () => {
|
|
32805
|
+
interrupted = true;
|
|
32806
|
+
};
|
|
32807
|
+
process.on("SIGINT", sigintHandler);
|
|
32808
|
+
try {
|
|
32809
|
+
for (let attempt = 1; attempt <= this.maxPollAttempts; attempt++) {
|
|
32810
|
+
if (interrupted) {
|
|
32811
|
+
this.logger.debug(`ACM certificate ${certificateArn} wait interrupted by SIGINT, proceeding`);
|
|
32812
|
+
return;
|
|
32813
|
+
}
|
|
32814
|
+
const resp = await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: certificateArn }));
|
|
32815
|
+
const status = resp.Certificate?.Status;
|
|
32816
|
+
const validations = resp.Certificate?.DomainValidationOptions ?? [];
|
|
32817
|
+
if (status === "ISSUED") {
|
|
32818
|
+
this.logger.debug(`ACM certificate ${certificateArn} is ISSUED`);
|
|
32819
|
+
return;
|
|
32820
|
+
}
|
|
32821
|
+
if (status === "FAILED" || status === "VALIDATION_TIMED_OUT" || status === "INACTIVE" || status === "REVOKED" || status === "EXPIRED") throw new Error(`ACM certificate ${logicalId} (${certificateArn}) entered terminal status ${status} during validation. Check ACM console / DNS records to diagnose.`);
|
|
32822
|
+
if (status === "PENDING_VALIDATION" && !validationOptionsLogged && validations.length > 0) {
|
|
32823
|
+
this.logValidationOptions(validations);
|
|
32824
|
+
validationOptionsLogged = true;
|
|
32825
|
+
}
|
|
32826
|
+
this.logger.debug(`ACM certificate ${certificateArn} status: ${status} (attempt ${attempt}/${this.maxPollAttempts})`);
|
|
32827
|
+
const sleepEnd = Date.now() + this.pollIntervalMs;
|
|
32828
|
+
const tickMs = Math.min(1e3, this.pollIntervalMs);
|
|
32829
|
+
while (Date.now() < sleepEnd && !interrupted) await new Promise((resolve) => setTimeout(resolve, tickMs));
|
|
32830
|
+
}
|
|
32831
|
+
throw new Error(`ACM certificate ${logicalId} (${certificateArn}) did not reach ISSUED status within ${this.maxPollAttempts * this.pollIntervalMs / 1e3}s. If your DNS zone is manually managed, you may need to increase --resource-timeout AWS::CertificateManager::Certificate=<duration> or set CDKD_NO_WAIT=true.`);
|
|
32832
|
+
} finally {
|
|
32833
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
32834
|
+
}
|
|
32835
|
+
}
|
|
32836
|
+
/**
|
|
32837
|
+
* Pretty-print the validation records AWS expects in the DNS zone, so the
|
|
32838
|
+
* user can copy / paste them into Route 53 / Cloudflare / etc. while the
|
|
32839
|
+
* cert is still PENDING_VALIDATION.
|
|
32840
|
+
*/
|
|
32841
|
+
logValidationOptions(validations) {
|
|
32842
|
+
const lines = ["ACM certificate is PENDING_VALIDATION. Add the following DNS records to validate:"];
|
|
32843
|
+
for (const v of validations) if (v.ValidationMethod === "DNS" && v.ResourceRecord) {
|
|
32844
|
+
const r = v.ResourceRecord;
|
|
32845
|
+
lines.push(` ${v.DomainName} — ${r.Type} ${r.Name} -> ${r.Value}`);
|
|
32846
|
+
} else if (v.ValidationMethod === "EMAIL") {
|
|
32847
|
+
const emails = (v.ValidationEmails ?? []).join(", ");
|
|
32848
|
+
lines.push(` ${v.DomainName} — confirmation email sent to: ${emails || "<none>"}`);
|
|
32849
|
+
}
|
|
32850
|
+
this.logger.info(lines.join("\n"));
|
|
32851
|
+
}
|
|
32852
|
+
async updateTags(certificateArn, newTags, oldTags) {
|
|
32853
|
+
const newTagMap = new Map((newTags || []).map((t) => [t.Key, t.Value]));
|
|
32854
|
+
const oldTagMap = new Map((oldTags || []).map((t) => [t.Key, t.Value]));
|
|
32855
|
+
const tagsToRemove = [];
|
|
32856
|
+
for (const key of oldTagMap.keys()) if (!newTagMap.has(key)) tagsToRemove.push({ Key: key });
|
|
32857
|
+
const tagsToAdd = [];
|
|
32858
|
+
for (const [key, value] of newTagMap) if (oldTagMap.get(key) !== value) tagsToAdd.push({
|
|
32859
|
+
Key: key,
|
|
32860
|
+
Value: value
|
|
32861
|
+
});
|
|
32862
|
+
if (tagsToRemove.length > 0) await this.acmClient.send(new RemoveTagsFromCertificateCommand({
|
|
32863
|
+
CertificateArn: certificateArn,
|
|
32864
|
+
Tags: tagsToRemove
|
|
32865
|
+
}));
|
|
32866
|
+
if (tagsToAdd.length > 0) await this.acmClient.send(new AddTagsToCertificateCommand({
|
|
32867
|
+
CertificateArn: certificateArn,
|
|
32868
|
+
Tags: tagsToAdd
|
|
32869
|
+
}));
|
|
32870
|
+
}
|
|
32871
|
+
};
|
|
32872
|
+
|
|
32528
32873
|
//#endregion
|
|
32529
32874
|
//#region src/provisioning/register-providers.ts
|
|
32530
32875
|
/**
|
|
@@ -32620,6 +32965,7 @@ function registerAllProviders(registry) {
|
|
|
32620
32965
|
registry.register("AWS::Route53::RecordSet", route53Provider);
|
|
32621
32966
|
registry.register("AWS::WAFv2::WebACL", new WAFv2WebACLProvider());
|
|
32622
32967
|
registry.register("AWS::Cognito::UserPool", new CognitoUserPoolProvider());
|
|
32968
|
+
registry.register("AWS::CertificateManager::Certificate", new ACMCertificateProvider());
|
|
32623
32969
|
const elasticacheProvider = new ElastiCacheProvider();
|
|
32624
32970
|
registry.register("AWS::ElastiCache::SubnetGroup", elasticacheProvider);
|
|
32625
32971
|
registry.register("AWS::ElastiCache::CacheCluster", elasticacheProvider);
|
|
@@ -41139,6 +41485,418 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
41139
41485
|
};
|
|
41140
41486
|
}
|
|
41141
41487
|
|
|
41488
|
+
//#endregion
|
|
41489
|
+
//#region src/local/s3-local-state-provider.ts
|
|
41490
|
+
/**
|
|
41491
|
+
* `S3LocalStateProvider` — implementation of {@link LocalStateProvider}
|
|
41492
|
+
* backed by cdkd's S3 state. Wraps the existing
|
|
41493
|
+
* `loadStateForStack` + `buildCrossStackResolver` helpers in
|
|
41494
|
+
* `src/cli/commands/local-state-loader.ts` so the four `cdkd local *`
|
|
41495
|
+
* commands can route both `--from-state` and `--from-cfn-stack`
|
|
41496
|
+
* through the same provider-shaped interface (issue #606).
|
|
41497
|
+
*
|
|
41498
|
+
* Behavior is identical to the pre-issue-#606 code path — this file
|
|
41499
|
+
* exists ONLY to give the CLI layer a single interface against which
|
|
41500
|
+
* to wire both flags, so adding a third state source (a future
|
|
41501
|
+
* `--from-tf-state`? out of scope for now) doesn't require touching
|
|
41502
|
+
* the four `local-*.ts` command files again.
|
|
41503
|
+
*/
|
|
41504
|
+
var S3LocalStateProvider = class {
|
|
41505
|
+
label = "--from-state";
|
|
41506
|
+
opts;
|
|
41507
|
+
disposers = [];
|
|
41508
|
+
constructor(opts) {
|
|
41509
|
+
this.opts = opts;
|
|
41510
|
+
}
|
|
41511
|
+
async load(stackName, synthRegion) {
|
|
41512
|
+
const loaded = await loadStateForStack(stackName, synthRegion, {
|
|
41513
|
+
statePrefix: this.opts.statePrefix,
|
|
41514
|
+
...this.opts.stackRegion !== void 0 && { stackRegion: this.opts.stackRegion },
|
|
41515
|
+
...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
|
|
41516
|
+
...this.opts.region !== void 0 && { region: this.opts.region },
|
|
41517
|
+
...this.opts.profile !== void 0 && { profile: this.opts.profile }
|
|
41518
|
+
});
|
|
41519
|
+
if (!loaded) return void 0;
|
|
41520
|
+
const outputs = {};
|
|
41521
|
+
for (const [k, v] of Object.entries(loaded.state.outputs ?? {})) if (typeof v === "string") outputs[k] = v;
|
|
41522
|
+
else if (typeof v === "number" || typeof v === "boolean") outputs[k] = String(v);
|
|
41523
|
+
else outputs[k] = JSON.stringify(v);
|
|
41524
|
+
return {
|
|
41525
|
+
resources: loaded.state.resources,
|
|
41526
|
+
outputs,
|
|
41527
|
+
region: loaded.region
|
|
41528
|
+
};
|
|
41529
|
+
}
|
|
41530
|
+
async buildCrossStackResolver(consumerRegion) {
|
|
41531
|
+
const built = await buildCrossStackResolver(consumerRegion, {
|
|
41532
|
+
statePrefix: this.opts.statePrefix,
|
|
41533
|
+
...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
|
|
41534
|
+
...this.opts.region !== void 0 && { region: this.opts.region },
|
|
41535
|
+
...this.opts.profile !== void 0 && { profile: this.opts.profile }
|
|
41536
|
+
});
|
|
41537
|
+
if (!built) return void 0;
|
|
41538
|
+
this.disposers.push(built.dispose);
|
|
41539
|
+
return built.resolver;
|
|
41540
|
+
}
|
|
41541
|
+
dispose() {
|
|
41542
|
+
while (this.disposers.length > 0) {
|
|
41543
|
+
const fn = this.disposers.pop();
|
|
41544
|
+
if (fn) try {
|
|
41545
|
+
fn();
|
|
41546
|
+
} catch {}
|
|
41547
|
+
}
|
|
41548
|
+
}
|
|
41549
|
+
};
|
|
41550
|
+
|
|
41551
|
+
//#endregion
|
|
41552
|
+
//#region src/local/cfn-local-state-provider.ts
|
|
41553
|
+
/**
|
|
41554
|
+
* `CfnLocalStateProvider` — implementation of {@link LocalStateProvider}
|
|
41555
|
+
* backed by a deployed CloudFormation stack. Powers `cdkd local *
|
|
41556
|
+
* --from-cfn-stack` (issue #606).
|
|
41557
|
+
*
|
|
41558
|
+
* The shape mirrors the SAM CLI's `sam local invoke --stack-name X`
|
|
41559
|
+
* behavior: reach into a deployed CFn stack via `DescribeStackResources`
|
|
41560
|
+
* to look up physical IDs of every same-stack resource, then make those
|
|
41561
|
+
* IDs available to the existing `state-resolver.ts` substitution engine.
|
|
41562
|
+
* This lets `cdkd local *` substitute env vars / secrets / images that
|
|
41563
|
+
* reference deployed resources in a CDK app deployed via the upstream
|
|
41564
|
+
* CDK CLI (`cdk deploy` → CloudFormation) WITHOUT first migrating the
|
|
41565
|
+
* stack to cdkd.
|
|
41566
|
+
*
|
|
41567
|
+
* Wire-format mapping:
|
|
41568
|
+
*
|
|
41569
|
+
* - `Ref: <LogicalId>` → resolved via the synthetic `ResourceState`
|
|
41570
|
+
* map built from `DescribeStackResources.StackResources[]` (one
|
|
41571
|
+
* entry per `(LogicalResourceId, PhysicalResourceId, ResourceType)`
|
|
41572
|
+
* tuple).
|
|
41573
|
+
* - `Fn::GetAtt: [<LogicalId>, <Attr>]` → **warn-and-drop**. CFn's
|
|
41574
|
+
* `DescribeStackResources` does NOT return per-attribute values
|
|
41575
|
+
* and the v1 policy (issue #606 recommendation (a)) is to surface
|
|
41576
|
+
* a per-key warn instead of pulling in the full provisioning layer
|
|
41577
|
+
* to call provider-specific describe APIs (e.g. `GetQueueAttributes`
|
|
41578
|
+
* for SQS, `GetFunction` for Lambda). Users override the affected
|
|
41579
|
+
* env var via `--env-vars` if the value is critical.
|
|
41580
|
+
* - `Fn::ImportValue: <exportName>` → resolved via `ListExports`
|
|
41581
|
+
* (paginated). Same-region only — CFn exports are region-scoped.
|
|
41582
|
+
* - `Fn::GetStackOutput` → rejected with a clear pointer that the
|
|
41583
|
+
* intrinsic is cdkd-specific (CFn has no equivalent — exports +
|
|
41584
|
+
* outputs are the only cross-stack vocabulary CFn understands).
|
|
41585
|
+
* - Stack outputs (consumed by both `Fn::GetStackOutput` and the
|
|
41586
|
+
* cross-stack-resolver's index-miss fallback) → sourced from
|
|
41587
|
+
* `DescribeStacks.Outputs[]`.
|
|
41588
|
+
*
|
|
41589
|
+
* Region handling: the provider takes a single region at construction
|
|
41590
|
+
* time (the `cdkd local *` commands resolve this from
|
|
41591
|
+
* `--stack-region` > `--region` > `AWS_REGION` > the synth-derived
|
|
41592
|
+
* region per the existing `--from-state` precedence). Cross-region
|
|
41593
|
+
* `Fn::ImportValue` is out of scope for v1 (CFn's `ListExports` is
|
|
41594
|
+
* region-scoped; a future PR can add a multi-region scan if real
|
|
41595
|
+
* usage justifies it).
|
|
41596
|
+
*
|
|
41597
|
+
* AWS API contract notes:
|
|
41598
|
+
*
|
|
41599
|
+
* - `DescribeStackResources` is unpaginated up to 500 resources (CFn's
|
|
41600
|
+
* hard stack cap). One call suffices for the entire stack.
|
|
41601
|
+
* - `DescribeStacks` is unpaginated when called with `StackName`.
|
|
41602
|
+
* - `ListExports` is paginated; the provider walks `NextToken` until
|
|
41603
|
+
* the page set is exhausted.
|
|
41604
|
+
*/
|
|
41605
|
+
var CfnLocalStateProvider = class {
|
|
41606
|
+
label = "--from-cfn-stack";
|
|
41607
|
+
cfnStackName;
|
|
41608
|
+
region;
|
|
41609
|
+
client;
|
|
41610
|
+
clientOptions;
|
|
41611
|
+
constructor(opts) {
|
|
41612
|
+
this.cfnStackName = opts.cfnStackName;
|
|
41613
|
+
this.region = opts.region;
|
|
41614
|
+
this.clientOptions = { region: opts.region };
|
|
41615
|
+
if (opts.profile !== void 0) this.clientOptions.profile = opts.profile;
|
|
41616
|
+
}
|
|
41617
|
+
getClient() {
|
|
41618
|
+
if (!this.client) this.client = new CloudFormationClient({ region: this.region });
|
|
41619
|
+
return this.client;
|
|
41620
|
+
}
|
|
41621
|
+
/**
|
|
41622
|
+
* Load the deployed CFn stack's resources + outputs and return them
|
|
41623
|
+
* as a synthetic `LocalStateRecord` (matching the shape the existing
|
|
41624
|
+
* S3-state-driven path produces). `synthRegion` is accepted for
|
|
41625
|
+
* interface parity with the S3 provider but ignored here — the
|
|
41626
|
+
* provider is region-bound at construction time.
|
|
41627
|
+
*
|
|
41628
|
+
* Best-effort: on any CFn API failure (stack not found, access
|
|
41629
|
+
* denied, throttling) the provider logs a warn and returns
|
|
41630
|
+
* `undefined`. The caller then falls back to the PR 1 warn-and-drop
|
|
41631
|
+
* behavior on every intrinsic-valued env var.
|
|
41632
|
+
*/
|
|
41633
|
+
async load(_stackName, _synthRegion) {
|
|
41634
|
+
const logger = getLogger();
|
|
41635
|
+
const client = this.getClient();
|
|
41636
|
+
let resourceMap;
|
|
41637
|
+
try {
|
|
41638
|
+
resourceMap = buildResourceStateMap((await client.send(new DescribeStackResourcesCommand({ StackName: this.cfnStackName }))).StackResources ?? []);
|
|
41639
|
+
} catch (err) {
|
|
41640
|
+
logger.warn(`${this.label}: DescribeStackResources(${this.cfnStackName}) failed: ${err instanceof Error ? err.message : String(err)}. Was the stack deployed in region '${this.region}'? Falling back.`);
|
|
41641
|
+
return;
|
|
41642
|
+
}
|
|
41643
|
+
let outputs;
|
|
41644
|
+
try {
|
|
41645
|
+
const stack = (await client.send(new DescribeStacksCommand({ StackName: this.cfnStackName }))).Stacks?.[0];
|
|
41646
|
+
if (!stack) {
|
|
41647
|
+
logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) returned no stack; outputs will be empty.`);
|
|
41648
|
+
outputs = {};
|
|
41649
|
+
} else outputs = buildOutputsMap(stack.Outputs ?? []);
|
|
41650
|
+
} catch (err) {
|
|
41651
|
+
logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) failed: ${err instanceof Error ? err.message : String(err)}. Outputs will be empty (Fn::GetStackOutput cannot resolve).`);
|
|
41652
|
+
outputs = {};
|
|
41653
|
+
}
|
|
41654
|
+
return {
|
|
41655
|
+
resources: resourceMap,
|
|
41656
|
+
outputs,
|
|
41657
|
+
region: this.region
|
|
41658
|
+
};
|
|
41659
|
+
}
|
|
41660
|
+
/**
|
|
41661
|
+
* Build a `CrossStackResolver` that resolves `Fn::ImportValue` via
|
|
41662
|
+
* `cloudformation:ListExports`. `Fn::GetStackOutput` is rejected here
|
|
41663
|
+
* — it's a cdkd-specific intrinsic with no CFn-side equivalent, and
|
|
41664
|
+
* the user-visible error message names the right intrinsic
|
|
41665
|
+
* (`Fn::ImportValue`) for that use case.
|
|
41666
|
+
*
|
|
41667
|
+
* `consumerRegion` is accepted for interface parity with the S3
|
|
41668
|
+
* provider but the `CfnLocalStateProvider` only resolves exports in
|
|
41669
|
+
* the region the stack lives in (which is the same region the
|
|
41670
|
+
* consumer Lambda runs in for the common single-region use case).
|
|
41671
|
+
* A future PR can extend this to multi-region by walking the SDK's
|
|
41672
|
+
* partition-aware region list.
|
|
41673
|
+
*/
|
|
41674
|
+
async buildCrossStackResolver(_consumerRegion) {
|
|
41675
|
+
const logger = getLogger();
|
|
41676
|
+
const client = this.getClient();
|
|
41677
|
+
const label = this.label;
|
|
41678
|
+
const region = this.region;
|
|
41679
|
+
let cachedExports;
|
|
41680
|
+
const ensureExports = async () => {
|
|
41681
|
+
if (cachedExports) return cachedExports;
|
|
41682
|
+
const result = await fetchAllExports(client).catch((err) => {
|
|
41683
|
+
logger.warn(`${label}: ListExports (${region}) failed: ${err instanceof Error ? err.message : String(err)}. Fn::ImportValue intrinsics will warn-and-drop.`);
|
|
41684
|
+
});
|
|
41685
|
+
if (result) cachedExports = result;
|
|
41686
|
+
return result;
|
|
41687
|
+
};
|
|
41688
|
+
return {
|
|
41689
|
+
async resolveImport(exportName) {
|
|
41690
|
+
const map = await ensureExports();
|
|
41691
|
+
if (!map) return void 0;
|
|
41692
|
+
return map.get(exportName);
|
|
41693
|
+
},
|
|
41694
|
+
async resolveGetStackOutput(producerStack, producerRegion, outputName) {
|
|
41695
|
+
logger.warn(`${label}: Fn::GetStackOutput '${producerStack}.${outputName}' (${producerRegion}) is a cdkd-specific intrinsic with no CloudFormation equivalent. Use Fn::ImportValue against an exported output instead, or deploy the producer stack via cdkd deploy and use --from-state.`);
|
|
41696
|
+
}
|
|
41697
|
+
};
|
|
41698
|
+
}
|
|
41699
|
+
dispose() {
|
|
41700
|
+
if (this.client) {
|
|
41701
|
+
this.client.destroy();
|
|
41702
|
+
this.client = void 0;
|
|
41703
|
+
}
|
|
41704
|
+
}
|
|
41705
|
+
};
|
|
41706
|
+
/**
|
|
41707
|
+
* Build the synthetic per-logical-id resource map from
|
|
41708
|
+
* `DescribeStackResources` output. Each `ResourceState` carries the
|
|
41709
|
+
* physical id (covers `Ref`) and the resource type; `attributes` is
|
|
41710
|
+
* left empty per issue #606's (a) recommendation — the warn-and-drop
|
|
41711
|
+
* policy on unresolvable `Fn::GetAtt` is the v1 contract. The other
|
|
41712
|
+
* `ResourceState` fields (`properties`, `dependencies`, etc.) are
|
|
41713
|
+
* also left empty since the substituter doesn't read them.
|
|
41714
|
+
*
|
|
41715
|
+
* Exported for unit testing.
|
|
41716
|
+
*/
|
|
41717
|
+
function buildResourceStateMap(stackResources) {
|
|
41718
|
+
const out = {};
|
|
41719
|
+
for (const r of stackResources) {
|
|
41720
|
+
if (!r.LogicalResourceId || !r.PhysicalResourceId || !r.ResourceType) continue;
|
|
41721
|
+
out[r.LogicalResourceId] = {
|
|
41722
|
+
physicalId: r.PhysicalResourceId,
|
|
41723
|
+
resourceType: r.ResourceType,
|
|
41724
|
+
properties: {},
|
|
41725
|
+
attributes: {},
|
|
41726
|
+
dependencies: []
|
|
41727
|
+
};
|
|
41728
|
+
}
|
|
41729
|
+
return out;
|
|
41730
|
+
}
|
|
41731
|
+
/**
|
|
41732
|
+
* Build the outputs map from `DescribeStacks.Outputs[]`. CFn outputs
|
|
41733
|
+
* are stringly typed at the wire level (key + value, with the value
|
|
41734
|
+
* always a string), so the cast is safe.
|
|
41735
|
+
*
|
|
41736
|
+
* Exported for unit testing.
|
|
41737
|
+
*/
|
|
41738
|
+
function buildOutputsMap(outputs) {
|
|
41739
|
+
const out = {};
|
|
41740
|
+
for (const o of outputs) {
|
|
41741
|
+
if (o.OutputKey === void 0 || o.OutputValue === void 0) continue;
|
|
41742
|
+
out[o.OutputKey] = o.OutputValue;
|
|
41743
|
+
}
|
|
41744
|
+
return out;
|
|
41745
|
+
}
|
|
41746
|
+
/**
|
|
41747
|
+
* Walk `ListExports` until every page is consumed and return the
|
|
41748
|
+
* `Name -> Value` map. Same-region only (CFn exports are
|
|
41749
|
+
* region-scoped); the caller picks the region at provider
|
|
41750
|
+
* construction time.
|
|
41751
|
+
*
|
|
41752
|
+
* Exported for unit testing.
|
|
41753
|
+
*/
|
|
41754
|
+
async function fetchAllExports(client) {
|
|
41755
|
+
const out = /* @__PURE__ */ new Map();
|
|
41756
|
+
let nextToken;
|
|
41757
|
+
let pages = 0;
|
|
41758
|
+
do {
|
|
41759
|
+
const resp = await client.send(new ListExportsCommand({ ...nextToken !== void 0 && { NextToken: nextToken } }));
|
|
41760
|
+
for (const exp of resp.Exports ?? []) {
|
|
41761
|
+
if (exp.Name === void 0 || exp.Value === void 0) continue;
|
|
41762
|
+
out.set(exp.Name, exp.Value);
|
|
41763
|
+
}
|
|
41764
|
+
nextToken = resp.NextToken;
|
|
41765
|
+
pages += 1;
|
|
41766
|
+
if (pages > 50) throw new Error("ListExports pagination exceeded 50 pages — likely a malformed NextToken loop.");
|
|
41767
|
+
} while (nextToken !== void 0);
|
|
41768
|
+
return out;
|
|
41769
|
+
}
|
|
41770
|
+
|
|
41771
|
+
//#endregion
|
|
41772
|
+
//#region src/cli/commands/local-state-source.ts
|
|
41773
|
+
/**
|
|
41774
|
+
* Single-source-of-truth helper that picks a {@link LocalStateProvider}
|
|
41775
|
+
* for the `cdkd local *` family from CLI flags (issue #606).
|
|
41776
|
+
*
|
|
41777
|
+
* The four `cdkd local *` commands all support two mutually-exclusive
|
|
41778
|
+
* state-source flags:
|
|
41779
|
+
*
|
|
41780
|
+
* - `--from-state` (S3-backed; reads cdkd's state for a stack
|
|
41781
|
+
* deployed via `cdkd deploy`).
|
|
41782
|
+
* - `--from-cfn-stack [<cfn-stack-name>]` (CFn-backed; reads a
|
|
41783
|
+
* deployed CloudFormation stack via `DescribeStackResources`).
|
|
41784
|
+
*
|
|
41785
|
+
* This module centralizes:
|
|
41786
|
+
*
|
|
41787
|
+
* - The mutual-exclusion check (rejected at the CLI layer before any
|
|
41788
|
+
* synth / AWS call fires).
|
|
41789
|
+
* - The bare-vs-explicit `--from-cfn-stack` resolution: bare flag uses
|
|
41790
|
+
* the cdkd stack name; explicit value overrides. Matches the
|
|
41791
|
+
* `cdkd import --migrate-from-cloudformation` precedent.
|
|
41792
|
+
* - Region resolution for the CFn client: reuses the existing
|
|
41793
|
+
* `--stack-region` flag (no separate `--cfn-stack-region`) per
|
|
41794
|
+
* issue #606 recommendation.
|
|
41795
|
+
*
|
|
41796
|
+
* Returns `undefined` when neither flag is set — the caller skips the
|
|
41797
|
+
* substitution pass entirely (which is the pre-issue-#606 behavior
|
|
41798
|
+
* when `--from-state` was absent).
|
|
41799
|
+
*/
|
|
41800
|
+
/**
|
|
41801
|
+
* Default cdkd stack name → CFn stack name. Matches the
|
|
41802
|
+
* `cdkd import --migrate-from-cloudformation` bare-form precedent:
|
|
41803
|
+
* bare `--from-cfn-stack` uses the cdkd stack name verbatim as the CFn
|
|
41804
|
+
* stack name (typical for CDK apps where the names are the same).
|
|
41805
|
+
* Override by passing `--from-cfn-stack <explicit-name>`.
|
|
41806
|
+
*
|
|
41807
|
+
* Exported for unit testing.
|
|
41808
|
+
*/
|
|
41809
|
+
function resolveCfnStackName(fromCfnStack, cdkdStackName) {
|
|
41810
|
+
if (typeof fromCfnStack === "string") return fromCfnStack;
|
|
41811
|
+
return cdkdStackName;
|
|
41812
|
+
}
|
|
41813
|
+
/**
|
|
41814
|
+
* Resolve the region used for the CFn client. The CFn provider is
|
|
41815
|
+
* region-bound at construction time; we apply the precedence chain
|
|
41816
|
+
* `--stack-region` > `--region` > `AWS_REGION` > `AWS_DEFAULT_REGION`
|
|
41817
|
+
* > the synth-derived stack region. Throws `LocalStateSourceError`
|
|
41818
|
+
* when none of these signals is set — the CFn API call needs a
|
|
41819
|
+
* concrete region and silently picking `us-east-1` would query the
|
|
41820
|
+
* wrong stack environment (worst case: succeed against the wrong
|
|
41821
|
+
* stack and return wrong physical IDs). Distinct from
|
|
41822
|
+
* `loadStateForStack`'s behavior: the S3 state bucket name is
|
|
41823
|
+
* account-scoped (not region-scoped) and the bucket's region is
|
|
41824
|
+
* auto-discovered via `GetBucketLocation`, so the S3 provider can
|
|
41825
|
+
* tolerate a missing region. The CFn provider cannot.
|
|
41826
|
+
*
|
|
41827
|
+
* Exported for unit testing.
|
|
41828
|
+
*/
|
|
41829
|
+
function resolveCfnRegion(options, synthRegion) {
|
|
41830
|
+
const region = options.stackRegion ?? options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion;
|
|
41831
|
+
if (region === void 0) throw new LocalStateSourceError("--from-cfn-stack requires a region to query CloudFormation. Set one of: --stack-region <region>, --region <region>, AWS_REGION env var, AWS_DEFAULT_REGION env var, or an env.region on the target CDK stack.");
|
|
41832
|
+
return region;
|
|
41833
|
+
}
|
|
41834
|
+
/**
|
|
41835
|
+
* Common error class for the mutual-exclusion check so the CLI layer
|
|
41836
|
+
* can surface a consistent error message from all four commands.
|
|
41837
|
+
*/
|
|
41838
|
+
var LocalStateSourceError = class extends Error {
|
|
41839
|
+
constructor(message) {
|
|
41840
|
+
super(message);
|
|
41841
|
+
this.name = "LocalStateSourceError";
|
|
41842
|
+
}
|
|
41843
|
+
};
|
|
41844
|
+
/**
|
|
41845
|
+
* Pre-flight check for `--from-cfn-stack <explicit-name>` when the
|
|
41846
|
+
* caller will construct one provider per routed stack (`local
|
|
41847
|
+
* start-api` / `local start-service`). An explicit value applies to
|
|
41848
|
+
* the SINGLE CFn stack named — when multiple cdkd stacks are routed,
|
|
41849
|
+
* every one of them would query the same CFn stack, yielding silent
|
|
41850
|
+
* wrong-physical-id substitutions for any logical id that happens to
|
|
41851
|
+
* collide between the user's stacks. Reject at the CLI layer instead.
|
|
41852
|
+
*
|
|
41853
|
+
* Bare `--from-cfn-stack` (the cdkdStackName-default) is fine for
|
|
41854
|
+
* multi-stack: each routed stack reads its own CFn counterpart.
|
|
41855
|
+
* `--from-state` is also fine: cdkd's state is per-(stack, region).
|
|
41856
|
+
*
|
|
41857
|
+
* Call this from `start-api` / `start-service` BEFORE the per-stack
|
|
41858
|
+
* `createLocalStateProvider` loop when `routedStackCount > 1`.
|
|
41859
|
+
*/
|
|
41860
|
+
function rejectExplicitCfnStackWithMultipleStacks(options, routedStackCount) {
|
|
41861
|
+
if (routedStackCount <= 1) return;
|
|
41862
|
+
if (typeof options.fromCfnStack !== "string") return;
|
|
41863
|
+
throw new LocalStateSourceError(`--from-cfn-stack <name> cannot be used with multiple routed stacks (got ${routedStackCount}). An explicit CFn stack name applies to one stack only and would silently mismap logical IDs across siblings. Use bare --from-cfn-stack (each cdkd stack uses its own name as the CFn stack name) or run one cdkd local invocation per stack.`);
|
|
41864
|
+
}
|
|
41865
|
+
/**
|
|
41866
|
+
* Pick and construct the right `LocalStateProvider` for the supplied
|
|
41867
|
+
* flag set. Returns `undefined` when neither flag is set (caller skips
|
|
41868
|
+
* the substitution pass). Throws `LocalStateSourceError` when both
|
|
41869
|
+
* flags are set (mutually exclusive — different state sources, asking
|
|
41870
|
+
* for both is ambiguous about precedence).
|
|
41871
|
+
*
|
|
41872
|
+
* `cdkdStackName` is the cdkd-side stack name the local command
|
|
41873
|
+
* resolved to its target — needed to apply the bare-`--from-cfn-stack`
|
|
41874
|
+
* default. `synthRegion` is the synth-derived stack region (`env.region`
|
|
41875
|
+
* on the CDK stack) — fallback for the CFn client when no explicit
|
|
41876
|
+
* region override is set.
|
|
41877
|
+
*
|
|
41878
|
+
* For multi-stack callers (`local start-api` / `local start-service`)
|
|
41879
|
+
* also invoke `rejectExplicitCfnStackWithMultipleStacks` BEFORE the
|
|
41880
|
+
* per-stack loop — see that helper's docstring for the rationale.
|
|
41881
|
+
*/
|
|
41882
|
+
function createLocalStateProvider(options, cdkdStackName, synthRegion) {
|
|
41883
|
+
const cfnStackOpt = options.fromCfnStack;
|
|
41884
|
+
const cfnFlagPresent = cfnStackOpt !== void 0 && cfnStackOpt !== false;
|
|
41885
|
+
if (options.fromState && cfnFlagPresent) throw new LocalStateSourceError("--from-state and --from-cfn-stack are mutually exclusive. Use --from-state for stacks deployed via `cdkd deploy`; use --from-cfn-stack for stacks deployed via `cdk deploy` (CloudFormation).");
|
|
41886
|
+
if (options.fromState) return new S3LocalStateProvider({
|
|
41887
|
+
statePrefix: options.statePrefix,
|
|
41888
|
+
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
41889
|
+
...options.region !== void 0 && { region: options.region },
|
|
41890
|
+
...options.profile !== void 0 && { profile: options.profile },
|
|
41891
|
+
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion }
|
|
41892
|
+
});
|
|
41893
|
+
if (cfnFlagPresent) return new CfnLocalStateProvider({
|
|
41894
|
+
cfnStackName: resolveCfnStackName(cfnStackOpt, cdkdStackName),
|
|
41895
|
+
region: resolveCfnRegion(options, synthRegion),
|
|
41896
|
+
...options.profile !== void 0 && { profile: options.profile }
|
|
41897
|
+
});
|
|
41898
|
+
}
|
|
41899
|
+
|
|
41142
41900
|
//#endregion
|
|
41143
41901
|
//#region src/local/intrinsic-image.ts
|
|
41144
41902
|
/**
|
|
@@ -53069,7 +53827,7 @@ async function localStartApiCommand(target, options) {
|
|
|
53069
53827
|
const m = buildCorsConfigByApiId(stack.template);
|
|
53070
53828
|
for (const [k, v] of m) corsConfigByApiId.set(k, v);
|
|
53071
53829
|
}
|
|
53072
|
-
const stateByStack = options.fromState ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options) : /* @__PURE__ */ new Map();
|
|
53830
|
+
const stateByStack = options.fromState || options.fromCfnStack !== void 0 && options.fromCfnStack !== false ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options) : /* @__PURE__ */ new Map();
|
|
53073
53831
|
const lambdaIds = uniqueLambdaIds(routes, routesWithAuth, webSocketApis);
|
|
53074
53832
|
const specs = /* @__PURE__ */ new Map();
|
|
53075
53833
|
for (let i = 0; i < lambdaIds.length; i++) {
|
|
@@ -54109,13 +54867,14 @@ function envHasIntrinsicValue$1(templateEnv) {
|
|
|
54109
54867
|
return false;
|
|
54110
54868
|
}
|
|
54111
54869
|
/**
|
|
54112
|
-
* Load
|
|
54870
|
+
* Load deployed state for every stack that owns a routed Lambda. Once
|
|
54113
54871
|
* per `synthesizeAndBuild` pass (initial boot + every reload), so a
|
|
54114
54872
|
* Lambda's per-spec build does not pay one round-trip per Lambda. Per-
|
|
54115
54873
|
* stack failures (no state, ambiguous region, bucket resolution error)
|
|
54116
|
-
* degrade to warn-and-fall-back via
|
|
54117
|
-
* affected stack's reachable Lambdas behave as if `--from-state`
|
|
54118
|
-
* not set, while sibling stacks with loadable
|
|
54874
|
+
* degrade to warn-and-fall-back via the active `LocalStateProvider` —
|
|
54875
|
+
* the affected stack's reachable Lambdas behave as if `--from-state` /
|
|
54876
|
+
* `--from-cfn-stack` were not set, while sibling stacks with loadable
|
|
54877
|
+
* state still substitute.
|
|
54119
54878
|
*
|
|
54120
54879
|
* Pseudo parameters are resolved per stack and only when at least one
|
|
54121
54880
|
* reachable Lambda in that stack has an intrinsic-valued env entry
|
|
@@ -54144,24 +54903,31 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options)
|
|
|
54144
54903
|
}
|
|
54145
54904
|
return false;
|
|
54146
54905
|
};
|
|
54906
|
+
rejectExplicitCfnStackWithMultipleStacks(options, reachableStackNames.size);
|
|
54147
54907
|
for (const stackName of reachableStackNames) {
|
|
54148
54908
|
const stack = stacks.find((s) => s.stackName === stackName);
|
|
54149
54909
|
if (!stack) continue;
|
|
54150
|
-
const
|
|
54151
|
-
|
|
54152
|
-
|
|
54153
|
-
|
|
54154
|
-
|
|
54155
|
-
|
|
54156
|
-
|
|
54157
|
-
|
|
54158
|
-
|
|
54159
|
-
|
|
54160
|
-
|
|
54161
|
-
|
|
54910
|
+
const provider = createLocalStateProvider(options, stack.stackName, stack.region);
|
|
54911
|
+
if (!provider) continue;
|
|
54912
|
+
try {
|
|
54913
|
+
const loaded = await provider.load(stack.stackName, stack.region);
|
|
54914
|
+
if (!loaded) continue;
|
|
54915
|
+
const bundle = { state: {
|
|
54916
|
+
version: 1,
|
|
54917
|
+
stackName: stack.stackName,
|
|
54918
|
+
resources: loaded.resources,
|
|
54919
|
+
outputs: loaded.outputs,
|
|
54920
|
+
lastModified: 0
|
|
54921
|
+
} };
|
|
54922
|
+
if (stackHasIntrinsicEnv(stackName)) {
|
|
54923
|
+
const pseudo = await resolvePseudoParametersForStartApi(loaded.region, options);
|
|
54924
|
+
if (pseudo) bundle.pseudoParameters = pseudo;
|
|
54925
|
+
}
|
|
54926
|
+
out.set(stackName, bundle);
|
|
54927
|
+
logger.debug(`${provider.label}: loaded state for ${stackName} (${loaded.region})`);
|
|
54928
|
+
} finally {
|
|
54929
|
+
provider.dispose();
|
|
54162
54930
|
}
|
|
54163
|
-
out.set(stackName, bundle);
|
|
54164
|
-
logger.debug(`--from-state: loaded state for ${stackName} (${loaded.region})`);
|
|
54165
54931
|
}
|
|
54166
54932
|
return out;
|
|
54167
54933
|
}
|
|
@@ -54174,7 +54940,7 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options)
|
|
|
54174
54940
|
* region takes priority).
|
|
54175
54941
|
*
|
|
54176
54942
|
* Region precedence: `--region` > `AWS_REGION` > `AWS_DEFAULT_REGION` >
|
|
54177
|
-
* the state record's region (returned by `
|
|
54943
|
+
* the state record's region (returned by the active `LocalStateProvider`).
|
|
54178
54944
|
*/
|
|
54179
54945
|
async function resolvePseudoParametersForStartApi(stateRegion, options) {
|
|
54180
54946
|
const logger = getLogger();
|
|
@@ -54238,7 +55004,7 @@ function resolveMtlsConfig(options) {
|
|
|
54238
55004
|
* Builder for the `start-api` subcommand. Wired up by `local.ts`.
|
|
54239
55005
|
*/
|
|
54240
55006
|
function createLocalStartApiCommand() {
|
|
54241
|
-
const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and REST v1 AWS_IAM (SigV4 signature verification only — IAM policy evaluation is NOT emulated; see docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--stack-region <region>", "Region of the
|
|
55007
|
+
const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and REST v1 AWS_IAM (SigV4 signature verification only — IAM policy evaluation is NOT emulated; see docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--allow-unverified-sigv4", "Opt-in: allow AWS_IAM SigV4 requests that cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) to pass through with a placeholder principalId. DEFAULT off — fail-closed so unauthenticated bypass is impossible against `event.requestContext.identity.accessKey`-trusting handler code. Use only in dev loops where you understand the risk.").default(false)).action(withErrorHandling(localStartApiCommand));
|
|
54242
55008
|
[
|
|
54243
55009
|
...commonOptions,
|
|
54244
55010
|
...appOptions,
|
|
@@ -55132,6 +55898,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
55132
55898
|
const state = createEcsRunState();
|
|
55133
55899
|
let sigintHandler;
|
|
55134
55900
|
let sigintCount = 0;
|
|
55901
|
+
let stateProvider;
|
|
55135
55902
|
let cleanupPromise;
|
|
55136
55903
|
const cleanup = async () => {
|
|
55137
55904
|
if (!cleanupPromise) cleanupPromise = (async () => {
|
|
@@ -55164,34 +55931,22 @@ async function localRunTaskCommand(target, options) {
|
|
|
55164
55931
|
...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } }
|
|
55165
55932
|
};
|
|
55166
55933
|
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
55167
|
-
const
|
|
55934
|
+
const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
|
|
55935
|
+
stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", candidate?.region);
|
|
55936
|
+
const imageContext = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
|
|
55168
55937
|
const task = resolveEcsTaskTarget(target, stacks, imageContext);
|
|
55169
55938
|
logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
|
|
55170
55939
|
const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
|
|
55171
|
-
|
|
55172
|
-
if (options.fromState && taskNeeds.needsCrossStackResolver) {
|
|
55940
|
+
if (stateProvider && taskNeeds.needsCrossStackResolver) {
|
|
55173
55941
|
const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? task.stack.region ?? "us-east-1";
|
|
55174
|
-
const
|
|
55175
|
-
|
|
55176
|
-
|
|
55177
|
-
...
|
|
55178
|
-
|
|
55942
|
+
const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
|
|
55943
|
+
if (resolver) await applyCrossStackResolverToTask(task, {
|
|
55944
|
+
resources: imageContext?.stateResources ?? {},
|
|
55945
|
+
...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
|
|
55946
|
+
consumerRegion,
|
|
55947
|
+
crossStackResolver: resolver
|
|
55179
55948
|
});
|
|
55180
|
-
|
|
55181
|
-
taskCrossStackDispose = built.dispose;
|
|
55182
|
-
try {
|
|
55183
|
-
await applyCrossStackResolverToTask(task, {
|
|
55184
|
-
resources: imageContext?.stateResources ?? {},
|
|
55185
|
-
...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
|
|
55186
|
-
consumerRegion,
|
|
55187
|
-
crossStackResolver: built.resolver
|
|
55188
|
-
});
|
|
55189
|
-
} finally {
|
|
55190
|
-
taskCrossStackDispose();
|
|
55191
|
-
taskCrossStackDispose = void 0;
|
|
55192
|
-
}
|
|
55193
|
-
}
|
|
55194
|
-
} else if (!options.fromState && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state to substitute them against deployed cdkd state.");
|
|
55949
|
+
} else if (!stateProvider && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state.");
|
|
55195
55950
|
sigintHandler = () => {
|
|
55196
55951
|
sigintCount += 1;
|
|
55197
55952
|
if (sigintCount >= 2) {
|
|
@@ -55237,6 +55992,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
55237
55992
|
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
55238
55993
|
} finally {
|
|
55239
55994
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
55995
|
+
if (stateProvider) stateProvider.dispose();
|
|
55240
55996
|
if (!options.detach) await cleanup();
|
|
55241
55997
|
}
|
|
55242
55998
|
}
|
|
@@ -55289,19 +56045,18 @@ async function assumeTaskRole$1(roleArn, region) {
|
|
|
55289
56045
|
*
|
|
55290
56046
|
* Tier 1 (pseudo parameters) fires `sts:GetCallerIdentity` once for
|
|
55291
56047
|
* `${AWS::AccountId}`; region / partition / URL suffix come from the CLI
|
|
55292
|
-
* (`--region` → env vars → synth-derived stack region). Tier 2
|
|
55293
|
-
*
|
|
55294
|
-
*
|
|
55295
|
-
*
|
|
56048
|
+
* (`--region` → env vars → synth-derived stack region). Tier 2 (state
|
|
56049
|
+
* load) routes through the active {@link LocalStateProvider} so both
|
|
56050
|
+
* `--from-state` and `--from-cfn-stack` produce the same downstream
|
|
56051
|
+
* context shape (issue #606).
|
|
55296
56052
|
*/
|
|
55297
|
-
async function buildEcsImageResolutionContext$1(
|
|
56053
|
+
async function buildEcsImageResolutionContext$1(candidate, stateProvider, options) {
|
|
55298
56054
|
const logger = getLogger();
|
|
55299
|
-
const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
|
|
55300
56055
|
if (!candidate) return void 0;
|
|
55301
56056
|
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
55302
56057
|
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
|
|
55303
56058
|
const ctx = {};
|
|
55304
|
-
const wantsPseudoForEnvOrSecret =
|
|
56059
|
+
const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
|
|
55305
56060
|
if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
|
|
55306
56061
|
const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
|
|
55307
56062
|
if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
|
|
@@ -55322,17 +56077,11 @@ async function buildEcsImageResolutionContext$1(target, stacks, options) {
|
|
|
55322
56077
|
};
|
|
55323
56078
|
}
|
|
55324
56079
|
const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
|
|
55325
|
-
if (
|
|
55326
|
-
const loaded = await
|
|
55327
|
-
|
|
55328
|
-
|
|
55329
|
-
|
|
55330
|
-
...options.region !== void 0 && { region: options.region },
|
|
55331
|
-
...options.profile !== void 0 && { profile: options.profile }
|
|
55332
|
-
});
|
|
55333
|
-
if (loaded) ctx.stateResources = loaded.state.resources;
|
|
55334
|
-
} else if (!options.fromState && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI (requires the stack to have been deployed via cdkd deploy). Otherwise the resolver will surface its existing error.");
|
|
55335
|
-
else if (!options.fromState && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state to substitute them against the deployed cdkd state. Without --from-state these entries are dropped (per-key warnings will follow).");
|
|
56080
|
+
if (stateProvider && wantsState) {
|
|
56081
|
+
const loaded = await stateProvider.load(candidate.stackName, candidate.region);
|
|
56082
|
+
if (loaded) ctx.stateResources = loaded.resources;
|
|
56083
|
+
} else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
|
|
56084
|
+
else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
|
|
55336
56085
|
return ctx;
|
|
55337
56086
|
}
|
|
55338
56087
|
function pickCandidateStack$1(stackPattern, stacks) {
|
|
@@ -55375,7 +56124,7 @@ function readEnvOverridesFile$2(filePath) {
|
|
|
55375
56124
|
return parsed;
|
|
55376
56125
|
}
|
|
55377
56126
|
function createLocalRunTaskCommand() {
|
|
55378
|
-
const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--stack-region <region>", "Region of the
|
|
56127
|
+
const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localRunTaskCommand));
|
|
55379
56128
|
[
|
|
55380
56129
|
...commonOptions,
|
|
55381
56130
|
...appOptions,
|
|
@@ -56451,6 +57200,7 @@ async function localStartServiceCommand(targets, options) {
|
|
|
56451
57200
|
warnIfDeprecatedRegion(options);
|
|
56452
57201
|
const skipPull = options.pull === false;
|
|
56453
57202
|
if (!targets || targets.length === 0) throw new LocalStartServiceError("cdkd local start-service requires at least one <target>. Pass one or more service paths like 'Stack/Orders' 'Stack/Frontend'.");
|
|
57203
|
+
rejectExplicitCfnStackWithMultipleStacks(options, targets.length);
|
|
56454
57204
|
const perTarget = targets.map((t) => ({
|
|
56455
57205
|
target: t,
|
|
56456
57206
|
runState: createServiceRunState()
|
|
@@ -56547,34 +57297,36 @@ async function localStartServiceCommand(targets, options) {
|
|
|
56547
57297
|
* outer code to wait + tear down.
|
|
56548
57298
|
*/
|
|
56549
57299
|
async function bootOneTarget(target, runState, stacks, options, discovery, skipPull) {
|
|
57300
|
+
const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
|
|
57301
|
+
const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", candidate?.region);
|
|
57302
|
+
try {
|
|
57303
|
+
return await runOneTarget(target, runState, stacks, options, discovery, skipPull, stateProvider);
|
|
57304
|
+
} finally {
|
|
57305
|
+
if (stateProvider) stateProvider.dispose();
|
|
57306
|
+
}
|
|
57307
|
+
}
|
|
57308
|
+
async function runOneTarget(target, runState, stacks, options, discovery, skipPull, stateProvider) {
|
|
56550
57309
|
const logger = getLogger();
|
|
56551
|
-
const imageContext = await buildEcsImageResolutionContext(target, stacks, options);
|
|
57310
|
+
const imageContext = await buildEcsImageResolutionContext(target, stacks, options, stateProvider);
|
|
56552
57311
|
const service = resolveEcsServiceTarget(target, stacks, imageContext);
|
|
56553
57312
|
logger.info(`Target: ${service.stack.stackName}/${service.serviceLogicalId} (service=${service.serviceName}, desiredCount=${service.desiredCount}, task=${service.task.taskDefinitionLogicalId})`);
|
|
56554
57313
|
for (const w of service.warnings) logger.warn(w);
|
|
56555
57314
|
if (service.serviceConnect) logger.info(`Service Connect: namespace='${service.serviceConnect.namespaceName}', ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
|
|
56556
57315
|
if (service.serviceRegistries.length > 0) logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);
|
|
56557
57316
|
const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === service.stack.stackName) ?? service.stack);
|
|
56558
|
-
if (
|
|
57317
|
+
if (stateProvider && taskNeeds.needsCrossStackResolver) {
|
|
56559
57318
|
const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? service.stack.region ?? "us-east-1";
|
|
56560
|
-
const
|
|
56561
|
-
|
|
56562
|
-
statePrefix: options.statePrefix,
|
|
56563
|
-
...options.region !== void 0 && { region: options.region },
|
|
56564
|
-
...options.profile !== void 0 && { profile: options.profile }
|
|
56565
|
-
});
|
|
56566
|
-
if (built) try {
|
|
57319
|
+
const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
|
|
57320
|
+
if (resolver) {
|
|
56567
57321
|
const subContext = {
|
|
56568
57322
|
resources: imageContext?.stateResources ?? {},
|
|
56569
57323
|
...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
|
|
56570
57324
|
consumerRegion,
|
|
56571
|
-
crossStackResolver:
|
|
57325
|
+
crossStackResolver: resolver
|
|
56572
57326
|
};
|
|
56573
57327
|
await applyCrossStackResolverToTask(service.task, subContext);
|
|
56574
|
-
} finally {
|
|
56575
|
-
built.dispose();
|
|
56576
57328
|
}
|
|
56577
|
-
} else if (!
|
|
57329
|
+
} else if (!stateProvider && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state.");
|
|
56578
57330
|
let assumedCredentials;
|
|
56579
57331
|
let resolvedRoleArn;
|
|
56580
57332
|
if (options.assumeTaskRole === true) {
|
|
@@ -56643,14 +57395,14 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
56643
57395
|
* the candidate stack picker differs because services and tasks share
|
|
56644
57396
|
* the same stack-pattern grammar.
|
|
56645
57397
|
*/
|
|
56646
|
-
async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
57398
|
+
async function buildEcsImageResolutionContext(target, stacks, options, stateProvider) {
|
|
56647
57399
|
const logger = getLogger();
|
|
56648
57400
|
const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
|
|
56649
57401
|
if (!candidate) return void 0;
|
|
56650
57402
|
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
56651
57403
|
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
|
|
56652
57404
|
const ctx = {};
|
|
56653
|
-
const wantsPseudoForEnvOrSecret =
|
|
57405
|
+
const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
|
|
56654
57406
|
if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
|
|
56655
57407
|
const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
|
|
56656
57408
|
if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
|
|
@@ -56671,17 +57423,11 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
|
56671
57423
|
};
|
|
56672
57424
|
}
|
|
56673
57425
|
const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
|
|
56674
|
-
if (
|
|
56675
|
-
const loaded = await
|
|
56676
|
-
|
|
56677
|
-
|
|
56678
|
-
|
|
56679
|
-
...options.region !== void 0 && { region: options.region },
|
|
56680
|
-
...options.profile !== void 0 && { profile: options.profile }
|
|
56681
|
-
});
|
|
56682
|
-
if (loaded) ctx.stateResources = loaded.state.resources;
|
|
56683
|
-
} else if (!options.fromState && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI.");
|
|
56684
|
-
else if (!options.fromState && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics. Pass --from-state to substitute them against the deployed cdkd state.");
|
|
57426
|
+
if (stateProvider && wantsState) {
|
|
57427
|
+
const loaded = await stateProvider.load(candidate.stackName, candidate.region);
|
|
57428
|
+
if (loaded) ctx.stateResources = loaded.resources;
|
|
57429
|
+
} else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI.");
|
|
57430
|
+
else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against the deployed cdkd state.");
|
|
56685
57431
|
return ctx;
|
|
56686
57432
|
}
|
|
56687
57433
|
function pickCandidateStack(stackPattern, stacks) {
|
|
@@ -56752,7 +57498,7 @@ function parseRestartPolicy(raw) {
|
|
|
56752
57498
|
throw new LocalStartServiceError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
|
|
56753
57499
|
}
|
|
56754
57500
|
function createLocalStartServiceCommand() {
|
|
56755
|
-
const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${83} due to the per-replica link-local /24 subnet allocator's range.`).default(3).argParser(parseMaxTasks)).addOption(new Option("--restart-policy <policy>", "How to react when an essential container exits. 'on-failure' (default) restarts only on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down and runs the service degraded.").default("on-failure").argParser(parseRestartPolicy)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes.").default(false)).addOption(new Option("--stack-region <region>", "Region of the
|
|
57501
|
+
const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${83} due to the per-replica link-local /24 subnet allocator's range.`).default(3).argParser(parseMaxTasks)).addOption(new Option("--restart-policy <policy>", "How to react when an essential container exits. 'on-failure' (default) restarts only on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down and runs the service degraded.").default("on-failure").argParser(parseRestartPolicy)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localStartServiceCommand));
|
|
56756
57502
|
[
|
|
56757
57503
|
...commonOptions,
|
|
56758
57504
|
...appOptions,
|
|
@@ -56866,19 +57612,19 @@ async function localInvokeCommand(target, options) {
|
|
|
56866
57612
|
let stateAudit;
|
|
56867
57613
|
let templateEnv = getTemplateEnv(lambda.resource);
|
|
56868
57614
|
let stateForRoleHint;
|
|
56869
|
-
|
|
56870
|
-
if (
|
|
56871
|
-
const loaded = await
|
|
56872
|
-
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
56873
|
-
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
56874
|
-
statePrefix: options.statePrefix,
|
|
56875
|
-
...options.region !== void 0 && { region: options.region },
|
|
56876
|
-
...options.profile !== void 0 && { profile: options.profile }
|
|
56877
|
-
});
|
|
57615
|
+
const stateProvider = createLocalStateProvider(options, lambda.stack.stackName, lambda.stack.region);
|
|
57616
|
+
if (stateProvider) try {
|
|
57617
|
+
const loaded = await stateProvider.load(lambda.stack.stackName, lambda.stack.region);
|
|
56878
57618
|
if (loaded) {
|
|
56879
|
-
stateForRoleHint =
|
|
57619
|
+
stateForRoleHint = {
|
|
57620
|
+
version: 1,
|
|
57621
|
+
stackName: lambda.stack.stackName,
|
|
57622
|
+
resources: loaded.resources,
|
|
57623
|
+
outputs: loaded.outputs,
|
|
57624
|
+
lastModified: 0
|
|
57625
|
+
};
|
|
56880
57626
|
const subContext = {
|
|
56881
|
-
resources: loaded.
|
|
57627
|
+
resources: loaded.resources,
|
|
56882
57628
|
consumerRegion: loaded.region
|
|
56883
57629
|
};
|
|
56884
57630
|
if (envHasIntrinsicValue(templateEnv)) {
|
|
@@ -56886,36 +57632,24 @@ async function localInvokeCommand(target, options) {
|
|
|
56886
57632
|
if (pseudo) subContext.pseudoParameters = pseudo;
|
|
56887
57633
|
}
|
|
56888
57634
|
if (envHasCrossStackIntrinsic(templateEnv)) {
|
|
56889
|
-
const
|
|
56890
|
-
|
|
56891
|
-
statePrefix: options.statePrefix,
|
|
56892
|
-
...options.region !== void 0 && { region: options.region },
|
|
56893
|
-
...options.profile !== void 0 && { profile: options.profile }
|
|
56894
|
-
});
|
|
56895
|
-
if (built) {
|
|
56896
|
-
subContext.crossStackResolver = built.resolver;
|
|
56897
|
-
crossStackDispose = built.dispose;
|
|
56898
|
-
}
|
|
56899
|
-
}
|
|
56900
|
-
try {
|
|
56901
|
-
const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
|
|
56902
|
-
templateEnv = env;
|
|
56903
|
-
stateAudit = audit;
|
|
56904
|
-
for (const key of audit.resolvedKeys) logger.debug(`--from-state: substituted env var ${key} from cdkd state`);
|
|
56905
|
-
for (const { key, reason } of audit.unresolved) logger.warn(`--from-state: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
|
|
56906
|
-
} finally {
|
|
56907
|
-
if (crossStackDispose) {
|
|
56908
|
-
crossStackDispose();
|
|
56909
|
-
crossStackDispose = void 0;
|
|
56910
|
-
}
|
|
57635
|
+
const resolver = await stateProvider.buildCrossStackResolver(loaded.region);
|
|
57636
|
+
if (resolver) subContext.crossStackResolver = resolver;
|
|
56911
57637
|
}
|
|
57638
|
+
const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
|
|
57639
|
+
templateEnv = env;
|
|
57640
|
+
stateAudit = audit;
|
|
57641
|
+
const label = stateProvider.label;
|
|
57642
|
+
for (const key of audit.resolvedKeys) logger.debug(`${label}: substituted env var ${key}`);
|
|
57643
|
+
for (const { key, reason } of audit.unresolved) logger.warn(`${label}: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
|
|
56912
57644
|
}
|
|
57645
|
+
} finally {
|
|
57646
|
+
stateProvider.dispose();
|
|
56913
57647
|
}
|
|
56914
57648
|
const overrides = readEnvOverridesFile(options.envVars);
|
|
56915
57649
|
const envResult = resolveEnvVars(lambda.logicalId, templateEnv, overrides);
|
|
56916
57650
|
for (const key of envResult.unresolved) {
|
|
56917
57651
|
if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;
|
|
56918
|
-
logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}) or pass --from-state to recover deployed values.`);
|
|
57652
|
+
logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}), or pass --from-state (cdkd-deployed) / --from-cfn-stack (cdk-deployed) to recover deployed values.`);
|
|
56919
57653
|
}
|
|
56920
57654
|
let resolvedAssumeRoleArn;
|
|
56921
57655
|
if (typeof options.assumeRole === "string") resolvedAssumeRoleArn = options.assumeRole;
|
|
@@ -57485,7 +58219,7 @@ function pickReferencedLogicalId(intrinsic) {
|
|
|
57485
58219
|
*/
|
|
57486
58220
|
function createLocalCommand() {
|
|
57487
58221
|
const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
|
|
57488
|
-
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--stack-region <region>", "Region of the
|
|
58222
|
+
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localInvokeCommand));
|
|
57489
58223
|
[
|
|
57490
58224
|
...commonOptions,
|
|
57491
58225
|
...appOptions,
|
|
@@ -58609,7 +59343,7 @@ function reorderArgs(argv) {
|
|
|
58609
59343
|
*/
|
|
58610
59344
|
async function main() {
|
|
58611
59345
|
const program = new Command();
|
|
58612
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
59346
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.158.0");
|
|
58613
59347
|
program.addCommand(createBootstrapCommand());
|
|
58614
59348
|
program.addCommand(createSynthCommand());
|
|
58615
59349
|
program.addCommand(createListCommand());
|