@go-to-k/cdkd 0.278.21 → 0.278.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-BN1QMZlz.js → asg-provider-ir7DmsIU.js} +2 -2
- package/dist/{asg-provider-BN1QMZlz.js.map → asg-provider-ir7DmsIU.js.map} +1 -1
- package/dist/cli.js +347 -49
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-dvS1K_ET.js → deploy-engine-D7KckQ5N.js} +52 -6
- package/dist/{deploy-engine-dvS1K_ET.js.map → deploy-engine-D7KckQ5N.js.map} +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-
|
|
2
|
+
import { $ as isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-D7KckQ5N.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-ir7DmsIU.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
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, 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";
|
|
7
|
+
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, 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";
|
|
8
8
|
import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachRolePolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreatePolicyCommand, CreatePolicyVersionCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeletePolicyCommand, DeletePolicyVersionCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachRolePolicyCommand, DetachUserPolicyCommand, GetAccessKeyLastUsedCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetPolicyCommand, GetPolicyVersionCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListEntitiesForPolicyCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListPoliciesCommand, ListPolicyTagsCommand, ListPolicyVersionsCommand, ListUserPoliciesCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagPolicyCommand, TagUserCommand, UntagPolicyCommand, UntagUserCommand, UpdateAccessKeyCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
|
|
9
9
|
import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, GetQueueUrlCommand, ListQueueTagsCommand, QueueDoesNotExist, SQSClient, SetQueueAttributesCommand, TagQueueCommand, UntagQueueCommand } from "@aws-sdk/client-sqs";
|
|
10
10
|
import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesCommand, GetTopicAttributesCommand, InvalidParameterException, ListSubscriptionsByTopicCommand, ListTagsForResourceCommand, ListTopicsCommand, NotFoundException, SNSClient, SetTopicAttributesCommand, SubscribeCommand, TagResourceCommand, UnsubscribeCommand, UntagResourceCommand } from "@aws-sdk/client-sns";
|
|
@@ -52,7 +52,7 @@ import { AddCustomAttributesCommand, CognitoIdentityProviderClient, CreateUserPo
|
|
|
52
52
|
import { CreateHttpNamespaceCommand, CreatePrivateDnsNamespaceCommand, CreatePublicDnsNamespaceCommand, CreateServiceCommand as CreateServiceCommand$1, DeleteNamespaceCommand, DeleteServiceAttributesCommand, DeleteServiceCommand as DeleteServiceCommand$1, GetNamespaceCommand, GetOperationCommand, GetServiceAttributesCommand, GetServiceCommand, ListNamespacesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, NamespaceNotFound, ServiceDiscoveryClient, ServiceNotFound, TagResourceCommand as TagResourceCommand$15, UntagResourceCommand as UntagResourceCommand$15, UpdateHttpNamespaceCommand, UpdatePrivateDnsNamespaceCommand, UpdatePublicDnsNamespaceCommand, UpdateServiceAttributesCommand, UpdateServiceCommand as UpdateServiceCommand$1 } from "@aws-sdk/client-servicediscovery";
|
|
53
53
|
import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$16, UntagResourceCommand as UntagResourceCommand$16, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
|
|
54
54
|
import { parse, print } from "graphql";
|
|
55
|
-
import { CrawlerRunningException, 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, GetJobCommand, GetSecurityConfigurationCommand, GetTableCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
|
|
55
|
+
import { ConcurrentModificationException, CrawlerRunningException, 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, GetJobCommand, GetSecurityConfigurationCommand, GetTableCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
|
|
56
56
|
import { BudgetsClient, CreateBudgetCommand, CreateNotificationCommand, CreateSubscriberCommand, DeleteBudgetCommand, DeleteNotificationCommand, DeleteSubscriberCommand, DescribeBudgetCommand, DuplicateRecordException, NotFoundException as NotFoundException$5, TagResourceCommand as TagResourceCommand$17, UntagResourceCommand as UntagResourceCommand$17, UpdateBudgetCommand } from "@aws-sdk/client-budgets";
|
|
57
57
|
import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, DescribeStreamSummaryCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListTagsForResourceCommand as ListTagsForResourceCommand$15, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$10, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$18, UntagResourceCommand as UntagResourceCommand$18, UpdateShardCountCommand, UpdateStreamModeCommand } from "@aws-sdk/client-kinesis";
|
|
58
58
|
import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, ResourceNotFoundException as ResourceNotFoundException$11, SchedulerClient, UpdateScheduleCommand } from "@aws-sdk/client-scheduler";
|
|
@@ -4868,7 +4868,7 @@ function coerceCfnBoolean(value) {
|
|
|
4868
4868
|
* S3's CreateBucket is synchronous - no polling needed, unlike CC API which
|
|
4869
4869
|
* requires async polling (1s→1.5s→2.25s...) adding seconds per resource.
|
|
4870
4870
|
*/
|
|
4871
|
-
var S3BucketProvider = class {
|
|
4871
|
+
var S3BucketProvider = class S3BucketProvider {
|
|
4872
4872
|
s3Client;
|
|
4873
4873
|
logger = getLogger().child("S3BucketProvider");
|
|
4874
4874
|
handledProperties = /* @__PURE__ */ new Map([["AWS::S3::Bucket", /* @__PURE__ */ new Set([
|
|
@@ -5142,6 +5142,7 @@ var S3BucketProvider = class {
|
|
|
5142
5142
|
* - BucketKeyEnabled
|
|
5143
5143
|
*/
|
|
5144
5144
|
async applyBucketEncryption(bucketName, encryptionConfig) {
|
|
5145
|
+
if (!Array.isArray(encryptionConfig?.ServerSideEncryptionConfiguration)) throw new Error(`BucketEncryption.ServerSideEncryptionConfiguration must be an array (got ${encryptionConfig?.ServerSideEncryptionConfiguration === void 0 ? "undefined" : typeof encryptionConfig.ServerSideEncryptionConfiguration}) — check for an unresolved intrinsic or a mis-nested template value`);
|
|
5145
5146
|
const rules = encryptionConfig.ServerSideEncryptionConfiguration.map((rule) => {
|
|
5146
5147
|
const byDefault = rule["ServerSideEncryptionByDefault"];
|
|
5147
5148
|
return {
|
|
@@ -5525,30 +5526,91 @@ var S3BucketProvider = class {
|
|
|
5525
5526
|
this.logger.debug(`Applied object lock configuration to bucket ${bucketName}`);
|
|
5526
5527
|
}
|
|
5527
5528
|
/**
|
|
5528
|
-
* Apply additional bucket configuration after creation
|
|
5529
|
+
* Apply additional bucket configuration after creation.
|
|
5530
|
+
*
|
|
5531
|
+
* `options.skipDiffManaged` is passed by `update()` for the three
|
|
5532
|
+
* sub-configs that moved onto the diff path (`VersioningConfiguration` /
|
|
5533
|
+
* `OwnershipControls` / `BucketEncryption`, issue #1466). They stay here for
|
|
5534
|
+
* CREATE — where there is no previous side to diff against — but on UPDATE
|
|
5535
|
+
* they must go through `applySubConfigDiffs` so a REMOVED property is reset
|
|
5536
|
+
* to the CloudFormation default instead of silently surviving. Applying them
|
|
5537
|
+
* in both places would double-PUT.
|
|
5529
5538
|
*/
|
|
5530
|
-
async applyConfiguration(bucketName, properties,
|
|
5539
|
+
async applyConfiguration(bucketName, properties, options = {}) {
|
|
5540
|
+
const { skipTags = false, skipDiffManaged = false } = options;
|
|
5531
5541
|
const versioningConfig = properties["VersioningConfiguration"];
|
|
5532
|
-
if (versioningConfig) await this.applyVersioning(bucketName, versioningConfig);
|
|
5542
|
+
if (!skipDiffManaged && versioningConfig) await this.applyVersioning(bucketName, versioningConfig);
|
|
5533
5543
|
const tags = properties["Tags"];
|
|
5534
5544
|
if (!skipTags && tags && Array.isArray(tags) && tags.length > 0) await this.applyTags(bucketName, tags);
|
|
5535
5545
|
const ownershipControls = properties["OwnershipControls"];
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
Bucket: bucketName,
|
|
5539
|
-
OwnershipControls: { Rules: ownershipControls.Rules.map((r) => ({ ObjectOwnership: r.ObjectOwnership })) }
|
|
5540
|
-
}));
|
|
5541
|
-
this.logger.debug(`Applied ownership controls to bucket ${bucketName}`);
|
|
5542
|
-
}
|
|
5546
|
+
const normalizedOwnership = S3BucketProvider.emptyListConfigToUndefined(ownershipControls, "Rules");
|
|
5547
|
+
if (!skipDiffManaged && normalizedOwnership !== void 0) await this.applyOwnershipControls(bucketName, normalizedOwnership);
|
|
5543
5548
|
const publicAccessBlock = properties["PublicAccessBlockConfiguration"];
|
|
5544
5549
|
if (publicAccessBlock) await this.applyPublicAccessBlockConfiguration(bucketName, publicAccessBlock);
|
|
5545
|
-
const
|
|
5546
|
-
if (
|
|
5550
|
+
const normalizedEncryption = S3BucketProvider.emptyListConfigToUndefined(properties["BucketEncryption"], "ServerSideEncryptionConfiguration");
|
|
5551
|
+
if (!skipDiffManaged && normalizedEncryption !== void 0) await this.applyBucketEncryption(bucketName, normalizedEncryption);
|
|
5552
|
+
}
|
|
5553
|
+
/**
|
|
5554
|
+
* CFn property: OwnershipControls
|
|
5555
|
+
* SDK: PutBucketOwnershipControls
|
|
5556
|
+
*/
|
|
5557
|
+
async applyOwnershipControls(bucketName, ownershipControls) {
|
|
5558
|
+
if (!Array.isArray(ownershipControls?.Rules)) throw new Error(`OwnershipControls.Rules must be an array (got ${ownershipControls?.Rules === void 0 ? "undefined" : typeof ownershipControls.Rules}) — check for an unresolved intrinsic or a mis-nested template value`);
|
|
5559
|
+
await this.s3Client.send(new PutBucketOwnershipControlsCommand({
|
|
5560
|
+
Bucket: bucketName,
|
|
5561
|
+
OwnershipControls: { Rules: ownershipControls.Rules.map((r) => ({ ObjectOwnership: r.ObjectOwnership })) }
|
|
5562
|
+
}));
|
|
5563
|
+
this.logger.debug(`Applied ownership controls to bucket ${bucketName}`);
|
|
5564
|
+
}
|
|
5565
|
+
/**
|
|
5566
|
+
* Normalize an "empty container" sub-config value to `undefined` so the diff
|
|
5567
|
+
* path cannot mistake a placeholder for a real value.
|
|
5568
|
+
*
|
|
5569
|
+
* `readCurrentState` always-emits placeholder shapes for un-configured
|
|
5570
|
+
* features (`BucketEncryption: { ServerSideEncryptionConfiguration: [] }`,
|
|
5571
|
+
* and the `OwnershipControls: { Rules: [] }` added with issue #1466), and a
|
|
5572
|
+
* `cdkd drift --revert` round-trip feeds those back through `update()`.
|
|
5573
|
+
*
|
|
5574
|
+
* The rule is "empty container == not declared", applied to BOTH sides. Per
|
|
5575
|
+
* pairing that means:
|
|
5576
|
+
* empty desired + absent previous -> no call (without this, a Put with an
|
|
5577
|
+
* empty list, which both APIs reject)
|
|
5578
|
+
* absent desired + empty previous -> no call (without this, a Delete
|
|
5579
|
+
* against a bucket that never had it)
|
|
5580
|
+
* empty desired + REAL previous -> Delete. This one is NOT suppressed
|
|
5581
|
+
* and must not be: it is a genuine
|
|
5582
|
+
* declared -> not-declared transition,
|
|
5583
|
+
* which is exactly what CloudFormation
|
|
5584
|
+
* removes.
|
|
5585
|
+
*
|
|
5586
|
+
* "Empty" is deliberately NARROW. Only two shapes fold to `undefined`:
|
|
5587
|
+
* a block with NO keys at all (`{}`), and a block whose list key holds an
|
|
5588
|
+
* empty array. Everything else — a non-object, an array where the object
|
|
5589
|
+
* belongs (wrong nesting), a block whose list key is absent but which
|
|
5590
|
+
* carries OTHER keys, or a non-array list (an unresolved intrinsic) — is
|
|
5591
|
+
* MALFORMED, not empty, and passes through so the apply call refuses it by
|
|
5592
|
+
* name. Folding any of those to `undefined` would emit a Delete and
|
|
5593
|
+
* silently downgrade a live bucket (KMS -> AES256) while the template still
|
|
5594
|
+
* declares the config.
|
|
5595
|
+
*
|
|
5596
|
+
* NOTE this helper does NOT cover `VersioningConfiguration` (it has no list
|
|
5597
|
+
* key), and that property is NOT malformed-shape-refused anywhere: a
|
|
5598
|
+
* malformed value there still resolves to 'Suspended', matching `main`.
|
|
5599
|
+
* That pre-existing gap is tracked in issue #1471 — see the note in
|
|
5600
|
+
* `applySubConfigDiffs` for why guarding it is not as simple as it looks.
|
|
5601
|
+
*/
|
|
5602
|
+
static emptyListConfigToUndefined(config, listKey) {
|
|
5603
|
+
if (config === void 0 || config === null) return void 0;
|
|
5604
|
+
if (typeof config !== "object" || Array.isArray(config)) return config;
|
|
5605
|
+
const list = config[listKey];
|
|
5606
|
+
if (list === void 0 || list === null) return Object.keys(config).length === 0 ? void 0 : config;
|
|
5607
|
+
if (Array.isArray(list) && list.length === 0) return void 0;
|
|
5608
|
+
return config;
|
|
5547
5609
|
}
|
|
5548
5610
|
/**
|
|
5549
5611
|
* Diff CFn-shape sub-config values between previous and new state.
|
|
5550
5612
|
*
|
|
5551
|
-
*
|
|
5613
|
+
* Four transitions:
|
|
5552
5614
|
* - undefined -> defined (value differs from previous, OR previous undefined): Put
|
|
5553
5615
|
* - defined -> undefined: Delete
|
|
5554
5616
|
* - defined -> defined (different): Put
|
|
@@ -5593,13 +5655,33 @@ var S3BucketProvider = class {
|
|
|
5593
5655
|
* Apply the diff between previous and new sub-configs, issuing Put / Delete
|
|
5594
5656
|
* SDK calls only for differing keys. Called from `update()`.
|
|
5595
5657
|
*
|
|
5596
|
-
*
|
|
5597
|
-
*
|
|
5598
|
-
*
|
|
5599
|
-
*
|
|
5600
|
-
*
|
|
5658
|
+
* PublicAccessBlockConfiguration and Tags stay on `applyConfiguration` /
|
|
5659
|
+
* `applyTagDiff`. `PublicAccessBlockConfiguration` is deliberately NOT on the
|
|
5660
|
+
* diff path: a live CloudFormation A/B (issue #1466) confirmed CFn ALSO
|
|
5661
|
+
* leaves the block in place when the property is removed, so cdkd is at
|
|
5662
|
+
* parity and adding an `onRemove` here would CREATE a divergence.
|
|
5663
|
+
*
|
|
5664
|
+
* VersioningConfiguration / OwnershipControls / BucketEncryption used to sit
|
|
5665
|
+
* on that always-PUT path too, on the assumption that their APIs had no clean
|
|
5666
|
+
* "delete" counterpart. That was wrong in all three cases and made removal a
|
|
5667
|
+
* silent no-op (issue #1466): the same A/B showed CFn suspends versioning,
|
|
5668
|
+
* deletes ownership controls, and resets encryption to the SSE-S3 default.
|
|
5669
|
+
* They are now diffed here like every other Put/Delete pair.
|
|
5601
5670
|
*/
|
|
5602
5671
|
async applySubConfigDiffs(bucketName, properties, previousProperties) {
|
|
5672
|
+
const previousVersioning = previousProperties["VersioningConfiguration"];
|
|
5673
|
+
const nextVersioning = properties["VersioningConfiguration"];
|
|
5674
|
+
const versioningChanged = JSON.stringify(previousVersioning ?? null) !== JSON.stringify(nextVersioning ?? null);
|
|
5675
|
+
const desiredVersioningStatus = nextVersioning == null ? "Suspended" : nextVersioning["Status"] || "Suspended";
|
|
5676
|
+
if (versioningChanged && nextVersioning != null && desiredVersioningStatus !== "Suspended") await this.applyVersioning(bucketName, nextVersioning);
|
|
5677
|
+
await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["OwnershipControls"], "Rules"), S3BucketProvider.emptyListConfigToUndefined(properties["OwnershipControls"], "Rules"), async (cfg) => this.applyOwnershipControls(bucketName, cfg), async () => {
|
|
5678
|
+
await this.s3Client.send(new DeleteBucketOwnershipControlsCommand({ Bucket: bucketName }));
|
|
5679
|
+
this.logger.debug(`Deleted ownership controls on bucket ${bucketName}`);
|
|
5680
|
+
});
|
|
5681
|
+
await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["BucketEncryption"], "ServerSideEncryptionConfiguration"), S3BucketProvider.emptyListConfigToUndefined(properties["BucketEncryption"], "ServerSideEncryptionConfiguration"), async (cfg) => this.applyBucketEncryption(bucketName, cfg), async () => {
|
|
5682
|
+
await this.s3Client.send(new DeleteBucketEncryptionCommand({ Bucket: bucketName }));
|
|
5683
|
+
this.logger.debug(`Deleted bucket encryption on bucket ${bucketName}`);
|
|
5684
|
+
});
|
|
5603
5685
|
await this.diffSubConfig(bucketName, previousProperties["LifecycleConfiguration"], properties["LifecycleConfiguration"], async (cfg) => {
|
|
5604
5686
|
if (!cfg.Rules || !Array.isArray(cfg.Rules) || cfg.Rules.length === 0) return;
|
|
5605
5687
|
await this.applyLifecycleConfiguration(bucketName, cfg);
|
|
@@ -5660,6 +5742,14 @@ var S3BucketProvider = class {
|
|
|
5660
5742
|
}));
|
|
5661
5743
|
this.logger.debug(`Deleted inventory configuration ${id} on bucket ${bucketName}`);
|
|
5662
5744
|
});
|
|
5745
|
+
if (versioningChanged && desiredVersioningStatus === "Suspended") {
|
|
5746
|
+
const hasObjectLock = (p) => {
|
|
5747
|
+
if (p["ObjectLockEnabled"] === true || p["ObjectLockEnabled"] === "true") return true;
|
|
5748
|
+
return p["ObjectLockConfiguration"]?.["ObjectLockEnabled"] === "Enabled";
|
|
5749
|
+
};
|
|
5750
|
+
if (hasObjectLock(properties) || hasObjectLock(previousProperties)) this.logger.warn(`Bucket ${bucketName}: versioning would be suspended (VersioningConfiguration removed or set to Suspended), but the bucket has Object Lock enabled and S3 does not allow suspending versioning on it. Leaving versioning enabled.`);
|
|
5751
|
+
else await this.applyVersioning(bucketName, { Status: "Suspended" });
|
|
5752
|
+
}
|
|
5663
5753
|
}
|
|
5664
5754
|
/**
|
|
5665
5755
|
* Apply ALL sub-configs unconditionally on initial create. Used by
|
|
@@ -5752,7 +5842,10 @@ var S3BucketProvider = class {
|
|
|
5752
5842
|
};
|
|
5753
5843
|
}
|
|
5754
5844
|
try {
|
|
5755
|
-
await this.applyConfiguration(physicalId, properties,
|
|
5845
|
+
await this.applyConfiguration(physicalId, properties, {
|
|
5846
|
+
skipTags: true,
|
|
5847
|
+
skipDiffManaged: true
|
|
5848
|
+
});
|
|
5756
5849
|
await this.applySubConfigDiffs(physicalId, properties, previousProperties);
|
|
5757
5850
|
await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
|
|
5758
5851
|
const attributes = await this.buildAttributes(physicalId);
|
|
@@ -5804,7 +5897,8 @@ var S3BucketProvider = class {
|
|
|
5804
5897
|
* returns `undefined`.
|
|
5805
5898
|
*
|
|
5806
5899
|
* Coverage: `BucketName`, `VersioningConfiguration`, `BucketEncryption`,
|
|
5807
|
-
* `PublicAccessBlockConfiguration`, `Tags`, plus all 12
|
|
5900
|
+
* `OwnershipControls`, `PublicAccessBlockConfiguration`, `Tags`, plus all 12
|
|
5901
|
+
* sub-configs:
|
|
5808
5902
|
* `LifecycleConfiguration`, `CorsConfiguration`, `WebsiteConfiguration`,
|
|
5809
5903
|
* `LoggingConfiguration`, `NotificationConfiguration`,
|
|
5810
5904
|
* `ReplicationConfiguration`, `ObjectLockConfiguration`,
|
|
@@ -5820,9 +5914,10 @@ var S3BucketProvider = class {
|
|
|
5820
5914
|
if (err instanceof NoSuchBucket || e.name === "NotFound" || e.name === "NoSuchBucket" || e.$metadata?.httpStatusCode === 404) return;
|
|
5821
5915
|
throw err;
|
|
5822
5916
|
}
|
|
5823
|
-
const [versioning, encryption, pab, tags, lifecycle, cors, website, logging, notification, replication, objectLock, accelerate, metrics, analytics, intelligentTier, inventory] = await Promise.all([
|
|
5917
|
+
const [versioning, encryption, ownership, pab, tags, lifecycle, cors, website, logging, notification, replication, objectLock, accelerate, metrics, analytics, intelligentTier, inventory] = await Promise.all([
|
|
5824
5918
|
this.readVersioning(physicalId),
|
|
5825
5919
|
this.readEncryption(physicalId),
|
|
5920
|
+
this.readOwnershipControls(physicalId),
|
|
5826
5921
|
this.readPublicAccessBlock(physicalId),
|
|
5827
5922
|
this.readTags(physicalId),
|
|
5828
5923
|
this.readLifecycle(physicalId),
|
|
@@ -5842,6 +5937,7 @@ var S3BucketProvider = class {
|
|
|
5842
5937
|
BucketName: physicalId,
|
|
5843
5938
|
VersioningConfiguration: versioning,
|
|
5844
5939
|
BucketEncryption: encryption,
|
|
5940
|
+
OwnershipControls: ownership,
|
|
5845
5941
|
PublicAccessBlockConfiguration: pab,
|
|
5846
5942
|
Tags: tags,
|
|
5847
5943
|
LifecycleConfiguration: lifecycle,
|
|
@@ -5861,6 +5957,22 @@ var S3BucketProvider = class {
|
|
|
5861
5957
|
async readVersioning(bucket) {
|
|
5862
5958
|
return { Status: (await this.s3Client.send(new GetBucketVersioningCommand({ Bucket: bucket }))).Status ?? "Suspended" };
|
|
5863
5959
|
}
|
|
5960
|
+
/**
|
|
5961
|
+
* OwnershipControls { Rules: [{ ObjectOwnership }] }. Always emit a
|
|
5962
|
+
* placeholder (empty `Rules`) so a console-side change on a bucket that
|
|
5963
|
+
* declares the property surfaces as drift. Added with issue #1466 — without
|
|
5964
|
+
* a read here, `cdkd drift` could not see ownership controls at all, which
|
|
5965
|
+
* is why the removal bug that issue fixes produced no signal from ANY
|
|
5966
|
+
* command.
|
|
5967
|
+
*/
|
|
5968
|
+
async readOwnershipControls(bucket) {
|
|
5969
|
+
try {
|
|
5970
|
+
return { Rules: ((await this.s3Client.send(new GetBucketOwnershipControlsCommand({ Bucket: bucket }))).OwnershipControls?.Rules ?? []).map((r) => ({ ObjectOwnership: r.ObjectOwnership })) };
|
|
5971
|
+
} catch (err) {
|
|
5972
|
+
if (err.name === "OwnershipControlsNotFoundError") return { Rules: [] };
|
|
5973
|
+
throw err;
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5864
5976
|
async readEncryption(bucket) {
|
|
5865
5977
|
try {
|
|
5866
5978
|
return { ServerSideEncryptionConfiguration: ((await this.s3Client.send(new GetBucketEncryptionCommand({ Bucket: bucket }))).ServerSideEncryptionConfiguration?.Rules ?? []).map((rule) => {
|
|
@@ -32934,10 +33046,10 @@ var GlueProvider = class {
|
|
|
32934
33046
|
default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
|
|
32935
33047
|
}
|
|
32936
33048
|
}
|
|
32937
|
-
async update(logicalId, physicalId, resourceType, properties,
|
|
33049
|
+
async update(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
32938
33050
|
switch (resourceType) {
|
|
32939
|
-
case "AWS::Glue::Database": return this.updateDatabase(logicalId, physicalId, resourceType, properties);
|
|
32940
|
-
case "AWS::Glue::Table": return this.updateTable(logicalId, physicalId, resourceType, properties);
|
|
33051
|
+
case "AWS::Glue::Database": return this.updateDatabase(logicalId, physicalId, resourceType, properties, previousProperties);
|
|
33052
|
+
case "AWS::Glue::Table": return this.updateTable(logicalId, physicalId, resourceType, properties, previousProperties);
|
|
32941
33053
|
default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
|
|
32942
33054
|
}
|
|
32943
33055
|
}
|
|
@@ -32970,16 +33082,19 @@ var GlueProvider = class {
|
|
|
32970
33082
|
throw new ProvisioningError(`Failed to create Glue Database ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
32971
33083
|
}
|
|
32972
33084
|
}
|
|
32973
|
-
async updateDatabase(logicalId, physicalId, resourceType, properties) {
|
|
33085
|
+
async updateDatabase(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
32974
33086
|
this.logger.debug(`Updating Glue Database ${logicalId}: ${physicalId}`);
|
|
32975
33087
|
const databaseInput = properties["DatabaseInput"];
|
|
32976
33088
|
if (!databaseInput) throw new ProvisioningError(`DatabaseInput is required for Glue Database update ${logicalId}`, resourceType, logicalId, physicalId);
|
|
32977
33089
|
const catalogId = properties["CatalogId"];
|
|
33090
|
+
const liveParameters = await this.readLiveDatabaseParameters(logicalId, resourceType, physicalId, catalogId);
|
|
32978
33091
|
try {
|
|
33092
|
+
const builtDatabaseInput = this.buildDatabaseInput(databaseInput, physicalId);
|
|
33093
|
+
this.preserveAwsManagedParameters(builtDatabaseInput, databaseInput["Parameters"], (previousProperties?.["DatabaseInput"])?.["Parameters"], liveParameters);
|
|
32979
33094
|
await this.getClient().send(new UpdateDatabaseCommand({
|
|
32980
33095
|
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
32981
33096
|
Name: physicalId,
|
|
32982
|
-
DatabaseInput:
|
|
33097
|
+
DatabaseInput: builtDatabaseInput
|
|
32983
33098
|
}));
|
|
32984
33099
|
this.logger.debug(`Successfully updated Glue Database ${logicalId}`);
|
|
32985
33100
|
return {
|
|
@@ -33039,7 +33154,7 @@ var GlueProvider = class {
|
|
|
33039
33154
|
throw new ProvisioningError(`Failed to create Glue Table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
|
|
33040
33155
|
}
|
|
33041
33156
|
}
|
|
33042
|
-
async updateTable(logicalId, physicalId, resourceType, properties) {
|
|
33157
|
+
async updateTable(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
33043
33158
|
this.logger.debug(`Updating Glue Table ${logicalId}: ${physicalId}`);
|
|
33044
33159
|
const [databaseName, tableName] = physicalId.split("|");
|
|
33045
33160
|
if (!databaseName || !tableName) throw new ProvisioningError(`Invalid Glue Table physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
|
|
@@ -33048,11 +33163,15 @@ var GlueProvider = class {
|
|
|
33048
33163
|
const catalogId = properties["CatalogId"];
|
|
33049
33164
|
const updateIcebergKey = findIcebergTableInputKey(properties);
|
|
33050
33165
|
if (updateIcebergKey !== void 0) this.logger.warn(icebergTableInputRefusalMessage(logicalId, updateIcebergKey, "update"));
|
|
33166
|
+
const live = await this.readLiveTableState(logicalId, resourceType, physicalId, databaseName, tableName, catalogId);
|
|
33051
33167
|
try {
|
|
33168
|
+
const builtTableInput = this.buildTableInput(tableInput, tableName);
|
|
33169
|
+
this.preserveAwsManagedParameters(builtTableInput, tableInput["Parameters"], (previousProperties?.["TableInput"])?.["Parameters"], live.parameters);
|
|
33052
33170
|
await this.getClient().send(new UpdateTableCommand$1({
|
|
33053
|
-
CatalogId: catalogId,
|
|
33171
|
+
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
33054
33172
|
DatabaseName: databaseName,
|
|
33055
|
-
TableInput:
|
|
33173
|
+
TableInput: builtTableInput,
|
|
33174
|
+
...live.versionId !== void 0 && { VersionId: live.versionId }
|
|
33056
33175
|
}));
|
|
33057
33176
|
this.logger.debug(`Successfully updated Glue Table ${logicalId}`);
|
|
33058
33177
|
return {
|
|
@@ -33061,7 +33180,13 @@ var GlueProvider = class {
|
|
|
33061
33180
|
};
|
|
33062
33181
|
} catch (error) {
|
|
33063
33182
|
const cause = error instanceof Error ? error : void 0;
|
|
33064
|
-
|
|
33183
|
+
let message = `Failed to update Glue Table ${logicalId}: ${error instanceof Error ? error.message : String(error)}`;
|
|
33184
|
+
if (error instanceof ConcurrentModificationException) {
|
|
33185
|
+
message += ". Another writer changed the table between cdkd's pre-update read and its UpdateTable call.";
|
|
33186
|
+
message += live.versionId !== void 0 ? ` cdkd sent the version it read (${live.versionId}) as a precondition and AWS rejected the write, rather than let it put back the AWS-managed Parameters read a moment earlier — which for an Apache Iceberg table would have pinned it to an older metadata_location (an effective snapshot rollback).` : " AWS returned no VersionId on the pre-update read, so cdkd could not attach a precondition; AWS rejected the write on its own.";
|
|
33187
|
+
message += ` Re-run the deploy to pick up the current values.`;
|
|
33188
|
+
}
|
|
33189
|
+
throw new ProvisioningError(message, resourceType, logicalId, physicalId, cause);
|
|
33065
33190
|
}
|
|
33066
33191
|
}
|
|
33067
33192
|
async deleteTable(logicalId, physicalId, resourceType, _properties, context) {
|
|
@@ -33088,6 +33213,150 @@ var GlueProvider = class {
|
|
|
33088
33213
|
}
|
|
33089
33214
|
}
|
|
33090
33215
|
/**
|
|
33216
|
+
* Merge AWS-AUTHORED `Parameters` back into a full-replace update payload
|
|
33217
|
+
* (issue [#1461](https://github.com/go-to-k/cdkd/issues/1461)).
|
|
33218
|
+
*
|
|
33219
|
+
* `UpdateTable` / `UpdateDatabase` replace `TableInput` / `DatabaseInput`
|
|
33220
|
+
* **wholesale**: whatever the payload omits is erased. `Parameters` is a
|
|
33221
|
+
* general-purpose bag that AWS itself writes into, so keys with NO template
|
|
33222
|
+
* representation were wiped by the first unrelated edit. For an Apache
|
|
33223
|
+
* Iceberg table that is not cosmetic — `table_type: ICEBERG` and
|
|
33224
|
+
* `metadata_location` are what make the table readable as Iceberg by
|
|
33225
|
+
* Athena / Spark / EMR, and losing them silently degrades it to a plain
|
|
33226
|
+
* external table pointing at Iceberg data files. Verified live 2026-08-10
|
|
33227
|
+
* in us-east-1: a deploy changing only `TableInput.Description` left
|
|
33228
|
+
* `Table.Parameters` as `null`. The same exposure covers a Glue crawler's
|
|
33229
|
+
* `classification`, `EXTERNAL`, `comment`, and Lake Formation markers.
|
|
33230
|
+
*
|
|
33231
|
+
* The merge is keyed on **"present in NEITHER template side"**, not on
|
|
33232
|
+
* "absent from the desired side" — the latter would make user-authored
|
|
33233
|
+
* parameters unremovable, i.e. the mirror image of the bug:
|
|
33234
|
+
*
|
|
33235
|
+
* | key in desired | key in previous | outcome |
|
|
33236
|
+
* | -------------- | --------------- | ------------------------------------ |
|
|
33237
|
+
* | yes | (either) | the user's value wins |
|
|
33238
|
+
* | no | yes | the USER REMOVED it -> stays removed |
|
|
33239
|
+
* | no | no | AWS-AUTHORED -> preserved from live |
|
|
33240
|
+
*
|
|
33241
|
+
* That keeps this provider on the repo's established clear-on-removal
|
|
33242
|
+
* position (docs/provider-development.md §2a, issue #1155): a removal the
|
|
33243
|
+
* user expressed in the template still reaches AWS. Glue's update APIs are
|
|
33244
|
+
* full-replace, so removal needs no explicit reset sentinel — omitting the
|
|
33245
|
+
* key IS the reset, and the only thing this helper adds back is the set of
|
|
33246
|
+
* keys the user never authored in the first place. The generalized rule for
|
|
33247
|
+
* any full-replace update API lives in docs/provider-development.md §2b.
|
|
33248
|
+
*
|
|
33249
|
+
* Audited siblings that do NOT get this treatment: `JobUpdate`'s
|
|
33250
|
+
* `DefaultArguments` / `NonOverridableArguments`, `ConnectionInput`'s
|
|
33251
|
+
* `ConnectionProperties`, `WorkflowUpdate.DefaultRunProperties` and
|
|
33252
|
+
* `Crawler.Configuration` are purely USER-authored bags — AWS does not
|
|
33253
|
+
* write into them, so merging live values back would only resurrect
|
|
33254
|
+
* console-side additions and break `cdkd drift --revert`.
|
|
33255
|
+
*
|
|
33256
|
+
* `desiredParameters` / `previousParameters` are the raw CFn-side maps
|
|
33257
|
+
* (their KEY SET is all that matters here; `buildTableInput` already
|
|
33258
|
+
* stringified the desired VALUES into `built.Parameters`).
|
|
33259
|
+
*/
|
|
33260
|
+
preserveAwsManagedParameters(built, desiredParameters, previousParameters, liveParameters) {
|
|
33261
|
+
if (!liveParameters || Object.keys(liveParameters).length === 0) return;
|
|
33262
|
+
const desiredKeys = parameterKeySet(desiredParameters);
|
|
33263
|
+
const previousKeys = parameterKeySet(previousParameters);
|
|
33264
|
+
const awsAuthored = {};
|
|
33265
|
+
for (const [key, value] of Object.entries(liveParameters)) {
|
|
33266
|
+
if (desiredKeys.has(key) || previousKeys.has(key)) continue;
|
|
33267
|
+
awsAuthored[key] = value;
|
|
33268
|
+
}
|
|
33269
|
+
if (Object.keys(awsAuthored).length === 0) return;
|
|
33270
|
+
built.Parameters = {
|
|
33271
|
+
...awsAuthored,
|
|
33272
|
+
...built.Parameters ?? {}
|
|
33273
|
+
};
|
|
33274
|
+
}
|
|
33275
|
+
/**
|
|
33276
|
+
* The live table state {@link preserveAwsManagedParameters} needs: its
|
|
33277
|
+
* current `Parameters` plus the `VersionId` that pins them.
|
|
33278
|
+
*
|
|
33279
|
+
* A missing table returns empty rather than throwing: `UpdateTable` is about
|
|
33280
|
+
* to surface the real, more actionable error. Any OTHER failure is fatal by
|
|
33281
|
+
* design — degrading to "skip the merge" would silently reinstate the very
|
|
33282
|
+
* wipe this read exists to prevent, and the wrapped cause keeps a transient
|
|
33283
|
+
* throttle retryable by the deploy engine's outer `withRetry` (every
|
|
33284
|
+
* `provider.update()` call site wraps it, including both rollback arms since
|
|
33285
|
+
* this issue).
|
|
33286
|
+
*
|
|
33287
|
+
* **Why `VersionId` matters and why it is SCOPED.** Reading `Parameters`
|
|
33288
|
+
* here and writing them back in the next call opens a TOCTOU window: an
|
|
33289
|
+
* Apache Iceberg commit from Spark / Athena / EMR landing in between would
|
|
33290
|
+
* be undone by writing back the `metadata_location` we read, pinning the
|
|
33291
|
+
* table to an older snapshot — silent data-visibility loss, strictly worse
|
|
33292
|
+
* than the bug this read fixes. `UpdateTableRequest.VersionId` is AWS's
|
|
33293
|
+
* optimistic-concurrency token for exactly this
|
|
33294
|
+
* (`ConcurrentModificationException` is a documented `UpdateTable` error),
|
|
33295
|
+
* so the caller sends the version it read and a concurrent commit makes the
|
|
33296
|
+
* update fail loudly instead of silently rolling back.
|
|
33297
|
+
*
|
|
33298
|
+
* It is sent UNCONDITIONALLY whenever this read returned a version, and the
|
|
33299
|
+
* earlier attempt to scope it to "only when the merge wrote back live
|
|
33300
|
+
* values" was wrong on both counts. Wrong on SAFETY: the merge also reports
|
|
33301
|
+
* nothing-written-back when the live read came back EMPTY, and that update
|
|
33302
|
+
* still ships a wholesale `TableInput` replace — so a commit landing in the
|
|
33303
|
+
* window would have been wiped with no guard at all, i.e. #1461 surviving
|
|
33304
|
+
* its own fix. Wrong on its PREMISE: this read runs milliseconds before the
|
|
33305
|
+
* send on every update, so a pure template push (`cdkd drift --revert`, the
|
|
33306
|
+
* case the scoping was meant to protect) carries a FRESH version too. An
|
|
33307
|
+
* unconditional guard therefore cannot fire spuriously — it fires only when
|
|
33308
|
+
* somebody else genuinely wrote in between, which is exactly when a revert
|
|
33309
|
+
* should stop and be re-run against current values rather than clobber a
|
|
33310
|
+
* change it never saw.
|
|
33311
|
+
*
|
|
33312
|
+
* The precondition's real-AWS semantics are pinned by the `data-analytics`
|
|
33313
|
+
* integ, which advances a table's version out of band and asserts cdkd's
|
|
33314
|
+
* update is REFUSED — without that, "AWS silently ignores a stale VersionId"
|
|
33315
|
+
* would make this guard a placebo.
|
|
33316
|
+
*/
|
|
33317
|
+
async readLiveTableState(logicalId, resourceType, physicalId, databaseName, tableName, catalogId) {
|
|
33318
|
+
try {
|
|
33319
|
+
const resp = await this.getClient().send(new GetTableCommand({
|
|
33320
|
+
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
33321
|
+
DatabaseName: databaseName,
|
|
33322
|
+
Name: tableName
|
|
33323
|
+
}));
|
|
33324
|
+
return {
|
|
33325
|
+
parameters: resp.Table?.Parameters,
|
|
33326
|
+
versionId: resp.Table?.VersionId
|
|
33327
|
+
};
|
|
33328
|
+
} catch (error) {
|
|
33329
|
+
if (error instanceof EntityNotFoundException) return {
|
|
33330
|
+
parameters: void 0,
|
|
33331
|
+
versionId: void 0
|
|
33332
|
+
};
|
|
33333
|
+
throw new ProvisioningError(preUpdateReadFailureMessage("Table", "glue:GetTable", logicalId, error), resourceType, logicalId, physicalId, error instanceof Error ? error : void 0);
|
|
33334
|
+
}
|
|
33335
|
+
}
|
|
33336
|
+
/**
|
|
33337
|
+
* `Database.Parameters` as AWS currently holds them, for
|
|
33338
|
+
* {@link preserveAwsManagedParameters}. Same not-found / fail-closed
|
|
33339
|
+
* contract as {@link readLiveTableState}.
|
|
33340
|
+
*
|
|
33341
|
+
* There is NO `VersionId` analogue here: `UpdateDatabaseRequest` has no such
|
|
33342
|
+
* member (verified against `@aws-sdk/client-glue`), so the database merge
|
|
33343
|
+
* carries the TOCTOU exposure the table merge closes. It is far narrower in
|
|
33344
|
+
* practice — nothing commits to a Glue DATABASE out of band the way an
|
|
33345
|
+
* Iceberg engine commits to a table — but it is real, and is written up in
|
|
33346
|
+
* docs/supported-resources.md rather than left implicit.
|
|
33347
|
+
*/
|
|
33348
|
+
async readLiveDatabaseParameters(logicalId, resourceType, physicalId, catalogId) {
|
|
33349
|
+
try {
|
|
33350
|
+
return (await this.getClient().send(new GetDatabaseCommand({
|
|
33351
|
+
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
33352
|
+
Name: physicalId
|
|
33353
|
+
}))).Database?.Parameters;
|
|
33354
|
+
} catch (error) {
|
|
33355
|
+
if (error instanceof EntityNotFoundException) return void 0;
|
|
33356
|
+
throw new ProvisioningError(preUpdateReadFailureMessage("Database", "glue:GetDatabase", logicalId, error), resourceType, logicalId, physicalId, error instanceof Error ? error : void 0);
|
|
33357
|
+
}
|
|
33358
|
+
}
|
|
33359
|
+
/**
|
|
33091
33360
|
* Build DatabaseInput for Glue API from CFn template properties.
|
|
33092
33361
|
*
|
|
33093
33362
|
* Used by both `createDatabase` and `updateDatabase` so the same
|
|
@@ -33101,7 +33370,7 @@ var GlueProvider = class {
|
|
|
33101
33370
|
const result = { Name: databaseInput["Name"] ?? fallbackName };
|
|
33102
33371
|
if (databaseInput["Description"] !== void 0) result.Description = databaseInput["Description"];
|
|
33103
33372
|
if (databaseInput["LocationUri"] !== void 0) result.LocationUri = databaseInput["LocationUri"];
|
|
33104
|
-
if (databaseInput["Parameters"] !== void 0) result.Parameters = databaseInput["Parameters"];
|
|
33373
|
+
if (databaseInput["Parameters"] !== void 0) result.Parameters = stringifyParameterValues(databaseInput["Parameters"]);
|
|
33105
33374
|
return result;
|
|
33106
33375
|
}
|
|
33107
33376
|
/**
|
|
@@ -33111,12 +33380,7 @@ var GlueProvider = class {
|
|
|
33111
33380
|
const result = { Name: tableInput["Name"] ?? fallbackName };
|
|
33112
33381
|
if (tableInput["Description"] !== void 0) result.Description = tableInput["Description"];
|
|
33113
33382
|
if (tableInput["TableType"] !== void 0) result.TableType = tableInput["TableType"];
|
|
33114
|
-
if (tableInput["Parameters"] !== void 0)
|
|
33115
|
-
const rawParams = tableInput["Parameters"];
|
|
33116
|
-
const params = {};
|
|
33117
|
-
for (const [k, v] of Object.entries(rawParams)) params[k] = String(v);
|
|
33118
|
-
result.Parameters = params;
|
|
33119
|
-
}
|
|
33383
|
+
if (tableInput["Parameters"] !== void 0) result.Parameters = stringifyParameterValues(tableInput["Parameters"]);
|
|
33120
33384
|
if (tableInput["Owner"] !== void 0) result.Owner = tableInput["Owner"];
|
|
33121
33385
|
if (tableInput["Retention"] !== void 0) result.Retention = tableInput["Retention"];
|
|
33122
33386
|
if (tableInput["ViewOriginalText"] !== void 0) result.ViewOriginalText = tableInput["ViewOriginalText"];
|
|
@@ -33188,10 +33452,11 @@ var GlueProvider = class {
|
|
|
33188
33452
|
* the provider's `create()` only handles Database/Table; CC API picks
|
|
33189
33453
|
* up drift detection for the rest.
|
|
33190
33454
|
*/
|
|
33191
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
33455
|
+
async readCurrentState(physicalId, _logicalId, resourceType, properties) {
|
|
33456
|
+
const catalogId = properties?.["CatalogId"];
|
|
33192
33457
|
switch (resourceType) {
|
|
33193
|
-
case "AWS::Glue::Database": return this.readDatabase(physicalId);
|
|
33194
|
-
case "AWS::Glue::Table": return this.readTable(physicalId);
|
|
33458
|
+
case "AWS::Glue::Database": return this.readDatabase(physicalId, catalogId);
|
|
33459
|
+
case "AWS::Glue::Table": return this.readTable(physicalId, catalogId);
|
|
33195
33460
|
default: return;
|
|
33196
33461
|
}
|
|
33197
33462
|
}
|
|
@@ -33209,10 +33474,13 @@ var GlueProvider = class {
|
|
|
33209
33474
|
if (resourceType === "AWS::Glue::Table") return ["OpenTableFormatInput"];
|
|
33210
33475
|
return [];
|
|
33211
33476
|
}
|
|
33212
|
-
async readDatabase(physicalId) {
|
|
33477
|
+
async readDatabase(physicalId, catalogId) {
|
|
33213
33478
|
let db;
|
|
33214
33479
|
try {
|
|
33215
|
-
db = (await this.getClient().send(new GetDatabaseCommand({
|
|
33480
|
+
db = (await this.getClient().send(new GetDatabaseCommand({
|
|
33481
|
+
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
33482
|
+
Name: physicalId
|
|
33483
|
+
}))).Database;
|
|
33216
33484
|
} catch (err) {
|
|
33217
33485
|
if (err instanceof EntityNotFoundException) return void 0;
|
|
33218
33486
|
throw err;
|
|
@@ -33227,12 +33495,13 @@ var GlueProvider = class {
|
|
|
33227
33495
|
if (db.Name !== void 0) result["DatabaseName"] = db.Name;
|
|
33228
33496
|
return result;
|
|
33229
33497
|
}
|
|
33230
|
-
async readTable(physicalId) {
|
|
33498
|
+
async readTable(physicalId, catalogId) {
|
|
33231
33499
|
const [databaseName, tableName] = physicalId.split("|");
|
|
33232
33500
|
if (!databaseName || !tableName) return void 0;
|
|
33233
33501
|
let table;
|
|
33234
33502
|
try {
|
|
33235
33503
|
table = (await this.getClient().send(new GetTableCommand({
|
|
33504
|
+
...catalogId !== void 0 && { CatalogId: catalogId },
|
|
33236
33505
|
DatabaseName: databaseName,
|
|
33237
33506
|
Name: tableName
|
|
33238
33507
|
}))).Table;
|
|
@@ -34049,6 +34318,35 @@ function buildJobCommonFields(p) {
|
|
|
34049
34318
|
return r;
|
|
34050
34319
|
}
|
|
34051
34320
|
/**
|
|
34321
|
+
* CFn `Parameters` map -> the SDK's `Record<string, string>`. CDK may deliver
|
|
34322
|
+
* booleans / numbers; the SDK member is string-valued.
|
|
34323
|
+
*/
|
|
34324
|
+
function stringifyParameterValues(raw) {
|
|
34325
|
+
const params = {};
|
|
34326
|
+
for (const [k, v] of Object.entries(raw ?? {})) params[k] = String(v);
|
|
34327
|
+
return params;
|
|
34328
|
+
}
|
|
34329
|
+
/**
|
|
34330
|
+
* Key set of a CFn-side `Parameters` map, for
|
|
34331
|
+
* {@link GlueProvider.preserveAwsManagedParameters}.
|
|
34332
|
+
*
|
|
34333
|
+
* A non-object (absent, or an unresolved intrinsic) contributes NO keys — so
|
|
34334
|
+
* a template that declares no `Parameters` at all can never be mistaken for
|
|
34335
|
+
* one that removed a key, and every live key stays preserved.
|
|
34336
|
+
*/
|
|
34337
|
+
function parameterKeySet(value) {
|
|
34338
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return /* @__PURE__ */ new Set();
|
|
34339
|
+
return new Set(Object.keys(value));
|
|
34340
|
+
}
|
|
34341
|
+
/**
|
|
34342
|
+
* Message for a failed pre-update `Get*` read (issue #1461). Names the IAM
|
|
34343
|
+
* action so a permission gap is one line away from fixed, and says what the
|
|
34344
|
+
* read is FOR so the failure does not read as gratuitous.
|
|
34345
|
+
*/
|
|
34346
|
+
function preUpdateReadFailureMessage(kind, iamAction, logicalId, error) {
|
|
34347
|
+
return `Failed to read the current Glue ${kind} ${logicalId} before update: ${error instanceof Error ? error.message : String(error)}. cdkd reads the live ${kind.toLowerCase()} so AWS-managed Parameters (Apache Iceberg's table_type / metadata_location, a crawler's classification, Lake Formation markers) survive an update that Glue applies as a full replace. Grant ${iamAction} to the deploy identity — proceeding without the read would silently erase them.`;
|
|
34348
|
+
}
|
|
34349
|
+
/**
|
|
34052
34350
|
* Coerce a CFn-delivered numeric property (often a string like `"10"`) to a
|
|
34053
34351
|
* JS number at the SDK wire boundary. Non-finite / unparseable inputs are
|
|
34054
34352
|
* returned unchanged so AWS surfaces a clear validation error rather than
|
|
@@ -65486,7 +65784,7 @@ function createMigrateCommand() {
|
|
|
65486
65784
|
*/
|
|
65487
65785
|
function buildProgram() {
|
|
65488
65786
|
const program = new Command();
|
|
65489
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.
|
|
65787
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.23");
|
|
65490
65788
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
65491
65789
|
const { profile } = actionCommand.optsWithGlobals();
|
|
65492
65790
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|