@go-to-k/cdkd 0.94.0 → 0.94.2
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 +208 -28
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.94.2.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.94.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -80029,7 +80029,8 @@ var PRIMARY_IDENTIFIER_FALLBACK = {
|
|
|
80029
80029
|
};
|
|
80030
80030
|
var COMPOSITE_ID_SPLITTERS = {
|
|
80031
80031
|
// cdkd stores `restApiId|resourceId|httpMethod` (apigateway-provider.ts);
|
|
80032
|
-
// CFn primary identifier is [RestApiId, ResourceId, HttpMethod] — same
|
|
80032
|
+
// CFn primary identifier is [RestApiId, ResourceId, HttpMethod] — same
|
|
80033
|
+
// order, and all three are writable Properties of AWS::ApiGateway::Method.
|
|
80033
80034
|
"AWS::ApiGateway::Method": (id) => {
|
|
80034
80035
|
const parts = id.split("|");
|
|
80035
80036
|
if (parts.length !== 3) {
|
|
@@ -80037,26 +80038,119 @@ var COMPOSITE_ID_SPLITTERS = {
|
|
|
80037
80038
|
`expected 3 parts (restApiId|resourceId|httpMethod), got ${parts.length}: '${id}'`
|
|
80038
80039
|
);
|
|
80039
80040
|
}
|
|
80040
|
-
|
|
80041
|
+
const map = { RestApiId: parts[0], ResourceId: parts[1], HttpMethod: parts[2] };
|
|
80042
|
+
return { resourceIdentifier: map };
|
|
80041
80043
|
},
|
|
80042
80044
|
// cdkd stores `restApiId|resourceId` (apigateway-provider.ts);
|
|
80043
|
-
// CFn primary identifier is [RestApiId, ResourceId]
|
|
80045
|
+
// CFn primary identifier is [RestApiId, ResourceId] — both are writable
|
|
80046
|
+
// Properties of AWS::ApiGateway::Resource.
|
|
80044
80047
|
"AWS::ApiGateway::Resource": (id) => {
|
|
80045
80048
|
const parts = id.split("|");
|
|
80046
80049
|
if (parts.length !== 2) {
|
|
80047
80050
|
throw new Error(`expected 2 parts (restApiId|resourceId), got ${parts.length}: '${id}'`);
|
|
80048
80051
|
}
|
|
80049
|
-
|
|
80052
|
+
const map = { RestApiId: parts[0], ResourceId: parts[1] };
|
|
80053
|
+
return { resourceIdentifier: map };
|
|
80050
80054
|
},
|
|
80051
80055
|
// cdkd stores `IGW|VpcId` (ec2-provider.ts);
|
|
80052
80056
|
// CFn primary identifier is [VpcId, InternetGatewayId] — DIFFERENT order
|
|
80053
|
-
// from cdkd. Splitter reorders explicitly.
|
|
80057
|
+
// from cdkd. Splitter reorders explicitly. Both are writable Properties.
|
|
80054
80058
|
"AWS::EC2::VPCGatewayAttachment": (id) => {
|
|
80055
80059
|
const parts = id.split("|");
|
|
80056
80060
|
if (parts.length !== 2) {
|
|
80057
80061
|
throw new Error(`expected 2 parts (IGW|VpcId), got ${parts.length}: '${id}'`);
|
|
80058
80062
|
}
|
|
80059
|
-
|
|
80063
|
+
const map = { VpcId: parts[1], InternetGatewayId: parts[0] };
|
|
80064
|
+
return { resourceIdentifier: map };
|
|
80065
|
+
},
|
|
80066
|
+
// cdkd stores just `IntegrationId` (apigatewayv2-provider.ts); the parent
|
|
80067
|
+
// `ApiId` lives in cdkd state's properties (`properties.ApiId`). CFn primary
|
|
80068
|
+
// identifier is [ApiId, IntegrationId]. ApiId IS a writable Property
|
|
80069
|
+
// (already in synth template via Ref); IntegrationId is tagged
|
|
80070
|
+
// `readOnlyProperties: ['/properties/IntegrationId']` in the CFn schema —
|
|
80071
|
+
// exclude it from propertiesOverlay so CFn doesn't reject writing a
|
|
80072
|
+
// read-only property at changeset-create.
|
|
80073
|
+
"AWS::ApiGatewayV2::Integration": (physicalId, properties) => {
|
|
80074
|
+
const apiId = readStringProperty(properties, "ApiId", "AWS::ApiGatewayV2::Integration");
|
|
80075
|
+
return {
|
|
80076
|
+
resourceIdentifier: { ApiId: apiId, IntegrationId: physicalId },
|
|
80077
|
+
propertiesOverlay: { ApiId: apiId }
|
|
80078
|
+
};
|
|
80079
|
+
},
|
|
80080
|
+
// cdkd stores just `RouteId` (apigatewayv2-provider.ts); parent `ApiId`
|
|
80081
|
+
// comes from properties. CFn primary identifier is [ApiId, RouteId]. Same
|
|
80082
|
+
// overlay narrowing as Integration above.
|
|
80083
|
+
"AWS::ApiGatewayV2::Route": (physicalId, properties) => {
|
|
80084
|
+
const apiId = readStringProperty(properties, "ApiId", "AWS::ApiGatewayV2::Route");
|
|
80085
|
+
return {
|
|
80086
|
+
resourceIdentifier: { ApiId: apiId, RouteId: physicalId },
|
|
80087
|
+
propertiesOverlay: { ApiId: apiId }
|
|
80088
|
+
};
|
|
80089
|
+
},
|
|
80090
|
+
// NOTE: `AWS::ApiGatewayV2::Stage` is intentionally NOT in this map.
|
|
80091
|
+
// (1) AWS reports its primaryIdentifier as `['/properties/Id']` (single-key),
|
|
80092
|
+
// so cdkd's single-key resolution path handles it without a splitter.
|
|
80093
|
+
// (2) But AWS CloudFormation does NOT support `AWS::ApiGatewayV2::Stage` in
|
|
80094
|
+
// IMPORT changesets (CreateChangeSet rejects with "ResourceTypes
|
|
80095
|
+
// [AWS::ApiGatewayV2::Stage] are not supported for Import"). This means
|
|
80096
|
+
// `cdkd export` cannot complete on any stack that includes an HttpApi
|
|
80097
|
+
// (CDK auto-creates a `$default` Stage). Tracked in a follow-up issue
|
|
80098
|
+
// (link in PR description); the workaround design is open
|
|
80099
|
+
// (pre-delete + phase-2-CREATE vs hard-block-with-clear-error).
|
|
80100
|
+
// cdkd stores `StatementId` (lambda-permission-provider.ts:124); for state
|
|
80101
|
+
// entries written by the older CC-API path (pre-SDK-provider), physicalId
|
|
80102
|
+
// may instead be the legacy `<functionArn>|<statementId>` shape — the
|
|
80103
|
+
// provider's own delete / update / getAttribute paths normalize via
|
|
80104
|
+
// `physicalId.split('|').pop()` (see lambda-permission-provider.ts:160 /
|
|
80105
|
+
// 222 / 290). Mirror that here so legacy state still produces the
|
|
80106
|
+
// correct CFn Id field; otherwise CFn IMPORT's identifier-match would
|
|
80107
|
+
// compare `Id: '<arn>|<sid>'` against the AWS-current Sid and reject.
|
|
80108
|
+
//
|
|
80109
|
+
// CFn primary identifier is [FunctionName, Id] (note: CFn schema calls
|
|
80110
|
+
// the field `Id`, not `StatementId`). FunctionName IS a writable
|
|
80111
|
+
// Property; `Id` is tagged `readOnlyProperties: ['/properties/Id']` in
|
|
80112
|
+
// the CFn schema (it's set at create time by AWS, not by the user).
|
|
80113
|
+
// Narrow overlay to FunctionName so CFn doesn't reject writing read-only
|
|
80114
|
+
// `Id` at changeset-create.
|
|
80115
|
+
"AWS::Lambda::Permission": (physicalId, properties) => {
|
|
80116
|
+
const functionName = readStringProperty(properties, "FunctionName", "AWS::Lambda::Permission");
|
|
80117
|
+
const statementId = physicalId.includes("|") ? physicalId.split("|").pop() : physicalId;
|
|
80118
|
+
return {
|
|
80119
|
+
resourceIdentifier: { FunctionName: functionName, Id: statementId },
|
|
80120
|
+
propertiesOverlay: { FunctionName: functionName }
|
|
80121
|
+
};
|
|
80122
|
+
}
|
|
80123
|
+
};
|
|
80124
|
+
function readStringProperty(properties, key, resourceType) {
|
|
80125
|
+
const v = properties[key];
|
|
80126
|
+
if (typeof v !== "string" || !v) {
|
|
80127
|
+
throw new Error(
|
|
80128
|
+
`cdkd state's properties for ${resourceType} is missing '${key}' (the parent identifier required to build the CFn ResourceIdentifier map). State entry may be corrupt or written by an older cdkd binary; re-deploy the resource to refresh state.`
|
|
80129
|
+
);
|
|
80130
|
+
}
|
|
80131
|
+
return v;
|
|
80132
|
+
}
|
|
80133
|
+
var IMPORT_UNSUPPORTED_RECREATABLE_TYPES = /* @__PURE__ */ new Set([
|
|
80134
|
+
"AWS::ApiGatewayV2::Stage"
|
|
80135
|
+
]);
|
|
80136
|
+
var PRE_DELETE_HANDLERS = {
|
|
80137
|
+
"AWS::ApiGatewayV2::Stage": async (entry) => {
|
|
80138
|
+
const { ApiGatewayV2Client: ApiGatewayV2Client2, DeleteStageCommand: DeleteStageCommand3, NotFoundException: NotFoundException7 } = await import("@aws-sdk/client-apigatewayv2");
|
|
80139
|
+
const apiId = entry.properties["ApiId"];
|
|
80140
|
+
if (typeof apiId !== "string" || !apiId) {
|
|
80141
|
+
throw new Error(
|
|
80142
|
+
`cdkd state's properties for ${entry.logicalId} (${entry.resourceType}) is missing 'ApiId'`
|
|
80143
|
+
);
|
|
80144
|
+
}
|
|
80145
|
+
const client = new ApiGatewayV2Client2({});
|
|
80146
|
+
try {
|
|
80147
|
+
await client.send(new DeleteStageCommand3({ ApiId: apiId, StageName: entry.physicalId }));
|
|
80148
|
+
} catch (err) {
|
|
80149
|
+
if (err instanceof NotFoundException7) {
|
|
80150
|
+
return;
|
|
80151
|
+
}
|
|
80152
|
+
throw err;
|
|
80153
|
+
}
|
|
80060
80154
|
}
|
|
80061
80155
|
};
|
|
80062
80156
|
async function exportCommand(stackArg, options) {
|
|
@@ -80172,10 +80266,13 @@ async function exportCommand(stackArg, options) {
|
|
|
80172
80266
|
}
|
|
80173
80267
|
}
|
|
80174
80268
|
try {
|
|
80175
|
-
const { phase1Imports, phase2Creates, blocked } = await buildImportPlan(
|
|
80269
|
+
const { phase1Imports, phase2Creates, recreateBeforePhase2, blocked } = await buildImportPlan(
|
|
80176
80270
|
state,
|
|
80177
80271
|
template,
|
|
80178
|
-
awsClients.cloudFormation
|
|
80272
|
+
awsClients.cloudFormation,
|
|
80273
|
+
{
|
|
80274
|
+
recreateImportUnsupported: options.recreateImportUnsupported
|
|
80275
|
+
}
|
|
80179
80276
|
);
|
|
80180
80277
|
if (blocked.length > 0) {
|
|
80181
80278
|
logger.error("The following resources block migration:");
|
|
@@ -80195,7 +80292,7 @@ async function exportCommand(stackArg, options) {
|
|
|
80195
80292
|
`${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.`
|
|
80196
80293
|
);
|
|
80197
80294
|
}
|
|
80198
|
-
if (phase1Imports.length === 0 && phase2Creates.length === 0) {
|
|
80295
|
+
if (phase1Imports.length === 0 && phase2Creates.length === 0 && recreateBeforePhase2.length === 0) {
|
|
80199
80296
|
logger.warn("No resources to migrate \u2014 cdkd state is empty.");
|
|
80200
80297
|
return;
|
|
80201
80298
|
}
|
|
@@ -80212,14 +80309,28 @@ async function exportCommand(stackArg, options) {
|
|
|
80212
80309
|
}
|
|
80213
80310
|
logger.info("");
|
|
80214
80311
|
}
|
|
80312
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80313
|
+
logger.info(
|
|
80314
|
+
`Phase 2 will also re-CREATE ${recreateBeforePhase2.length} IMPORT-unsupported resource(s) after cdkd deletes the AWS-side resource:`
|
|
80315
|
+
);
|
|
80316
|
+
for (const r of recreateBeforePhase2) {
|
|
80317
|
+
logger.info(` ${r.logicalId} (${r.resourceType}) \u2014 physicalId: ${r.physicalId}`);
|
|
80318
|
+
}
|
|
80319
|
+
logger.info(
|
|
80320
|
+
" 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."
|
|
80321
|
+
);
|
|
80322
|
+
logger.info("");
|
|
80323
|
+
}
|
|
80215
80324
|
if (options.dryRun) {
|
|
80216
80325
|
logger.info("--dry-run: no CloudFormation changeset will be created.");
|
|
80217
80326
|
return;
|
|
80218
80327
|
}
|
|
80219
80328
|
if (!options.yes) {
|
|
80220
80329
|
const phase2Note = phase2Creates.length > 0 ? ` Phase 2 will then CREATE ${phase2Creates.length} non-importable resource(s) (invoking each Custom Resource's onCreate handler).` : "";
|
|
80330
|
+
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).` : "";
|
|
80331
|
+
const unchangedClaim = recreateBeforePhase2.length > 0 ? ` All other AWS resources are unchanged on import.` : ` AWS resources are unchanged on import.`;
|
|
80221
80332
|
const ok = await confirmPrompt6(
|
|
80222
|
-
`Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note +
|
|
80333
|
+
`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.`
|
|
80223
80334
|
);
|
|
80224
80335
|
if (!ok) {
|
|
80225
80336
|
logger.info("Migration cancelled. cdkd state and CloudFormation are unchanged.");
|
|
@@ -80273,7 +80384,42 @@ async function exportCommand(stackArg, options) {
|
|
|
80273
80384
|
logger.info(
|
|
80274
80385
|
`\u2713 Phase 1: CloudFormation stack '${cfnStackName}' created via IMPORT. ${phase1Imports.length} resource(s) imported.`
|
|
80275
80386
|
);
|
|
80276
|
-
if (
|
|
80387
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80388
|
+
for (const entry of recreateBeforePhase2) {
|
|
80389
|
+
const handler = PRE_DELETE_HANDLERS[entry.resourceType];
|
|
80390
|
+
if (!handler) {
|
|
80391
|
+
throw new Error(
|
|
80392
|
+
`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.`
|
|
80393
|
+
);
|
|
80394
|
+
}
|
|
80395
|
+
logger.info(
|
|
80396
|
+
`Pre-deleting AWS resource for ${entry.logicalId} (${entry.resourceType}) so CFn can re-CREATE in phase 2...`
|
|
80397
|
+
);
|
|
80398
|
+
try {
|
|
80399
|
+
await handler(entry);
|
|
80400
|
+
logger.info(` \u2713 deleted ${entry.physicalId}`);
|
|
80401
|
+
} catch (err) {
|
|
80402
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
80403
|
+
throw new Error(
|
|
80404
|
+
`Phase 1 (IMPORT) succeeded; pre-delete of ${entry.logicalId} (${entry.resourceType}, physicalId: ${entry.physicalId}) failed: ${msg}
|
|
80405
|
+
|
|
80406
|
+
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:
|
|
80407
|
+
1. Fix the failure cause (typically IAM permissions for the underlying AWS API \u2014 e.g. apigatewayv2:DeleteStage for AWS::ApiGatewayV2::Stage).
|
|
80408
|
+
2. Delete the remaining AWS-side IMPORT-unsupported resources by hand:
|
|
80409
|
+
aws apigatewayv2 delete-stage --api-id <ApiId> --stage-name <StageName>
|
|
80410
|
+
(one per entry in the pre-delete list logged above).
|
|
80411
|
+
3. Run the phase-2 UPDATE manually with the full synth template:
|
|
80412
|
+
aws cloudformation create-change-set --stack-name ${cfnStackName} \\
|
|
80413
|
+
--change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
|
|
80414
|
+
--template-body file://<full-template.json>
|
|
80415
|
+
4. Once phase 2 succeeds, run: cdkd state orphan ${resolvedStackName}
|
|
80416
|
+
to clean up cdkd's stale state record.`
|
|
80417
|
+
);
|
|
80418
|
+
}
|
|
80419
|
+
}
|
|
80420
|
+
}
|
|
80421
|
+
const phase2Count = phase2Creates.length + recreateBeforePhase2.length;
|
|
80422
|
+
if (phase2Count > 0) {
|
|
80277
80423
|
try {
|
|
80278
80424
|
await executeUpdateChangeSet(
|
|
80279
80425
|
awsClients.cloudFormation,
|
|
@@ -80281,14 +80427,23 @@ async function exportCommand(stackArg, options) {
|
|
|
80281
80427
|
template,
|
|
80282
80428
|
cfnParameters
|
|
80283
80429
|
);
|
|
80284
|
-
|
|
80430
|
+
const parts = [];
|
|
80431
|
+
if (phase2Creates.length > 0) {
|
|
80432
|
+
parts.push(`${phase2Creates.length} non-importable resource(s) CREATEd`);
|
|
80433
|
+
}
|
|
80434
|
+
if (recreateBeforePhase2.length > 0) {
|
|
80435
|
+
parts.push(`${recreateBeforePhase2.length} IMPORT-unsupported resource(s) re-CREATEd`);
|
|
80436
|
+
}
|
|
80437
|
+
logger.info(`\u2713 Phase 2: ${parts.join("; ")}.`);
|
|
80285
80438
|
} catch (err) {
|
|
80286
80439
|
const msg = err instanceof Error ? err.message : String(err);
|
|
80440
|
+
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.
|
|
80441
|
+
` : "";
|
|
80287
80442
|
throw new Error(
|
|
80288
80443
|
`Phase 1 (IMPORT) succeeded; phase 2 (UPDATE) failed: ${msg}
|
|
80289
80444
|
|
|
80290
|
-
The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing
|
|
80291
|
-
1. Fix the failure cause (typically an onCreate Lambda error).
|
|
80445
|
+
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:
|
|
80446
|
+
` + recreateNote + ` 1. Fix the failure cause (typically an onCreate Lambda error).
|
|
80292
80447
|
2. Re-run the phase 2 UPDATE manually with the full synth template:
|
|
80293
80448
|
aws cloudformation create-change-set --stack-name ${cfnStackName} \\
|
|
80294
80449
|
--change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
|
|
@@ -80396,13 +80551,14 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
|
|
|
80396
80551
|
function isPhase2CreatableType(resourceType) {
|
|
80397
80552
|
return isCustomResourceType(resourceType);
|
|
80398
80553
|
}
|
|
80399
|
-
async function buildImportPlan(state, template, cfnClient) {
|
|
80554
|
+
async function buildImportPlan(state, template, cfnClient, options = { recreateImportUnsupported: true }) {
|
|
80400
80555
|
const templateResources = template["Resources"];
|
|
80401
80556
|
if (!templateResources || typeof templateResources !== "object" || Array.isArray(templateResources)) {
|
|
80402
80557
|
throw new Error("Template has no Resources section.");
|
|
80403
80558
|
}
|
|
80404
80559
|
const phase1Imports = [];
|
|
80405
80560
|
const phase2Creates = [];
|
|
80561
|
+
const recreateBeforePhase2 = [];
|
|
80406
80562
|
const blocked = [];
|
|
80407
80563
|
const identifierCache = /* @__PURE__ */ new Map();
|
|
80408
80564
|
for (const [logicalId, raw] of Object.entries(templateResources)) {
|
|
@@ -80436,11 +80592,29 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
80436
80592
|
});
|
|
80437
80593
|
continue;
|
|
80438
80594
|
}
|
|
80439
|
-
|
|
80595
|
+
if (IMPORT_UNSUPPORTED_RECREATABLE_TYPES.has(resourceType)) {
|
|
80596
|
+
if (options.recreateImportUnsupported) {
|
|
80597
|
+
recreateBeforePhase2.push({
|
|
80598
|
+
logicalId,
|
|
80599
|
+
resourceType,
|
|
80600
|
+
physicalId: stateEntry.physicalId,
|
|
80601
|
+
properties: stateEntry.properties ?? {}
|
|
80602
|
+
});
|
|
80603
|
+
} else {
|
|
80604
|
+
blocked.push({
|
|
80605
|
+
logicalId,
|
|
80606
|
+
resourceType,
|
|
80607
|
+
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).`
|
|
80608
|
+
});
|
|
80609
|
+
}
|
|
80610
|
+
continue;
|
|
80611
|
+
}
|
|
80612
|
+
let resolved;
|
|
80440
80613
|
try {
|
|
80441
|
-
|
|
80614
|
+
resolved = await resolveResourceIdentifier(
|
|
80442
80615
|
resourceType,
|
|
80443
80616
|
stateEntry.physicalId,
|
|
80617
|
+
stateEntry.properties ?? {},
|
|
80444
80618
|
cfnClient,
|
|
80445
80619
|
identifierCache
|
|
80446
80620
|
);
|
|
@@ -80456,19 +80630,21 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
80456
80630
|
logicalId,
|
|
80457
80631
|
resourceType,
|
|
80458
80632
|
physicalId: stateEntry.physicalId,
|
|
80459
|
-
resourceIdentifier
|
|
80633
|
+
resourceIdentifier: resolved.resourceIdentifier,
|
|
80634
|
+
propertiesOverlay: resolved.propertiesOverlay ?? resolved.resourceIdentifier
|
|
80460
80635
|
});
|
|
80461
80636
|
}
|
|
80462
|
-
return { phase1Imports, phase2Creates, blocked };
|
|
80637
|
+
return { phase1Imports, phase2Creates, recreateBeforePhase2, blocked };
|
|
80463
80638
|
}
|
|
80464
|
-
async function resolveResourceIdentifier(resourceType, physicalId, cfnClient, cache2) {
|
|
80639
|
+
async function resolveResourceIdentifier(resourceType, physicalId, properties, cfnClient, cache2) {
|
|
80465
80640
|
let entry = cache2.get(resourceType);
|
|
80466
80641
|
if (entry === void 0) {
|
|
80467
80642
|
entry = await fetchPrimaryIdentifier(resourceType, cfnClient);
|
|
80468
80643
|
cache2.set(resourceType, entry);
|
|
80469
80644
|
}
|
|
80470
80645
|
if (entry.fields.length === 1) {
|
|
80471
|
-
|
|
80646
|
+
const map = { [entry.fields[0]]: physicalId };
|
|
80647
|
+
return { resourceIdentifier: map };
|
|
80472
80648
|
}
|
|
80473
80649
|
const splitter = COMPOSITE_ID_SPLITTERS[resourceType];
|
|
80474
80650
|
if (!splitter) {
|
|
@@ -80476,22 +80652,22 @@ async function resolveResourceIdentifier(resourceType, physicalId, cfnClient, ca
|
|
|
80476
80652
|
`resource type uses a composite primary identifier (${entry.fields.length} fields: ${entry.fields.join(", ")}); add an entry to COMPOSITE_ID_SPLITTERS in src/cli/commands/export.ts that parses cdkd's physicalId for this type, or destroy the resource first and let CFn create it fresh`
|
|
80477
80653
|
);
|
|
80478
80654
|
}
|
|
80479
|
-
let
|
|
80655
|
+
let result;
|
|
80480
80656
|
try {
|
|
80481
|
-
|
|
80657
|
+
result = splitter(physicalId, properties);
|
|
80482
80658
|
} catch (err) {
|
|
80483
80659
|
throw new Error(
|
|
80484
80660
|
`composite-id splitter for ${resourceType} failed: ` + (err instanceof Error ? err.message : String(err))
|
|
80485
80661
|
);
|
|
80486
80662
|
}
|
|
80487
80663
|
for (const f of entry.fields) {
|
|
80488
|
-
if (!(f in
|
|
80664
|
+
if (!(f in result.resourceIdentifier)) {
|
|
80489
80665
|
throw new Error(
|
|
80490
|
-
`composite-id splitter for ${resourceType} did not produce field '${f}' (produced: ${Object.keys(
|
|
80666
|
+
`composite-id splitter for ${resourceType} did not produce field '${f}' (produced: ${Object.keys(result.resourceIdentifier).join(", ")})`
|
|
80491
80667
|
);
|
|
80492
80668
|
}
|
|
80493
80669
|
}
|
|
80494
|
-
return
|
|
80670
|
+
return result;
|
|
80495
80671
|
}
|
|
80496
80672
|
async function fetchPrimaryIdentifier(resourceType, cfnClient) {
|
|
80497
80673
|
try {
|
|
@@ -80614,7 +80790,8 @@ function overlayResourceIdentifierOnProperties(resource, entry) {
|
|
|
80614
80790
|
const r = resource;
|
|
80615
80791
|
const existingProperties = r["Properties"];
|
|
80616
80792
|
const properties = existingProperties && typeof existingProperties === "object" && !Array.isArray(existingProperties) ? { ...existingProperties } : {};
|
|
80617
|
-
|
|
80793
|
+
const overlay = entry.propertiesOverlay ?? entry.resourceIdentifier;
|
|
80794
|
+
for (const [field, value] of Object.entries(overlay)) {
|
|
80618
80795
|
properties[field] = value;
|
|
80619
80796
|
}
|
|
80620
80797
|
return { ...r, Properties: properties };
|
|
@@ -80944,6 +81121,9 @@ function createExportCommand() {
|
|
|
80944
81121
|
"--strict-cross-stack",
|
|
80945
81122
|
"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.",
|
|
80946
81123
|
false
|
|
81124
|
+
).option(
|
|
81125
|
+
"--no-recreate-import-unsupported",
|
|
81126
|
+
"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."
|
|
80947
81127
|
).action(withErrorHandling(exportCommand));
|
|
80948
81128
|
[...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
|
|
80949
81129
|
(opt) => cmd.addOption(opt)
|
|
@@ -80982,7 +81162,7 @@ function reorderArgs(argv) {
|
|
|
80982
81162
|
}
|
|
80983
81163
|
async function main() {
|
|
80984
81164
|
const program = new Command18();
|
|
80985
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.
|
|
81165
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.2");
|
|
80986
81166
|
program.addCommand(createBootstrapCommand());
|
|
80987
81167
|
program.addCommand(createSynthCommand());
|
|
80988
81168
|
program.addCommand(createListCommand());
|