@friggframework/devtools 2.0.0--canary.428.a3d2e56.0 → 2.0.0--canary.428.95f8e10.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.
@@ -1,10 +1,10 @@
1
- let EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand;
1
+ let EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand;
2
2
  let KMSClient, ListKeysCommand, DescribeKeyCommand;
3
3
  let STSClient, GetCallerIdentityCommand;
4
4
 
5
5
  function loadEC2() {
6
6
  if (!EC2Client) {
7
- ({ EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand } = require('@aws-sdk/client-ec2'));
7
+ ({ EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, DescribeRouteTablesCommand, DescribeNatGatewaysCommand, DescribeAddressesCommand, DescribeInternetGatewaysCommand } = require('@aws-sdk/client-ec2'));
8
8
  }
9
9
  }
10
10
 
@@ -289,9 +289,9 @@ class AWSDiscovery {
289
289
  }
290
290
  ]
291
291
  });
292
-
292
+
293
293
  const response = await this.ec2Client.send(command);
294
-
294
+
295
295
  if (!response.Subnets || response.Subnets.length === 0) {
296
296
  throw new Error(`No subnets found in VPC ${vpcId}`);
297
297
  }
@@ -308,10 +308,15 @@ class AWSDiscovery {
308
308
  }
309
309
 
310
310
  if (publicSubnets.length === 0) {
311
- throw new Error(`No public subnets found in VPC ${vpcId} for NAT Gateway placement`);
311
+ // If no public subnets found, we need to create one or inform the user
312
+ console.warn(`WARNING: No public subnets found in VPC ${vpcId}`);
313
+ console.warn('A public subnet with Internet Gateway route is required for NAT Gateway placement');
314
+ console.warn('Please create a public subnet or use VPC endpoints instead');
315
+ return null; // Return null instead of throwing to allow graceful handling
312
316
  }
313
317
 
314
318
  // Return first public subnet for NAT Gateway
319
+ console.log(`Found ${publicSubnets.length} public subnets, using ${publicSubnets[0].SubnetId} for NAT Gateway`);
315
320
  return publicSubnets[0];
316
321
  } catch (error) {
317
322
  console.error('Error finding public subnets:', error);
@@ -383,27 +388,44 @@ class AWSDiscovery {
383
388
  }
384
389
  ]
385
390
  });
386
-
391
+
387
392
  const response = await this.ec2Client.send(command);
388
-
393
+
389
394
  if (response.NatGateways && response.NatGateways.length > 0) {
390
- // Find a NAT Gateway tagged for Frigg first
391
- const friggNatGateway = response.NatGateways.find(nat =>
392
- nat.Tags && nat.Tags.some(tag =>
395
+ // Check each NAT Gateway to ensure it's in a public subnet
396
+ for (const natGateway of response.NatGateways) {
397
+ const subnetId = natGateway.SubnetId;
398
+ const isPrivate = await this.isSubnetPrivate(subnetId);
399
+
400
+ if (isPrivate) {
401
+ console.warn(`WARNING: NAT Gateway ${natGateway.NatGatewayId} is in private subnet ${subnetId} - this will not work!`);
402
+ console.warn('NAT Gateways MUST be placed in public subnets with Internet Gateway routes');
403
+ console.warn('Skipping this misconfigured NAT Gateway...');
404
+ continue; // Skip this NAT Gateway
405
+ }
406
+
407
+ // Check if it's a Frigg-tagged NAT Gateway
408
+ const isFriggNat = natGateway.Tags && natGateway.Tags.some(tag =>
393
409
  tag.Key === 'Name' && tag.Value.includes('frigg')
394
- )
395
- );
396
-
397
- if (friggNatGateway) {
398
- console.log(`Found existing Frigg NAT Gateway: ${friggNatGateway.NatGatewayId}`);
399
- return friggNatGateway;
410
+ );
411
+
412
+ if (isFriggNat) {
413
+ console.log(`Found existing Frigg NAT Gateway in public subnet: ${natGateway.NatGatewayId}`);
414
+ return natGateway;
415
+ }
416
+
417
+ // Keep track of first valid NAT Gateway as fallback
418
+ console.log(`Found existing NAT Gateway in public subnet: ${natGateway.NatGatewayId}`);
419
+ return natGateway; // Return first NAT Gateway that's in a public subnet
400
420
  }
401
-
402
- // Return first available NAT Gateway if no Frigg-specific one found
403
- console.log(`Found existing NAT Gateway: ${response.NatGateways[0].NatGatewayId}`);
404
- return response.NatGateways[0];
421
+
422
+ // All NAT Gateways are in private subnets - don't use any of them
423
+ console.error(`ERROR: Found ${response.NatGateways.length} NAT Gateway(s) but all are in private subnets!`);
424
+ console.error('These NAT Gateways will not provide internet connectivity');
425
+ console.error('A new NAT Gateway will be created in a public subnet');
426
+ return null; // Return null to trigger creation of new NAT Gateway
405
427
  }
406
-
428
+
407
429
  return null;
408
430
  } catch (error) {
409
431
  console.warn('Error finding existing NAT Gateway:', error.message);
@@ -554,7 +576,7 @@ class AWSDiscovery {
554
576
  defaultSecurityGroupId: securityGroup.GroupId,
555
577
  privateSubnetId1: privateSubnets[0]?.SubnetId,
556
578
  privateSubnetId2: privateSubnets[1]?.SubnetId || privateSubnets[0]?.SubnetId,
557
- publicSubnetId: publicSubnet.SubnetId,
579
+ publicSubnetId: publicSubnet?.SubnetId || null, // May be null if no public subnet exists
558
580
  privateRouteTableId: routeTable.RouteTableId,
559
581
  defaultKmsKeyId: kmsKeyArn,
560
582
  existingNatGatewayId: natGatewayId,
@@ -565,6 +587,40 @@ class AWSDiscovery {
565
587
  throw error;
566
588
  }
567
589
  }
590
+
591
+ /**
592
+ * Find an existing Internet Gateway attached to the VPC
593
+ * @param {string} vpcId - VPC ID to search in
594
+ * @returns {Promise<Object|null>} Internet Gateway object or null if none found
595
+ */
596
+ async findInternetGateway(vpcId) {
597
+ try {
598
+ const command = new DescribeInternetGatewaysCommand({
599
+ Filters: [
600
+ {
601
+ Name: 'attachment.vpc-id',
602
+ Values: [vpcId]
603
+ },
604
+ {
605
+ Name: 'attachment.state',
606
+ Values: ['available']
607
+ }
608
+ ]
609
+ });
610
+
611
+ const response = await this.ec2Client.send(command);
612
+
613
+ if (response.InternetGateways && response.InternetGateways.length > 0) {
614
+ console.log(`Found existing Internet Gateway: ${response.InternetGateways[0].InternetGatewayId}`);
615
+ return response.InternetGateways[0];
616
+ }
617
+
618
+ return null;
619
+ } catch (error) {
620
+ console.warn('Error finding Internet Gateway:', error.message);
621
+ return null;
622
+ }
623
+ }
568
624
  }
569
625
 
570
626
  module.exports = { AWSDiscovery };
@@ -1103,6 +1103,89 @@ const composeServerlessDefinition = async (AppDefinition) => {
1103
1103
  };
1104
1104
  }
1105
1105
 
1106
+ // If no public subnet exists, create one for NAT Gateway placement
1107
+ if (!discoveredResources.publicSubnetId) {
1108
+ console.log('No public subnet found, creating one for NAT Gateway placement...');
1109
+
1110
+ // Check if Internet Gateway exists or create one
1111
+ if (!discoveredResources.internetGatewayId) {
1112
+ definition.resources.Resources.FriggInternetGateway = {
1113
+ Type: 'AWS::EC2::InternetGateway',
1114
+ Properties: {
1115
+ Tags: [
1116
+ {
1117
+ Key: 'Name',
1118
+ Value: '${self:service}-${self:provider.stage}-igw',
1119
+ },
1120
+ ],
1121
+ },
1122
+ };
1123
+
1124
+ definition.resources.Resources.FriggIGWAttachment = {
1125
+ Type: 'AWS::EC2::VPCGatewayAttachment',
1126
+ Properties: {
1127
+ VpcId: discoveredResources.defaultVpcId,
1128
+ InternetGatewayId: { Ref: 'FriggInternetGateway' },
1129
+ },
1130
+ };
1131
+ }
1132
+
1133
+ // Create a small public subnet for NAT Gateway
1134
+ definition.resources.Resources.FriggPublicSubnet = {
1135
+ Type: 'AWS::EC2::Subnet',
1136
+ Properties: {
1137
+ VpcId: discoveredResources.defaultVpcId,
1138
+ CidrBlock: '${self:custom.publicSubnetCidr, "172.31.250.0/24"}', // Small /24 subnet
1139
+ AvailabilityZone: '${self:provider.region}a',
1140
+ MapPublicIpOnLaunch: true,
1141
+ Tags: [
1142
+ {
1143
+ Key: 'Name',
1144
+ Value: '${self:service}-${self:provider.stage}-public-subnet',
1145
+ },
1146
+ {
1147
+ Key: 'Type',
1148
+ Value: 'Public',
1149
+ },
1150
+ ],
1151
+ },
1152
+ };
1153
+
1154
+ // Create route table for public subnet
1155
+ definition.resources.Resources.FriggPublicRouteTable = {
1156
+ Type: 'AWS::EC2::RouteTable',
1157
+ Properties: {
1158
+ VpcId: discoveredResources.defaultVpcId,
1159
+ Tags: [
1160
+ {
1161
+ Key: 'Name',
1162
+ Value: '${self:service}-${self:provider.stage}-public-rt',
1163
+ },
1164
+ ],
1165
+ },
1166
+ };
1167
+
1168
+ // Add route to Internet Gateway
1169
+ definition.resources.Resources.FriggPublicRoute = {
1170
+ Type: 'AWS::EC2::Route',
1171
+ DependsOn: discoveredResources.internetGatewayId ? [] : 'FriggIGWAttachment',
1172
+ Properties: {
1173
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
1174
+ DestinationCidrBlock: '0.0.0.0/0',
1175
+ GatewayId: discoveredResources.internetGatewayId || { Ref: 'FriggInternetGateway' },
1176
+ },
1177
+ };
1178
+
1179
+ // Associate public subnet with public route table
1180
+ definition.resources.Resources.FriggPublicSubnetRouteTableAssociation = {
1181
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
1182
+ Properties: {
1183
+ SubnetId: { Ref: 'FriggPublicSubnet' },
1184
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
1185
+ },
1186
+ };
1187
+ }
1188
+
1106
1189
  definition.resources.Resources.FriggNATGateway = {
1107
1190
  Type: 'AWS::EC2::NatGateway',
1108
1191
  Properties: {
@@ -1113,9 +1196,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
1113
1196
  'AllocationId',
1114
1197
  ],
1115
1198
  },
1116
- SubnetId:
1117
- discoveredResources.publicSubnetId ||
1118
- discoveredResources.privateSubnetId1, // Use first discovered subnet if no public subnet found
1199
+ SubnetId: discoveredResources.publicSubnetId || { Ref: 'FriggPublicSubnet' },
1119
1200
  Tags: [
1120
1201
  {
1121
1202
  Key: 'Name',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.428.a3d2e56.0",
4
+ "version": "2.0.0--canary.428.95f8e10.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0--canary.428.a3d2e56.0",
13
- "@friggframework/test": "2.0.0--canary.428.a3d2e56.0",
12
+ "@friggframework/schemas": "2.0.0--canary.428.95f8e10.0",
13
+ "@friggframework/test": "2.0.0--canary.428.95f8e10.0",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,8 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0--canary.428.a3d2e56.0",
36
- "@friggframework/prettier-config": "2.0.0--canary.428.a3d2e56.0",
35
+ "@friggframework/eslint-config": "2.0.0--canary.428.95f8e10.0",
36
+ "@friggframework/prettier-config": "2.0.0--canary.428.95f8e10.0",
37
37
  "jest": "^30.1.3",
38
38
  "prettier": "^2.7.1",
39
39
  "serverless": "3.39.0",
@@ -66,5 +66,5 @@
66
66
  "publishConfig": {
67
67
  "access": "public"
68
68
  },
69
- "gitHead": "a3d2e56038d2324c64df71c6ed49ff8e3d57873e"
69
+ "gitHead": "95f8e103995bc1c11331a44e49b232dfdd2e5604"
70
70
  }