@go-to-k/cdkd 0.284.39 → 0.284.40
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-Db6lnsaA.js → asg-provider-BeKDzUe9.js} +2 -2
- package/dist/{asg-provider-Db6lnsaA.js.map → asg-provider-BeKDzUe9.js.map} +1 -1
- package/dist/cli.js +138 -25
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-rKBFVifY.js → deploy-engine-B3MQFW1K.js} +3 -3
- package/dist/{deploy-engine-rKBFVifY.js.map → deploy-engine-B3MQFW1K.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as startInterruptWatch, $n as CdkdError, $t as WorkGraph, A as renderStatefulReason, An as resolveStateBucketWithDefault, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as expectedOwnerParam, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as synthesisStatusMessage, Cr as isMarkedNonRetryable, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as resolveAutoAssetStorage, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as resolveApp, Er as markNonRetryable, Et as errorCauseChain, F as green, Fn as CFN_TEMPLATE_BODY_LIMIT, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as processStackMessages, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as canonicalizeRegion, Ht as describeTypeWithThrottleRetry, I as red, In as CFN_TEMPLATE_URL_LIMIT, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jn as AwsClients, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kt as LockManager, L as yellow, Ln as MIGRATE_TMP_PREFIX, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveUseCdkBootstrapAssets, Mt as classifyReplaySecretRegion, N as cyan, Nn as stateBucketExistenceConfirmed, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveCaptureObservedState, Ot as maskSecretsInError, P as gray, Pn as warnDeprecatedNoPrefixCliFlag, Pt as s3BucketArn, Q as isInterruptedWaitError, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as findLargeInlineResources, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as Synthesizer, Sr as withErrorHandling, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as getLegacyStateBucketName, Tr as isThrottlingError, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as derivePartitionAndUrlSuffix, Ut as withRetry, V as isExportAliasCollision, Vn as PARTITION_TABLE, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as AssemblyReader, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as resetAwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as getAwsClients, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as setAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as getDockerCmd, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as LocalInvokeBuildError$1, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as AssetManifestLoader, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as PartialFailureError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as ProvisioningError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as formatDockerLoginError, gr as StackTerminationProtectionError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as StackHasActiveImportsError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, ir as DynamicReferenceRegionAmbiguousError, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveStateBucketWithDefaultAndSource, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveSkipPrefix, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as MissingCdkCliError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ResourceUpdateNotSupportedError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as LocalMigrateError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as ResourceTimeoutError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as resolveBucketRegion, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as DeployCancelledError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalStartServiceError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CrossAccountSecretRefusalError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ur as NestedStackChildDirectDestroyError, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as runDockerForeground, vt as requireConfigArray, w as refusesFinalSnapshot, wn as getDefaultStateBucketName, wr as isRetryableTransientError, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as getDockerImageBySourceHash, xr as normalizeAwsError, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as runDockerStreaming, yt as requireConfigObject, z as collectPublishedOutputNames, zn as uploadCfnTemplate, zt as applyRoleArnIfSet } from "./deploy-engine-
|
|
2
|
+
import { $ as startInterruptWatch, $n as CdkdError, $t as WorkGraph, A as renderStatefulReason, An as resolveStateBucketWithDefault, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as expectedOwnerParam, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as synthesisStatusMessage, Cr as isMarkedNonRetryable, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as resolveAutoAssetStorage, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as resolveApp, Er as markNonRetryable, Et as errorCauseChain, F as green, Fn as CFN_TEMPLATE_BODY_LIMIT, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as processStackMessages, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as canonicalizeRegion, Ht as describeTypeWithThrottleRetry, I as red, In as CFN_TEMPLATE_URL_LIMIT, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jn as AwsClients, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kt as LockManager, L as yellow, Ln as MIGRATE_TMP_PREFIX, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveUseCdkBootstrapAssets, Mt as classifyReplaySecretRegion, N as cyan, Nn as stateBucketExistenceConfirmed, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveCaptureObservedState, Ot as maskSecretsInError, P as gray, Pn as warnDeprecatedNoPrefixCliFlag, Pt as s3BucketArn, Q as isInterruptedWaitError, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as findLargeInlineResources, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as Synthesizer, Sr as withErrorHandling, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as getLegacyStateBucketName, Tr as isThrottlingError, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as derivePartitionAndUrlSuffix, Ut as withRetry, V as isExportAliasCollision, Vn as PARTITION_TABLE, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as AssemblyReader, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as resetAwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as getAwsClients, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as setAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as getDockerCmd, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as LocalInvokeBuildError$1, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as AssetManifestLoader, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as PartialFailureError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as ProvisioningError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as formatDockerLoginError, gr as StackTerminationProtectionError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as StackHasActiveImportsError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, ir as DynamicReferenceRegionAmbiguousError, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveStateBucketWithDefaultAndSource, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveSkipPrefix, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as MissingCdkCliError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ResourceUpdateNotSupportedError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as LocalMigrateError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as ResourceTimeoutError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as resolveBucketRegion, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as DeployCancelledError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalStartServiceError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CrossAccountSecretRefusalError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ur as NestedStackChildDirectDestroyError, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as runDockerForeground, vt as requireConfigArray, w as refusesFinalSnapshot, wn as getDefaultStateBucketName, wr as isRetryableTransientError, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as getDockerImageBySourceHash, xr as normalizeAwsError, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as runDockerStreaming, yt as requireConfigObject, z as collectPublishedOutputNames, zn as uploadCfnTemplate, zt as applyRoleArnIfSet } from "./deploy-engine-B3MQFW1K.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-BeKDzUe9.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
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";
|
|
@@ -23919,9 +23919,10 @@ const generations = /* @__PURE__ */ new Map();
|
|
|
23919
23919
|
* long-lived process cannot grow the map without bound.
|
|
23920
23920
|
*/
|
|
23921
23921
|
const MAX_TRACKED_GENERATIONS = 1e5;
|
|
23922
|
-
const tokenKey = (scope, logicalId) => `${scope}\u0000${process.env["AWS_REGION"] ?? ""}\u0000${getCurrentStackName() ?? ""}\u0000${logicalId}`;
|
|
23923
|
-
const derive = (key, generation, maxLength) => {
|
|
23924
|
-
|
|
23922
|
+
const tokenKey = (scope, logicalId, maxLength, charset) => `${scope}\u0000${process.env["AWS_REGION"] ?? ""}\u0000${getCurrentStackName() ?? ""}\u0000${logicalId}\u0000${maxLength}\u0000${charset}`;
|
|
23923
|
+
const derive = (key, generation, maxLength, charset) => {
|
|
23924
|
+
const digest = createHash("sha256").update(`${PROCESS_NONCE}\u0000${key}\u0000${generation}`).digest("hex");
|
|
23925
|
+
return `cdkd${charset === "alphanumeric" ? "" : "-"}${digest}`.slice(0, maxLength);
|
|
23925
23926
|
};
|
|
23926
23927
|
/**
|
|
23927
23928
|
* Get the idempotency token for one logical create, minting it on first use and
|
|
@@ -23935,10 +23936,10 @@ const derive = (key, generation, maxLength) => {
|
|
|
23935
23936
|
* ```
|
|
23936
23937
|
*/
|
|
23937
23938
|
function acquireIdempotencyToken(options) {
|
|
23938
|
-
const { scope, logicalId, maxLength = 64 } = options;
|
|
23939
|
-
const key = tokenKey(scope, logicalId);
|
|
23939
|
+
const { scope, logicalId, maxLength = 64, charset = "default" } = options;
|
|
23940
|
+
const key = tokenKey(scope, logicalId, maxLength, charset);
|
|
23940
23941
|
const existing = inFlight.get(key);
|
|
23941
|
-
const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength);
|
|
23942
|
+
const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength, charset);
|
|
23942
23943
|
if (existing === void 0) inFlight.set(key, value);
|
|
23943
23944
|
return {
|
|
23944
23945
|
value,
|
|
@@ -59380,6 +59381,19 @@ function isCertificateInUseError(error) {
|
|
|
59380
59381
|
* consumers (CloudFront, ALB) will fail to start if they reach the cert
|
|
59381
59382
|
* before it issues, but that's the documented trade-off.
|
|
59382
59383
|
*
|
|
59384
|
+
* **A create that fails after requesting DELETES its certificate** (issue
|
|
59385
|
+
* [#2169](https://github.com/go-to-k/cdkd/issues/2169)). `RequestCertificate`
|
|
59386
|
+
* materializes the certificate immediately and everything after it can fail —
|
|
59387
|
+
* most commonly the ISSUED wait, which a DNS-validated certificate whose
|
|
59388
|
+
* validation records are not live yet exhausts by construction. Before this,
|
|
59389
|
+
* the ARN was thrown away with the error: the certificate existed in AWS with
|
|
59390
|
+
* nothing naming it (absent from state, so invisible to `cdkd state show` and
|
|
59391
|
+
* unreachable by `cdkd destroy`), and the next `cdkd deploy` requested another
|
|
59392
|
+
* one — an orphan per attempt. `create()` now retires the certificate it just
|
|
59393
|
+
* requested before re-throwing, so a failed deploy leaves nothing behind and a
|
|
59394
|
+
* retry starts clean. See {@link ACMCertificateProvider.cleanupRequestedCertificate}
|
|
59395
|
+
* for why deleting is safe and why it is preferred over recording the remnant.
|
|
59396
|
+
*
|
|
59383
59397
|
* **CloudFront cross-region note**: ACM certificates referenced by a
|
|
59384
59398
|
* CloudFront Distribution MUST live in `us-east-1`. cdkd does not enforce
|
|
59385
59399
|
* this — it's the developer's responsibility to deploy the certificate
|
|
@@ -59429,12 +59443,22 @@ var ACMCertificateProvider = class {
|
|
|
59429
59443
|
if (Object.keys(options).length > 0) input["Options"] = options;
|
|
59430
59444
|
const tags = properties["Tags"];
|
|
59431
59445
|
if (tags && Array.isArray(tags) && tags.length > 0) input["Tags"] = tags;
|
|
59446
|
+
const idempotencyToken = acquireIdempotencyToken({
|
|
59447
|
+
scope: "RequestCertificate",
|
|
59448
|
+
logicalId,
|
|
59449
|
+
maxLength: 32,
|
|
59450
|
+
charset: "alphanumeric"
|
|
59451
|
+
});
|
|
59452
|
+
input["IdempotencyToken"] = idempotencyToken.value;
|
|
59453
|
+
let requestedArn;
|
|
59432
59454
|
try {
|
|
59433
59455
|
const certificateArn = (await this.acmClient.send(new RequestCertificateCommand(input))).CertificateArn;
|
|
59434
59456
|
if (!certificateArn) throw new ProvisioningError(`RequestCertificate succeeded but no CertificateArn returned for ${logicalId}`, resourceType, logicalId);
|
|
59457
|
+
requestedArn = certificateArn;
|
|
59435
59458
|
this.logger.debug(`Requested ACM certificate: ${certificateArn}`);
|
|
59436
|
-
if (!(process.env["CDKD_NO_WAIT"] === "true")) await this.waitForCertificateIssued(certificateArn, logicalId);
|
|
59459
|
+
if (!(process.env["CDKD_NO_WAIT"] === "true")) await this.waitForCertificateIssued(certificateArn, logicalId, resourceType);
|
|
59437
59460
|
else this.logger.warn(`Skipping wait for ACM certificate ${logicalId} (CDKD_NO_WAIT=true). Downstream consumers (CloudFront / ALB) will fail until the cert reaches ISSUED.`);
|
|
59461
|
+
idempotencyToken.release();
|
|
59438
59462
|
return {
|
|
59439
59463
|
physicalId: certificateArn,
|
|
59440
59464
|
attributes: {
|
|
@@ -59443,9 +59467,84 @@ var ACMCertificateProvider = class {
|
|
|
59443
59467
|
}
|
|
59444
59468
|
};
|
|
59445
59469
|
} catch (error) {
|
|
59446
|
-
|
|
59470
|
+
const survivorNote = requestedArn ? await this.cleanupRequestedCertificate(requestedArn, logicalId, idempotencyToken) : void 0;
|
|
59471
|
+
if (error instanceof CdkdError) {
|
|
59472
|
+
if (survivorNote === void 0) throw error;
|
|
59473
|
+
throw new ProvisioningError(`${error.message} ${survivorNote}`, resourceType, logicalId, requestedArn, error);
|
|
59474
|
+
}
|
|
59447
59475
|
const cause = error instanceof Error ? error : void 0;
|
|
59448
|
-
throw new ProvisioningError(`Failed to create ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}
|
|
59476
|
+
throw new ProvisioningError(`Failed to create ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}` + (survivorNote === void 0 ? "" : ` ${survivorNote}`), resourceType, logicalId, requestedArn, cause);
|
|
59477
|
+
}
|
|
59478
|
+
}
|
|
59479
|
+
/**
|
|
59480
|
+
* Retire the certificate a FAILED `create()` just requested, so the failure
|
|
59481
|
+
* leaves nothing behind (issue
|
|
59482
|
+
* [#2169](https://github.com/go-to-k/cdkd/issues/2169)).
|
|
59483
|
+
*
|
|
59484
|
+
* Returns `undefined` when the certificate is gone, or a one-line note naming
|
|
59485
|
+
* the survivor when it could not be deleted. Never throws: the original
|
|
59486
|
+
* create error is what the user needs to see, and a cleanup failure must
|
|
59487
|
+
* degrade that message rather than replace it.
|
|
59488
|
+
*
|
|
59489
|
+
* ## Why DELETING is right, when the obvious objection is that it throws away
|
|
59490
|
+
* the user's DNS work
|
|
59491
|
+
*
|
|
59492
|
+
* It does not. ACM derives a domain's validation CNAME from the domain and
|
|
59493
|
+
* the account, not from the certificate, and documents that the record "must
|
|
59494
|
+
* be added to your DNS database only once": *"Without the need to repeat
|
|
59495
|
+
* validation, you can request additional ACM certificates for your fully
|
|
59496
|
+
* qualified domain name (FQDN) for as long as the CNAME record remains in
|
|
59497
|
+
* place ... You can also replace a deleted certificate."*
|
|
59498
|
+
* (https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html)
|
|
59499
|
+
* So the CNAMEs a user adds after seeing this failure validate the NEXT
|
|
59500
|
+
* certificate too, and the retry issues normally. The intuition that a
|
|
59501
|
+
* `PENDING_VALIDATION` certificate is precious does not hold for ACM.
|
|
59502
|
+
*
|
|
59503
|
+
* ## Why not RECORD the remnant in cdkd state instead
|
|
59504
|
+
*
|
|
59505
|
+
* That was this fix's first shape, and it does not work. A state record makes
|
|
59506
|
+
* the next deploy diff the resource as `NO_CHANGE` -- the recorded properties
|
|
59507
|
+
* ARE the template's -- so cdkd would never touch that certificate again:
|
|
59508
|
+
* `cdkd deploy` would print "No changes detected" and exit 0 over a
|
|
59509
|
+
* certificate that is still unusable, and the CloudFront / ALB consuming it
|
|
59510
|
+
* would fail with no explanation. Turning a loud failure into a silent one is
|
|
59511
|
+
* worse than the orphan it fixes. Making the diff re-provision it instead
|
|
59512
|
+
* needs a marker on the state record -- a schema bump -- to buy behaviour
|
|
59513
|
+
* that deleting gives for free.
|
|
59514
|
+
*
|
|
59515
|
+
* ## The "keep it" escape hatch already exists
|
|
59516
|
+
*
|
|
59517
|
+
* A user who WANTS the certificate to outlive the deploy has
|
|
59518
|
+
* `CDKD_NO_WAIT=true` (`cdkd deploy --no-wait`), which returns success with
|
|
59519
|
+
* the certificate recorded in state and never reaches this path. "Wait for
|
|
59520
|
+
* it" and "keep it even if the wait fails" are different requests, and cdkd
|
|
59521
|
+
* already has a flag for the second.
|
|
59522
|
+
*/
|
|
59523
|
+
/**
|
|
59524
|
+
* The region an ACM ARN names, for the manual retire command. Taken from the
|
|
59525
|
+
* ARN rather than from the client so the command a user pastes matches the
|
|
59526
|
+
* certificate it names even when their shell default differs. Falls back to
|
|
59527
|
+
* the literal placeholder for a shape this cannot parse -- a wrong region
|
|
59528
|
+
* would make the command silently address a different account/region.
|
|
59529
|
+
*/
|
|
59530
|
+
regionOfArn(arn) {
|
|
59531
|
+
return arn.split(":")[3] || "<region>";
|
|
59532
|
+
}
|
|
59533
|
+
async cleanupRequestedCertificate(certificateArn, logicalId, idempotencyToken) {
|
|
59534
|
+
try {
|
|
59535
|
+
await this.acmClient.send(new DeleteCertificateCommand({ CertificateArn: certificateArn }));
|
|
59536
|
+
this.logger.info(`Deleted the ACM certificate ${certificateArn} that the failed create of ${logicalId} requested, so nothing is left behind. DNS validation records you have already added stay valid for the retry.`);
|
|
59537
|
+
idempotencyToken.release();
|
|
59538
|
+
return;
|
|
59539
|
+
} catch (cleanupError) {
|
|
59540
|
+
if (cleanupError instanceof ResourceNotFoundException$7 || cleanupError instanceof Error && cleanupError.name === "ResourceNotFoundException") {
|
|
59541
|
+
this.logger.info(`The ACM certificate ${certificateArn} that the failed create of ${logicalId} requested was already gone; nothing to clean up.`);
|
|
59542
|
+
idempotencyToken.release();
|
|
59543
|
+
return;
|
|
59544
|
+
}
|
|
59545
|
+
const detail = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
|
|
59546
|
+
this.logger.warn(`Could not delete the ACM certificate ${certificateArn} that the failed create of ${logicalId} requested: ${detail}`);
|
|
59547
|
+
return `The certificate ${certificateArn} this attempt created could NOT be deleted, and cdkd is not tracking it -- retire it with \`aws acm delete-certificate --certificate-arn ${certificateArn} --region ${this.regionOfArn(certificateArn)}\` (the reason is in the warning above).`;
|
|
59449
59548
|
}
|
|
59450
59549
|
}
|
|
59451
59550
|
async update(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
@@ -59618,11 +59717,25 @@ var ACMCertificateProvider = class {
|
|
|
59618
59717
|
* Throws on `VALIDATION_TIMED_OUT` / `FAILED` (terminal failures) and on
|
|
59619
59718
|
* polling-cap exhaustion (treated as timeout). SIGINT short-circuits the
|
|
59620
59719
|
* loop and returns control to the deploy engine's cleanup path.
|
|
59720
|
+
*
|
|
59721
|
+
* Both throws are `ProvisioningError`s carrying `certificateArn` as their
|
|
59722
|
+
* `physicalId` (issue
|
|
59723
|
+
* [#2169](https://github.com/go-to-k/cdkd/issues/2169)). They used to be
|
|
59724
|
+
* plain `Error`s whose only reference to the certificate was the message
|
|
59725
|
+
* TEXT, which is not something a caller can act on. The certificate is real
|
|
59726
|
+
* by the time either fires, so the error that reports the failure is the one
|
|
59727
|
+
* piece of the failure path that should still be able to name it.
|
|
59728
|
+
*
|
|
59729
|
+
* Says nothing about what happens to the certificate afterwards: the caller
|
|
59730
|
+
* owns that. `create()`'s catch retires it (see
|
|
59731
|
+
* {@link ACMCertificateProvider.cleanupRequestedCertificate}) and appends a
|
|
59732
|
+
* note to this message only when that cleanup could NOT.
|
|
59621
59733
|
*/
|
|
59622
|
-
async waitForCertificateIssued(certificateArn, logicalId) {
|
|
59734
|
+
async waitForCertificateIssued(certificateArn, logicalId, resourceType) {
|
|
59623
59735
|
this.logger.debug(`Waiting for ACM certificate ${certificateArn} to reach ISSUED status...`);
|
|
59624
59736
|
let interrupted = false;
|
|
59625
|
-
|
|
59737
|
+
/** The last validation-record block printed, so we re-print only on a CHANGE. */
|
|
59738
|
+
let lastValidationOptions;
|
|
59626
59739
|
const sigintHandler = () => {
|
|
59627
59740
|
interrupted = true;
|
|
59628
59741
|
};
|
|
@@ -59640,17 +59753,20 @@ var ACMCertificateProvider = class {
|
|
|
59640
59753
|
this.logger.debug(`ACM certificate ${certificateArn} is ISSUED`);
|
|
59641
59754
|
return;
|
|
59642
59755
|
}
|
|
59643
|
-
if (status === "FAILED" || status === "VALIDATION_TIMED_OUT" || status === "INACTIVE" || status === "REVOKED" || status === "EXPIRED") throw new
|
|
59644
|
-
if (status === "PENDING_VALIDATION"
|
|
59645
|
-
this.
|
|
59646
|
-
|
|
59756
|
+
if (status === "FAILED" || status === "VALIDATION_TIMED_OUT" || status === "INACTIVE" || status === "REVOKED" || status === "EXPIRED") throw new ProvisioningError(`ACM certificate ${logicalId} (${certificateArn}) entered terminal status ${status} during validation. Check ACM console / DNS records to diagnose.`, resourceType, logicalId, certificateArn);
|
|
59757
|
+
if (status === "PENDING_VALIDATION") {
|
|
59758
|
+
const rendered = this.renderValidationOptions(validations);
|
|
59759
|
+
if (rendered !== void 0 && rendered !== lastValidationOptions) {
|
|
59760
|
+
this.logger.info(rendered);
|
|
59761
|
+
lastValidationOptions = rendered;
|
|
59762
|
+
}
|
|
59647
59763
|
}
|
|
59648
59764
|
this.logger.debug(`ACM certificate ${certificateArn} status: ${status} (attempt ${attempt}/${this.maxPollAttempts})`);
|
|
59649
59765
|
const sleepEnd = Date.now() + this.pollIntervalMs;
|
|
59650
59766
|
const tickMs = Math.min(1e3, this.pollIntervalMs);
|
|
59651
59767
|
while (Date.now() < sleepEnd && !interrupted) await new Promise((resolve) => setTimeout(resolve, tickMs));
|
|
59652
59768
|
}
|
|
59653
|
-
throw new
|
|
59769
|
+
throw new ProvisioningError(`ACM certificate ${logicalId} (${certificateArn}) did not reach ISSUED status within ${this.maxPollAttempts * this.pollIntervalMs / 1e3}s. ` + (lastValidationOptions === void 0 ? "AWS had not published this certificate's validation records before the wait ran out, so none were printed — re-run the deploy to request them again. " : `Add the validation records printed above to your DNS zone and re-run the deploy. `) + "ACM reuses a domain's validation CNAME across certificates, so records you add now validate the next attempt. To wait LONGER, raise CDKD_ACM_POLL_ATTEMPTS / CDKD_ACM_POLL_INTERVAL_MS — this cap is the provider's OWN and is what just fired; --resource-timeout alone will not lengthen it. Past ~30m also raise --resource-timeout AWS::CertificateManager::Certificate=<duration>, or the engine's per-resource deadline becomes the shorter of the two. Or set CDKD_NO_WAIT=true to keep the certificate and validate it out of band.", resourceType, logicalId, certificateArn);
|
|
59654
59770
|
} finally {
|
|
59655
59771
|
process.removeListener("SIGINT", sigintHandler);
|
|
59656
59772
|
}
|
|
@@ -59660,16 +59776,13 @@ var ACMCertificateProvider = class {
|
|
|
59660
59776
|
* user can copy / paste them into Route 53 / Cloudflare / etc. while the
|
|
59661
59777
|
* cert is still PENDING_VALIDATION.
|
|
59662
59778
|
*/
|
|
59663
|
-
|
|
59779
|
+
renderValidationOptions(validations) {
|
|
59664
59780
|
const lines = ["ACM certificate is PENDING_VALIDATION. Add the following DNS records to validate:"];
|
|
59665
59781
|
for (const v of validations) if (v.ValidationMethod === "DNS" && v.ResourceRecord) {
|
|
59666
59782
|
const r = v.ResourceRecord;
|
|
59667
59783
|
lines.push(` ${v.DomainName} — ${r.Type} ${r.Name} -> ${r.Value}`);
|
|
59668
|
-
} else if (v.ValidationMethod === "EMAIL") {
|
|
59669
|
-
|
|
59670
|
-
lines.push(` ${v.DomainName} — confirmation email sent to: ${emails || "<none>"}`);
|
|
59671
|
-
}
|
|
59672
|
-
this.logger.info(lines.join("\n"));
|
|
59784
|
+
} else if (v.ValidationMethod === "EMAIL" && (v.ValidationEmails ?? []).length > 0) lines.push(` ${v.DomainName} — confirmation email sent to: ${v.ValidationEmails.join(", ")}`);
|
|
59785
|
+
return lines.length === 1 ? void 0 : lines.join("\n");
|
|
59673
59786
|
}
|
|
59674
59787
|
async updateTags(certificateArn, newTags, oldTags) {
|
|
59675
59788
|
const newTagMap = new Map((newTags || []).map((t) => [t.Key, t.Value]));
|
|
@@ -86442,7 +86555,7 @@ function createMigrateCommand() {
|
|
|
86442
86555
|
*/
|
|
86443
86556
|
function buildProgram() {
|
|
86444
86557
|
const program = new Command();
|
|
86445
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.
|
|
86558
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.40");
|
|
86446
86559
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
86447
86560
|
const { profile } = actionCommand.optsWithGlobals();
|
|
86448
86561
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|