@go-to-k/cdkd 0.102.7 → 0.103.1
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 +1 -0
- package/dist/cli.js +534 -14
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-DGKtcKF6.js → deploy-engine-D6nbHjNM.js} +36 -5
- package/dist/deploy-engine-D6nbHjNM.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DGKtcKF6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-CuHRHcyW.js";
|
|
3
|
-
import { A as resolveApp, B as CdkdError, C as AssetPublisher, D as Synthesizer, E as buildDockerImage, F as warnDeprecatedNoPrefixCliFlag, G as PartialFailureError, I as AssemblyReader, J as ResourceUpdateNotSupportedError, K as ProvisioningError, M as resolveSkipPrefix, N as resolveStateBucketWithDefault, O as getDefaultStateBucketName, P as resolveStateBucketWithDefaultAndSource, R as resolveBucketRegion, S as shouldRetainResource, T as WorkGraph, U as LocalInvokeBuildError, X as StackHasActiveImportsError, Y as RouteDiscoveryError, Z as StackTerminationProtectionError, _ as DiffCalculator, a as withRetry, at as getLogger, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, ct as getLiveRenderer, d as normalizeAwsTagsToCfn, dt as generateResourceName, f as resolveExplicitPhysicalId, ft as generateResourceNameWithFallback, g as IntrinsicFunctionResolver, h as assertRegionMatch, i as withResourceDeadline, j as resolveCaptureObservedState, k as getLegacyStateBucketName, l as CDK_PATH_TAG, lt as PATTERN_B_NAME_PROPERTIES, m as CloudControlProvider, mt as withStackName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as normalizeAwsError, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as withSkipPrefix, q as ResourceTimeoutError, r as DeployEngine, rt as withErrorHandling, s as IAMRoleProvider, st as runStackBuffered, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as PATTERN_B_RESOURCE_TYPES, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-
|
|
3
|
+
import { A as resolveApp, B as CdkdError, C as AssetPublisher, D as Synthesizer, E as buildDockerImage, F as warnDeprecatedNoPrefixCliFlag, G as PartialFailureError, I as AssemblyReader, J as ResourceUpdateNotSupportedError, K as ProvisioningError, M as resolveSkipPrefix, N as resolveStateBucketWithDefault, O as getDefaultStateBucketName, P as resolveStateBucketWithDefaultAndSource, R as resolveBucketRegion, S as shouldRetainResource, T as WorkGraph, U as LocalInvokeBuildError, X as StackHasActiveImportsError, Y as RouteDiscoveryError, Z as StackTerminationProtectionError, _ as DiffCalculator, a as withRetry, at as getLogger, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, ct as getLiveRenderer, d as normalizeAwsTagsToCfn, dt as generateResourceName, f as resolveExplicitPhysicalId, ft as generateResourceNameWithFallback, g as IntrinsicFunctionResolver, h as assertRegionMatch, i as withResourceDeadline, j as resolveCaptureObservedState, k as getLegacyStateBucketName, l as CDK_PATH_TAG, lt as PATTERN_B_NAME_PROPERTIES, m as CloudControlProvider, mt as withStackName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as normalizeAwsError, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as withSkipPrefix, q as ResourceTimeoutError, r as DeployEngine, rt as withErrorHandling, s as IAMRoleProvider, st as runStackBuffered, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as PATTERN_B_RESOURCE_TYPES, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-D6nbHjNM.js";
|
|
4
4
|
import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from "node:crypto";
|
|
5
5
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectCommand, 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";
|
|
6
6
|
import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachUserPolicyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachUserPolicyCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListInstanceProfilesCommand, ListUserPoliciesCommand, ListUserTagsCommand, ListUsersCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagUserCommand, UntagUserCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
|
|
@@ -9,7 +9,7 @@ import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesComman
|
|
|
9
9
|
import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteEventSourceMappingCommand, DeleteFunctionCommand, DeleteFunctionUrlConfigCommand, DeleteLayerVersionCommand, GetEventSourceMappingCommand, GetFunctionCommand, GetFunctionUrlConfigCommand, GetLayerVersionByArnCommand, GetPolicyCommand, LambdaClient, ListFunctionsCommand, ListLayersCommand, ListTagsCommand, PublishLayerVersionCommand, RemovePermissionCommand, ResourceNotFoundException, TagResourceCommand as TagResourceCommand$1, UntagResourceCommand as UntagResourceCommand$1, UpdateEventSourceMappingCommand, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
10
10
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
11
11
|
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";
|
|
12
|
-
import { CreateTableCommand, DeleteTableCommand, DescribeTableCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand } from "@aws-sdk/client-dynamodb";
|
|
12
|
+
import { CreateTableCommand, DeleteTableCommand, DescribeTableCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
|
|
13
13
|
import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
14
14
|
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";
|
|
15
15
|
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";
|
|
@@ -30,14 +30,14 @@ import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCo
|
|
|
30
30
|
import { promisify } from "node:util";
|
|
31
31
|
import { CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$7, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$9 } from "@aws-sdk/client-ecr";
|
|
32
32
|
import graphlib from "graphlib";
|
|
33
|
+
import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBSubnetGroupCommand, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBSubnetGroupCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBSubnetGroupCommand, RDSClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
|
|
33
34
|
import { Command, Option } from "commander";
|
|
34
35
|
import { writeFileSync as writeFileSync$1 } from "fs";
|
|
35
36
|
import { join as join$1 } from "path";
|
|
36
37
|
import * as zlib from "node:zlib";
|
|
37
38
|
import { ApiGatewayV2Client, CreateApiCommand, CreateAuthorizerCommand as CreateAuthorizerCommand$1, CreateIntegrationCommand, CreateRouteCommand as CreateRouteCommand$1, CreateStageCommand as CreateStageCommand$1, DeleteApiCommand, DeleteAuthorizerCommand as DeleteAuthorizerCommand$1, DeleteIntegrationCommand, DeleteRouteCommand as DeleteRouteCommand$1, DeleteStageCommand as DeleteStageCommand$1, GetApiCommand, GetApisCommand, GetAuthorizerCommand as GetAuthorizerCommand$1, GetIntegrationCommand, GetRouteCommand, GetStageCommand as GetStageCommand$1, NotFoundException as NotFoundException$3, UpdateApiCommand, UpdateAuthorizerCommand as UpdateAuthorizerCommand$1, UpdateIntegrationCommand, UpdateRouteCommand, UpdateStageCommand as UpdateStageCommand$1 } from "@aws-sdk/client-apigatewayv2";
|
|
38
|
-
import { CreateStateMachineCommand, DeleteStateMachineCommand, DescribeStateMachineCommand, ListStateMachinesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$
|
|
39
|
-
import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, ListClustersCommand, ListServicesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$
|
|
40
|
-
import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBSubnetGroupCommand, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBSubnetGroupCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$10, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBSubnetGroupCommand, RDSClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
|
|
39
|
+
import { CreateStateMachineCommand, DeleteStateMachineCommand, DescribeStateMachineCommand, ListStateMachinesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$9, SFNClient, StateMachineDoesNotExist, TagResourceCommand as TagResourceCommand$10, UntagResourceCommand as UntagResourceCommand$9, UpdateStateMachineCommand } from "@aws-sdk/client-sfn";
|
|
40
|
+
import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, ListClustersCommand, ListServicesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$10, PutClusterCapacityProvidersCommand, RegisterTaskDefinitionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$10, UpdateClusterCommand, UpdateServiceCommand } from "@aws-sdk/client-ecs";
|
|
41
41
|
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";
|
|
42
42
|
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";
|
|
43
43
|
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";
|
|
@@ -7839,6 +7839,524 @@ var DynamoDBTableProvider = class {
|
|
|
7839
7839
|
}
|
|
7840
7840
|
};
|
|
7841
7841
|
|
|
7842
|
+
//#endregion
|
|
7843
|
+
//#region src/provisioning/providers/dynamodb-globaltable-provider.ts
|
|
7844
|
+
/**
|
|
7845
|
+
* AWS DynamoDB GlobalTable Provider
|
|
7846
|
+
*
|
|
7847
|
+
* Implements resource provisioning for AWS::DynamoDB::GlobalTable using the
|
|
7848
|
+
* standard DynamoDB SDK (2019.11.21 API generation, NOT the legacy
|
|
7849
|
+
* 2017.11.29 endpoints). CDK v2's `dynamodb.TableV2` construct synthesizes
|
|
7850
|
+
* as this type.
|
|
7851
|
+
*
|
|
7852
|
+
* WHY a dedicated SDK provider:
|
|
7853
|
+
* - Pre-PR the type fell through to Cloud Control API which did not pass
|
|
7854
|
+
* a `TableName` field, so AWS auto-generated random names like
|
|
7855
|
+
* `yq2phLewTEUtzr4sy2gYFRU4I-1OGJ0UFLOKOOV` instead of the cdkd
|
|
7856
|
+
* `${stackName}-X<hash>` shape (Issue #383).
|
|
7857
|
+
* - Per memory rule `feedback_dedicated_provider_over_special_case.md`,
|
|
7858
|
+
* the consistent fix is a dedicated SDK Provider rather than adding
|
|
7859
|
+
* the type to `FALLBACK_NAME_RULES`.
|
|
7860
|
+
*
|
|
7861
|
+
* **CRITICAL**: do NOT use the legacy 2017.11.29 endpoints
|
|
7862
|
+
* (`CreateGlobalTableCommand` / `UpdateGlobalTableCommand` /
|
|
7863
|
+
* `DescribeGlobalTableCommand`). The CFn type `AWS::DynamoDB::GlobalTable`
|
|
7864
|
+
* is the 2019.11.21 generation, which uses the regular DynamoDB CRUD API
|
|
7865
|
+
* (`CreateTableCommand` + `UpdateTableCommand` with `ReplicaUpdates`).
|
|
7866
|
+
*
|
|
7867
|
+
* MVP scope:
|
|
7868
|
+
* - `update()` throws `ResourceUpdateNotSupportedError`. In-place GlobalTable
|
|
7869
|
+
* updates (replica add/remove, GSI add/remove, BillingMode flip, throughput
|
|
7870
|
+
* rewrites) are out of scope and a follow-up PR.
|
|
7871
|
+
* - `getDriftUnknownPaths` declares TTL + throughput-settings paths because
|
|
7872
|
+
* cdkd's create/update flows surface them but the read-side reverse
|
|
7873
|
+
* mapping is non-trivial and would fire false drift.
|
|
7874
|
+
* - Per-replica drift (`ContributorInsightsSpecification` /
|
|
7875
|
+
* `PointInTimeRecoverySpecification` / `KinesisStreamSpecification`) is
|
|
7876
|
+
* out of scope for v1.
|
|
7877
|
+
*/
|
|
7878
|
+
var DynamoDBGlobalTableProvider = class {
|
|
7879
|
+
dynamoDBClient;
|
|
7880
|
+
logger = getLogger().child("DynamoDBGlobalTableProvider");
|
|
7881
|
+
attributeCache = /* @__PURE__ */ new Map();
|
|
7882
|
+
handledProperties = new Map([["AWS::DynamoDB::GlobalTable", new Set([
|
|
7883
|
+
"TableName",
|
|
7884
|
+
"KeySchema",
|
|
7885
|
+
"AttributeDefinitions",
|
|
7886
|
+
"BillingMode",
|
|
7887
|
+
"StreamSpecification",
|
|
7888
|
+
"GlobalSecondaryIndexes",
|
|
7889
|
+
"LocalSecondaryIndexes",
|
|
7890
|
+
"SSESpecification",
|
|
7891
|
+
"Replicas",
|
|
7892
|
+
"TableClass",
|
|
7893
|
+
"TimeToLiveSpecification",
|
|
7894
|
+
"WriteProvisionedThroughputSettings",
|
|
7895
|
+
"WriteOnDemandThroughputSettings",
|
|
7896
|
+
"DeletionProtectionEnabled",
|
|
7897
|
+
"Tags"
|
|
7898
|
+
])]]);
|
|
7899
|
+
constructor() {
|
|
7900
|
+
const awsClients = getAwsClients();
|
|
7901
|
+
this.dynamoDBClient = awsClients.dynamoDB;
|
|
7902
|
+
}
|
|
7903
|
+
/**
|
|
7904
|
+
* Create a DynamoDB Global Table (CDK TableV2).
|
|
7905
|
+
*
|
|
7906
|
+
* GlobalTable is built on the regular DynamoDB Table primitive: cdkd issues
|
|
7907
|
+
* `CreateTableCommand` first (which only creates the table in the local
|
|
7908
|
+
* region), waits for `ACTIVE`, then issues one `UpdateTableCommand` per
|
|
7909
|
+
* additional replica region via `ReplicaUpdates: [{ Create: {...} }]`.
|
|
7910
|
+
*
|
|
7911
|
+
* Streams must be enabled with `NEW_AND_OLD_IMAGES` when the table has any
|
|
7912
|
+
* cross-region replicas — AWS rejects the replica-add otherwise. cdkd
|
|
7913
|
+
* auto-enables them with an info log when the template omits it.
|
|
7914
|
+
*
|
|
7915
|
+
* Partial-create cleanup (PR #374-class): if any post-`CreateTableCommand`
|
|
7916
|
+
* wiring (wait ACTIVE → replica adds → TTL → Tags) throws, cdkd issues a
|
|
7917
|
+
* best-effort `DeleteTableCommand` so AWS is not left holding a billing
|
|
7918
|
+
* orphan with no cdkd state record. Cleanup failures escalate to WARN
|
|
7919
|
+
* with the exact `aws dynamodb delete-table --table-name <id>` recovery
|
|
7920
|
+
* command.
|
|
7921
|
+
*/
|
|
7922
|
+
async create(logicalId, resourceType, properties) {
|
|
7923
|
+
this.logger.debug(`Creating DynamoDB GlobalTable ${logicalId}`);
|
|
7924
|
+
const tableName = properties["TableName"] || generateResourceName(logicalId, { maxLength: 255 });
|
|
7925
|
+
const keySchema = properties["KeySchema"];
|
|
7926
|
+
const attributeDefinitions = properties["AttributeDefinitions"];
|
|
7927
|
+
if (!keySchema) throw new ProvisioningError(`KeySchema is required for DynamoDB GlobalTable ${logicalId}`, resourceType, logicalId);
|
|
7928
|
+
if (!attributeDefinitions) throw new ProvisioningError(`AttributeDefinitions is required for DynamoDB GlobalTable ${logicalId}`, resourceType, logicalId);
|
|
7929
|
+
const billingMode = properties["BillingMode"] ?? "PAY_PER_REQUEST";
|
|
7930
|
+
const currentRegion = await this.dynamoDBClient.config.region() ?? "";
|
|
7931
|
+
const replicas = properties["Replicas"] ?? [];
|
|
7932
|
+
const createParams = {
|
|
7933
|
+
TableName: tableName,
|
|
7934
|
+
KeySchema: keySchema,
|
|
7935
|
+
AttributeDefinitions: attributeDefinitions,
|
|
7936
|
+
BillingMode: billingMode
|
|
7937
|
+
};
|
|
7938
|
+
if (billingMode === "PROVISIONED") {
|
|
7939
|
+
const writeAutoScaling = properties["WriteProvisionedThroughputSettings"]?.["WriteCapacityAutoScalingSettings"];
|
|
7940
|
+
const writeCapacity = Number(writeAutoScaling?.["MinCapacity"] ?? 5);
|
|
7941
|
+
const readAutoScaling = (replicas.find((r) => r["Region"] === currentRegion)?.["ReadProvisionedThroughputSettings"])?.["ReadCapacityAutoScalingSettings"];
|
|
7942
|
+
createParams.ProvisionedThroughput = {
|
|
7943
|
+
ReadCapacityUnits: Number(readAutoScaling?.["MinCapacity"] ?? 5),
|
|
7944
|
+
WriteCapacityUnits: writeCapacity
|
|
7945
|
+
};
|
|
7946
|
+
}
|
|
7947
|
+
const streamSpecInput = properties["StreamSpecification"];
|
|
7948
|
+
const needsStream = replicas.some((r) => r["Region"] !== currentRegion) || replicas.length > 1;
|
|
7949
|
+
if (streamSpecInput) createParams.StreamSpecification = {
|
|
7950
|
+
StreamEnabled: true,
|
|
7951
|
+
StreamViewType: streamSpecInput["StreamViewType"] ?? "NEW_AND_OLD_IMAGES"
|
|
7952
|
+
};
|
|
7953
|
+
else if (needsStream) {
|
|
7954
|
+
this.logger.info(`Auto-enabling streams (NEW_AND_OLD_IMAGES) on ${logicalId} — required for cross-region replication`);
|
|
7955
|
+
createParams.StreamSpecification = {
|
|
7956
|
+
StreamEnabled: true,
|
|
7957
|
+
StreamViewType: "NEW_AND_OLD_IMAGES"
|
|
7958
|
+
};
|
|
7959
|
+
}
|
|
7960
|
+
if (properties["GlobalSecondaryIndexes"]) createParams.GlobalSecondaryIndexes = properties["GlobalSecondaryIndexes"];
|
|
7961
|
+
if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
|
|
7962
|
+
if (properties["SSESpecification"]) {
|
|
7963
|
+
const sse = properties["SSESpecification"];
|
|
7964
|
+
const sseInput = { Enabled: sse["SSEEnabled"] !== void 0 ? Boolean(sse["SSEEnabled"]) : true };
|
|
7965
|
+
if (sse["SSEType"]) sseInput.SSEType = sse["SSEType"];
|
|
7966
|
+
createParams.SSESpecification = sseInput;
|
|
7967
|
+
}
|
|
7968
|
+
if (properties["DeletionProtectionEnabled"] !== void 0) createParams.DeletionProtectionEnabled = properties["DeletionProtectionEnabled"];
|
|
7969
|
+
if (properties["TableClass"]) createParams.TableClass = properties["TableClass"];
|
|
7970
|
+
const wodts = properties["WriteOnDemandThroughputSettings"];
|
|
7971
|
+
if (wodts?.["MaxWriteRequestUnits"] !== void 0) createParams.OnDemandThroughput = { MaxWriteRequestUnits: Number(wodts["MaxWriteRequestUnits"]) };
|
|
7972
|
+
if (properties["Tags"]) createParams.Tags = properties["Tags"];
|
|
7973
|
+
try {
|
|
7974
|
+
await this.dynamoDBClient.send(new CreateTableCommand(createParams));
|
|
7975
|
+
this.logger.debug(`CreateTable initiated for ${tableName}, waiting for ACTIVE`);
|
|
7976
|
+
} catch (error) {
|
|
7977
|
+
const cause = error instanceof Error ? error : void 0;
|
|
7978
|
+
throw new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, tableName, cause);
|
|
7979
|
+
}
|
|
7980
|
+
try {
|
|
7981
|
+
const tableInfo = await this.waitForTableActive(tableName);
|
|
7982
|
+
for (const replica of replicas) {
|
|
7983
|
+
const region = replica["Region"];
|
|
7984
|
+
if (!region || region === currentRegion) continue;
|
|
7985
|
+
await this.addReplica(tableName, replica, region);
|
|
7986
|
+
}
|
|
7987
|
+
if (properties["TimeToLiveSpecification"]) {
|
|
7988
|
+
const ttl = properties["TimeToLiveSpecification"];
|
|
7989
|
+
const attributeName = ttl["AttributeName"];
|
|
7990
|
+
const enabled = ttl["Enabled"] !== void 0 ? Boolean(ttl["Enabled"]) : true;
|
|
7991
|
+
if (attributeName) await this.dynamoDBClient.send(new UpdateTimeToLiveCommand({
|
|
7992
|
+
TableName: tableName,
|
|
7993
|
+
TimeToLiveSpecification: {
|
|
7994
|
+
Enabled: enabled,
|
|
7995
|
+
AttributeName: attributeName
|
|
7996
|
+
}
|
|
7997
|
+
}));
|
|
7998
|
+
}
|
|
7999
|
+
this.logger.debug(`Successfully created DynamoDB GlobalTable ${logicalId}: ${tableName}`);
|
|
8000
|
+
return {
|
|
8001
|
+
physicalId: tableName,
|
|
8002
|
+
attributes: {
|
|
8003
|
+
Arn: tableInfo.tableArn,
|
|
8004
|
+
TableId: tableInfo.tableId,
|
|
8005
|
+
StreamArn: tableInfo.streamArn,
|
|
8006
|
+
TableName: tableName
|
|
8007
|
+
}
|
|
8008
|
+
};
|
|
8009
|
+
} catch (wiringError) {
|
|
8010
|
+
this.logger.warn(`Wiring failed after CreateTable for ${tableName}; attempting best-effort cleanup`);
|
|
8011
|
+
try {
|
|
8012
|
+
await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
|
|
8013
|
+
} catch (cleanupErr) {
|
|
8014
|
+
const cleanupMsg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr);
|
|
8015
|
+
this.logger.warn(`Partial-create cleanup failed for ${tableName}: ${cleanupMsg}. Run: aws dynamodb delete-table --table-name ${tableName} to remove the orphaned AWS-side table.`);
|
|
8016
|
+
}
|
|
8017
|
+
const cause = wiringError instanceof Error ? wiringError : void 0;
|
|
8018
|
+
throw new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${wiringError instanceof Error ? wiringError.message : String(wiringError)}`, resourceType, logicalId, tableName, cause);
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
/**
|
|
8022
|
+
* Add a single replica region. Issues one `UpdateTableCommand` with
|
|
8023
|
+
* `ReplicaUpdates: [{ Create: { RegionName, ... } }]` and polls
|
|
8024
|
+
* `DescribeTable` until the replica's `ReplicaStatus` flips to ACTIVE.
|
|
8025
|
+
* Capped at 10 minutes per replica (AWS Replica provisioning typically
|
|
8026
|
+
* takes 1–5 min).
|
|
8027
|
+
*/
|
|
8028
|
+
async addReplica(tableName, replica, region) {
|
|
8029
|
+
const create = { RegionName: region };
|
|
8030
|
+
if (replica["KMSMasterKeyId"]) create.KMSMasterKeyId = replica["KMSMasterKeyId"];
|
|
8031
|
+
if (replica["GlobalSecondaryIndexes"]) create.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
|
|
8032
|
+
if (replica["TableClassOverride"]) create.TableClassOverride = replica["TableClassOverride"];
|
|
8033
|
+
const replicaUpdates = [{ Create: create }];
|
|
8034
|
+
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
8035
|
+
TableName: tableName,
|
|
8036
|
+
ReplicaUpdates: replicaUpdates
|
|
8037
|
+
}));
|
|
8038
|
+
await this.waitForReplicaActive(tableName, region);
|
|
8039
|
+
}
|
|
8040
|
+
/**
|
|
8041
|
+
* Update a DynamoDB Global Table.
|
|
8042
|
+
*
|
|
8043
|
+
* MVP: in-place updates are out of scope — replica add/remove, GSI
|
|
8044
|
+
* add/remove, BillingMode flip, and throughput rewrites each have
|
|
8045
|
+
* distinct UpdateTable shapes and ordering rules. `cdkd drift --revert`
|
|
8046
|
+
* surfaces this as `ResourceUpdateNotSupportedError` (exit code 2);
|
|
8047
|
+
* the user falls back to `cdkd deploy --replace` or destroy + redeploy.
|
|
8048
|
+
*/
|
|
8049
|
+
async update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
|
|
8050
|
+
throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "GlobalTable in-place updates are not yet supported; use 'cdkd deploy --replace' or destroy + redeploy");
|
|
8051
|
+
}
|
|
8052
|
+
/**
|
|
8053
|
+
* Delete a DynamoDB Global Table.
|
|
8054
|
+
*
|
|
8055
|
+
* Order is load-bearing:
|
|
8056
|
+
* 1. Optional `--remove-protection` flip-off (idempotent).
|
|
8057
|
+
* 2. `DescribeTable` → drop every non-local replica via UpdateTable
|
|
8058
|
+
* `ReplicaUpdates: [{ Delete: { RegionName } }]`, one at a time
|
|
8059
|
+
* (AWS rejects multiple replica updates per call). Each delete
|
|
8060
|
+
* polls until the replica disappears from `Replicas[]`.
|
|
8061
|
+
* 3. `DeleteTableCommand` on the local region only.
|
|
8062
|
+
* 4. Wait for `ResourceNotFoundException` on subsequent DescribeTable.
|
|
8063
|
+
*
|
|
8064
|
+
* DELETE idempotency: a `ResourceNotFoundException` is treated as
|
|
8065
|
+
* success ONLY when the client region matches the state region
|
|
8066
|
+
* (`assertRegionMatch`). A mismatched destroy would otherwise silently
|
|
8067
|
+
* strip a still-existing resource from state.
|
|
8068
|
+
*/
|
|
8069
|
+
async delete(logicalId, physicalId, resourceType, _properties, context) {
|
|
8070
|
+
this.logger.debug(`Deleting DynamoDB GlobalTable ${logicalId}: ${physicalId}`);
|
|
8071
|
+
if (context?.removeProtection === true) try {
|
|
8072
|
+
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
8073
|
+
TableName: physicalId,
|
|
8074
|
+
DeletionProtectionEnabled: false
|
|
8075
|
+
}));
|
|
8076
|
+
this.logger.debug(`Disabled DeletionProtectionEnabled on ${logicalId}, waiting for ACTIVE`);
|
|
8077
|
+
try {
|
|
8078
|
+
await this.waitForTableActiveAfterUpdate(physicalId);
|
|
8079
|
+
} catch (waitErr) {
|
|
8080
|
+
this.logger.debug(`Could not wait for table ${physicalId} ACTIVE after protection flip: ${waitErr instanceof Error ? waitErr.message : String(waitErr)}`);
|
|
8081
|
+
}
|
|
8082
|
+
} catch (flipError) {
|
|
8083
|
+
if (!(flipError instanceof ResourceNotFoundException$1)) this.logger.debug(`Could not disable DeletionProtectionEnabled on ${physicalId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`);
|
|
8084
|
+
}
|
|
8085
|
+
let currentRegion;
|
|
8086
|
+
try {
|
|
8087
|
+
currentRegion = await this.dynamoDBClient.config.region();
|
|
8088
|
+
} catch (regionErr) {
|
|
8089
|
+
const cause = regionErr instanceof Error ? regionErr : void 0;
|
|
8090
|
+
throw new ProvisioningError(`Could not resolve client region for DynamoDB GlobalTable ${logicalId} delete — would risk dropping the local replica`, resourceType, logicalId, physicalId, cause);
|
|
8091
|
+
}
|
|
8092
|
+
try {
|
|
8093
|
+
const replicas = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table?.Replicas ?? [];
|
|
8094
|
+
for (const replica of replicas) {
|
|
8095
|
+
const region = replica.RegionName;
|
|
8096
|
+
if (!region || region === currentRegion) continue;
|
|
8097
|
+
try {
|
|
8098
|
+
await this.dynamoDBClient.send(new UpdateTableCommand({
|
|
8099
|
+
TableName: physicalId,
|
|
8100
|
+
ReplicaUpdates: [{ Delete: { RegionName: region } }]
|
|
8101
|
+
}));
|
|
8102
|
+
await this.waitForReplicaGone(physicalId, region);
|
|
8103
|
+
} catch (replicaErr) {
|
|
8104
|
+
if (!(replicaErr instanceof ResourceNotFoundException$1)) throw replicaErr;
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
} catch (describeErr) {
|
|
8108
|
+
if (!(describeErr instanceof ResourceNotFoundException$1)) {
|
|
8109
|
+
const cause = describeErr instanceof Error ? describeErr : void 0;
|
|
8110
|
+
throw new ProvisioningError(`Failed to describe DynamoDB GlobalTable ${logicalId} before delete: ${describeErr instanceof Error ? describeErr.message : String(describeErr)}`, resourceType, logicalId, physicalId, cause);
|
|
8111
|
+
}
|
|
8112
|
+
}
|
|
8113
|
+
try {
|
|
8114
|
+
await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: physicalId }));
|
|
8115
|
+
await this.waitForTableGone(physicalId);
|
|
8116
|
+
this.logger.debug(`Successfully deleted DynamoDB GlobalTable ${logicalId}`);
|
|
8117
|
+
} catch (error) {
|
|
8118
|
+
if (error instanceof ResourceNotFoundException$1) {
|
|
8119
|
+
assertRegionMatch(await this.dynamoDBClient.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
8120
|
+
this.logger.debug(`DynamoDB GlobalTable ${physicalId} does not exist, skipping`);
|
|
8121
|
+
return;
|
|
8122
|
+
}
|
|
8123
|
+
const cause = error instanceof Error ? error : void 0;
|
|
8124
|
+
throw new ProvisioningError(`Failed to delete DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
/**
|
|
8128
|
+
* Resolve a single `Fn::GetAtt` attribute for an existing DynamoDB GlobalTable.
|
|
8129
|
+
*
|
|
8130
|
+
* Cached per `(physicalId, attribute)` for the deploy run to avoid
|
|
8131
|
+
* repeated `DescribeTable` calls.
|
|
8132
|
+
*/
|
|
8133
|
+
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
8134
|
+
const cacheKey = `${physicalId}::${attributeName}`;
|
|
8135
|
+
if (this.attributeCache.has(cacheKey)) return this.attributeCache.get(cacheKey);
|
|
8136
|
+
try {
|
|
8137
|
+
const resp = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }));
|
|
8138
|
+
let value;
|
|
8139
|
+
switch (attributeName) {
|
|
8140
|
+
case "Arn":
|
|
8141
|
+
value = resp.Table?.TableArn;
|
|
8142
|
+
break;
|
|
8143
|
+
case "StreamArn":
|
|
8144
|
+
value = resp.Table?.LatestStreamArn;
|
|
8145
|
+
break;
|
|
8146
|
+
case "TableId":
|
|
8147
|
+
value = resp.Table?.TableId;
|
|
8148
|
+
break;
|
|
8149
|
+
default: value = void 0;
|
|
8150
|
+
}
|
|
8151
|
+
this.attributeCache.set(cacheKey, value);
|
|
8152
|
+
return value;
|
|
8153
|
+
} catch (err) {
|
|
8154
|
+
if (err instanceof ResourceNotFoundException$1) return void 0;
|
|
8155
|
+
throw err;
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
/**
|
|
8159
|
+
* Read the AWS-current DynamoDB GlobalTable configuration in CFn-property shape.
|
|
8160
|
+
*
|
|
8161
|
+
* Reverse-maps `DescribeTable` + `ListTagsOfResource` into the
|
|
8162
|
+
* `AWS::DynamoDB::GlobalTable` property set.
|
|
8163
|
+
*
|
|
8164
|
+
* Type-discriminator gating (memory rule
|
|
8165
|
+
* `feedback_always_emit_check_type_discriminator.md`):
|
|
8166
|
+
* - `ProvisionedThroughput`-bearing fields are only surfaced when
|
|
8167
|
+
* `BillingMode === 'PROVISIONED'`. Emitting placeholders on
|
|
8168
|
+
* PAY_PER_REQUEST tables (or vice versa) would fire false drift on
|
|
8169
|
+
* every clean run.
|
|
8170
|
+
* - StreamSpecification / SSESpecification follow the existing
|
|
8171
|
+
* DynamoDB::Table provider's Class 1 guard: only surfaced when AWS
|
|
8172
|
+
* reports the feature actually enabled.
|
|
8173
|
+
*
|
|
8174
|
+
* `getDriftUnknownPaths` declares TTL + write-throughput-settings —
|
|
8175
|
+
* those round-trip in the create path but the reverse-mapping is not
|
|
8176
|
+
* yet implemented and would fire guaranteed false drift.
|
|
8177
|
+
*/
|
|
8178
|
+
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
8179
|
+
try {
|
|
8180
|
+
const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
|
|
8181
|
+
if (!table) return void 0;
|
|
8182
|
+
const result = {};
|
|
8183
|
+
if (table.TableName !== void 0) result["TableName"] = table.TableName;
|
|
8184
|
+
if (table.KeySchema) result["KeySchema"] = table.KeySchema;
|
|
8185
|
+
if (table.AttributeDefinitions) result["AttributeDefinitions"] = table.AttributeDefinitions;
|
|
8186
|
+
const billingMode = table.BillingModeSummary?.BillingMode;
|
|
8187
|
+
if (billingMode) result["BillingMode"] = billingMode;
|
|
8188
|
+
if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) result["GlobalSecondaryIndexes"] = table.GlobalSecondaryIndexes;
|
|
8189
|
+
if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) result["LocalSecondaryIndexes"] = table.LocalSecondaryIndexes;
|
|
8190
|
+
if (table.StreamSpecification?.StreamEnabled && table.StreamSpecification.StreamViewType) result["StreamSpecification"] = {
|
|
8191
|
+
StreamEnabled: true,
|
|
8192
|
+
StreamViewType: table.StreamSpecification.StreamViewType
|
|
8193
|
+
};
|
|
8194
|
+
if (table.SSEDescription?.Status === "ENABLED") {
|
|
8195
|
+
const sse = { SSEEnabled: true };
|
|
8196
|
+
if (table.SSEDescription.SSEType !== void 0) sse["SSEType"] = table.SSEDescription.SSEType;
|
|
8197
|
+
result["SSESpecification"] = sse;
|
|
8198
|
+
}
|
|
8199
|
+
result["Replicas"] = (table.Replicas ?? []).map((r) => ({
|
|
8200
|
+
Region: r.RegionName,
|
|
8201
|
+
...r.KMSMasterKeyId !== void 0 && { KMSMasterKeyId: r.KMSMasterKeyId }
|
|
8202
|
+
}));
|
|
8203
|
+
if (table.TableClassSummary?.TableClass) result["TableClass"] = table.TableClassSummary.TableClass;
|
|
8204
|
+
if (table.DeletionProtectionEnabled !== void 0) result["DeletionProtectionEnabled"] = table.DeletionProtectionEnabled;
|
|
8205
|
+
if (table.TableArn) try {
|
|
8206
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: table.TableArn }))).Tags);
|
|
8207
|
+
} catch (err) {
|
|
8208
|
+
if (err instanceof ResourceNotFoundException$1) return void 0;
|
|
8209
|
+
result["Tags"] = [];
|
|
8210
|
+
}
|
|
8211
|
+
else result["Tags"] = [];
|
|
8212
|
+
return result;
|
|
8213
|
+
} catch (err) {
|
|
8214
|
+
if (err instanceof ResourceNotFoundException$1) return void 0;
|
|
8215
|
+
throw err;
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
/**
|
|
8219
|
+
* State property paths cdkd's GlobalTable readCurrentState cannot (yet)
|
|
8220
|
+
* reverse-map. The drift comparator skips these so a templated value
|
|
8221
|
+
* doesn't fire guaranteed false drift on every clean run.
|
|
8222
|
+
*
|
|
8223
|
+
* - `TimeToLiveSpecification`: cdkd's create() applies it via
|
|
8224
|
+
* UpdateTimeToLive, but the reverse-mapping needs a separate
|
|
8225
|
+
* DescribeTimeToLive call (not yet implemented).
|
|
8226
|
+
* - `WriteProvisionedThroughputSettings` /
|
|
8227
|
+
* `WriteOnDemandThroughputSettings`: CFn's shapes wrap
|
|
8228
|
+
* auto-scaling / on-demand max-RU settings whose reverse-mapping
|
|
8229
|
+
* from `DescribeTable.ProvisionedThroughput` / `OnDemandThroughput`
|
|
8230
|
+
* is non-trivial and would fire false drift in v1.
|
|
8231
|
+
*/
|
|
8232
|
+
getDriftUnknownPaths(_resourceType) {
|
|
8233
|
+
return [
|
|
8234
|
+
"TimeToLiveSpecification",
|
|
8235
|
+
"WriteProvisionedThroughputSettings",
|
|
8236
|
+
"WriteOnDemandThroughputSettings"
|
|
8237
|
+
];
|
|
8238
|
+
}
|
|
8239
|
+
/**
|
|
8240
|
+
* Adopt an existing DynamoDB GlobalTable into cdkd state.
|
|
8241
|
+
*
|
|
8242
|
+
* Lookup order:
|
|
8243
|
+
* 1. `--resource` override or `Properties.TableName` → verify via DescribeTable.
|
|
8244
|
+
* 2. `ListTables` + `ListTagsOfResource`, match `aws:cdk:path` tag.
|
|
8245
|
+
*
|
|
8246
|
+
* Same shape as `DynamoDBTableProvider.import()`. The provider returns
|
|
8247
|
+
* the physical id only; cdkd's import flow does the attribute capture
|
|
8248
|
+
* separately.
|
|
8249
|
+
*/
|
|
8250
|
+
async import(input) {
|
|
8251
|
+
const explicit = resolveExplicitPhysicalId(input, "TableName");
|
|
8252
|
+
if (explicit) try {
|
|
8253
|
+
await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: explicit }));
|
|
8254
|
+
return {
|
|
8255
|
+
physicalId: explicit,
|
|
8256
|
+
attributes: {}
|
|
8257
|
+
};
|
|
8258
|
+
} catch (err) {
|
|
8259
|
+
if (err instanceof ResourceNotFoundException$1) return null;
|
|
8260
|
+
throw err;
|
|
8261
|
+
}
|
|
8262
|
+
if (!input.cdkPath) return null;
|
|
8263
|
+
let exclusiveStartTableName;
|
|
8264
|
+
do {
|
|
8265
|
+
const list = await this.dynamoDBClient.send(new ListTablesCommand({ ...exclusiveStartTableName && { ExclusiveStartTableName: exclusiveStartTableName } }));
|
|
8266
|
+
for (const name of list.TableNames ?? []) try {
|
|
8267
|
+
const arn = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: name }))).Table?.TableArn;
|
|
8268
|
+
if (!arn) continue;
|
|
8269
|
+
if (matchesCdkPath((await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: arn }))).Tags, input.cdkPath)) return {
|
|
8270
|
+
physicalId: name,
|
|
8271
|
+
attributes: {}
|
|
8272
|
+
};
|
|
8273
|
+
} catch (err) {
|
|
8274
|
+
if (err instanceof ResourceNotFoundException$1) continue;
|
|
8275
|
+
throw err;
|
|
8276
|
+
}
|
|
8277
|
+
exclusiveStartTableName = list.LastEvaluatedTableName;
|
|
8278
|
+
} while (exclusiveStartTableName);
|
|
8279
|
+
return null;
|
|
8280
|
+
}
|
|
8281
|
+
async waitForTableActive(tableName, maxAttempts = 120) {
|
|
8282
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
8283
|
+
const response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
|
|
8284
|
+
const status = response.Table?.TableStatus;
|
|
8285
|
+
this.logger.debug(`Table ${tableName} status: ${status} (attempt ${attempt}/${maxAttempts})`);
|
|
8286
|
+
if (status === "ACTIVE") return {
|
|
8287
|
+
tableArn: response.Table?.TableArn,
|
|
8288
|
+
tableId: response.Table?.TableId,
|
|
8289
|
+
streamArn: response.Table?.LatestStreamArn
|
|
8290
|
+
};
|
|
8291
|
+
if (status !== "CREATING" && status !== "UPDATING") throw new Error(`Unexpected table status: ${status}`);
|
|
8292
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8293
|
+
}
|
|
8294
|
+
throw new Error(`Table ${tableName} did not reach ACTIVE within ${maxAttempts}s`);
|
|
8295
|
+
}
|
|
8296
|
+
/**
|
|
8297
|
+
* Wait for the table to reach ACTIVE after an UpdateTable call. Unlike
|
|
8298
|
+
* `waitForTableActive`, this tolerates any non-terminal status — the
|
|
8299
|
+
* table may already be ACTIVE on the no-op path (already-disabled
|
|
8300
|
+
* protection) or transition through UPDATING.
|
|
8301
|
+
*/
|
|
8302
|
+
async waitForTableActiveAfterUpdate(tableName, maxAttempts = 120) {
|
|
8303
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
8304
|
+
if ((await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.TableStatus === "ACTIVE") return;
|
|
8305
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8306
|
+
}
|
|
8307
|
+
throw new Error(`Table ${tableName} did not reach ACTIVE within ${maxAttempts}s after UpdateTable`);
|
|
8308
|
+
}
|
|
8309
|
+
/**
|
|
8310
|
+
* Wait until a specific replica's `ReplicaStatus` flips to ACTIVE.
|
|
8311
|
+
* Replica provisioning typically takes 1–5 min; cap at 10 min.
|
|
8312
|
+
*/
|
|
8313
|
+
async waitForReplicaActive(tableName, region, maxAttempts = 600) {
|
|
8314
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
8315
|
+
const replica = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region);
|
|
8316
|
+
if (replica?.ReplicaStatus === "ACTIVE") return;
|
|
8317
|
+
this.logger.debug(`Replica ${region} status: ${replica?.ReplicaStatus} (attempt ${attempt}/${maxAttempts})`);
|
|
8318
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8319
|
+
}
|
|
8320
|
+
throw new Error(`Replica ${region} for table ${tableName} did not reach ACTIVE within ${maxAttempts}s`);
|
|
8321
|
+
}
|
|
8322
|
+
/**
|
|
8323
|
+
* Wait until a specific replica disappears from `Replicas[]` after a
|
|
8324
|
+
* Delete replica update. Replica deletion typically takes 1–5 min;
|
|
8325
|
+
* cap at 10 min.
|
|
8326
|
+
*/
|
|
8327
|
+
async waitForReplicaGone(tableName, region, maxAttempts = 600) {
|
|
8328
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
8329
|
+
if (!(await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region)) return;
|
|
8330
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8331
|
+
} catch (err) {
|
|
8332
|
+
if (err instanceof ResourceNotFoundException$1) return;
|
|
8333
|
+
throw err;
|
|
8334
|
+
}
|
|
8335
|
+
throw new Error(`Replica ${region} for table ${tableName} did not disappear within ${maxAttempts}s`);
|
|
8336
|
+
}
|
|
8337
|
+
/**
|
|
8338
|
+
* Wait for `DescribeTable` to return `ResourceNotFoundException`,
|
|
8339
|
+
* confirming the table has actually been removed. `DeleteTable` is
|
|
8340
|
+
* async — the call returns immediately with `TableStatus: DELETING`
|
|
8341
|
+
* and AWS only removes the table some seconds later. Without this
|
|
8342
|
+
* wait, downstream observers (siblings deleted in the same destroy
|
|
8343
|
+
* run, integ scripts that re-check via `aws dynamodb describe-table`)
|
|
8344
|
+
* see "destroy succeeded" but the table is still listed by AWS.
|
|
8345
|
+
* Typical small-table delete completes in 5–30s; cap at 10 min for
|
|
8346
|
+
* worst-case large-table / replica-cascade scenarios.
|
|
8347
|
+
*/
|
|
8348
|
+
async waitForTableGone(tableName, maxAttempts = 600) {
|
|
8349
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
8350
|
+
await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
|
|
8351
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8352
|
+
} catch (err) {
|
|
8353
|
+
if (err instanceof ResourceNotFoundException$1) return;
|
|
8354
|
+
throw err;
|
|
8355
|
+
}
|
|
8356
|
+
throw new Error(`Table ${tableName} did not disappear within ${maxAttempts}s`);
|
|
8357
|
+
}
|
|
8358
|
+
};
|
|
8359
|
+
|
|
7842
8360
|
//#endregion
|
|
7843
8361
|
//#region src/provisioning/providers/logs-loggroup-provider.ts
|
|
7844
8362
|
/**
|
|
@@ -14812,7 +15330,7 @@ var StepFunctionsProvider = class {
|
|
|
14812
15330
|
result["EncryptionConfiguration"] = ec;
|
|
14813
15331
|
}
|
|
14814
15332
|
try {
|
|
14815
|
-
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$
|
|
15333
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: physicalId }))).tags);
|
|
14816
15334
|
} catch (err) {
|
|
14817
15335
|
if (!(err instanceof StateMachineDoesNotExist)) throw err;
|
|
14818
15336
|
}
|
|
@@ -14850,7 +15368,7 @@ var StepFunctionsProvider = class {
|
|
|
14850
15368
|
const list = await this.getClient().send(new ListStateMachinesCommand({ ...nextToken && { nextToken } }));
|
|
14851
15369
|
for (const sm of list.stateMachines ?? []) {
|
|
14852
15370
|
if (!sm.stateMachineArn) continue;
|
|
14853
|
-
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$
|
|
15371
|
+
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: sm.stateMachineArn }));
|
|
14854
15372
|
if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
|
|
14855
15373
|
physicalId: sm.stateMachineArn,
|
|
14856
15374
|
attributes: {}
|
|
@@ -15786,7 +16304,7 @@ var ECSProvider = class {
|
|
|
15786
16304
|
do {
|
|
15787
16305
|
const list = await this.getClient().send(new ListClustersCommand({ ...nextToken && { nextToken } }));
|
|
15788
16306
|
for (const arn of list.clusterArns ?? []) {
|
|
15789
|
-
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$
|
|
16307
|
+
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$10({ resourceArn: arn }));
|
|
15790
16308
|
if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
|
|
15791
16309
|
physicalId: arn.substring(arn.lastIndexOf("/") + 1),
|
|
15792
16310
|
attributes: {}
|
|
@@ -15813,7 +16331,7 @@ var ECSProvider = class {
|
|
|
15813
16331
|
...svcToken && { nextToken: svcToken }
|
|
15814
16332
|
}));
|
|
15815
16333
|
for (const svcArn of svcList.serviceArns ?? []) {
|
|
15816
|
-
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$
|
|
16334
|
+
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$10({ resourceArn: svcArn }));
|
|
15817
16335
|
if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
|
|
15818
16336
|
physicalId: `${clusterArn}|${svcArn.substring(svcArn.lastIndexOf("/") + 1)}`,
|
|
15819
16337
|
attributes: {}
|
|
@@ -17142,7 +17660,7 @@ var RDSProvider = class {
|
|
|
17142
17660
|
*/
|
|
17143
17661
|
async attachTags(result, arn) {
|
|
17144
17662
|
try {
|
|
17145
|
-
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$
|
|
17663
|
+
result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: arn }))).TagList);
|
|
17146
17664
|
} catch (err) {
|
|
17147
17665
|
this.logger.debug(`RDS ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17148
17666
|
}
|
|
@@ -17165,7 +17683,7 @@ var RDSProvider = class {
|
|
|
17165
17683
|
const list = await this.getClient().send(new DescribeDBInstancesCommand({ ...marker && { Marker: marker } }));
|
|
17166
17684
|
for (const inst of list.DBInstances ?? []) {
|
|
17167
17685
|
if (!inst.DBInstanceIdentifier || !inst.DBInstanceArn) continue;
|
|
17168
|
-
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$
|
|
17686
|
+
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: inst.DBInstanceArn }))).TagList, input.cdkPath)) return {
|
|
17169
17687
|
physicalId: inst.DBInstanceIdentifier,
|
|
17170
17688
|
attributes: {}
|
|
17171
17689
|
};
|
|
@@ -17192,7 +17710,7 @@ var RDSProvider = class {
|
|
|
17192
17710
|
const list = await this.getClient().send(new DescribeDBClustersCommand({ ...marker && { Marker: marker } }));
|
|
17193
17711
|
for (const c of list.DBClusters ?? []) {
|
|
17194
17712
|
if (!c.DBClusterIdentifier || !c.DBClusterArn) continue;
|
|
17195
|
-
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$
|
|
17713
|
+
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: c.DBClusterArn }))).TagList, input.cdkPath)) return {
|
|
17196
17714
|
physicalId: c.DBClusterIdentifier,
|
|
17197
17715
|
attributes: {}
|
|
17198
17716
|
};
|
|
@@ -17219,7 +17737,7 @@ var RDSProvider = class {
|
|
|
17219
17737
|
const list = await this.getClient().send(new DescribeDBSubnetGroupsCommand({ ...marker && { Marker: marker } }));
|
|
17220
17738
|
for (const sg of list.DBSubnetGroups ?? []) {
|
|
17221
17739
|
if (!sg.DBSubnetGroupName || !sg.DBSubnetGroupArn) continue;
|
|
17222
|
-
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$
|
|
17740
|
+
if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: sg.DBSubnetGroupArn }))).TagList, input.cdkPath)) return {
|
|
17223
17741
|
physicalId: sg.DBSubnetGroupName,
|
|
17224
17742
|
attributes: {}
|
|
17225
17743
|
};
|
|
@@ -27905,6 +28423,7 @@ function registerAllProviders(registry) {
|
|
|
27905
28423
|
registry.register("AWS::Lambda::EventSourceMapping", new LambdaEventSourceMappingProvider());
|
|
27906
28424
|
registry.register("AWS::Lambda::LayerVersion", new LambdaLayerVersionProvider());
|
|
27907
28425
|
registry.register("AWS::DynamoDB::Table", new DynamoDBTableProvider());
|
|
28426
|
+
registry.register("AWS::DynamoDB::GlobalTable", new DynamoDBGlobalTableProvider());
|
|
27908
28427
|
registry.register("AWS::Logs::LogGroup", new LogsLogGroupProvider());
|
|
27909
28428
|
registry.register("AWS::CloudWatch::Alarm", new CloudWatchAlarmProvider());
|
|
27910
28429
|
registry.register("AWS::SecretsManager::Secret", new SecretsManagerSecretProvider());
|
|
@@ -29427,6 +29946,7 @@ const PROTECTION_PROPERTY_BY_TYPE = {
|
|
|
29427
29946
|
"AWS::Neptune::DBCluster": "DeletionProtection",
|
|
29428
29947
|
"AWS::Neptune::DBInstance": "DeletionProtection",
|
|
29429
29948
|
"AWS::DynamoDB::Table": "DeletionProtectionEnabled",
|
|
29949
|
+
"AWS::DynamoDB::GlobalTable": "DeletionProtectionEnabled",
|
|
29430
29950
|
"AWS::EC2::Instance": "DisableApiTermination",
|
|
29431
29951
|
"AWS::Cognito::UserPool": "DeletionProtection",
|
|
29432
29952
|
"AWS::AutoScaling::AutoScalingGroup": "DeletionProtection"
|
|
@@ -42932,7 +43452,7 @@ function reorderArgs(argv) {
|
|
|
42932
43452
|
*/
|
|
42933
43453
|
async function main() {
|
|
42934
43454
|
const program = new Command();
|
|
42935
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
43455
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.103.1");
|
|
42936
43456
|
program.addCommand(createBootstrapCommand());
|
|
42937
43457
|
program.addCommand(createSynthCommand());
|
|
42938
43458
|
program.addCommand(createListCommand());
|