@go-to-k/cdkd 0.280.42 → 0.280.44
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-a9iV8VG_.js → asg-provider-BBKdxCVP.js} +2 -2
- package/dist/{asg-provider-a9iV8VG_.js.map → asg-provider-BBKdxCVP.js.map} +1 -1
- package/dist/cli.js +269 -45
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-CuwZzHlM.js → deploy-engine-nEjymkij.js} +3 -3
- package/dist/{deploy-engine-CuwZzHlM.js.map → deploy-engine-nEjymkij.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 requireConfigArray, $t as expectedOwnerParam, A as green, An as withErrorHandling, At as getDockerCmd, B as slowCcOperationTimeoutMs, Bt as resolveAutoAssetStorage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackHasActiveImportsError, Ct as ensureAssetStorage, D as bold, Dt as validateContainerRepoName, E as formatResourceLine, Et as validateAssetBucketName, F as clearOnUpdateRemoval, Ft as Synthesizer, G as refStateLookupFromResource, Gt as resolveUseCdkBootstrapAssets, H as isTerminationProtectionPropagationError, Ht as resolveSkipPrefix, I as ProviderRegistry, It as synthesisStatusMessage, J as resolveExplicitPhysicalId, Jt as CFN_TEMPLATE_BODY_LIMIT, K as WAFv2WebACLProvider, Kt as stateBucketExistenceConfirmed, L as findActionableSilentDrops, Lt as getDefaultStateBucketName, M as yellow, Mt as runDockerStreaming, N as IAMRoleProvider, Nt as AssetManifestLoader, O as cyan, Ot as buildDockerImage, P as collectInlinePolicyNamesManagedBySiblings, Pt as getDockerImageBySourceHash, Q as replayWarn, Qt as uploadCfnTemplate, R as findSilentDropProperties, Rt as getLegacyStateBucketName, S as extractDeploymentEventError, Sn as ResourceUpdateNotSupportedError, St as BOOTSTRAP_MARKER_PREFIX, T as renderStatefulReason, Tt as parseBootstrapMarker, U as IntrinsicFunctionResolver, Ut as resolveStateBucketWithDefault, V as disableInstanceApiTermination, Vt as resolveCaptureObservedState, W as cfnRefValueFromPhysicalId, Wt as resolveStateBucketWithDefaultAndSource, X as configStringRefusal, Xt as MIGRATE_TMP_PREFIX, Y as assertRegionMatch, Yt as CFN_TEMPLATE_URL_LIMIT, Z as readConfigString, Zt as findLargeInlineResources, _ as createPreDeleteFinalSnapshot, _n as MissingCdkCliError, _t as buildAssetRedirectMap, a as DeploymentEventsStore, an as getAwsClients, at as withRetry, b as unsupportedFinalSnapshotError, bn as ProvisioningError, bt as rewriteTemplateAssetReferences, c as replayFailedOperations, ct as DagBuilder, d as IMPLICIT_DELETE_DEPENDENCIES, dt as S3StateBackend, en as AssemblyReader, et as requireConfigObject, f as computeImplicitDeleteEdges, fn as DeployCancelledError, ft as rebuildClientForBucketRegion, g as ccRoutedFinalSnapshotError, gt as WorkGraph, h as buildFinalSnapshotIdentifier, hn as LocalStartServiceError, ht as stringifyValue, i as DeploymentEventsReader, in as AwsClients, it as describeTypeWithThrottleRetry, j as red, jt as runDockerForeground, k as gray, kn as normalizeAwsError, kt as formatDockerLoginError, l as replayRollback, ln as CdkdError, lt as TemplateParser, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalMigrateError, mt as AssetPublisher, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as applyRoleArnIfSet, o as planFailedOps, on as resetAwsClients, ot as isRetryableTransientError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalInvokeBuildError$1, pt as shouldRetainResource, q as normalizeAwsTagsToCfn, qt as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, rn as resolveBucketRegion, rt as DiffCalculator, s as planRollback, sn as setAwsClients, st as isThrottlingError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as processStackMessages, tt as requireConfigString, u as withResourceDeadline, ut as LockManager, v as isFinalSnapshotError, vn as NestedStackChildDirectDestroyError, vt as createAssetRedirectResolver, w as isStatefulRecreateTargetSync, wn as StackTerminationProtectionError, wt as getBootstrapMarkerKey, x as makeCanonicalizePropertiesFn, xn as ResourceTimeoutError, xt as AssetModeResolver, y as refusesFinalSnapshot, yn as PartialFailureError, yt as loadPublishableAssetManifest, z as CloudControlProvider, zt as resolveApp } from "./deploy-engine-
|
|
2
|
+
import { $ as requireConfigArray, $t as expectedOwnerParam, A as green, An as withErrorHandling, At as getDockerCmd, B as slowCcOperationTimeoutMs, Bt as resolveAutoAssetStorage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackHasActiveImportsError, Ct as ensureAssetStorage, D as bold, Dt as validateContainerRepoName, E as formatResourceLine, Et as validateAssetBucketName, F as clearOnUpdateRemoval, Ft as Synthesizer, G as refStateLookupFromResource, Gt as resolveUseCdkBootstrapAssets, H as isTerminationProtectionPropagationError, Ht as resolveSkipPrefix, I as ProviderRegistry, It as synthesisStatusMessage, J as resolveExplicitPhysicalId, Jt as CFN_TEMPLATE_BODY_LIMIT, K as WAFv2WebACLProvider, Kt as stateBucketExistenceConfirmed, L as findActionableSilentDrops, Lt as getDefaultStateBucketName, M as yellow, Mt as runDockerStreaming, N as IAMRoleProvider, Nt as AssetManifestLoader, O as cyan, Ot as buildDockerImage, P as collectInlinePolicyNamesManagedBySiblings, Pt as getDockerImageBySourceHash, Q as replayWarn, Qt as uploadCfnTemplate, R as findSilentDropProperties, Rt as getLegacyStateBucketName, S as extractDeploymentEventError, Sn as ResourceUpdateNotSupportedError, St as BOOTSTRAP_MARKER_PREFIX, T as renderStatefulReason, Tt as parseBootstrapMarker, U as IntrinsicFunctionResolver, Ut as resolveStateBucketWithDefault, V as disableInstanceApiTermination, Vt as resolveCaptureObservedState, W as cfnRefValueFromPhysicalId, Wt as resolveStateBucketWithDefaultAndSource, X as configStringRefusal, Xt as MIGRATE_TMP_PREFIX, Y as assertRegionMatch, Yt as CFN_TEMPLATE_URL_LIMIT, Z as readConfigString, Zt as findLargeInlineResources, _ as createPreDeleteFinalSnapshot, _n as MissingCdkCliError, _t as buildAssetRedirectMap, a as DeploymentEventsStore, an as getAwsClients, at as withRetry, b as unsupportedFinalSnapshotError, bn as ProvisioningError, bt as rewriteTemplateAssetReferences, c as replayFailedOperations, ct as DagBuilder, d as IMPLICIT_DELETE_DEPENDENCIES, dt as S3StateBackend, en as AssemblyReader, et as requireConfigObject, f as computeImplicitDeleteEdges, fn as DeployCancelledError, ft as rebuildClientForBucketRegion, g as ccRoutedFinalSnapshotError, gt as WorkGraph, h as buildFinalSnapshotIdentifier, hn as LocalStartServiceError, ht as stringifyValue, i as DeploymentEventsReader, in as AwsClients, it as describeTypeWithThrottleRetry, j as red, jt as runDockerForeground, k as gray, kn as normalizeAwsError, kt as formatDockerLoginError, l as replayRollback, ln as CdkdError, lt as TemplateParser, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalMigrateError, mt as AssetPublisher, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as applyRoleArnIfSet, o as planFailedOps, on as resetAwsClients, ot as isRetryableTransientError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalInvokeBuildError$1, pt as shouldRetainResource, q as normalizeAwsTagsToCfn, qt as warnDeprecatedNoPrefixCliFlag, r as DeployEngine, rn as resolveBucketRegion, rt as DiffCalculator, s as planRollback, sn as setAwsClients, st as isThrottlingError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as processStackMessages, tt as requireConfigString, u as withResourceDeadline, ut as LockManager, v as isFinalSnapshotError, vn as NestedStackChildDirectDestroyError, vt as createAssetRedirectResolver, w as isStatefulRecreateTargetSync, wn as StackTerminationProtectionError, wt as getBootstrapMarkerKey, x as makeCanonicalizePropertiesFn, xn as ResourceTimeoutError, xt as AssetModeResolver, y as refusesFinalSnapshot, yn as PartialFailureError, yt as loadPublishableAssetManifest, z as CloudControlProvider, zt as resolveApp } from "./deploy-engine-nEjymkij.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, 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-BBKdxCVP.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";
|
|
@@ -33013,6 +33013,102 @@ function isZoneDisabledForMutationError(error) {
|
|
|
33013
33013
|
return error instanceof Error && error.message.includes("is marked disabled for mutation");
|
|
33014
33014
|
}
|
|
33015
33015
|
/**
|
|
33016
|
+
* A `HostedZoneName` that resolved cleanly but matches no zone.
|
|
33017
|
+
*
|
|
33018
|
+
* Distinct from "no zone was specified": the DELETE path treats a vanished
|
|
33019
|
+
* zone as already-gone (the zone's records went with it) rather than surfacing
|
|
33020
|
+
* a misconfiguration error that would block `cdkd destroy`.
|
|
33021
|
+
*/
|
|
33022
|
+
var HostedZoneNameNotFoundError = class HostedZoneNameNotFoundError extends ProvisioningError {
|
|
33023
|
+
constructor(message, resourceType, logicalId, physicalId, cause) {
|
|
33024
|
+
super(message, resourceType, logicalId, physicalId, cause);
|
|
33025
|
+
this.name = "HostedZoneNameNotFoundError";
|
|
33026
|
+
Object.setPrototypeOf(this, HostedZoneNameNotFoundError.prototype);
|
|
33027
|
+
}
|
|
33028
|
+
};
|
|
33029
|
+
/** Segment count of cdkd's `AWS::Route53::RecordSet` composite physicalId. */
|
|
33030
|
+
const RECORD_SET_COMPOSITE_SEGMENTS = 3;
|
|
33031
|
+
/**
|
|
33032
|
+
* Parse cdkd's composite record-set physicalId `<hostedZoneId>|<Name>|<Type>`.
|
|
33033
|
+
*
|
|
33034
|
+
* Returns `undefined` for anything else — most importantly CloudFormation's
|
|
33035
|
+
* OWN physicalId for this type, which is the record name alone (`Ref` on an
|
|
33036
|
+
* `AWS::Route53::RecordSet` returns the domain name, and a DNS name can never
|
|
33037
|
+
* contain `|`). `cdkd import` stored that scalar verbatim before issue #1658,
|
|
33038
|
+
* so state files in the wild carry BOTH shapes and every consumer has to know
|
|
33039
|
+
* which one it is holding.
|
|
33040
|
+
*/
|
|
33041
|
+
function parseRecordSetCompositeId(physicalId) {
|
|
33042
|
+
const parts = physicalId.split("|");
|
|
33043
|
+
if (parts.length !== RECORD_SET_COMPOSITE_SEGMENTS) return void 0;
|
|
33044
|
+
const [hostedZoneId, name, type] = parts;
|
|
33045
|
+
if (!hostedZoneId || !name || !type) return void 0;
|
|
33046
|
+
return {
|
|
33047
|
+
hostedZoneId,
|
|
33048
|
+
name,
|
|
33049
|
+
type
|
|
33050
|
+
};
|
|
33051
|
+
}
|
|
33052
|
+
/**
|
|
33053
|
+
* Octal escapes that must NOT be decoded, because decoding them is not
|
|
33054
|
+
* injective — two genuinely different names would compare EQUAL:
|
|
33055
|
+
*
|
|
33056
|
+
* - `056` is a period INSIDE a label. Route 53 reports the single-label
|
|
33057
|
+
* record `a.b` (period escaped) as `a\\056b.example.com.`, which decodes to
|
|
33058
|
+
* `a.b.example.com.` — identical to the different, two-label record
|
|
33059
|
+
* `a.b.example.com`.
|
|
33060
|
+
* - `134` is a literal backslash, which would re-introduce escape ambiguity.
|
|
33061
|
+
*
|
|
33062
|
+
* A false MATCH is far worse than a false miss here: the compare drives which
|
|
33063
|
+
* record `readCurrentState` reports and which hosted zone `create` picks, so
|
|
33064
|
+
* collapsing two records would target the wrong one.
|
|
33065
|
+
*/
|
|
33066
|
+
const UNDECODABLE_OCTAL_ESCAPES = /* @__PURE__ */ new Set(["056", "134"]);
|
|
33067
|
+
/**
|
|
33068
|
+
* Canonicalize a record name for comparison.
|
|
33069
|
+
*
|
|
33070
|
+
* Two AWS-side spellings have to be absorbed, and missing either one makes
|
|
33071
|
+
* `readCurrentState` return `undefined` — which reads as "drift unknown"
|
|
33072
|
+
* rather than as an error:
|
|
33073
|
+
*
|
|
33074
|
+
* - Route 53 reports every name fully qualified with a TRAILING DOT, while a
|
|
33075
|
+
* template may declare it either way.
|
|
33076
|
+
* - Special characters come back OCTAL-ESCAPED: `*.example.com` is reported as
|
|
33077
|
+
* `\\052.example.com.`. That affects every wildcard record, including ones
|
|
33078
|
+
* cdkd created itself, so decoding it here fixes drift for the whole
|
|
33079
|
+
* population rather than only the imported ones.
|
|
33080
|
+
*
|
|
33081
|
+
* Known bound: Route 53 escapes per BYTE, so a non-ASCII name decodes to
|
|
33082
|
+
* latin-1 mojibake and will not match its template spelling. That is a false
|
|
33083
|
+
* MISS (drift unknown), never a false match, and CDK emits punycode for
|
|
33084
|
+
* internationalized names in practice.
|
|
33085
|
+
*/
|
|
33086
|
+
function normalizeRecordName(name) {
|
|
33087
|
+
const lowered = name.replace(/\\([0-7]{3})/g, (match, oct) => UNDECODABLE_OCTAL_ESCAPES.has(oct) ? match : String.fromCharCode(parseInt(oct, 8))).toLowerCase();
|
|
33088
|
+
return lowered.endsWith(".") ? lowered : `${lowered}.`;
|
|
33089
|
+
}
|
|
33090
|
+
/**
|
|
33091
|
+
* Canonicalize a name for the QUERY side of a Route 53 list call.
|
|
33092
|
+
*
|
|
33093
|
+
* `normalizeRecordName` above is the COMPARE-side canonicalizer, and the two
|
|
33094
|
+
* are deliberately NOT the same function. Route 53 orders both hosted zones
|
|
33095
|
+
* and record sets by reversed labels of the name it STORES — lower-cased and
|
|
33096
|
+
* escape-ENCODED — and a `DNSName` / `StartRecordName` start key in any other
|
|
33097
|
+
* spelling positions the window somewhere else entirely. So the query side
|
|
33098
|
+
* must move TOWARD AWS's spelling (lower-case, encode `*`), where the compare
|
|
33099
|
+
* side moves AWS's answer toward the template's (decode escapes).
|
|
33100
|
+
*
|
|
33101
|
+
* Decoding here would be actively wrong: `*.example.com` sorts at `*` (0x2A)
|
|
33102
|
+
* while AWS stores `\052.example.com` and sorts it at `\` (0x5C), so every
|
|
33103
|
+
* name whose first label begins with `-` / `+` / a digit / an upper-case
|
|
33104
|
+
* letter falls BETWEEN them — with a bounded page, the wildcard record we
|
|
33105
|
+
* asked for is skipped straight past.
|
|
33106
|
+
*/
|
|
33107
|
+
function canonicalizeQueryName(name) {
|
|
33108
|
+
const lowered = name.toLowerCase().replaceAll("*", "\\052");
|
|
33109
|
+
return lowered.endsWith(".") ? lowered : `${lowered}.`;
|
|
33110
|
+
}
|
|
33111
|
+
/**
|
|
33016
33112
|
* AWS Route 53 Provider
|
|
33017
33113
|
*
|
|
33018
33114
|
* Implements resource provisioning for Route 53 resources:
|
|
@@ -33351,10 +33447,20 @@ var Route53Provider = class {
|
|
|
33351
33447
|
}
|
|
33352
33448
|
async deleteRecordSet(logicalId, physicalId, resourceType, properties, context) {
|
|
33353
33449
|
this.logger.debug(`Deleting Route 53 record set ${logicalId}: ${physicalId}`);
|
|
33354
|
-
const parts = physicalId.split("|");
|
|
33355
|
-
if (parts.length !== 3) throw new ProvisioningError(`Invalid record set physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
|
|
33356
|
-
const [hostedZoneId] = parts;
|
|
33357
33450
|
if (!properties) throw new ProvisioningError(`Properties required to delete record set ${logicalId}`, resourceType, logicalId, physicalId);
|
|
33451
|
+
const composite = parseRecordSetCompositeId(physicalId);
|
|
33452
|
+
let hostedZoneId;
|
|
33453
|
+
if (composite) hostedZoneId = composite.hostedZoneId;
|
|
33454
|
+
else try {
|
|
33455
|
+
hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType, physicalId, { requireUnambiguousZoneName: true });
|
|
33456
|
+
} catch (error) {
|
|
33457
|
+
if (error instanceof HostedZoneNameNotFoundError) {
|
|
33458
|
+
assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
33459
|
+
this.logger.warn(`Hosted zone "${String(properties?.["HostedZoneName"])}" for record set ${logicalId} (${physicalId}) no longer exists; treating the record as already deleted and dropping it from state.`);
|
|
33460
|
+
return;
|
|
33461
|
+
}
|
|
33462
|
+
throw error;
|
|
33463
|
+
}
|
|
33358
33464
|
try {
|
|
33359
33465
|
const resourceRecordSet = this.buildResourceRecordSet(properties);
|
|
33360
33466
|
const sendDelete = () => this.getClient().send(new ChangeResourceRecordSetsCommand({
|
|
@@ -33702,22 +33808,65 @@ var Route53Provider = class {
|
|
|
33702
33808
|
* If HostedZoneId is provided, use it directly.
|
|
33703
33809
|
* If HostedZoneName is provided, resolve it to a HostedZoneId via ListHostedZonesByName.
|
|
33704
33810
|
*/
|
|
33705
|
-
async resolveHostedZoneId(properties, logicalId, resourceType, physicalId) {
|
|
33811
|
+
async resolveHostedZoneId(properties, logicalId, resourceType, physicalId, options) {
|
|
33706
33812
|
const hostedZoneId = properties["HostedZoneId"];
|
|
33707
|
-
if (hostedZoneId) return hostedZoneId;
|
|
33813
|
+
if (typeof hostedZoneId === "string" && hostedZoneId) return hostedZoneId;
|
|
33814
|
+
let lookupErrorMessage;
|
|
33815
|
+
let lookupErrorCause;
|
|
33708
33816
|
const hostedZoneName = properties["HostedZoneName"];
|
|
33709
|
-
if (hostedZoneName)
|
|
33710
|
-
|
|
33711
|
-
|
|
33712
|
-
|
|
33713
|
-
|
|
33714
|
-
|
|
33715
|
-
|
|
33817
|
+
if (typeof hostedZoneName === "string" && hostedZoneName) {
|
|
33818
|
+
let matched = [];
|
|
33819
|
+
let absenceIsSound = false;
|
|
33820
|
+
try {
|
|
33821
|
+
const response = await this.getClient().send(new ListHostedZonesByNameCommand({
|
|
33822
|
+
DNSName: canonicalizeQueryName(hostedZoneName),
|
|
33823
|
+
MaxItems: 5
|
|
33824
|
+
}));
|
|
33825
|
+
const zones = response.HostedZones ?? [];
|
|
33826
|
+
const normalizedName = normalizeRecordName(hostedZoneName);
|
|
33827
|
+
matched = zones.filter((z) => normalizeRecordName(z.Name ?? "") === normalizedName);
|
|
33828
|
+
absenceIsSound = !hostedZoneName.includes("\\") && (zones.length > 0 || response.IsTruncated !== true);
|
|
33829
|
+
} catch (error) {
|
|
33830
|
+
lookupErrorMessage = error instanceof Error ? error.message : String(error);
|
|
33831
|
+
lookupErrorCause = error instanceof Error ? error : void 0;
|
|
33832
|
+
this.logger.warn(`Failed to resolve HostedZoneName "${hostedZoneName}" for ${logicalId}: ${lookupErrorMessage}`);
|
|
33833
|
+
}
|
|
33834
|
+
if (matched.length > 1 && options?.requireUnambiguousZoneName) throw new ProvisioningError(`HostedZoneName "${hostedZoneName}" matches ${matched.length} hosted zones for ${logicalId} (split-horizon public/private DNS). Refusing to guess which one this record belongs to — set HostedZoneId explicitly, or pass the '<zoneId>|<name>|<type>' composite via --resource.`, resourceType, logicalId, physicalId);
|
|
33835
|
+
const matchedZone = matched[0];
|
|
33716
33836
|
if (matchedZone?.Id) return matchedZone.Id.replace("/hostedzone/", "");
|
|
33717
|
-
|
|
33718
|
-
|
|
33837
|
+
if (absenceIsSound) throw new HostedZoneNameNotFoundError(`HostedZoneName "${hostedZoneName}" matches no hosted zone in this account/region for ${logicalId}`, resourceType, logicalId, physicalId);
|
|
33838
|
+
}
|
|
33839
|
+
throw new ProvisioningError(lookupErrorMessage ? `Could not resolve HostedZoneName "${String(hostedZoneName)}" for record set ${logicalId}: ${lookupErrorMessage}` : `Either HostedZoneId or HostedZoneName is required for record set ${logicalId}`, resourceType, logicalId, physicalId, lookupErrorCause);
|
|
33840
|
+
}
|
|
33841
|
+
/**
|
|
33842
|
+
* Recover `(hostedZoneId, name, type)` for a record set from EITHER shape of
|
|
33843
|
+
* physicalId (issue #1658).
|
|
33844
|
+
*
|
|
33845
|
+
* cdkd's own composite carries all three. CloudFormation's physicalId is the
|
|
33846
|
+
* record name alone, so the zone has to be resolved from the recorded
|
|
33847
|
+
* properties — which is exactly what `createRecordSet` does, so the answer is
|
|
33848
|
+
* the same triple the composite would have held. Returns `undefined` (rather
|
|
33849
|
+
* than throwing) when the zone cannot be resolved, because both callers of
|
|
33850
|
+
* this helper treat "cannot determine" as "nothing to report" rather than as
|
|
33851
|
+
* a failure.
|
|
33852
|
+
*/
|
|
33853
|
+
async resolveRecordSetIdentity(physicalId, properties, options) {
|
|
33854
|
+
const composite = parseRecordSetCompositeId(physicalId);
|
|
33855
|
+
if (composite) return composite;
|
|
33856
|
+
if (!properties) return void 0;
|
|
33857
|
+
const name = typeof properties["Name"] === "string" ? properties["Name"] : physicalId;
|
|
33858
|
+
const type = properties["Type"];
|
|
33859
|
+
if (!name || typeof type !== "string" || !type) return void 0;
|
|
33860
|
+
try {
|
|
33861
|
+
return {
|
|
33862
|
+
hostedZoneId: await this.resolveHostedZoneId(properties, "RecordSet", "AWS::Route53::RecordSet", physicalId, options),
|
|
33863
|
+
name,
|
|
33864
|
+
type
|
|
33865
|
+
};
|
|
33866
|
+
} catch (err) {
|
|
33867
|
+
this.logger.debug(`Cannot resolve record-set identity for '${physicalId}': ${err instanceof Error ? err.message : String(err)}`);
|
|
33868
|
+
return;
|
|
33719
33869
|
}
|
|
33720
|
-
throw new ProvisioningError(`Either HostedZoneId or HostedZoneName is required for record set ${logicalId}`, resourceType, logicalId, physicalId);
|
|
33721
33870
|
}
|
|
33722
33871
|
/**
|
|
33723
33872
|
* Read the AWS-current Route 53 resource configuration in CFn-property shape.
|
|
@@ -33733,17 +33882,21 @@ var Route53Provider = class {
|
|
|
33733
33882
|
* `CloudWatchLogsLogGroupArn` to match cdkd state).
|
|
33734
33883
|
* - `RecordSet` → `ListResourceRecordSets` filtered to the exact
|
|
33735
33884
|
* `(name, type)` pair from the composite physicalId
|
|
33736
|
-
* (`{zoneId}|{name}|{type}`)
|
|
33885
|
+
* (`{zoneId}|{name}|{type}`), or — for a state record carrying
|
|
33886
|
+
* CloudFormation's scalar physicalId, written by `cdkd import`
|
|
33887
|
+
* before issue #1658 — from the recorded properties, so drift is
|
|
33888
|
+
* reported for those records instead of silently skipped.
|
|
33889
|
+
* Surfaces TTL, ResourceRecords (with
|
|
33737
33890
|
* `[{Value}]` -> string[] re-shape to match cdkd state), AliasTarget,
|
|
33738
33891
|
* Weight, Region, Failover, MultiValueAnswer, HealthCheckId,
|
|
33739
33892
|
* GeoLocation, GeoProximityLocation, CidrRoutingConfig, SetIdentifier.
|
|
33740
33893
|
*
|
|
33741
33894
|
* Returns `undefined` when the parent zone is gone (`NoSuchHostedZone`).
|
|
33742
33895
|
*/
|
|
33743
|
-
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
33896
|
+
async readCurrentState(physicalId, _logicalId, resourceType, properties) {
|
|
33744
33897
|
switch (resourceType) {
|
|
33745
33898
|
case "AWS::Route53::HostedZone": return this.readHostedZone(physicalId);
|
|
33746
|
-
case "AWS::Route53::RecordSet": return this.readRecordSet(physicalId);
|
|
33899
|
+
case "AWS::Route53::RecordSet": return this.readRecordSet(physicalId, properties);
|
|
33747
33900
|
default: return;
|
|
33748
33901
|
}
|
|
33749
33902
|
}
|
|
@@ -33789,24 +33942,24 @@ var Route53Provider = class {
|
|
|
33789
33942
|
}
|
|
33790
33943
|
return result;
|
|
33791
33944
|
}
|
|
33792
|
-
async readRecordSet(physicalId) {
|
|
33793
|
-
const
|
|
33794
|
-
if (
|
|
33795
|
-
const
|
|
33796
|
-
if (!hostedZoneId || !name || !type) return void 0;
|
|
33945
|
+
async readRecordSet(physicalId, properties) {
|
|
33946
|
+
const identity = await this.resolveRecordSetIdentity(physicalId, properties);
|
|
33947
|
+
if (!identity) return void 0;
|
|
33948
|
+
const { hostedZoneId, name, type } = identity;
|
|
33797
33949
|
let resp;
|
|
33798
33950
|
try {
|
|
33799
33951
|
resp = await this.getClient().send(new ListResourceRecordSetsCommand({
|
|
33800
33952
|
HostedZoneId: hostedZoneId,
|
|
33801
|
-
StartRecordName: name,
|
|
33953
|
+
StartRecordName: canonicalizeQueryName(name),
|
|
33802
33954
|
StartRecordType: type,
|
|
33803
|
-
MaxItems:
|
|
33955
|
+
MaxItems: 5
|
|
33804
33956
|
}));
|
|
33805
33957
|
} catch (err) {
|
|
33806
33958
|
if (err instanceof Error && err.name === "NoSuchHostedZone") return void 0;
|
|
33807
33959
|
throw err;
|
|
33808
33960
|
}
|
|
33809
|
-
const
|
|
33961
|
+
const wantedName = normalizeRecordName(name);
|
|
33962
|
+
const recordSet = resp.ResourceRecordSets?.find((r) => r.Name !== void 0 && normalizeRecordName(r.Name) === wantedName && r.Type === type);
|
|
33810
33963
|
if (!recordSet) return void 0;
|
|
33811
33964
|
const result = {
|
|
33812
33965
|
HostedZoneId: hostedZoneId,
|
|
@@ -33861,23 +34014,89 @@ var Route53Provider = class {
|
|
|
33861
34014
|
/**
|
|
33862
34015
|
* Adopt an existing Route 53 resource into cdkd state.
|
|
33863
34016
|
*
|
|
33864
|
-
* Supported types: `AWS::Route53::HostedZone` (
|
|
33865
|
-
*
|
|
33866
|
-
*
|
|
33867
|
-
*
|
|
34017
|
+
* Supported types: `AWS::Route53::HostedZone` (override-only);
|
|
34018
|
+
* `AWS::Route53::RecordSet` (override-only too — the supplied id is
|
|
34019
|
+
* CANONICALIZED rather than trusted, using the template's
|
|
34020
|
+
* `HostedZoneId` / `HostedZoneName` + `Name` + `Type` and verified
|
|
34021
|
+
* against AWS, but an import with no override still declines because
|
|
34022
|
+
* a RecordSet has no standalone identity for auto mode to key on).
|
|
33868
34023
|
*/
|
|
33869
34024
|
async import(input) {
|
|
33870
34025
|
switch (input.resourceType) {
|
|
33871
34026
|
case "AWS::Route53::HostedZone": return this.importHostedZone(input);
|
|
33872
|
-
case "AWS::Route53::RecordSet":
|
|
33873
|
-
if (input.knownPhysicalId) return {
|
|
33874
|
-
physicalId: input.knownPhysicalId,
|
|
33875
|
-
attributes: {}
|
|
33876
|
-
};
|
|
33877
|
-
return null;
|
|
34027
|
+
case "AWS::Route53::RecordSet": return this.importRecordSet(input);
|
|
33878
34028
|
default: return null;
|
|
33879
34029
|
}
|
|
33880
34030
|
}
|
|
34031
|
+
/**
|
|
34032
|
+
* Adopt an existing record set (issue #1658).
|
|
34033
|
+
*
|
|
34034
|
+
* cdkd's physicalId is the composite `<hostedZoneId>|<Name>|<Type>` that
|
|
34035
|
+
* `createRecordSet` returns, but CloudFormation's is the record NAME alone.
|
|
34036
|
+
* In auto / `--migrate-from-cloudformation` mode `knownPhysicalId` is NOT a
|
|
34037
|
+
* user choice — the overrides are pre-populated from the CFn stack's
|
|
34038
|
+
* `DescribeStackResources` — so storing it verbatim wrote an id this
|
|
34039
|
+
* provider's own `delete` could not parse, and the migrated stack became
|
|
34040
|
+
* undestroyable with the defect only surfacing after the CFn stack had
|
|
34041
|
+
* already been retired.
|
|
34042
|
+
*
|
|
34043
|
+
* So re-canonicalize instead, the way `AWS::EC2::EIP` already does: an
|
|
34044
|
+
* already-composite override passes through (the documented
|
|
34045
|
+
* `--resource 'Id=zone|name|type'` form), anything else is rebuilt from the
|
|
34046
|
+
* template properties and VERIFIED against AWS.
|
|
34047
|
+
*
|
|
34048
|
+
* **Canonicalization is BEST-EFFORT and never drops the row.** When the
|
|
34049
|
+
* identity cannot be resolved or verified, the supplied id is adopted
|
|
34050
|
+
* VERBATIM with a warning rather than returning `null`. Returning `null`
|
|
34051
|
+
* there would be strictly worse than the bug this fixes: `import.ts` only
|
|
34052
|
+
* aborts when ZERO resources import, so under
|
|
34053
|
+
* `--migrate-from-cloudformation` the run proceeds to `UpdateStack(Retain)`
|
|
34054
|
+
* + `DeleteStack` and the record ends up in NEITHER CloudFormation nor cdkd
|
|
34055
|
+
* state — after which the next deploy CREATEs over a live RRSet and fails.
|
|
34056
|
+
* Adopting verbatim is safe now precisely because `deleteRecordSet` and
|
|
34057
|
+
* `readRecordSet` both accept the scalar form (the other half of this fix).
|
|
34058
|
+
*
|
|
34059
|
+
* The ordinary shape that makes verification legitimately fail is a
|
|
34060
|
+
* `HostedZoneId` still carrying an unresolved intrinsic (`import.ts`
|
|
34061
|
+
* substitutes only single-key `{Ref}` before calling a provider). A
|
|
34062
|
+
* split-horizon zone name is refused for safety and lands here too.
|
|
34063
|
+
* (Wildcard records used to fail here as well; `normalizeRecordName` now
|
|
34064
|
+
* decodes Route 53's octal escapes, so they canonicalize normally.)
|
|
34065
|
+
*
|
|
34066
|
+
* This stays OVERRIDE-ONLY (see the "sub-resources without a standalone
|
|
34067
|
+
* identity" list in docs/import.md): the id is now derived from the template
|
|
34068
|
+
* rather than trusted, but an import with no override at all still declines,
|
|
34069
|
+
* because a RecordSet has no standalone identity for auto mode to key on.
|
|
34070
|
+
*/
|
|
34071
|
+
async importRecordSet(input) {
|
|
34072
|
+
const known = input.knownPhysicalId;
|
|
34073
|
+
if (!known) return null;
|
|
34074
|
+
if (parseRecordSetCompositeId(known)) return {
|
|
34075
|
+
physicalId: known,
|
|
34076
|
+
attributes: {}
|
|
34077
|
+
};
|
|
34078
|
+
const adoptVerbatim = (reason) => {
|
|
34079
|
+
this.logger.warn(`Record set ${input.logicalId}: could not canonicalize physical id '${known}' (${reason}). Adopting it as-is — delete and drift both accept this form, but a later deploy that UPDATEs the record will rewrite it to cdkd's '<zoneId>|<name>|<type>' composite.`);
|
|
34080
|
+
return {
|
|
34081
|
+
physicalId: known,
|
|
34082
|
+
attributes: {}
|
|
34083
|
+
};
|
|
34084
|
+
};
|
|
34085
|
+
const identity = await this.resolveRecordSetIdentity(known, input.properties, { requireUnambiguousZoneName: true });
|
|
34086
|
+
if (!identity) return adoptVerbatim("the template does not unambiguously resolve to a hosted zone + Name + Type");
|
|
34087
|
+
const compositeId = `${identity.hostedZoneId}|${identity.name}|${identity.type}`;
|
|
34088
|
+
let observed;
|
|
34089
|
+
try {
|
|
34090
|
+
observed = await this.readRecordSet(compositeId);
|
|
34091
|
+
} catch (err) {
|
|
34092
|
+
return adoptVerbatim(`verification failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
34093
|
+
}
|
|
34094
|
+
if (!observed) return adoptVerbatim("the record was not found under the resolved hosted zone");
|
|
34095
|
+
return {
|
|
34096
|
+
physicalId: compositeId,
|
|
34097
|
+
attributes: {}
|
|
34098
|
+
};
|
|
34099
|
+
}
|
|
33881
34100
|
async importHostedZone(input) {
|
|
33882
34101
|
if (input.knownPhysicalId) try {
|
|
33883
34102
|
await this.getClient().send(new GetHostedZoneCommand({ Id: input.knownPhysicalId }));
|
|
@@ -61573,7 +61792,7 @@ async function importCommand(stackArg, options) {
|
|
|
61573
61792
|
accountIdForNestedSynth = identity.Account;
|
|
61574
61793
|
}
|
|
61575
61794
|
const selectiveMode = overrides.size > 0 && !options.auto && !options.migrateFromCloudformation;
|
|
61576
|
-
if (selectiveMode) logger.info(`Selective mode: only importing the ${overrides.size} resource(s) you listed (${[...overrides.keys()].join(", ")}). Pass --auto to also
|
|
61795
|
+
if (selectiveMode) logger.info(`Selective mode: only importing the ${overrides.size} resource(s) you listed (${[...overrides.keys()].join(", ")}). Pass --auto to also auto-resolve the rest.`);
|
|
61577
61796
|
if (!selectiveMode && !migrationCfnStackName) {
|
|
61578
61797
|
const cfnResources = await tryGetCloudFormationResourceMap(stackInfo.stackName, awsClients.cloudFormation);
|
|
61579
61798
|
if (cfnResources) {
|
|
@@ -62147,9 +62366,14 @@ async function confirmPrompt(prompt) {
|
|
|
62147
62366
|
* Three usage modes:
|
|
62148
62367
|
*
|
|
62149
62368
|
* 1. **Auto mode** (no overrides): `cdkd import MyStack`
|
|
62150
|
-
* Imports every resource in the template
|
|
62151
|
-
*
|
|
62152
|
-
*
|
|
62369
|
+
* Imports every resource in the template, resolving each physical id
|
|
62370
|
+
* from the template's explicit physical-name property and then from a
|
|
62371
|
+
* same-named CloudFormation stack's `DescribeStackResources` (issue
|
|
62372
|
+
* #1128 / #1130). cdkd's value-add over CDK CLI — useful for adopting
|
|
62373
|
+
* a whole stack that was previously deployed by `cdk deploy`. (There is
|
|
62374
|
+
* deliberately no `aws:cdk:path` tag walk: AWS reserves the `aws:` tag
|
|
62375
|
+
* prefix, so that tag never exists on a real resource — issue #1134
|
|
62376
|
+
* removed the walk from every provider.)
|
|
62153
62377
|
*
|
|
62154
62378
|
* 2. **Selective mode** (CDK CLI parity, default when overrides given):
|
|
62155
62379
|
* `cdkd import MyStack --resource MyBucket=my-bucket-name`
|
|
@@ -62163,11 +62387,11 @@ async function confirmPrompt(prompt) {
|
|
|
62163
62387
|
* 3. **Hybrid mode** (`--auto` with overrides):
|
|
62164
62388
|
* `cdkd import MyStack --resource MyBucket=name --auto`
|
|
62165
62389
|
* Listed resources use the explicit physical id; all other
|
|
62166
|
-
* resources still go through
|
|
62390
|
+
* resources still go through auto-resolution. The pre-PR
|
|
62167
62391
|
* default behavior, now opt-in.
|
|
62168
62392
|
*/
|
|
62169
62393
|
function createImportCommand() {
|
|
62170
|
-
const cmd = new Command("import").description("Adopt already-deployed AWS resources into cdkd state. Reads the CDK app to find logical IDs, resource types, and dependencies. With no flags, imports every resource
|
|
62394
|
+
const cmd = new Command("import").description("Adopt already-deployed AWS resources into cdkd state. Reads the CDK app to find logical IDs, resource types, and dependencies. With no flags, imports every resource, resolving each physical id from the template's physical-name property and then from a same-named CloudFormation stack's DescribeStackResources. With --resource / --resource-mapping, only the listed resources are imported (CDK CLI parity); pass --auto to auto-resolve the rest.").argument("[stack]", "Stack to import. Optional when the synthesized app contains exactly one stack.").option("--resource <id=physical>", "Explicit physical-id override for one logical ID. Repeatable. When at least one --resource is given, only listed resources are imported (CDK CLI parity). Pass --auto to also auto-resolve everything else.", collectMultiple, []).option("--resource-mapping <file>", "Path to a JSON file of {logicalId: physicalId} overrides (CDK CLI `cdk import --resource-mapping` compatible). Implies selective mode unless --auto is set. Mutually exclusive with --resource-mapping-inline.").option("--resource-mapping-inline <json>", "Inline JSON object of {logicalId: physicalId} overrides (CDK CLI `cdk import --resource-mapping-inline` compatible). Same shape as --resource-mapping but supplied as a string — useful for non-TTY CI scripts that do not want a separate file. Implies selective mode unless --auto is set. Mutually exclusive with --resource-mapping.").option("--record-resource-mapping <file>", "After cdkd resolves every logical ID (via --resource / --resource-mapping / auto-resolution), write the resulting {logicalId: physicalId} map to <file> as JSON. Useful in auto / hybrid mode for capturing the auto-resolved mapping and feeding it back as --resource-mapping in non-interactive CI re-runs. Written before the confirmation prompt (so the user can review the file before saying \"yes\") and even when the user says \"no\". Mirrors `cdk import --record-resource-mapping`.").option("--auto", "Hybrid mode: when explicit overrides are supplied, ALSO auto-resolve every other resource in the template. Without this flag, --resource / --resource-mapping behave as a whitelist (CDK CLI parity).", false).option("--dry-run", "Show planned imports without writing state", false).option("--force", "Confirm a destructive write to existing state. Required for auto / whole-stack import when state already exists (rebuilds the entire resource map). Also required in selective mode if a listed override would overwrite a resource already in state. Not needed for a pure selective merge (adding new resources without touching unlisted entries).", false).option("--migrate-from-cloudformation [cfn-stack-name]", "After cdkd state is written, retire the named CloudFormation stack (deletes the CFn stack record; AWS resources are NOT deleted): inject DeletionPolicy=Retain and UpdateReplacePolicy=Retain on every resource via UpdateStack, then DeleteStack. cdkd takes over management. Pass without a value to use the cdkd stack name as the CFn stack name (the typical case for a CDK app that was previously deployed via `cdk deploy`); pass an explicit value when the CFn stack name differs.").addOption(useCdkBootstrapAssetsOption).action(withErrorHandling(importCommand));
|
|
62171
62395
|
[
|
|
62172
62396
|
...commonOptions,
|
|
62173
62397
|
...appOptions,
|
|
@@ -71931,7 +72155,7 @@ function createMigrateCommand() {
|
|
|
71931
72155
|
*/
|
|
71932
72156
|
function buildProgram() {
|
|
71933
72157
|
const program = new Command();
|
|
71934
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.
|
|
72158
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.44");
|
|
71935
72159
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
71936
72160
|
const { profile } = actionCommand.optsWithGlobals();
|
|
71937
72161
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|