@go-to-k/cdkd 0.290.28 → 0.290.30
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-DiZsq-4k.js → asg-provider-De5KONKr.js} +3 -3
- package/dist/{asg-provider-DiZsq-4k.js.map → asg-provider-De5KONKr.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-CcFbq2q8.js → deploy-engine-mI1Xv1oi.js} +237 -80
- package/dist/{deploy-engine-CcFbq2q8.js.map → deploy-engine-mI1Xv1oi.js.map} +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{logger-6LP1UiFA.js → logger-BjWM-UVj.js} +42 -2
- package/dist/logger-BjWM-UVj.js.map +1 -0
- package/dist/{program-C1iVGcxJ.js → program-qSSjqstP.js} +219 -40
- package/dist/{program-C1iVGcxJ.js.map → program-qSSjqstP.js.map} +1 -1
- package/dist/{version-B2-UuEte.js → version-C15BftsD.js} +2 -2
- package/dist/{version-B2-UuEte.js.map → version-C15BftsD.js.map} +1 -1
- package/package.json +1 -1
- package/dist/logger-6LP1UiFA.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as displayAwsMessage, E as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-BjWM-UVj.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-C15BftsD.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -20580,6 +20580,7 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
20580
20580
|
"FifoThroughputScope",
|
|
20581
20581
|
"FifoTopic",
|
|
20582
20582
|
"KmsMasterKeyId",
|
|
20583
|
+
"MaximumMessageSize",
|
|
20583
20584
|
"SignatureVersion",
|
|
20584
20585
|
"Subscription",
|
|
20585
20586
|
"Tags",
|
|
@@ -23136,7 +23137,8 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
|
|
|
23136
23137
|
}
|
|
23137
23138
|
const promise = (async () => {
|
|
23138
23139
|
const logger = getLogger().child("role-arn");
|
|
23139
|
-
|
|
23140
|
+
const displayRoleArn = displayIdent(roleArn, { maxCodePoints: 613 });
|
|
23141
|
+
logger.debug(`Assuming role for cross-account state read: ${displayRoleArn}`);
|
|
23140
23142
|
const sts = new STSClient({ ...awsClientDefaults() });
|
|
23141
23143
|
try {
|
|
23142
23144
|
let response;
|
|
@@ -23147,13 +23149,13 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
|
|
|
23147
23149
|
DurationSeconds: 3600
|
|
23148
23150
|
}));
|
|
23149
23151
|
} catch (err) {
|
|
23150
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
23151
|
-
throw new Error(`AssumeRole into ${
|
|
23152
|
+
const message = displayAwsMessage(err instanceof Error ? err.message : String(err));
|
|
23153
|
+
throw new Error(`AssumeRole into ${displayRoleArn} failed: ${message}. If this is a trust-policy issue, the producer's role must allow sts:AssumeRole from the consumer's principal. See https://github.com/go-to-k/cdkd/blob/main/docs/cross-stack-references.md for the trust-policy template.`, { cause: err instanceof Error ? err : void 0 });
|
|
23152
23154
|
}
|
|
23153
|
-
if (!response.Credentials) throw new Error(`AssumeRole for cross-account Fn::GetStackOutput returned no credentials (RoleArn=${
|
|
23155
|
+
if (!response.Credentials) throw new Error(`AssumeRole for cross-account Fn::GetStackOutput returned no credentials (RoleArn=${displayRoleArn})`);
|
|
23154
23156
|
const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;
|
|
23155
|
-
if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing required credentials fields for cross-account state read (RoleArn=${
|
|
23156
|
-
logger.info(`Assumed role for cross-account state read: ${
|
|
23157
|
+
if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing required credentials fields for cross-account state read (RoleArn=${displayRoleArn})`);
|
|
23158
|
+
logger.info(`Assumed role for cross-account state read: ${displayRoleArn} (session expires ${Expiration?.toISOString() ?? "unknown"})`);
|
|
23157
23159
|
return {
|
|
23158
23160
|
accessKeyId: AccessKeyId,
|
|
23159
23161
|
secretAccessKey: SecretAccessKey,
|
|
@@ -23261,18 +23263,23 @@ async function applyRoleArnIfSet(opts) {
|
|
|
23261
23263
|
if (!roleArn) return;
|
|
23262
23264
|
if (getAssumedRoleCredentials() !== void 0) throw new Error(SECOND_ROLE_PUBLISH_REFUSAL);
|
|
23263
23265
|
const logger = getLogger().child("role-arn");
|
|
23264
|
-
const displayRoleArn =
|
|
23266
|
+
const displayRoleArn = displayIdent(roleArn, { maxCodePoints: 613 });
|
|
23265
23267
|
logger.debug(`Assuming role ${displayRoleArn}...`);
|
|
23266
23268
|
const sts = new STSClient({
|
|
23267
23269
|
...awsClientDefaults({ ignoreAssumedRole: true }),
|
|
23268
23270
|
...opts.region && { region: opts.region }
|
|
23269
23271
|
});
|
|
23270
23272
|
try {
|
|
23271
|
-
|
|
23272
|
-
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23273
|
+
let response;
|
|
23274
|
+
try {
|
|
23275
|
+
response = await sts.send(new AssumeRoleCommand({
|
|
23276
|
+
RoleArn: roleArn,
|
|
23277
|
+
RoleSessionName: `cdkd-${Date.now()}`,
|
|
23278
|
+
DurationSeconds: 3600
|
|
23279
|
+
}));
|
|
23280
|
+
} catch (err) {
|
|
23281
|
+
throw new Error(`AssumeRole for ${displayRoleArn} failed: ${displayAwsMessage(err instanceof Error ? err.message : String(err))}`, { cause: err instanceof Error ? err : void 0 });
|
|
23282
|
+
}
|
|
23276
23283
|
if (!response.Credentials) throw new Error(`AssumeRole returned no credentials for role ${displayRoleArn}`);
|
|
23277
23284
|
const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;
|
|
23278
23285
|
if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing credentials fields for role ${displayRoleArn}`);
|
|
@@ -27647,7 +27654,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27647
27654
|
const value = context.parameters[logicalId];
|
|
27648
27655
|
const declared = context.template.Parameters;
|
|
27649
27656
|
const paramDef = declared != null && Object.hasOwn(declared, logicalId) ? declared[logicalId] : void 0;
|
|
27650
|
-
this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.
|
|
27657
|
+
this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
|
|
27651
27658
|
this.recordInheritedParameterSecrets(logicalId, value, context);
|
|
27652
27659
|
return value;
|
|
27653
27660
|
}
|
|
@@ -27820,11 +27827,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27820
27827
|
this.rejectPlaceholderArnAttribute(resource, attributeName, flatValue, logicalId, context);
|
|
27821
27828
|
if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
|
|
27822
27829
|
const nameServers = flatValue === "" ? [] : flatValue.split(",");
|
|
27823
|
-
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.
|
|
27830
|
+
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
|
|
27824
27831
|
return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
|
|
27825
27832
|
}
|
|
27826
|
-
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.
|
|
27827
|
-
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.
|
|
27833
|
+
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
|
|
27834
|
+
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.displayLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
|
|
27828
27835
|
return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
|
|
27829
27836
|
}
|
|
27830
27837
|
if (attributeName.includes(".")) {
|
|
@@ -27836,7 +27843,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27836
27843
|
break;
|
|
27837
27844
|
}
|
|
27838
27845
|
if (cursor !== void 0) {
|
|
27839
|
-
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.
|
|
27846
|
+
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
|
|
27840
27847
|
return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
|
|
27841
27848
|
}
|
|
27842
27849
|
}
|
|
@@ -27844,10 +27851,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27844
27851
|
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)) {
|
|
27845
27852
|
const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.maskSecretsForLog(this.outputNameLogText(k, context), context));
|
|
27846
27853
|
const declaredText = declared.length > 0 ? declared.join(", ") : "(none)";
|
|
27847
|
-
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
27854
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.displayMasked(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.displayMasked(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
|
|
27848
27855
|
}
|
|
27849
27856
|
const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
|
|
27850
|
-
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.
|
|
27857
|
+
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
|
|
27851
27858
|
return value;
|
|
27852
27859
|
}
|
|
27853
27860
|
/**
|
|
@@ -27940,7 +27947,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27940
27947
|
rejectPlaceholderArnAttribute(resource, attributeName, value, logicalId, context) {
|
|
27941
27948
|
if (!REF_RETURNS_ARN_FROM_STATE.get(resource.resourceType)?.includes(attributeName)) return;
|
|
27942
27949
|
if (typeof value !== "string" || !isPlaceholderArn(value)) return;
|
|
27943
|
-
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
27950
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
|
|
27944
27951
|
}
|
|
27945
27952
|
/**
|
|
27946
27953
|
* Construct resource attribute value based on resource type, refusing to
|
|
@@ -27985,7 +27992,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27985
27992
|
async constructGuardedAttribute(resource, attributeName, context, logicalId) {
|
|
27986
27993
|
const accountInfo = await getAccountInfo(this.resolverRegion);
|
|
27987
27994
|
const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
|
|
27988
|
-
if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
27995
|
+
if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
|
|
27989
27996
|
return value;
|
|
27990
27997
|
}
|
|
27991
27998
|
/**
|
|
@@ -28030,7 +28037,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28030
28037
|
const associations = (await ec2.send(new DescribeVpcsCommand({ VpcIds: [physicalId] }))).Vpcs?.[0]?.Ipv6CidrBlockAssociationSet || [];
|
|
28031
28038
|
const blocks = associations.filter((a) => a.Ipv6CidrBlockState?.State === "associated").map((a) => a.Ipv6CidrBlock);
|
|
28032
28039
|
if (blocks.length > 0) {
|
|
28033
|
-
this.logger.debug(`Resolved VPC Ipv6CidrBlocks for ${physicalId}: ${this.
|
|
28040
|
+
this.logger.debug(`Resolved VPC Ipv6CidrBlocks for ${physicalId}: ${this.displayMasked(JSON.stringify(this.maskValueLeaves(blocks, context)), context)}`);
|
|
28034
28041
|
return blocks;
|
|
28035
28042
|
}
|
|
28036
28043
|
if (associations.filter((a) => a.Ipv6CidrBlockState?.State === "associating").length === 0) {
|
|
@@ -28047,7 +28054,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28047
28054
|
return [];
|
|
28048
28055
|
}
|
|
28049
28056
|
case "DefaultSecurityGroup": {
|
|
28050
|
-
if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${
|
|
28057
|
+
if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
|
|
28051
28058
|
const cachedGroupId = cachedVpcDefaultSecurityGroups[physicalId];
|
|
28052
28059
|
if (cachedGroupId !== void 0) return cachedGroupId;
|
|
28053
28060
|
let groupId;
|
|
@@ -28262,7 +28269,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28262
28269
|
if (resourceType === "AWS::EC2::SecurityGroup") switch (attributeName) {
|
|
28263
28270
|
case "GroupId": return physicalId;
|
|
28264
28271
|
case "VpcId": {
|
|
28265
|
-
if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${
|
|
28272
|
+
if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
|
|
28266
28273
|
const cachedVpcId = cachedSecurityGroupVpcIds[physicalId];
|
|
28267
28274
|
if (cachedVpcId !== void 0) return cachedVpcId;
|
|
28268
28275
|
let vpcId;
|
|
@@ -28356,7 +28363,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28356
28363
|
resourceType,
|
|
28357
28364
|
physicalId,
|
|
28358
28365
|
context,
|
|
28359
|
-
observed: `DescribeInstances reports no ${this.
|
|
28366
|
+
observed: `DescribeInstances reports no ${this.displayMasked(attributeName, context)} yet (${observedState})`,
|
|
28360
28367
|
remedy: settled ? "Check the instance in the console; a terminated or stopped instance has no such attribute to serve." : "Deploy without --no-wait so the instance is running before its attributes are read, or reference the attribute from a later deploy once it is."
|
|
28361
28368
|
});
|
|
28362
28369
|
}
|
|
@@ -28369,7 +28376,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28369
28376
|
const value = attributeName === "LatestVersionNumber" ? lt?.LatestVersionNumber : lt?.DefaultVersionNumber;
|
|
28370
28377
|
if (value !== void 0 && value !== null) return String(value);
|
|
28371
28378
|
} catch (err) {
|
|
28372
|
-
this.logger.warn(`DescribeLaunchTemplates(${physicalId}) failed for ${this.
|
|
28379
|
+
this.logger.warn(`DescribeLaunchTemplates(${physicalId}) failed for ${this.displayMasked(attributeName, context)}: ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
|
|
28373
28380
|
}
|
|
28374
28381
|
return attributeName === "LatestVersionNumber" ? "$Latest" : "$Default";
|
|
28375
28382
|
}
|
|
@@ -28379,7 +28386,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28379
28386
|
if (resourceType === "AWS::CloudFront::Distribution") switch (attributeName) {
|
|
28380
28387
|
case "Id": return physicalId;
|
|
28381
28388
|
case "DomainName": {
|
|
28382
|
-
if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${
|
|
28389
|
+
if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
|
|
28383
28390
|
const cachedDomainName = cachedCloudFrontDomainNames[physicalId];
|
|
28384
28391
|
if (cachedDomainName !== void 0) return cachedDomainName;
|
|
28385
28392
|
let domainName;
|
|
@@ -28413,7 +28420,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28413
28420
|
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
28414
28421
|
}
|
|
28415
28422
|
if (resourceType === "AWS::RDS::DBProxy" || resourceType === "AWS::RDS::DBProxyEndpoint") {
|
|
28416
|
-
if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
28423
|
+
if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
|
|
28417
28424
|
return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
28418
28425
|
}
|
|
28419
28426
|
return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
@@ -28472,7 +28479,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28472
28479
|
*/
|
|
28473
28480
|
refuseUnservedAttribute(site) {
|
|
28474
28481
|
const { logicalId, attributeName, resourceType, physicalId, context, observed, remedy } = site;
|
|
28475
|
-
throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
28482
|
+
throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
|
|
28476
28483
|
}
|
|
28477
28484
|
/**
|
|
28478
28485
|
* The `observed` clause of {@link refuseUnservedAttribute} for a live read
|
|
@@ -28484,7 +28491,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28484
28491
|
const name = displaySafe(err instanceof Error && err.name ? err.name : "Error", { asciiOnly: true }).slice(0, 64).trim() || "Error";
|
|
28485
28492
|
const status = err?.$metadata?.httpStatusCode;
|
|
28486
28493
|
const errorClass = typeof status === "number" && Number.isInteger(status) ? `${name}, HTTP ${status}` : name;
|
|
28487
|
-
this.logger.debug(`${read} failed (${errorClass}): ${this.
|
|
28494
|
+
this.logger.debug(`${read} failed (${errorClass}): ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
|
|
28488
28495
|
return `${read} failed (${errorClass}); re-run with --verbose for the AWS error text`;
|
|
28489
28496
|
}
|
|
28490
28497
|
/**
|
|
@@ -28519,11 +28526,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28519
28526
|
const expectsUrlShape = attributeName.endsWith("Url") && !/^https?:\/\//.test(physicalId);
|
|
28520
28527
|
if (expectsArnShape || expectsUrlShape) {
|
|
28521
28528
|
const expectedShape = expectsArnShape ? "an ARN (arn:...)" : "a URL (http(s)://...)";
|
|
28522
|
-
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
28529
|
+
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28523
28530
|
}
|
|
28524
|
-
if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.
|
|
28531
|
+
if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
|
|
28525
28532
|
this.physicalIdFallbackCount++;
|
|
28526
|
-
this.logger.warn(`Unknown attribute ${this.
|
|
28533
|
+
this.logger.warn(`Unknown attribute ${this.displayMasked(attributeName, context)} for resource type ${resourceType}, returning physical ID`);
|
|
28527
28534
|
return physicalId;
|
|
28528
28535
|
}
|
|
28529
28536
|
/**
|
|
@@ -28594,7 +28601,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28594
28601
|
complete
|
|
28595
28602
|
});
|
|
28596
28603
|
this.rememberLogTwin(context, result, twin);
|
|
28597
|
-
this.logger.debug(`Resolved Fn::Join: ${this.
|
|
28604
|
+
this.logger.debug(`Resolved Fn::Join: ${this.displayMasked(this.logTwinText(result, twin, context), context)}`);
|
|
28598
28605
|
return result;
|
|
28599
28606
|
}
|
|
28600
28607
|
/**
|
|
@@ -28843,7 +28850,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28843
28850
|
complete
|
|
28844
28851
|
});
|
|
28845
28852
|
this.rememberLogTwin(context, result, twin);
|
|
28846
|
-
this.logger.debug(`Resolved Fn::Sub: ${this.
|
|
28853
|
+
this.logger.debug(`Resolved Fn::Sub: ${this.displayMasked(this.logTwinText(result, twin, context), context)}`);
|
|
28847
28854
|
return result;
|
|
28848
28855
|
}
|
|
28849
28856
|
/**
|
|
@@ -29210,7 +29217,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29210
29217
|
}
|
|
29211
29218
|
return v;
|
|
29212
29219
|
};
|
|
29213
|
-
this.logger.debug(`Re-resolving dynamic reference(s) in ${this.
|
|
29220
|
+
this.logger.debug(`Re-resolving dynamic reference(s) in ${this.displayMasked(origin, context)}`);
|
|
29214
29221
|
const reresolved = await walk(value);
|
|
29215
29222
|
if (sourceKey !== void 0 && typeof value === "string" && isSingleDynamicReferenceToken(value) && typeof reresolved === "string" && context.recordedSecretValues?.has(reresolved) === true && isSecretExpressionByVerdictOrSpelling(value)) recordCrossStackExpression(context.recordedSecretValues, sourceKey, value, reresolved);
|
|
29216
29223
|
return reresolved;
|
|
@@ -29313,13 +29320,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29313
29320
|
try {
|
|
29314
29321
|
entry = await context.exportIndex.lookup(exportName);
|
|
29315
29322
|
} catch (err) {
|
|
29316
|
-
this.logger.warn(`Exports index lookup failed for '${loggedExportName}': ${this.
|
|
29323
|
+
this.logger.warn(`Exports index lookup failed for '${loggedExportName}': ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}; falling back to state.json scan`);
|
|
29317
29324
|
entry = void 0;
|
|
29318
29325
|
}
|
|
29319
29326
|
if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {
|
|
29320
29327
|
this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);
|
|
29321
|
-
this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.
|
|
29322
|
-
return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${this.
|
|
29328
|
+
this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.displayMasked(entry.producerStack, context)} / ${this.displayMasked(entry.producerRegion, context)}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
|
|
29329
|
+
return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${this.displayLeaf(exportName, context)}' (producer ${this.displayLeaf(entry.producerStack, context)} / ${this.displayLeaf(entry.producerRegion, context)})`, sourceKey, {
|
|
29323
29330
|
stackName: entry.producerStack,
|
|
29324
29331
|
region: entry.producerRegion,
|
|
29325
29332
|
outputKey: exportName
|
|
@@ -29332,30 +29339,30 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29332
29339
|
for (const ref of allStacks) {
|
|
29333
29340
|
const { stackName: refStack, region: refRegion } = ref;
|
|
29334
29341
|
if (context.stackName && refStack === context.stackName) {
|
|
29335
|
-
this.logger.debug(`Skipping current stack: ${this.
|
|
29342
|
+
this.logger.debug(`Skipping current stack: ${this.displayMasked(refStack, context)}`);
|
|
29336
29343
|
continue;
|
|
29337
29344
|
}
|
|
29338
29345
|
try {
|
|
29339
29346
|
const lookupRegion = refRegion ?? this.resolverRegion ?? "";
|
|
29340
29347
|
if (!lookupRegion) {
|
|
29341
|
-
this.logger.debug(`No region available for stack '${this.
|
|
29348
|
+
this.logger.debug(`No region available for stack '${this.displayMasked(refStack, context)}' — skipping (cdkd cannot read state without a region)`);
|
|
29342
29349
|
continue;
|
|
29343
29350
|
}
|
|
29344
29351
|
const stateData = await context.stateBackend.getState(refStack, lookupRegion);
|
|
29345
29352
|
if (!stateData) {
|
|
29346
|
-
this.logger.debug(`No state found for stack: ${this.
|
|
29353
|
+
this.logger.debug(`No state found for stack: ${this.displayMasked(refStack, context)} (${this.displayMasked(lookupRegion, context)})`);
|
|
29347
29354
|
continue;
|
|
29348
29355
|
}
|
|
29349
29356
|
const { state } = stateData;
|
|
29350
29357
|
if (importableOutputKeys(state).includes(exportName)) {
|
|
29351
29358
|
const value = state.outputs[exportName];
|
|
29352
|
-
this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.
|
|
29359
|
+
this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.displayMasked(refStack, context)} / ${this.displayMasked(lookupRegion, context)}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
|
|
29353
29360
|
if (context.exportIndex) context.exportIndex.patchEntry(exportName, {
|
|
29354
29361
|
value,
|
|
29355
29362
|
producerStack: refStack,
|
|
29356
29363
|
producerRegion: lookupRegion
|
|
29357
29364
|
}).catch((err) => {
|
|
29358
|
-
this.logger.debug(`Failed to patch exports index for '${this.
|
|
29365
|
+
this.logger.debug(`Failed to patch exports index for '${this.displayMasked(exportName, context)}': ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
|
|
29359
29366
|
});
|
|
29360
29367
|
this.recordImport(context, exportName, refStack, lookupRegion);
|
|
29361
29368
|
found = {
|
|
@@ -29366,11 +29373,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29366
29373
|
break;
|
|
29367
29374
|
}
|
|
29368
29375
|
} catch (error) {
|
|
29369
|
-
this.logger.warn(`Failed to read state for stack ${this.
|
|
29376
|
+
this.logger.warn(`Failed to read state for stack ${this.displayMasked(refStack, context)}: ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}`);
|
|
29370
29377
|
continue;
|
|
29371
29378
|
}
|
|
29372
29379
|
}
|
|
29373
|
-
if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${this.
|
|
29380
|
+
if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${this.displayLeaf(exportName, context)}' (producer ${this.displayLeaf(found.refStack, context)} / ${this.displayLeaf(found.lookupRegion, context)})`, sourceKey, {
|
|
29374
29381
|
stackName: found.refStack,
|
|
29375
29382
|
region: found.lookupRegion,
|
|
29376
29383
|
outputKey: exportName
|
|
@@ -29413,7 +29420,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29413
29420
|
};
|
|
29414
29421
|
return;
|
|
29415
29422
|
} catch (error) {
|
|
29416
|
-
this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${this.
|
|
29423
|
+
this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${this.displayMasked(exportName, context)}' (region ${this.resolverRegion}): ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}. Grant cloudformation:ListExports to resolve exports from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
|
|
29417
29424
|
return;
|
|
29418
29425
|
}
|
|
29419
29426
|
}
|
|
@@ -29445,7 +29452,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29445
29452
|
describeAvailableOutputs(keys, context) {
|
|
29446
29453
|
if (keys.length === 0) return "(none)";
|
|
29447
29454
|
const shown = keys.slice(0, MAX_LISTED_AVAILABLE_OUTPUTS);
|
|
29448
|
-
const rendered = shown.map((k) => this.
|
|
29455
|
+
const rendered = shown.map((k) => this.displayMasked(k, context)).join(", ");
|
|
29449
29456
|
const hidden = keys.length - shown.length;
|
|
29450
29457
|
return hidden > 0 ? `${rendered} (+${hidden} more)` : rendered;
|
|
29451
29458
|
}
|
|
@@ -29485,8 +29492,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29485
29492
|
return await fetch;
|
|
29486
29493
|
} catch (error) {
|
|
29487
29494
|
const message = error instanceof Error ? error.message : String(error);
|
|
29488
|
-
const cfnNameMask = this.positionalNameMask([[stackName, this.
|
|
29489
|
-
this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.
|
|
29495
|
+
const cfnNameMask = this.positionalNameMask([[stackName, this.displayMasked(stackName, context)], [region, this.displayMasked(loggedRegionText, context)]]);
|
|
29496
|
+
this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.displayMasked(stackName, context)}' (${this.displayMasked(loggedRegionText, context)}): ${displayAwsMessage(this.maskSecretsForLog(cfnNameMask ? cfnNameMask(message) : message, context))}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
|
|
29490
29497
|
return;
|
|
29491
29498
|
}
|
|
29492
29499
|
}
|
|
@@ -29596,7 +29603,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29596
29603
|
const resolvedRegion = await this.resolveValue(args["Region"], context);
|
|
29597
29604
|
if (typeof resolvedRegion !== "string" || resolvedRegion === "") throw new Error(`Fn::GetStackOutput: Region must resolve to a non-empty string, got ${typeof resolvedRegion}`);
|
|
29598
29605
|
const requestedRegion = canonicalizeRegion(resolvedRegion);
|
|
29599
|
-
if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.
|
|
29606
|
+
if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.displayMasked(this.logTextOfLeaf(resolvedRegion, context) !== resolvedRegion ? "***" : resolvedRegion, context).slice(0, 64)}' is not a valid AWS region name. The region selects both the AWS endpoint and the state-file key, so cdkd will not use it.`);
|
|
29600
29607
|
region = requestedRegion;
|
|
29601
29608
|
loggedRegionText = this.regionLogText(resolvedRegion, context);
|
|
29602
29609
|
}
|
|
@@ -29606,11 +29613,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29606
29613
|
if (typeof raw !== "string" || raw === "") throw new Error(`Fn::GetStackOutput: RoleArn must be a literal string in the template (no Ref / Fn::GetAtt / Fn::Sub allowed for cross-account references). Got ${raw === null ? "null" : Array.isArray(raw) ? "array" : typeof raw}${typeof raw === "object" ? ` (intrinsic shape: ${JSON.stringify(raw).slice(0, 80)})` : ""}.`);
|
|
29607
29614
|
roleArn = raw;
|
|
29608
29615
|
}
|
|
29609
|
-
if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.
|
|
29610
|
-
const loggedStackName = this.
|
|
29611
|
-
const loggedOutputName = this.
|
|
29616
|
+
if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.displayMasked(stackName, context)}' in the same region '${this.displayMasked(loggedRegionText, context)}'`);
|
|
29617
|
+
const loggedStackName = this.displayMasked(stackName, context);
|
|
29618
|
+
const loggedOutputName = this.displayMasked(outputName, context);
|
|
29612
29619
|
const loggedRegion = this.maskSecretsForLog(loggedRegionText, context);
|
|
29613
|
-
|
|
29620
|
+
const shownRoleArn = roleArn ? displayIdent(roleArn, { maxCodePoints: 613 }) : "";
|
|
29621
|
+
this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${shownRoleArn}` : ""}`);
|
|
29614
29622
|
let stateData;
|
|
29615
29623
|
try {
|
|
29616
29624
|
stateData = roleArn ? await this.getCrossAccountStackState(roleArn, stackName, region, context) : await this.getSameAccountStackState(stackName, region, context);
|
|
@@ -29630,7 +29638,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29630
29638
|
return value;
|
|
29631
29639
|
}
|
|
29632
29640
|
}
|
|
29633
|
-
throw new Error(`Fn::GetStackOutput: stack '${loggedStackName}' not found in region '${loggedRegion}'${roleArn ? ` (cross-account via ${
|
|
29641
|
+
throw new Error(`Fn::GetStackOutput: stack '${loggedStackName}' not found in region '${loggedRegion}'${roleArn ? ` (cross-account via ${shownRoleArn})` : ""}. ${!roleArn && this.cfnFallback ? "Searched cdkd state and CloudFormation stacks. Make sure the producer stack has been deployed (via cdkd or CloudFormation)." : `Make sure the producer stack has been deployed via cdkd.`}`);
|
|
29634
29642
|
}
|
|
29635
29643
|
if (!hasReadableOutputs(stateData.state)) throw markNonRetryable(new MalformedProducerRecordRefusalError(`Fn::GetStackOutput: the state record of producer stack '${loggedStackName}' (${loggedRegion}) has no readable 'outputs' map — the record is malformed or truncated. cdkd refuses to resolve from it rather than reading it as a map: 'Object.hasOwn' answers TRUE for '0' on a string and for an index on a list, so continuing would resolve ONE CHARACTER or element of the record as this reference's value and this deploy would send it to AWS. Inspect the producer's record with 'cdkd state show' --json, repair or remove it, then re-run.`));
|
|
29636
29644
|
const outputs = stateData.state.outputs ?? {};
|
|
@@ -29639,10 +29647,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29639
29647
|
throw new Error(`Fn::GetStackOutput: output '${loggedOutputName}' not found in stack '${loggedStackName}' (${loggedRegion}). Available outputs: ${available}`);
|
|
29640
29648
|
}
|
|
29641
29649
|
const value = outputs[outputName];
|
|
29642
|
-
this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${
|
|
29650
|
+
this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${shownRoleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
|
|
29643
29651
|
if (!roleArn) this.recordOutputRead(context, stackName, region, outputName);
|
|
29644
|
-
if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${loggedOutputName}' of stack '${loggedStackName}' (${loggedRegion}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${
|
|
29645
|
-
return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${this.
|
|
29652
|
+
if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${loggedOutputName}' of stack '${loggedStackName}' (${loggedRegion}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${shownRoleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
|
|
29653
|
+
return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${this.displayLeaf(outputName, context)}' (producer ${this.displayLeaf(stackName, context)} / ${this.displayLeaf(loggedRegionText, context)})`, sourceKey, {
|
|
29646
29654
|
stackName,
|
|
29647
29655
|
region,
|
|
29648
29656
|
outputKey: outputName,
|
|
@@ -29794,7 +29802,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29794
29802
|
*/
|
|
29795
29803
|
async getCrossAccountStackState(roleArn, stackName, region, context) {
|
|
29796
29804
|
const parsed = parseIamRoleArn(roleArn);
|
|
29797
|
-
if (!parsed) throw new Error(`Fn::GetStackOutput: RoleArn '${roleArn}' is not a valid IAM role ARN. Expected shape: arn:<partition>:iam::<12-digit-account-id>:role/<role-name> (e.g. arn:aws:iam::123456789012:role/MyRole, arn:aws-us-gov:iam::...).`);
|
|
29805
|
+
if (!parsed) throw new Error(`Fn::GetStackOutput: RoleArn '${displayIdent(roleArn, { maxCodePoints: 613 })}' is not a valid IAM role ARN. Expected shape: arn:<partition>:iam::<12-digit-account-id>:role/<role-name> (e.g. arn:aws:iam::123456789012:role/MyRole, arn:aws-us-gov:iam::...).`);
|
|
29798
29806
|
const credentials = await assumeRoleForCrossAccountStateRead(roleArn);
|
|
29799
29807
|
const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(parsed.accountId, credentials);
|
|
29800
29808
|
const prefix = context.stateBackend?.prefix ?? "cdkd";
|
|
@@ -29851,19 +29859,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29851
29859
|
}
|
|
29852
29860
|
if (!map) {
|
|
29853
29861
|
if (hasDefaultValue) return await resolveDefault();
|
|
29854
|
-
throw new Error(`Fn::FindInMap: mapping '${this.
|
|
29862
|
+
throw new Error(`Fn::FindInMap: mapping '${this.displayMasked(mapName, context)}' not found in Mappings section`);
|
|
29855
29863
|
}
|
|
29856
29864
|
const topLevel = Object.hasOwn(map, topLevelKey) ? map[topLevelKey] : void 0;
|
|
29857
29865
|
if (!topLevel || typeof topLevel !== "object") {
|
|
29858
29866
|
if (hasDefaultValue) return await resolveDefault();
|
|
29859
|
-
throw new Error(`Fn::FindInMap: top-level key '${this.
|
|
29867
|
+
throw new Error(`Fn::FindInMap: top-level key '${this.displayMasked(topLevelKey, context)}' not found in mapping '${this.displayMasked(mapName, context)}'`);
|
|
29860
29868
|
}
|
|
29861
29869
|
if (!Object.hasOwn(topLevel, secondLevelKey)) {
|
|
29862
29870
|
if (hasDefaultValue) return await resolveDefault();
|
|
29863
|
-
throw new Error(`Fn::FindInMap: second-level key '${this.
|
|
29871
|
+
throw new Error(`Fn::FindInMap: second-level key '${this.displayMasked(secondLevelKey, context)}' not found in mapping '${this.displayMasked(mapName, context)}' -> '${this.displayMasked(topLevelKey, context)}'`);
|
|
29864
29872
|
}
|
|
29865
29873
|
const result = topLevel[secondLevelKey];
|
|
29866
|
-
this.logger.debug(`Resolved Fn::FindInMap: ${this.
|
|
29874
|
+
this.logger.debug(`Resolved Fn::FindInMap: ${this.displayMasked(mapName, context)}.${this.displayMasked(topLevelKey, context)}.${this.displayMasked(secondLevelKey, context)} -> ${this.displayMasked(JSON.stringify(this.maskValueLeaves(result, context)), context)}`);
|
|
29867
29875
|
return result;
|
|
29868
29876
|
}
|
|
29869
29877
|
/**
|
|
@@ -29878,7 +29886,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29878
29886
|
const result = Buffer.from(resolvedValue).toString("base64");
|
|
29879
29887
|
const inputLogText = this.logTextOfLeaf(resolvedValue, context);
|
|
29880
29888
|
if (context.recordedSecretValues && (inputLogText !== resolvedValue || this.maskNeedlesForLog(resolvedValue, context) !== resolvedValue)) recordMaskOnlyValue(context.recordedSecretValues, result);
|
|
29881
|
-
this.logger.debug(`Resolved Fn::Base64: ${this.
|
|
29889
|
+
this.logger.debug(`Resolved Fn::Base64: ${this.displayMasked(inputLogText, context)} -> ${this.displayMasked(inputLogText !== resolvedValue ? "***" : result, context)}`);
|
|
29882
29890
|
return result;
|
|
29883
29891
|
}
|
|
29884
29892
|
/**
|
|
@@ -29910,7 +29918,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29910
29918
|
let loggedRegionText;
|
|
29911
29919
|
if (typeof resolvedValue === "string" && resolvedValue !== "") {
|
|
29912
29920
|
const requested = canonicalizeRegion(resolvedValue);
|
|
29913
|
-
if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.
|
|
29921
|
+
if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.displayMasked(this.logTextOfLeaf(resolvedValue, context) !== resolvedValue ? "***" : resolvedValue, context).slice(0, 64)}' is not a valid AWS region name. A region is substituted into the AWS service hostname, so cdkd will not build a client from it.`);
|
|
29914
29922
|
region = requested;
|
|
29915
29923
|
clientRegion = requested;
|
|
29916
29924
|
loggedRegionText = this.regionLogText(resolvedValue, context);
|
|
@@ -29920,7 +29928,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29920
29928
|
}
|
|
29921
29929
|
const cached = cachedAvailabilityZones[region];
|
|
29922
29930
|
if (cached) {
|
|
29923
|
-
this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.
|
|
29931
|
+
this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
|
|
29924
29932
|
return cached;
|
|
29925
29933
|
}
|
|
29926
29934
|
const ec2Client = this.clientsForRegion(clientRegion, loggedRegionText === void 0 ? void 0 : this.maskSecretsForLog(loggedRegionText, context)).ec2;
|
|
@@ -29934,11 +29942,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29934
29942
|
Values: ["available"]
|
|
29935
29943
|
}] }))).AvailabilityZones || []).map((az) => az.ZoneName).filter((name) => name !== void 0).sort();
|
|
29936
29944
|
} catch (error) {
|
|
29937
|
-
throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.
|
|
29945
|
+
throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.displayMasked(loggedRegionText ?? region, context)}': ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}`);
|
|
29938
29946
|
}
|
|
29939
|
-
if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.
|
|
29947
|
+
if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.displayMasked(loggedRegionText ?? region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
|
|
29940
29948
|
cachedAvailabilityZones[region] = azNames;
|
|
29941
|
-
this.logger.debug(`Resolved Fn::GetAZs: ${this.
|
|
29949
|
+
this.logger.debug(`Resolved Fn::GetAZs: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
|
|
29942
29950
|
return azNames;
|
|
29943
29951
|
}
|
|
29944
29952
|
/**
|
|
@@ -30406,6 +30414,100 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30406
30414
|
maskThenStripThenMask(value, context) {
|
|
30407
30415
|
return this.maskSecretsForLog(stripControlChars(this.maskSecretsForLog(value, context)), context);
|
|
30408
30416
|
}
|
|
30417
|
+
/**
|
|
30418
|
+
* The ONE way a masked value reaches a message in this file.
|
|
30419
|
+
*
|
|
30420
|
+
* ## Why this exists rather than a rule about call sites
|
|
30421
|
+
*
|
|
30422
|
+
* `maskSecretsForLog` answers "does this text contain a recorded secret". It
|
|
30423
|
+
* makes NO claim about control characters, and it cannot be taught to: its
|
|
30424
|
+
* result is also used as a COMPARISON (`maskNeedlesForLog(text) !== text`
|
|
30425
|
+
* decides whether a twin was registered), so stripping inside it would change
|
|
30426
|
+
* what that comparison means at every call site.
|
|
30427
|
+
*
|
|
30428
|
+
* That split produced a four-round treadmill on go-to-k/cdkd#3408. Round 1
|
|
30429
|
+
* found the four `Fn::GetStackOutput` throws rendering a masked name raw and
|
|
30430
|
+
* fixed them. Round 2 found the CloudFormation-fallback warn — the DEFAULT
|
|
30431
|
+
* path, at DEFAULT verbosity — and the self-reference throw. Round 3 found
|
|
30432
|
+
* the `reresolveCrossStackValue` origin builder and
|
|
30433
|
+
* `describeAvailableOutputs`. Each round fixed what it found and the next
|
|
30434
|
+
* round found more, because the population was never enumerated. Measured
|
|
30435
|
+
* 2026-09-19 with a comment-stripped scan: SEVENTY-ONE interpolations
|
|
30436
|
+
* rendered a `maskSecretsForLog` result directly, plus thirteen hand-spelled
|
|
30437
|
+
* `displaySafe(maskThenStripThenMask(...))` compositions and two bare
|
|
30438
|
+
* `maskThenStripThenMask` interpolations — and three reviewers between them
|
|
30439
|
+
* reached eight of the eighty-six.
|
|
30440
|
+
*
|
|
30441
|
+
* So the repair is not another site. Interpolating the MASKER is now itself
|
|
30442
|
+
* the defect, and this is the only sanctioned way to render one — which turns
|
|
30443
|
+
* "did someone remember to strip here" into a property one scanner checks
|
|
30444
|
+
* over the whole file at once.
|
|
30445
|
+
* `tests/unit/deployment/resolver-display-masked-population.test.ts` is that
|
|
30446
|
+
* scanner: it fails on any `${this.maskSecretsForLog(...)}`, so a new raw
|
|
30447
|
+
* render in THAT SHAPE cannot be added silently.
|
|
30448
|
+
*
|
|
30449
|
+
* ## KNOWN RESIDUAL — the class is NARROWED, not closed
|
|
30450
|
+
*
|
|
30451
|
+
* Stated here rather than implied away, because an earlier revision of this
|
|
30452
|
+
* comment claimed the class was "closed by CONSTRUCTION" and that is FALSE.
|
|
30453
|
+
* The scanner is line-shaped and forbids only the `${this.<masker>(` spelling.
|
|
30454
|
+
* TEN local bindings take a raw masker result and interpolate it later, which
|
|
30455
|
+
* it cannot see — and at least one is a live exposure:
|
|
30456
|
+
* `loggedExportName` (bound from `maskSecretsForLog`) reaches an
|
|
30457
|
+
* `Fn::ImportValue` throw carrying an unstripped ESC and CR, measured on this
|
|
30458
|
+
* tree. `loggedSecretId`, `loggedJsonKey`, `loggedParameterName`,
|
|
30459
|
+
* `maskedAttributeName` and `loggedOrigin` are the same shape.
|
|
30460
|
+
*
|
|
30461
|
+
* Do NOT close those by hand at the ten sites: that is the fifth round of the
|
|
30462
|
+
* enumeration this builder exists to end, and the mechanism that resolves an
|
|
30463
|
+
* identifier to its initializer ALREADY EXISTS in this repo —
|
|
30464
|
+
* `sanitizedLocals` in
|
|
30465
|
+
* `tests/unit/cli/local-profile-display-population.test.ts`.
|
|
30466
|
+
* go-to-k/cdkd#3426 owns binding the scanner to it; go-to-k/cdkd#3405 and
|
|
30467
|
+
* go-to-k/cdkd#3411 own the surrounding scope and parser work.
|
|
30468
|
+
*
|
|
30469
|
+
* ## What it does, in order
|
|
30470
|
+
*
|
|
30471
|
+
* `maskThenStripThenMask` first — mask, strip, mask — because
|
|
30472
|
+
* `stripControlChars` DELETES, so a plaintext split by an invisible would be
|
|
30473
|
+
* reconstituted contiguous by a strip applied after a single mask. Then
|
|
30474
|
+
* `displaySafe`, which covers the class `stripControlChars` does not:
|
|
30475
|
+
* `U+2028` / `U+2029` (line terminators to JSON and web log viewers) and the
|
|
30476
|
+
* Trojan-Source bidi overrides. That composition is what the `physicalId`
|
|
30477
|
+
* renders here already spelled by hand; this names it, and they now share it.
|
|
30478
|
+
*
|
|
30479
|
+
* It is listed in `MASKERS` in `scripts/check-resolver-mask-coverage.ts`,
|
|
30480
|
+
* which is a SECURITY decision and a sound one: every path through it passes
|
|
30481
|
+
* `maskSecretsForLog`, so it is strictly stronger than the entry it replaces
|
|
30482
|
+
* and cannot be weaker at any input.
|
|
30483
|
+
*/
|
|
30484
|
+
displayMasked(value, context) {
|
|
30485
|
+
return displaySafe(this.maskThenStripThenMask(value, context));
|
|
30486
|
+
}
|
|
30487
|
+
/**
|
|
30488
|
+
* The display form of a LOG-TWIN leaf — the second render route, named for
|
|
30489
|
+
* the same reason as {@link displayMasked} and closed by the same scanner.
|
|
30490
|
+
*
|
|
30491
|
+
* `logTextOfLeaf` resolves a value to its registered twin (or `SECRET_MASK`),
|
|
30492
|
+
* which is a MASKING answer and, like `maskSecretsForLog`'s, says nothing
|
|
30493
|
+
* about control characters. Four `origin` builders interpolated it raw — the
|
|
30494
|
+
* `Fn::ImportValue` pair, the `Fn::GetStackOutput` one and the nested-stack
|
|
30495
|
+
* attribute one — and those strings flow into `redactedAttributeReads[].display`
|
|
30496
|
+
* and out through a `ProvisioningError` message.
|
|
30497
|
+
*
|
|
30498
|
+
* This route is why fixing the masker alone did not close the class:
|
|
30499
|
+
* `displayMasked` covers every site that interpolates `maskSecretsForLog`,
|
|
30500
|
+
* and these interpolate something else. Two routes, two names, one scanner
|
|
30501
|
+
* forbidding the raw form of both.
|
|
30502
|
+
*
|
|
30503
|
+
* Composed rather than re-derived. `maskSecretsInText` is idempotent (see
|
|
30504
|
+
* {@link maskThenStripThenMask}), so masking twin text again costs nothing
|
|
30505
|
+
* and cannot change the twin — which keeps this a STRICT addition of the
|
|
30506
|
+
* strip and the `displaySafe` pass, with the twin resolution untouched.
|
|
30507
|
+
*/
|
|
30508
|
+
displayLeaf(value, context) {
|
|
30509
|
+
return this.displayMasked(this.logTextOfLeaf(value, context), context);
|
|
30510
|
+
}
|
|
30409
30511
|
async resolveDynamicReferences(value, context) {
|
|
30410
30512
|
return (await this.resolveDynamicReferencesWithLogTwin(value, value, context)).result;
|
|
30411
30513
|
}
|
|
@@ -30455,7 +30557,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30455
30557
|
continue;
|
|
30456
30558
|
}
|
|
30457
30559
|
const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
|
|
30458
|
-
if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.
|
|
30560
|
+
if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.displayMasked(tokenLogText, context)}: it names '${this.displayMasked(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.displayMasked(regionVerdict.foreignProducerRegions.map((r) => this.maskSecretsForLog(r, context)).join(", "), context)} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
|
|
30459
30561
|
if (regionVerdict.kind === "named-region") {
|
|
30460
30562
|
const foreign = await this.resolverForProducerRegion(regionVerdict.region, context, nameLogText(regionVerdict.region)).resolveDynamicReferencesWithLogTwin(fullMatch, fullMatch, withoutProducerRegions(context), {
|
|
30461
30563
|
tokenLogText,
|
|
@@ -30520,11 +30622,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30520
30622
|
resolved = param.value;
|
|
30521
30623
|
} else if (service === "ssm-secure") {
|
|
30522
30624
|
const param = await this.resolveSSMReference(parts, true, "ssm-secure", context, nameLogText);
|
|
30523
|
-
if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.
|
|
30625
|
+
if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.displayMasked(tokenLogText, context)}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
|
|
30524
30626
|
isSecret = true;
|
|
30525
30627
|
resolved = param.value;
|
|
30526
30628
|
} else {
|
|
30527
|
-
this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.
|
|
30629
|
+
this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.displayMasked(nameLogText(String(service)), context)}`, context));
|
|
30528
30630
|
complete = false;
|
|
30529
30631
|
continue;
|
|
30530
30632
|
}
|
|
@@ -30614,7 +30716,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30614
30716
|
if (!secretId) throw new Error("Dynamic reference: secretsmanager SECRET_ID is required");
|
|
30615
30717
|
const loggedSecretId = this.maskSecretsForLog(nameLogText(secretId), context);
|
|
30616
30718
|
const loggedJsonKey = this.maskSecretsForLog(nameLogText(jsonKey), context);
|
|
30617
|
-
this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.
|
|
30719
|
+
this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.displayMasked(nameLogText(versionStage), context)}:${this.displayMasked(nameLogText(versionId), context)}`);
|
|
30618
30720
|
const client = this.clientsForRegion(this.explicitRegion).secretsManager;
|
|
30619
30721
|
const command = new GetSecretValueCommand({
|
|
30620
30722
|
SecretId: secretId,
|
|
@@ -30656,7 +30758,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30656
30758
|
const count = Number(await this.resolveValue(rawCount, context));
|
|
30657
30759
|
const cidrBits = Number(await this.resolveValue(rawCidrBits, context));
|
|
30658
30760
|
if (!ipBlock || typeof ipBlock !== "string") throw new Error(`Fn::Cidr: ipBlock must be a string, got ${typeof ipBlock}: ${JSON.stringify(this.maskValueLeaves(ipBlock, context))}`);
|
|
30659
|
-
this.logger.debug(`Resolving Fn::Cidr: ipBlock=${this.
|
|
30761
|
+
this.logger.debug(`Resolving Fn::Cidr: ipBlock=${this.displayMasked(JSON.stringify(this.maskValueLeaves(ipBlock, context)), context)}, count=${count}, cidrBits=${cidrBits}`);
|
|
30660
30762
|
const isIpv6 = ipBlock.includes(":");
|
|
30661
30763
|
const results = [];
|
|
30662
30764
|
if (isIpv6) {
|
|
@@ -30688,7 +30790,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30688
30790
|
results.push(`${a}.${b}.${c}.${d}/${subnetPrefix}`);
|
|
30689
30791
|
}
|
|
30690
30792
|
}
|
|
30691
|
-
this.logger.debug(`Fn::Cidr result: ${this.
|
|
30793
|
+
this.logger.debug(`Fn::Cidr result: ${this.displayMasked(JSON.stringify(this.maskValueLeaves(results, context)), context)}`);
|
|
30692
30794
|
return results;
|
|
30693
30795
|
}
|
|
30694
30796
|
/** Expand IPv6 address to full 8-group form */
|
|
@@ -32476,7 +32578,7 @@ var CloudControlProvider = class {
|
|
|
32476
32578
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
32477
32579
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
32478
32580
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
32479
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
32581
|
+
const { ASGProvider } = await import("./asg-provider-De5KONKr.js").then((n) => n.n);
|
|
32480
32582
|
const asgProvider = new ASGProvider();
|
|
32481
32583
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
32482
32584
|
}
|
|
@@ -40443,6 +40545,34 @@ function retainedSurvivorMessages(logicalId, resourceType, survivorPhysicalId, s
|
|
|
40443
40545
|
reason: `UpdateReplacePolicy: Retain kept the replacement's new ${resourceType} (${survivorPhysicalId}); it is live, still billing, and no longer tracked by cdkd. ${stateClause}`
|
|
40444
40546
|
};
|
|
40445
40547
|
}
|
|
40548
|
+
function requireRestorableBaseline(bag, logger, { logicalId, consequence, remedy, retry }) {
|
|
40549
|
+
if (isRestorableBag(bag)) return true;
|
|
40550
|
+
if (bag === void 0) {
|
|
40551
|
+
logger.warn(` Rollback: Cannot restore ${safe(logicalId)} \u2014 its recorded previous state has no \`properties\` bag, so there is nothing to restore it to. An empty desired state would ${consequence}. The resource is therefore left exactly as it is. ${remedy}`);
|
|
40552
|
+
return false;
|
|
40553
|
+
}
|
|
40554
|
+
const shape = bag === null ? "null" : Array.isArray(bag) ? "an array" : `a ${typeof bag}`;
|
|
40555
|
+
throw new CdkdError(`Cannot roll ${safe(logicalId)} back: its recorded previous state has a \`properties\` field that is not a property bag (${shape}), so cdkd cannot tell what to restore it to. Replaying it would do one of two things, and cdkd does neither: send the malformed value to the provider as-is, or send an empty desired state (which would ${consequence}). The rollback JOURNAL record is kept: once that record holds a property bag again, ${retry}. Otherwise fix forward with \`cdkd deploy\`, or remove the stack with \`cdkd destroy\`.`, "ROLLBACK_UNUSABLE_BASELINE");
|
|
40556
|
+
}
|
|
40557
|
+
/**
|
|
40558
|
+
* Is this a desired bag a replay can actually restore TO?
|
|
40559
|
+
*
|
|
40560
|
+
* Absent is the shape issue #3203 started from, but `=== undefined` is the
|
|
40561
|
+
* WRONG test and a 0-red mutation row is what said so: `null ?? {}` is `{}`,
|
|
40562
|
+
* so a `null` bag reaches the provider as an empty desired state exactly as an
|
|
40563
|
+
* absent one does. A non-object bag (`"abc"`, `[]`) is worse still -- it goes
|
|
40564
|
+
* to the provider VERBATIM. go-to-k/cdkd#3149 refuses those shapes for a
|
|
40565
|
+
* JOURNAL-sourced record at the parser, but `previousState` is equally often
|
|
40566
|
+
* STATE-sourced, and `parseStateBody` deliberately validates no inner shape
|
|
40567
|
+
* (go-to-k/cdkd#2947's placement decision), so this is the only boundary that
|
|
40568
|
+
* sees them on that path.
|
|
40569
|
+
*
|
|
40570
|
+
* A PRESENT but empty `{}` is restorable and must pass: the operator recorded
|
|
40571
|
+
* a resource that really has no properties. Only "no usable bag" is refused.
|
|
40572
|
+
*/
|
|
40573
|
+
function isRestorableBag(bag) {
|
|
40574
|
+
return typeof bag === "object" && bag !== null && !Array.isArray(bag);
|
|
40575
|
+
}
|
|
40446
40576
|
/**
|
|
40447
40577
|
* `DeletionPolicy: Snapshot` on a rolled-back CREATE (issue #1358) — the
|
|
40448
40578
|
* executor's copy of the deploy engine's `prepareFinalSnapshotForDelete`
|
|
@@ -41331,6 +41461,15 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
41331
41461
|
case "reverse-replacement": {
|
|
41332
41462
|
const current = stateResources[op.logicalId];
|
|
41333
41463
|
const prev = op.previousState;
|
|
41464
|
+
if (!requireRestorableBaseline(prev.properties, logger, {
|
|
41465
|
+
logicalId: op.logicalId,
|
|
41466
|
+
consequence: "create a default-configured resource and then delete the live one",
|
|
41467
|
+
remedy: "Re-run `cdkd deploy` to re-converge it.",
|
|
41468
|
+
retry: "re-running `cdkd rollback` retries this op"
|
|
41469
|
+
})) {
|
|
41470
|
+
result.warnings++;
|
|
41471
|
+
return;
|
|
41472
|
+
}
|
|
41334
41473
|
const resolvedPrevProps = await resolveReplayProps(prev.properties, resolver, secrets, ctx, op.logicalId) ?? {};
|
|
41335
41474
|
refuseMaskedReplayBaseline(resolvedPrevProps, op.logicalId);
|
|
41336
41475
|
recordNestedStackParameterExpressions(secrets, op.resourceType, resolvedPrevProps, prev.properties, STATE_DERIVED_RULES);
|
|
@@ -41436,6 +41575,15 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
41436
41575
|
result.warnings++;
|
|
41437
41576
|
return;
|
|
41438
41577
|
}
|
|
41578
|
+
if (!requireRestorableBaseline(previousState.properties, logger, {
|
|
41579
|
+
logicalId: op.logicalId,
|
|
41580
|
+
consequence: "be applied as a complete desired state: a patch provider removes every property, and an SDK provider may reset a subset or replace the resource",
|
|
41581
|
+
remedy: "Re-run `cdkd deploy` to re-converge it.",
|
|
41582
|
+
retry: "re-running `cdkd rollback` retries this op"
|
|
41583
|
+
})) {
|
|
41584
|
+
result.warnings++;
|
|
41585
|
+
return;
|
|
41586
|
+
}
|
|
41439
41587
|
logger.info(` Rollback: Restoring ${safe(op.logicalId)} (${safe(op.resourceType)}) to previous state`);
|
|
41440
41588
|
const { provider } = ctx.providerRegistry.getProviderFor({
|
|
41441
41589
|
resourceType: op.resourceType,
|
|
@@ -41605,6 +41753,15 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
41605
41753
|
case "revert-failed-update": {
|
|
41606
41754
|
const current = stateResources[op.logicalId];
|
|
41607
41755
|
const prev = op.previousState;
|
|
41756
|
+
if (!requireRestorableBaseline(prev.properties, logger, {
|
|
41757
|
+
logicalId: op.logicalId,
|
|
41758
|
+
consequence: "be applied as a complete desired state: a patch provider removes every property, and an SDK provider may reset a subset or replace the resource",
|
|
41759
|
+
remedy: "Inspect it with `cdkd drift` (this op died mid-flight, so its remote state is unknown) and re-converge with `cdkd drift --revert` or `cdkd deploy`.",
|
|
41760
|
+
retry: "re-running `cdkd rollback --revert-failed` retries this op (a plain `cdkd rollback` replays only the COMPLETED ops and then pops the whole segment, discarding this record)"
|
|
41761
|
+
})) {
|
|
41762
|
+
result.warnings++;
|
|
41763
|
+
break;
|
|
41764
|
+
}
|
|
41608
41765
|
logger.info(` Rollback: force-reverting failed UPDATE of ${safe(op.logicalId)} (${safe(op.resourceType)}) to its pre-deploy properties (--revert-failed; remote state is unknown)`);
|
|
41609
41766
|
const { provider } = ctx.providerRegistry.getProviderFor({
|
|
41610
41767
|
resourceType: op.resourceType,
|
|
@@ -45488,4 +45645,4 @@ var DeployEngine = class DeployEngine {
|
|
|
45488
45645
|
|
|
45489
45646
|
//#endregion
|
|
45490
45647
|
export { secretBearing as $, SynthesisError as $i, recordMaskOnlyValue as $n, AssetManifestLoader as $r, s3BucketArn as $t, extractDeploymentEventError as A, getAwsClients as Ai, refuseMalformedState as An, BOOTSTRAP_MARKER_PREFIX as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, IntrinsicResolutionRefusalError as Bi, STATE_SOURCED_BASELINE_RULES as Bn, buildDockerImage as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, canonicalizeRegion as Ci, producerRecordKey as Cn, stringifyValue as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, clearBucketRegionCache as Di, refuseMalformedOutputsForDestroy as Dn, loadPublishableAssetManifest as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, processStackMessages as Ei, refuseMalformedOutputs as En, createAssetRedirectResolver as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, ConfigError as Fi, findSilentDropProperties as Fn, parseBootstrapMarker as Fr, isSettledInstanceState as Ft, gray as G, NestedStackChildDirectDestroyError as Gi, createSecretMasker as Gn, formatDockerLoginError as Gr, readConfigString as Gt, formatResourceLine as H, LocalStartServiceError as Hi, STATE_SOURCED_READBACK_RULES as Hn, describeDockerExecFailure as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, CrossAccountSecretRefusalError as Ii, describeTypeWithThrottleRetry as In, readBootstrapMarkerBody as Ir, WAFv2WebACLProvider as It, yellow as J, ResourceTimeoutError as Ji, identityKeyFor as Jn, redactDockerArgvValues as Jr, requireConfigObject as Jt, green as K, PartialFailureError as Ki, dynamicReferenceTokens as Kn, getDockerCmd as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DependencyError as Li, withRetry as Ln, validateAssetBucketName as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, setAwsClients as Mi, repairMalformedResourcePropertiesForReadOnly as Mn, ensureAssetStorage as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, AssetError as Ni, repairMalformedResourcesForReadOnly as Nn, getBootstrapMarkerKey as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, resolveBucketRegion as Oi, refuseMalformedResourcePropertiesForOrphan as On, rewriteTemplateAssetReferences as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CdkdError as Pi, findActionableSilentDrops as Pn, isCrossRegionRedirect as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, StateError as Qi, maskSecretsInText as Qn, stripControlChars as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, DeployCancelledError as Ri, DagBuilder as Rn, validateContainerRepoName as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, PARTITION_TABLE as Si, malformedStateRefusalMessage as Sn, AssetPublisher as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, AssemblyReader as Ti, refuseMalformedNestedChildOutputs as Tn, buildAssetRedirectMap as Tr, carriesDynamicReference as Tt, bold as U, LockError as Ui, TEMPLATE_SOURCED_RULES as Un, describeDockerFailure as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LocalInvokeBuildError as Vi, STATE_SOURCED_CROSS_GENERATION_RULES as Vn, describeDockerCapturedOutput as Vr, coerceCfnInteger as Vt, cyan as W, MalformedProducerRecordRefusalError as Wi, carriesSecretMask as Wn, dockerSpawnEnvWithSensitive as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StackHasActiveImportsError as Xi, markSameGenerationBag as Xn, runDockerStreaming as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, ResourceUpdateNotSupportedError as Yi, isSingleDynamicReferenceToken as Yn, runDockerForeground as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, StackTerminationProtectionError as Zi, maskSecretsInError as Zn, escapeRegExp$1 as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, findLargeInlineResources as _i, malformedOutputsRefusalMessage as _n, importableOutputs as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isRetryableTransientError as aa, resolveApp as ai, AWS_CREDENTIAL_ENV_KEYS as an, rebuildClientForBucketRegion as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, decodeListingKey as bi, malformedResourcePropertiesWarning as bn, orphansCarriedFrom as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, markNonRetryable as ca, resolveSkipPrefix as ci, envCredentialsAreAssumedRole as cn, buildLockContentionMessage as cr, createMaskedRetryLogger as ct, updatePartialReason as d, describeAwsFailure as da, resolveUseCdkBootstrapAssets as di, STATE_RESOURCES_MALFORMED as dn, shellQuote as dr, beginCommandInterruptScope as dt, formatError as ea, getDockerImageBySourceHash as ei, s3BucketDomainName as en, recoverMaskedOutput as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, safeStringify$1 as fa, stateBucketExistenceConfirmed as fi, hasReadableOutputs as fn, CUSTOM_RESOURCE_RESPONSE_PREFIX as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, MIGRATE_TMP_PREFIX as gi, malformedLocalOutputsWarning as gn, importableOutputKeys as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, CFN_TEMPLATE_URL_LIMIT as hi, malformedExportSourceWarning as hn, hasReadableExportSet as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isMarkedNonRetryable as ia, getLegacyStateBucketName as ii, applyRoleArnIfSet as in, S3StateBackend as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, resetAwsClients as ji, repairMalformedOutputsForReadOnly as jn, assertAssetBucketRegion as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, AwsClients as ki, refuseMalformedResourcesForDestroy as kn, AssetModeResolver as kr, getAccountInfo as kt, replayRollback as l, markRedactedCause as la, resolveStateBucketWithDefault as li, DiffCalculator as ln, forceQuitRecoveryClause as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, CFN_TEMPLATE_BODY_LIMIT as mi, malformedExportNamesWarning as mn, exportNamesCarriedFrom as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, normalizeAwsError as na, synthesisStatusMessage as ni, s3BucketRegionalDomainName as nn, scrubResourceRecord as nr, getCurrentResourceSecrets as nt, planFailedOps as o, isThrottlingError as oa, resolveAutoAssetStorage as oi, applyCallerIdentityCredentials as on, UNREPRODUCIBLE_LOCK_CLAUSE as or, ProviderRegistry as ot, maskingRetryLogger as p, __exportAll as pa, warnDeprecatedNoPrefixCliFlag as pi, hasReadableResources as pn, DEFAULT_STATE_PREFIX as pr, interruptWatchListenerCount as pt, red as q, ProvisioningError as qi, errorCauseChain as qn, partitionSensitiveEnv as qr, requireConfigArray as qt, DeployEngine as r, withErrorHandling as ra, getDefaultStateBucketName as ri, s3BucketWebsiteUrl as rn, LockManager as rr, IAMRoleProvider as rt, planRollback as s, isTransientServerError as sa, resolveCaptureObservedState as si, callerEnvCredentials as sn, buildForceUnlockCommand as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, isCdkdError as ta, Synthesizer as ti, s3BucketDualStackDomainName as tn, redactSecretsForState as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, retryClassificationText as ua, resolveStateBucketWithDefaultAndSource as ui, INTRINSIC_KEYS as un, formatLockExpiry as ur, maskerOrIdentity as ut, keptWholeReasonText as v, uploadCfnTemplate as vi, malformedOutputsWarning as vn, isReadableBag as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, derivePartitionAndUrlSuffix as wi, refuseDivergentRecordRegionForDestroy as wn, WorkGraph as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, expectedOwnerParam as xi, malformedResourcesWarning as xn, shouldRetainResource as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as yi, malformedRenderedContainersWarning as yn, orphansAfterRollback as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, DynamicReferenceRegionAmbiguousError as zi, TemplateParser as zn, buildDenyExternalAccessPolicy as zr, assertRegionMatch as zt };
|
|
45491
|
-
//# sourceMappingURL=deploy-engine-
|
|
45648
|
+
//# sourceMappingURL=deploy-engine-mI1Xv1oi.js.map
|