@go-to-k/cdkd 0.30.2 → 0.31.0
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/README.md +25 -6
- package/dist/cli.js +160 -2
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.31.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.30.2.tgz +0 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- **Diff calculation**: Self-implemented resource/property-level diff between desired template and current state
|
|
25
25
|
- **S3-based state management**: No DynamoDB required, uses S3 conditional writes for locking
|
|
26
26
|
- **DAG-based parallelization**: Analyze `Ref`/`Fn::GetAtt` dependencies and execute in parallel
|
|
27
|
-
- **`--no-wait` for async resources**: Skip the multi-minute wait on CloudFront / RDS / ElastiCache and return as soon as the create call returns (CloudFormation always blocks)
|
|
27
|
+
- **`--no-wait` for async resources**: Skip the multi-minute wait on CloudFront / RDS / ElastiCache / NAT Gateway and return as soon as the create call returns (CloudFormation always blocks)
|
|
28
28
|
|
|
29
29
|
> **Note**: Resource types not covered by either SDK Providers or Cloud Control API cannot be deployed with cdkd. If you encounter an unsupported resource type, deployment will fail with a clear error message.
|
|
30
30
|
|
|
@@ -372,11 +372,11 @@ cdkd state destroy MyStack --region us-east-1
|
|
|
372
372
|
|
|
373
373
|
## `--no-wait`: skip async-resource waits
|
|
374
374
|
|
|
375
|
-
CloudFront Distributions, RDS Clusters/Instances, and
|
|
376
|
-
typically take
|
|
377
|
-
cdkd waits for them to reach a ready state — the same
|
|
378
|
-
CloudFormation. Pass `--no-wait` to return as soon as the
|
|
379
|
-
returns:
|
|
375
|
+
CloudFront Distributions, RDS Clusters/Instances, ElastiCache, and
|
|
376
|
+
NAT Gateways typically take 1–15 minutes for AWS to fully provision.
|
|
377
|
+
By default cdkd waits for them to reach a ready state — the same
|
|
378
|
+
behavior as CloudFormation. Pass `--no-wait` to return as soon as the
|
|
379
|
+
create call returns:
|
|
380
380
|
|
|
381
381
|
```bash
|
|
382
382
|
cdkd deploy --no-wait
|
|
@@ -386,6 +386,25 @@ The resource is fully functional once AWS finishes the async
|
|
|
386
386
|
deployment in the background. CloudFormation has no equivalent — once
|
|
387
387
|
you submit a stack, you wait for everything.
|
|
388
388
|
|
|
389
|
+
NAT Gateway is included as of v0.31. Provisioning typically takes
|
|
390
|
+
1–2 minutes and is the dominant cost in many VPC stacks; with
|
|
391
|
+
`cdkd deploy --no-wait`, `CreateNatGateway` returns the `NatGatewayId`
|
|
392
|
+
immediately and dependent Routes that only reference the ID can
|
|
393
|
+
proceed against a still-`pending` gateway. AWS continues NAT
|
|
394
|
+
provisioning asynchronously after the deploy returns. Use this only
|
|
395
|
+
when nothing in the deploy flow needs actual NAT-routed egress (e.g.
|
|
396
|
+
no Lambda invoked during deploy that hits the internet).
|
|
397
|
+
|
|
398
|
+
`--no-wait` is **deploy-only**. `cdkd destroy` always waits for NAT
|
|
399
|
+
Gateway to reach `deleted` state — while the gateway is in
|
|
400
|
+
`deleting` AWS keeps the ENI / EIP / route-table associations
|
|
401
|
+
attached, so any concurrent `DeleteSubnet` / `DeleteInternetGateway`
|
|
402
|
+
/ `DeleteVpc` returns `DependencyViolation` and the destroy enters a
|
|
403
|
+
retry storm. Other `--no-wait` resources (CloudFront / RDS /
|
|
404
|
+
ElastiCache) don't apply to destroy either — their providers are
|
|
405
|
+
already non-blocking on delete because they're leaves in the destroy
|
|
406
|
+
DAG.
|
|
407
|
+
|
|
389
408
|
## Other CLI flags
|
|
390
409
|
|
|
391
410
|
For concurrency knobs (`--concurrency`, `--stack-concurrency`,
|
package/dist/cli.js
CHANGED
|
@@ -606,6 +606,10 @@ function validateResourceTimeouts(opts) {
|
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
|
+
var noWaitOption = new Option(
|
|
610
|
+
"--no-wait",
|
|
611
|
+
"Skip waiting for async resources to stabilize (CloudFront, RDS, ElastiCache, NAT Gateway)"
|
|
612
|
+
);
|
|
609
613
|
var deployOptions = [
|
|
610
614
|
new Option("--concurrency <number>", "Maximum concurrent resource operations").default(10).argParser((value) => parseInt(value, 10)),
|
|
611
615
|
new Option("--stack-concurrency <number>", "Maximum concurrent stack deployments").default(4).argParser((value) => parseInt(value, 10)),
|
|
@@ -617,7 +621,7 @@ var deployOptions = [
|
|
|
617
621
|
new Option("--dry-run", "Show changes without applying").default(false),
|
|
618
622
|
new Option("--skip-assets", "Skip asset publishing").default(false),
|
|
619
623
|
new Option("--no-rollback", "Skip rollback on deployment failure"),
|
|
620
|
-
|
|
624
|
+
noWaitOption,
|
|
621
625
|
new Option(
|
|
622
626
|
"-e, --exclusively",
|
|
623
627
|
"Only deploy requested stacks, do not include dependencies"
|
|
@@ -16790,6 +16794,11 @@ import {
|
|
|
16790
16794
|
DeleteInternetGatewayCommand,
|
|
16791
16795
|
AttachInternetGatewayCommand,
|
|
16792
16796
|
DetachInternetGatewayCommand,
|
|
16797
|
+
CreateNatGatewayCommand,
|
|
16798
|
+
DeleteNatGatewayCommand,
|
|
16799
|
+
DescribeNatGatewaysCommand,
|
|
16800
|
+
waitUntilNatGatewayAvailable,
|
|
16801
|
+
waitUntilNatGatewayDeleted,
|
|
16793
16802
|
CreateRouteTableCommand,
|
|
16794
16803
|
DeleteRouteTableCommand,
|
|
16795
16804
|
CreateRouteCommand,
|
|
@@ -16835,6 +16844,20 @@ var EC2Provider = class {
|
|
|
16835
16844
|
],
|
|
16836
16845
|
["AWS::EC2::InternetGateway", /* @__PURE__ */ new Set(["Tags"])],
|
|
16837
16846
|
["AWS::EC2::VPCGatewayAttachment", /* @__PURE__ */ new Set(["VpcId", "InternetGatewayId"])],
|
|
16847
|
+
[
|
|
16848
|
+
"AWS::EC2::NatGateway",
|
|
16849
|
+
/* @__PURE__ */ new Set([
|
|
16850
|
+
"AllocationId",
|
|
16851
|
+
"SubnetId",
|
|
16852
|
+
"ConnectivityType",
|
|
16853
|
+
"PrivateIpAddress",
|
|
16854
|
+
"SecondaryAllocationIds",
|
|
16855
|
+
"SecondaryPrivateIpAddresses",
|
|
16856
|
+
"SecondaryPrivateIpAddressCount",
|
|
16857
|
+
"MaxDrainDurationSeconds",
|
|
16858
|
+
"Tags"
|
|
16859
|
+
])
|
|
16860
|
+
],
|
|
16838
16861
|
["AWS::EC2::RouteTable", /* @__PURE__ */ new Set(["VpcId", "Tags"])],
|
|
16839
16862
|
[
|
|
16840
16863
|
"AWS::EC2::Route",
|
|
@@ -16922,6 +16945,8 @@ var EC2Provider = class {
|
|
|
16922
16945
|
return this.createInternetGateway(logicalId, resourceType, properties);
|
|
16923
16946
|
case "AWS::EC2::VPCGatewayAttachment":
|
|
16924
16947
|
return this.createVpcGatewayAttachment(logicalId, resourceType, properties);
|
|
16948
|
+
case "AWS::EC2::NatGateway":
|
|
16949
|
+
return this.createNatGateway(logicalId, resourceType, properties);
|
|
16925
16950
|
case "AWS::EC2::RouteTable":
|
|
16926
16951
|
return this.createRouteTable(logicalId, resourceType, properties);
|
|
16927
16952
|
case "AWS::EC2::Route":
|
|
@@ -16958,6 +16983,8 @@ var EC2Provider = class {
|
|
|
16958
16983
|
return this.updateInternetGateway(logicalId, physicalId);
|
|
16959
16984
|
case "AWS::EC2::VPCGatewayAttachment":
|
|
16960
16985
|
return this.updateVpcGatewayAttachment(logicalId, physicalId);
|
|
16986
|
+
case "AWS::EC2::NatGateway":
|
|
16987
|
+
return this.updateNatGateway(logicalId, physicalId);
|
|
16961
16988
|
case "AWS::EC2::RouteTable":
|
|
16962
16989
|
return this.updateRouteTable(logicalId, physicalId);
|
|
16963
16990
|
case "AWS::EC2::Route":
|
|
@@ -17011,6 +17038,8 @@ var EC2Provider = class {
|
|
|
17011
17038
|
return this.deleteInternetGateway(logicalId, physicalId, resourceType, context);
|
|
17012
17039
|
case "AWS::EC2::VPCGatewayAttachment":
|
|
17013
17040
|
return this.deleteVpcGatewayAttachment(logicalId, physicalId, resourceType, context);
|
|
17041
|
+
case "AWS::EC2::NatGateway":
|
|
17042
|
+
return this.deleteNatGateway(logicalId, physicalId, resourceType, context);
|
|
17014
17043
|
case "AWS::EC2::RouteTable":
|
|
17015
17044
|
return this.deleteRouteTable(logicalId, physicalId, resourceType, context);
|
|
17016
17045
|
case "AWS::EC2::Route":
|
|
@@ -17579,6 +17608,118 @@ var EC2Provider = class {
|
|
|
17579
17608
|
);
|
|
17580
17609
|
}
|
|
17581
17610
|
}
|
|
17611
|
+
// ─── AWS::EC2::NatGateway ─────────────────────────────────────────
|
|
17612
|
+
//
|
|
17613
|
+
// CloudFormation parity: by default we wait for the new NAT gateway to
|
|
17614
|
+
// reach `available` state before marking the resource created. NAT
|
|
17615
|
+
// provisioning takes ~1–2 minutes (often the longest single step in a
|
|
17616
|
+
// VPC stack). Pass `--no-wait` to skip the wait — `CreateNatGateway`
|
|
17617
|
+
// returns the `NatGatewayId` immediately so dependent Routes /
|
|
17618
|
+
// Subnets that only need the ID can proceed against a still-`pending`
|
|
17619
|
+
// gateway. Anything that requires actual NAT-routed egress (e.g. a
|
|
17620
|
+
// Lambda invocation that hits the internet during deploy) must not
|
|
17621
|
+
// rely on the gateway being live; with `--no-wait`, AWS continues
|
|
17622
|
+
// provisioning asynchronously after the deploy returns.
|
|
17623
|
+
async createNatGateway(logicalId, resourceType, properties) {
|
|
17624
|
+
this.logger.debug(`Creating NatGateway ${logicalId}`);
|
|
17625
|
+
const subnetId = properties["SubnetId"];
|
|
17626
|
+
if (!subnetId) {
|
|
17627
|
+
throw new ProvisioningError(
|
|
17628
|
+
`SubnetId is required for NatGateway ${logicalId}`,
|
|
17629
|
+
resourceType,
|
|
17630
|
+
logicalId
|
|
17631
|
+
);
|
|
17632
|
+
}
|
|
17633
|
+
try {
|
|
17634
|
+
const response = await this.ec2Client.send(
|
|
17635
|
+
new CreateNatGatewayCommand({
|
|
17636
|
+
SubnetId: subnetId,
|
|
17637
|
+
AllocationId: properties["AllocationId"],
|
|
17638
|
+
ConnectivityType: properties["ConnectivityType"] ?? void 0,
|
|
17639
|
+
PrivateIpAddress: properties["PrivateIpAddress"],
|
|
17640
|
+
SecondaryAllocationIds: properties["SecondaryAllocationIds"],
|
|
17641
|
+
SecondaryPrivateIpAddresses: properties["SecondaryPrivateIpAddresses"],
|
|
17642
|
+
SecondaryPrivateIpAddressCount: properties["SecondaryPrivateIpAddressCount"]
|
|
17643
|
+
})
|
|
17644
|
+
);
|
|
17645
|
+
const natGatewayId = response.NatGateway.NatGatewayId;
|
|
17646
|
+
await this.applyTags(natGatewayId, properties, logicalId);
|
|
17647
|
+
if (process.env["CDKD_NO_WAIT"] !== "true") {
|
|
17648
|
+
this.logger.debug(`Waiting for NatGateway ${natGatewayId} to reach available state...`);
|
|
17649
|
+
await waitUntilNatGatewayAvailable(
|
|
17650
|
+
// 15-min cap matches AWS's documented worst case for NAT
|
|
17651
|
+
// provisioning. Per-resource `--resource-timeout` (default
|
|
17652
|
+
// 30 min) still bounds the outer call as a backstop.
|
|
17653
|
+
{ client: this.ec2Client, maxWaitTime: 15 * 60 },
|
|
17654
|
+
{ NatGatewayIds: [natGatewayId] }
|
|
17655
|
+
);
|
|
17656
|
+
this.logger.debug(`NatGateway ${natGatewayId} is available`);
|
|
17657
|
+
} else {
|
|
17658
|
+
this.logger.debug(
|
|
17659
|
+
`NatGateway ${natGatewayId} created (skipping available-state wait per --no-wait)`
|
|
17660
|
+
);
|
|
17661
|
+
}
|
|
17662
|
+
this.logger.debug(`Successfully created NatGateway ${logicalId}: ${natGatewayId}`);
|
|
17663
|
+
return {
|
|
17664
|
+
physicalId: natGatewayId,
|
|
17665
|
+
attributes: {
|
|
17666
|
+
NatGatewayId: natGatewayId
|
|
17667
|
+
}
|
|
17668
|
+
};
|
|
17669
|
+
} catch (error) {
|
|
17670
|
+
const cause = error instanceof Error ? error : void 0;
|
|
17671
|
+
throw new ProvisioningError(
|
|
17672
|
+
`Failed to create NatGateway ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
17673
|
+
resourceType,
|
|
17674
|
+
logicalId,
|
|
17675
|
+
void 0,
|
|
17676
|
+
cause
|
|
17677
|
+
);
|
|
17678
|
+
}
|
|
17679
|
+
}
|
|
17680
|
+
updateNatGateway(logicalId, physicalId) {
|
|
17681
|
+
this.logger.debug(`Updating NatGateway ${logicalId}: ${physicalId} (no-op)`);
|
|
17682
|
+
return Promise.resolve({ physicalId, wasReplaced: false });
|
|
17683
|
+
}
|
|
17684
|
+
async deleteNatGateway(logicalId, physicalId, resourceType, context) {
|
|
17685
|
+
this.logger.debug(`Deleting NatGateway ${logicalId}: ${physicalId}`);
|
|
17686
|
+
try {
|
|
17687
|
+
await this.ec2Client.send(new DeleteNatGatewayCommand({ NatGatewayId: physicalId }));
|
|
17688
|
+
} catch (error) {
|
|
17689
|
+
if (this.isNotFoundError(error)) {
|
|
17690
|
+
const clientRegion = await this.ec2Client.config.region();
|
|
17691
|
+
assertRegionMatch(
|
|
17692
|
+
clientRegion,
|
|
17693
|
+
context?.expectedRegion,
|
|
17694
|
+
resourceType,
|
|
17695
|
+
logicalId,
|
|
17696
|
+
physicalId
|
|
17697
|
+
);
|
|
17698
|
+
this.logger.debug(`NatGateway ${physicalId} does not exist, skipping deletion`);
|
|
17699
|
+
return;
|
|
17700
|
+
}
|
|
17701
|
+
const cause = error instanceof Error ? error : void 0;
|
|
17702
|
+
throw new ProvisioningError(
|
|
17703
|
+
`Failed to delete NatGateway ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
17704
|
+
resourceType,
|
|
17705
|
+
logicalId,
|
|
17706
|
+
physicalId,
|
|
17707
|
+
cause
|
|
17708
|
+
);
|
|
17709
|
+
}
|
|
17710
|
+
this.logger.debug(`Waiting for NatGateway ${physicalId} to reach deleted state...`);
|
|
17711
|
+
try {
|
|
17712
|
+
await waitUntilNatGatewayDeleted(
|
|
17713
|
+
{ client: this.ec2Client, maxWaitTime: 15 * 60 },
|
|
17714
|
+
{ NatGatewayIds: [physicalId] }
|
|
17715
|
+
);
|
|
17716
|
+
} catch (error) {
|
|
17717
|
+
this.logger.warn(
|
|
17718
|
+
`Wait for NatGateway ${physicalId} deletion did not complete cleanly: ${error instanceof Error ? error.message : String(error)} \u2014 proceeding with downstream delete steps`
|
|
17719
|
+
);
|
|
17720
|
+
}
|
|
17721
|
+
this.logger.debug(`Successfully deleted NatGateway ${logicalId}`);
|
|
17722
|
+
}
|
|
17582
17723
|
// ─── AWS::EC2::RouteTable ─────────────────────────────────────────
|
|
17583
17724
|
async createRouteTable(logicalId, resourceType, properties) {
|
|
17584
17725
|
this.logger.debug(`Creating RouteTable ${logicalId}`);
|
|
@@ -18788,6 +18929,15 @@ var EC2Provider = class {
|
|
|
18788
18929
|
const sg = resp.SecurityGroups?.[0];
|
|
18789
18930
|
return sg?.GroupId ? { physicalId: sg.GroupId, attributes: {} } : null;
|
|
18790
18931
|
}
|
|
18932
|
+
case "AWS::EC2::NatGateway": {
|
|
18933
|
+
const resp = await this.ec2Client.send(
|
|
18934
|
+
new DescribeNatGatewaysCommand({
|
|
18935
|
+
Filter: [{ Name: `tag:${CDK_PATH_TAG}`, Values: [input.cdkPath] }]
|
|
18936
|
+
})
|
|
18937
|
+
);
|
|
18938
|
+
const gw = resp.NatGateways?.find((g) => g.State !== "deleted" && g.State !== "deleting");
|
|
18939
|
+
return gw?.NatGatewayId ? { physicalId: gw.NatGatewayId, attributes: {} } : null;
|
|
18940
|
+
}
|
|
18791
18941
|
default:
|
|
18792
18942
|
return null;
|
|
18793
18943
|
}
|
|
@@ -18816,6 +18966,13 @@ var EC2Provider = class {
|
|
|
18816
18966
|
);
|
|
18817
18967
|
return resp.SecurityGroups?.[0] ? { physicalId, attributes: {} } : null;
|
|
18818
18968
|
}
|
|
18969
|
+
case "AWS::EC2::NatGateway": {
|
|
18970
|
+
const resp = await this.ec2Client.send(
|
|
18971
|
+
new DescribeNatGatewaysCommand({ NatGatewayIds: [physicalId] })
|
|
18972
|
+
);
|
|
18973
|
+
const gw = resp.NatGateways?.find((g) => g.State !== "deleted" && g.State !== "deleting");
|
|
18974
|
+
return gw ? { physicalId, attributes: {} } : null;
|
|
18975
|
+
}
|
|
18819
18976
|
default:
|
|
18820
18977
|
return null;
|
|
18821
18978
|
}
|
|
@@ -31139,6 +31296,7 @@ function registerAllProviders(registry) {
|
|
|
31139
31296
|
registry.register("AWS::EC2::Subnet", ec2Provider);
|
|
31140
31297
|
registry.register("AWS::EC2::InternetGateway", ec2Provider);
|
|
31141
31298
|
registry.register("AWS::EC2::VPCGatewayAttachment", ec2Provider);
|
|
31299
|
+
registry.register("AWS::EC2::NatGateway", ec2Provider);
|
|
31142
31300
|
registry.register("AWS::EC2::RouteTable", ec2Provider);
|
|
31143
31301
|
registry.register("AWS::EC2::Route", ec2Provider);
|
|
31144
31302
|
registry.register("AWS::EC2::SubnetRouteTableAssociation", ec2Provider);
|
|
@@ -36172,7 +36330,7 @@ function reorderArgs(argv) {
|
|
|
36172
36330
|
}
|
|
36173
36331
|
async function main() {
|
|
36174
36332
|
const program = new Command13();
|
|
36175
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
36333
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.31.0");
|
|
36176
36334
|
program.addCommand(createBootstrapCommand());
|
|
36177
36335
|
program.addCommand(createSynthCommand());
|
|
36178
36336
|
program.addCommand(createListCommand());
|