@go-to-k/cdkd 0.94.1 → 0.94.3
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/cli.js +130 -11
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.94.3.tgz +0 -0
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.94.1.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -17838,6 +17838,14 @@ var PATTERN_B_RESOURCE_TYPES = [
|
|
|
17838
17838
|
"AWS::ElasticLoadBalancingV2::LoadBalancer",
|
|
17839
17839
|
"AWS::ElasticLoadBalancingV2::TargetGroup"
|
|
17840
17840
|
];
|
|
17841
|
+
var PATTERN_B_NAME_PROPERTIES = {
|
|
17842
|
+
"AWS::IAM::Role": "RoleName",
|
|
17843
|
+
"AWS::IAM::User": "UserName",
|
|
17844
|
+
"AWS::IAM::Group": "GroupName",
|
|
17845
|
+
"AWS::IAM::InstanceProfile": "InstanceProfileName",
|
|
17846
|
+
"AWS::ElasticLoadBalancingV2::LoadBalancer": "Name",
|
|
17847
|
+
"AWS::ElasticLoadBalancingV2::TargetGroup": "Name"
|
|
17848
|
+
};
|
|
17841
17849
|
function generateResourceName(name, options) {
|
|
17842
17850
|
const {
|
|
17843
17851
|
maxLength,
|
|
@@ -65532,6 +65540,12 @@ function findPendingPrefixRenames(stackName, state) {
|
|
|
65532
65540
|
continue;
|
|
65533
65541
|
if (!resource.physicalId.startsWith(prefix))
|
|
65534
65542
|
continue;
|
|
65543
|
+
const nameProperty = PATTERN_B_NAME_PROPERTIES[resource.resourceType];
|
|
65544
|
+
if (!nameProperty)
|
|
65545
|
+
continue;
|
|
65546
|
+
const userSuppliedName = resource.properties?.[nameProperty];
|
|
65547
|
+
if (typeof userSuppliedName !== "string" || userSuppliedName === "")
|
|
65548
|
+
continue;
|
|
65535
65549
|
const newPhysicalId = resource.physicalId.slice(prefix.length);
|
|
65536
65550
|
if (newPhysicalId.length === 0)
|
|
65537
65551
|
continue;
|
|
@@ -80130,6 +80144,29 @@ function readStringProperty(properties, key, resourceType) {
|
|
|
80130
80144
|
}
|
|
80131
80145
|
return v;
|
|
80132
80146
|
}
|
|
80147
|
+
var IMPORT_UNSUPPORTED_RECREATABLE_TYPES = /* @__PURE__ */ new Set([
|
|
80148
|
+
"AWS::ApiGatewayV2::Stage"
|
|
80149
|
+
]);
|
|
80150
|
+
var PRE_DELETE_HANDLERS = {
|
|
80151
|
+
"AWS::ApiGatewayV2::Stage": async (entry) => {
|
|
80152
|
+
const { ApiGatewayV2Client: ApiGatewayV2Client2, DeleteStageCommand: DeleteStageCommand3, NotFoundException: NotFoundException7 } = await import("@aws-sdk/client-apigatewayv2");
|
|
80153
|
+
const apiId = entry.properties["ApiId"];
|
|
80154
|
+
if (typeof apiId !== "string" || !apiId) {
|
|
80155
|
+
throw new Error(
|
|
80156
|
+
`cdkd state's properties for ${entry.logicalId} (${entry.resourceType}) is missing 'ApiId'`
|
|
80157
|
+
);
|
|
80158
|
+
}
|
|
80159
|
+
const client = new ApiGatewayV2Client2({});
|
|
80160
|
+
try {
|
|
80161
|
+
await client.send(new DeleteStageCommand3({ ApiId: apiId, StageName: entry.physicalId }));
|
|
80162
|
+
} catch (err) {
|
|
80163
|
+
if (err instanceof NotFoundException7) {
|
|
80164
|
+
return;
|
|
80165
|
+
}
|
|
80166
|
+
throw err;
|
|
80167
|
+
}
|
|
80168
|
+
}
|
|
80169
|
+
};
|
|
80133
80170
|
async function exportCommand(stackArg, options) {
|
|
80134
80171
|
const logger = getLogger();
|
|
80135
80172
|
if (options.verbose) {
|
|
@@ -80243,10 +80280,13 @@ async function exportCommand(stackArg, options) {
|
|
|
80243
80280
|
}
|
|
80244
80281
|
}
|
|
80245
80282
|
try {
|
|
80246
|
-
const { phase1Imports, phase2Creates, blocked } = await buildImportPlan(
|
|
80283
|
+
const { phase1Imports, phase2Creates, recreateBeforePhase2, blocked } = await buildImportPlan(
|
|
80247
80284
|
state,
|
|
80248
80285
|
template,
|
|
80249
|
-
awsClients.cloudFormation
|
|
80286
|
+
awsClients.cloudFormation,
|
|
80287
|
+
{
|
|
80288
|
+
recreateImportUnsupported: options.recreateImportUnsupported
|
|
80289
|
+
}
|
|
80250
80290
|
);
|
|
80251
80291
|
if (blocked.length > 0) {
|
|
80252
80292
|
logger.error("The following resources block migration:");
|
|
@@ -80266,7 +80306,7 @@ async function exportCommand(stackArg, options) {
|
|
|
80266
80306
|
`${phase2Creates.length} non-importable resource(s) detected (Custom::*). Pass --include-non-importable to run a 2-phase migration: phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones (re-invoking each Custom Resource's backing Lambda onCreate handler \u2014 make sure those are idempotent). Or destroy these resources first.`
|
|
80267
80307
|
);
|
|
80268
80308
|
}
|
|
80269
|
-
if (phase1Imports.length === 0 && phase2Creates.length === 0) {
|
|
80309
|
+
if (phase1Imports.length === 0 && phase2Creates.length === 0 && recreateBeforePhase2.length === 0) {
|
|
80270
80310
|
logger.warn("No resources to migrate \u2014 cdkd state is empty.");
|
|
80271
80311
|
return;
|
|
80272
80312
|
}
|
|
@@ -80283,14 +80323,28 @@ async function exportCommand(stackArg, options) {
|
|
|
80283
80323
|
}
|
|
80284
80324
|
logger.info("");
|
|
80285
80325
|
}
|
|
80326
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80327
|
+
logger.info(
|
|
80328
|
+
`Phase 2 will also re-CREATE ${recreateBeforePhase2.length} IMPORT-unsupported resource(s) after cdkd deletes the AWS-side resource:`
|
|
80329
|
+
);
|
|
80330
|
+
for (const r of recreateBeforePhase2) {
|
|
80331
|
+
logger.info(` ${r.logicalId} (${r.resourceType}) \u2014 physicalId: ${r.physicalId}`);
|
|
80332
|
+
}
|
|
80333
|
+
logger.info(
|
|
80334
|
+
" Brief unavailability window (~10s for Stage; HttpApi endpoint URL is unchanged because it embeds ApiId, not StageName). Pass --no-recreate-import-unsupported to block instead."
|
|
80335
|
+
);
|
|
80336
|
+
logger.info("");
|
|
80337
|
+
}
|
|
80286
80338
|
if (options.dryRun) {
|
|
80287
80339
|
logger.info("--dry-run: no CloudFormation changeset will be created.");
|
|
80288
80340
|
return;
|
|
80289
80341
|
}
|
|
80290
80342
|
if (!options.yes) {
|
|
80291
80343
|
const phase2Note = phase2Creates.length > 0 ? ` Phase 2 will then CREATE ${phase2Creates.length} non-importable resource(s) (invoking each Custom Resource's onCreate handler).` : "";
|
|
80344
|
+
const recreateNote = recreateBeforePhase2.length > 0 ? ` cdkd will also DELETE ${recreateBeforePhase2.length} AWS resource(s) between phases so CFn can re-CREATE them in phase 2 (brief unavailability window \u2014 see plan above for the affected resources).` : "";
|
|
80345
|
+
const unchangedClaim = recreateBeforePhase2.length > 0 ? ` All other AWS resources are unchanged on import.` : ` AWS resources are unchanged on import.`;
|
|
80292
80346
|
const ok = await confirmPrompt6(
|
|
80293
|
-
`Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note +
|
|
80347
|
+
`Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note + recreateNote + unchangedClaim + ` cdkd state for '${resolvedStackName}' will be deleted on success.`
|
|
80294
80348
|
);
|
|
80295
80349
|
if (!ok) {
|
|
80296
80350
|
logger.info("Migration cancelled. cdkd state and CloudFormation are unchanged.");
|
|
@@ -80344,7 +80398,42 @@ async function exportCommand(stackArg, options) {
|
|
|
80344
80398
|
logger.info(
|
|
80345
80399
|
`\u2713 Phase 1: CloudFormation stack '${cfnStackName}' created via IMPORT. ${phase1Imports.length} resource(s) imported.`
|
|
80346
80400
|
);
|
|
80347
|
-
if (
|
|
80401
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80402
|
+
for (const entry of recreateBeforePhase2) {
|
|
80403
|
+
const handler = PRE_DELETE_HANDLERS[entry.resourceType];
|
|
80404
|
+
if (!handler) {
|
|
80405
|
+
throw new Error(
|
|
80406
|
+
`No pre-delete handler registered for ${entry.resourceType} (${entry.logicalId}). This is a cdkd bug \u2014 the resource is in IMPORT_UNSUPPORTED_RECREATABLE_TYPES but lacks a PRE_DELETE_HANDLERS entry. Phase 1 IMPORT already succeeded; cdkd state is intact. To recover, delete the AWS resource manually and run the phase 2 UPDATE.`
|
|
80407
|
+
);
|
|
80408
|
+
}
|
|
80409
|
+
logger.info(
|
|
80410
|
+
`Pre-deleting AWS resource for ${entry.logicalId} (${entry.resourceType}) so CFn can re-CREATE in phase 2...`
|
|
80411
|
+
);
|
|
80412
|
+
try {
|
|
80413
|
+
await handler(entry);
|
|
80414
|
+
logger.info(` \u2713 deleted ${entry.physicalId}`);
|
|
80415
|
+
} catch (err) {
|
|
80416
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
80417
|
+
throw new Error(
|
|
80418
|
+
`Phase 1 (IMPORT) succeeded; pre-delete of ${entry.logicalId} (${entry.resourceType}, physicalId: ${entry.physicalId}) failed: ${msg}
|
|
80419
|
+
|
|
80420
|
+
The CloudFormation stack '${cfnStackName}' contains the phase-1 imports but the IMPORT-unsupported resources (${recreateBeforePhase2.length} total) still exist in AWS unmanaged. cdkd state is UNCHANGED. Re-running \`cdkd export\` does NOT work \u2014 the existing-stack check rejects it. To recover manually:
|
|
80421
|
+
1. Fix the failure cause (typically IAM permissions for the underlying AWS API \u2014 e.g. apigatewayv2:DeleteStage for AWS::ApiGatewayV2::Stage).
|
|
80422
|
+
2. Delete the remaining AWS-side IMPORT-unsupported resources by hand:
|
|
80423
|
+
aws apigatewayv2 delete-stage --api-id <ApiId> --stage-name <StageName>
|
|
80424
|
+
(one per entry in the pre-delete list logged above).
|
|
80425
|
+
3. Run the phase-2 UPDATE manually with the full synth template:
|
|
80426
|
+
aws cloudformation create-change-set --stack-name ${cfnStackName} \\
|
|
80427
|
+
--change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
|
|
80428
|
+
--template-body file://<full-template.json>
|
|
80429
|
+
4. Once phase 2 succeeds, run: cdkd state orphan ${resolvedStackName}
|
|
80430
|
+
to clean up cdkd's stale state record.`
|
|
80431
|
+
);
|
|
80432
|
+
}
|
|
80433
|
+
}
|
|
80434
|
+
}
|
|
80435
|
+
const phase2Count = phase2Creates.length + recreateBeforePhase2.length;
|
|
80436
|
+
if (phase2Count > 0) {
|
|
80348
80437
|
try {
|
|
80349
80438
|
await executeUpdateChangeSet(
|
|
80350
80439
|
awsClients.cloudFormation,
|
|
@@ -80352,14 +80441,23 @@ async function exportCommand(stackArg, options) {
|
|
|
80352
80441
|
template,
|
|
80353
80442
|
cfnParameters
|
|
80354
80443
|
);
|
|
80355
|
-
|
|
80444
|
+
const parts = [];
|
|
80445
|
+
if (phase2Creates.length > 0) {
|
|
80446
|
+
parts.push(`${phase2Creates.length} non-importable resource(s) CREATEd`);
|
|
80447
|
+
}
|
|
80448
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80449
|
+
parts.push(`${recreateBeforePhase2.length} IMPORT-unsupported resource(s) re-CREATEd`);
|
|
80450
|
+
}
|
|
80451
|
+
logger.info(`\u2713 Phase 2: ${parts.join("; ")}.`);
|
|
80356
80452
|
} catch (err) {
|
|
80357
80453
|
const msg = err instanceof Error ? err.message : String(err);
|
|
80454
|
+
const recreateNote = recreateBeforePhase2.length > 0 ? ` - cdkd deleted ${recreateBeforePhase2.length} AWS resource(s) before phase 2 (Stage etc.). They are gone in AWS but absent from the CFn stack. Running phase 2 UPDATE manually will CFn-CREATE them fresh.
|
|
80455
|
+
` : "";
|
|
80358
80456
|
throw new Error(
|
|
80359
80457
|
`Phase 1 (IMPORT) succeeded; phase 2 (UPDATE) failed: ${msg}
|
|
80360
80458
|
|
|
80361
|
-
The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing
|
|
80362
|
-
1. Fix the failure cause (typically an onCreate Lambda error).
|
|
80459
|
+
The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing ${phase2Count} resource(s) (${phase2Creates.length} Custom Resource(s) + ${recreateBeforePhase2.length} IMPORT-unsupported). cdkd state is UNCHANGED so you can inspect what's in it, but DO NOT run \`cdkd deploy\` against this stack (the imported resources are now CFn-managed). To recover:
|
|
80460
|
+
` + recreateNote + ` 1. Fix the failure cause (typically an onCreate Lambda error).
|
|
80363
80461
|
2. Re-run the phase 2 UPDATE manually with the full synth template:
|
|
80364
80462
|
aws cloudformation create-change-set --stack-name ${cfnStackName} \\
|
|
80365
80463
|
--change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
|
|
@@ -80467,13 +80565,14 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
|
|
|
80467
80565
|
function isPhase2CreatableType(resourceType) {
|
|
80468
80566
|
return isCustomResourceType(resourceType);
|
|
80469
80567
|
}
|
|
80470
|
-
async function buildImportPlan(state, template, cfnClient) {
|
|
80568
|
+
async function buildImportPlan(state, template, cfnClient, options = { recreateImportUnsupported: true }) {
|
|
80471
80569
|
const templateResources = template["Resources"];
|
|
80472
80570
|
if (!templateResources || typeof templateResources !== "object" || Array.isArray(templateResources)) {
|
|
80473
80571
|
throw new Error("Template has no Resources section.");
|
|
80474
80572
|
}
|
|
80475
80573
|
const phase1Imports = [];
|
|
80476
80574
|
const phase2Creates = [];
|
|
80575
|
+
const recreateBeforePhase2 = [];
|
|
80477
80576
|
const blocked = [];
|
|
80478
80577
|
const identifierCache = /* @__PURE__ */ new Map();
|
|
80479
80578
|
for (const [logicalId, raw] of Object.entries(templateResources)) {
|
|
@@ -80507,6 +80606,23 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
80507
80606
|
});
|
|
80508
80607
|
continue;
|
|
80509
80608
|
}
|
|
80609
|
+
if (IMPORT_UNSUPPORTED_RECREATABLE_TYPES.has(resourceType)) {
|
|
80610
|
+
if (options.recreateImportUnsupported) {
|
|
80611
|
+
recreateBeforePhase2.push({
|
|
80612
|
+
logicalId,
|
|
80613
|
+
resourceType,
|
|
80614
|
+
physicalId: stateEntry.physicalId,
|
|
80615
|
+
properties: stateEntry.properties ?? {}
|
|
80616
|
+
});
|
|
80617
|
+
} else {
|
|
80618
|
+
blocked.push({
|
|
80619
|
+
logicalId,
|
|
80620
|
+
resourceType,
|
|
80621
|
+
reason: `AWS CloudFormation does not support ${resourceType} in IMPORT changesets (${resourceType} has no IMPORT handler). Re-run without --no-recreate-import-unsupported to let cdkd delete the AWS-side resource before phase 2 (CFn will then CREATE it fresh; brief unavailability window).`
|
|
80622
|
+
});
|
|
80623
|
+
}
|
|
80624
|
+
continue;
|
|
80625
|
+
}
|
|
80510
80626
|
let resolved;
|
|
80511
80627
|
try {
|
|
80512
80628
|
resolved = await resolveResourceIdentifier(
|
|
@@ -80532,7 +80648,7 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
80532
80648
|
propertiesOverlay: resolved.propertiesOverlay ?? resolved.resourceIdentifier
|
|
80533
80649
|
});
|
|
80534
80650
|
}
|
|
80535
|
-
return { phase1Imports, phase2Creates, blocked };
|
|
80651
|
+
return { phase1Imports, phase2Creates, recreateBeforePhase2, blocked };
|
|
80536
80652
|
}
|
|
80537
80653
|
async function resolveResourceIdentifier(resourceType, physicalId, properties, cfnClient, cache2) {
|
|
80538
80654
|
let entry = cache2.get(resourceType);
|
|
@@ -81019,6 +81135,9 @@ function createExportCommand() {
|
|
|
81019
81135
|
"--strict-cross-stack",
|
|
81020
81136
|
"Refuse to export when sibling cdkd stacks in the same CDK app reference the exporting stack via Fn::GetStackOutput. Without the flag, cdkd warns but proceeds \u2014 the user is expected to migrate the consumer stacks in a follow-up.",
|
|
81021
81137
|
false
|
|
81138
|
+
).option(
|
|
81139
|
+
"--no-recreate-import-unsupported",
|
|
81140
|
+
"Block instead of auto-handling resource types AWS does NOT support in IMPORT changesets (currently only AWS::ApiGatewayV2::Stage, emitted by CDK HttpApi). Default behavior: cdkd skips these from phase 1, deletes the AWS-side resource between phases, and lets CFn re-CREATE in phase 2 (brief unavailability window). With this flag, the export aborts with a clear error instead."
|
|
81022
81141
|
).action(withErrorHandling(exportCommand));
|
|
81023
81142
|
[...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
|
|
81024
81143
|
(opt) => cmd.addOption(opt)
|
|
@@ -81057,7 +81176,7 @@ function reorderArgs(argv) {
|
|
|
81057
81176
|
}
|
|
81058
81177
|
async function main() {
|
|
81059
81178
|
const program = new Command18();
|
|
81060
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.
|
|
81179
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.3");
|
|
81061
81180
|
program.addCommand(createBootstrapCommand());
|
|
81062
81181
|
program.addCommand(createSynthCommand());
|
|
81063
81182
|
program.addCommand(createListCommand());
|