@go-to-k/cdkd 0.280.43 → 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-DhDS2HGs.js → asg-provider-BBKdxCVP.js} +2 -2
- package/dist/{asg-provider-DhDS2HGs.js.map → asg-provider-BBKdxCVP.js.map} +1 -1
- package/dist/cli.js +258 -39
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-92FflEWX.js → deploy-engine-nEjymkij.js} +3 -3
- package/dist/{deploy-engine-92FflEWX.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 }));
|
|
@@ -71936,7 +72155,7 @@ function createMigrateCommand() {
|
|
|
71936
72155
|
*/
|
|
71937
72156
|
function buildProgram() {
|
|
71938
72157
|
const program = new Command();
|
|
71939
|
-
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");
|
|
71940
72159
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
71941
72160
|
const { profile } = actionCommand.optsWithGlobals();
|
|
71942
72161
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|