@friggframework/devtools 2.0.0--canary.398.a2fbc38.0 → 2.0.0--canary.398.90b58c8.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/frigg-cli/build-command/index.js +1 -22
- package/frigg-cli/deploy-command/index.js +1 -22
- package/infrastructure/aws-discovery.js +54 -10
- package/infrastructure/create-frigg-infrastructure.js +2 -2
- package/infrastructure/run-discovery.js +3 -1
- package/infrastructure/serverless-template.js +70 -13
- package/package.json +5 -5
|
@@ -4,28 +4,7 @@ const path = require('path');
|
|
|
4
4
|
async function buildCommand(options) {
|
|
5
5
|
console.log('Building the serverless application...');
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
try {
|
|
9
|
-
const discoveryPath = path.resolve(__dirname, '../../infrastructure/run-discovery.js');
|
|
10
|
-
console.log('🔍 Running AWS resource discovery...');
|
|
11
|
-
|
|
12
|
-
const discoveryResult = spawnSync('node', [discoveryPath], {
|
|
13
|
-
stdio: 'inherit',
|
|
14
|
-
shell: true,
|
|
15
|
-
env: process.env
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
if (discoveryResult.status !== 0) {
|
|
19
|
-
console.error('❌ AWS discovery failed. Build cannot continue.');
|
|
20
|
-
console.error(' See error messages above for troubleshooting steps.');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.error('❌ Could not run AWS discovery:', error.message);
|
|
25
|
-
console.error(' This may indicate a configuration or dependency issue.');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
7
|
+
// AWS discovery is now handled directly in serverless-template.js
|
|
29
8
|
console.log('📦 Packaging serverless application...');
|
|
30
9
|
const backendPath = path.resolve(process.cwd());
|
|
31
10
|
const infrastructurePath = 'infrastructure.js';
|
|
@@ -4,28 +4,7 @@ const path = require('path');
|
|
|
4
4
|
async function deployCommand(options) {
|
|
5
5
|
console.log('Deploying the serverless application...');
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
try {
|
|
9
|
-
const discoveryPath = path.resolve(__dirname, '../../infrastructure/run-discovery.js');
|
|
10
|
-
console.log('🔍 Running AWS resource discovery...');
|
|
11
|
-
|
|
12
|
-
const discoveryResult = spawnSync('node', [discoveryPath], {
|
|
13
|
-
stdio: 'inherit',
|
|
14
|
-
shell: true,
|
|
15
|
-
env: process.env
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
if (discoveryResult.status !== 0) {
|
|
19
|
-
console.error('❌ AWS discovery failed. Deployment cannot continue.');
|
|
20
|
-
console.error(' See error messages above for troubleshooting steps.');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.error('❌ Could not run AWS discovery:', error.message);
|
|
25
|
-
console.error(' This may indicate a configuration or dependency issue.');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
7
|
+
// AWS discovery is now handled directly in serverless-template.js
|
|
29
8
|
console.log('🚀 Deploying serverless application...');
|
|
30
9
|
const backendPath = path.resolve(process.cwd());
|
|
31
10
|
const infrastructurePath = 'infrastructure.js';
|
|
@@ -149,27 +149,71 @@ class AWSDiscovery {
|
|
|
149
149
|
*/
|
|
150
150
|
async isSubnetPrivate(subnetId) {
|
|
151
151
|
try {
|
|
152
|
-
|
|
152
|
+
// First, get the subnet details to find its VPC
|
|
153
|
+
const subnetCommand = new DescribeSubnetsCommand({
|
|
154
|
+
SubnetIds: [subnetId]
|
|
155
|
+
});
|
|
156
|
+
const subnetResponse = await this.ec2Client.send(subnetCommand);
|
|
157
|
+
|
|
158
|
+
if (!subnetResponse.Subnets || subnetResponse.Subnets.length === 0) {
|
|
159
|
+
throw new Error(`Subnet ${subnetId} not found`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const subnet = subnetResponse.Subnets[0];
|
|
163
|
+
const vpcId = subnet.VpcId;
|
|
164
|
+
|
|
165
|
+
// Get all route tables for this VPC
|
|
166
|
+
const routeTablesCommand = new DescribeRouteTablesCommand({
|
|
153
167
|
Filters: [
|
|
154
168
|
{
|
|
155
|
-
Name: '
|
|
156
|
-
Values: [
|
|
169
|
+
Name: 'vpc-id',
|
|
170
|
+
Values: [vpcId]
|
|
157
171
|
}
|
|
158
172
|
]
|
|
159
173
|
});
|
|
160
174
|
|
|
161
|
-
const
|
|
175
|
+
const routeTablesResponse = await this.ec2Client.send(routeTablesCommand);
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
// Find the route table for this subnet
|
|
178
|
+
let routeTable = null;
|
|
179
|
+
|
|
180
|
+
// First check for explicit association
|
|
181
|
+
for (const rt of routeTablesResponse.RouteTables || []) {
|
|
182
|
+
for (const assoc of rt.Associations || []) {
|
|
183
|
+
if (assoc.SubnetId === subnetId) {
|
|
184
|
+
routeTable = rt;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (routeTable) break;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// If no explicit association, use the main route table
|
|
192
|
+
if (!routeTable) {
|
|
193
|
+
for (const rt of routeTablesResponse.RouteTables || []) {
|
|
194
|
+
for (const assoc of rt.Associations || []) {
|
|
195
|
+
if (assoc.Main === true) {
|
|
196
|
+
routeTable = rt;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
168
199
|
}
|
|
200
|
+
if (routeTable) break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!routeTable) {
|
|
205
|
+
console.warn(`No route table found for subnet ${subnetId}`);
|
|
206
|
+
return true; // Default to private for safety
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Check if route table has a route to an Internet Gateway
|
|
210
|
+
for (const route of routeTable.Routes || []) {
|
|
211
|
+
if (route.GatewayId && route.GatewayId.startsWith('igw-')) {
|
|
212
|
+
return false; // It's a public subnet
|
|
169
213
|
}
|
|
170
214
|
}
|
|
171
215
|
|
|
172
|
-
return true; // No IGW route found,
|
|
216
|
+
return true; // No IGW route found, it's private
|
|
173
217
|
} catch (error) {
|
|
174
218
|
console.warn(`Could not determine if subnet ${subnetId} is private:`, error);
|
|
175
219
|
return true; // Default to private for safety
|
|
@@ -4,7 +4,7 @@ const { composeServerlessDefinition } = require('./serverless-template');
|
|
|
4
4
|
|
|
5
5
|
const { findNearestBackendPackageJson } = require('@friggframework/core');
|
|
6
6
|
|
|
7
|
-
function createFriggInfrastructure() {
|
|
7
|
+
async function createFriggInfrastructure() {
|
|
8
8
|
const backendPath = findNearestBackendPackageJson();
|
|
9
9
|
if (!backendPath) {
|
|
10
10
|
throw new Error('Could not find backend package.json');
|
|
@@ -23,7 +23,7 @@ function createFriggInfrastructure() {
|
|
|
23
23
|
// __dirname,
|
|
24
24
|
// './serverless-template.js'
|
|
25
25
|
// ));
|
|
26
|
-
const definition = composeServerlessDefinition(
|
|
26
|
+
const definition = await composeServerlessDefinition(
|
|
27
27
|
appDefinition,
|
|
28
28
|
backend.IntegrationFactory
|
|
29
29
|
);
|
|
@@ -10,6 +10,8 @@ const { findNearestBackendPackageJson } = require('@friggframework/core');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
12
|
async function runDiscovery() {
|
|
13
|
+
let appDefinition;
|
|
14
|
+
|
|
13
15
|
try {
|
|
14
16
|
console.log('🔍 Starting AWS resource discovery...');
|
|
15
17
|
|
|
@@ -30,7 +32,7 @@ async function runDiscovery() {
|
|
|
30
32
|
|
|
31
33
|
// Load the app definition
|
|
32
34
|
const backend = require(backendFilePath);
|
|
33
|
-
|
|
35
|
+
appDefinition = backend.Definition;
|
|
34
36
|
|
|
35
37
|
if (!appDefinition) {
|
|
36
38
|
console.log('⚠️ No Definition found in backend/index.js, skipping discovery');
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
+
const { AWSDiscovery } = require('./aws-discovery');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if AWS discovery should run based on AppDefinition
|
|
7
|
+
* @param {Object} AppDefinition - Application definition
|
|
8
|
+
* @returns {boolean} True if discovery should run
|
|
9
|
+
*/
|
|
10
|
+
const shouldRunDiscovery = (AppDefinition) => {
|
|
11
|
+
return AppDefinition.vpc?.enable === true ||
|
|
12
|
+
AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true ||
|
|
13
|
+
AppDefinition.ssm?.enable === true;
|
|
14
|
+
};
|
|
3
15
|
|
|
4
16
|
/**
|
|
5
17
|
* Find the actual path to node_modules directory
|
|
@@ -438,7 +450,44 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
438
450
|
* @param {boolean} [AppDefinition.websockets.enable=false] - Enable WebSocket support for live update streaming
|
|
439
451
|
* @returns {Object} Complete serverless framework configuration
|
|
440
452
|
*/
|
|
441
|
-
const composeServerlessDefinition = (AppDefinition) => {
|
|
453
|
+
const composeServerlessDefinition = async (AppDefinition) => {
|
|
454
|
+
// Store discovered resources
|
|
455
|
+
let discoveredResources = {};
|
|
456
|
+
|
|
457
|
+
// Run AWS discovery if needed
|
|
458
|
+
if (shouldRunDiscovery(AppDefinition)) {
|
|
459
|
+
console.log('🔍 Running AWS resource discovery for serverless template...');
|
|
460
|
+
try {
|
|
461
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
462
|
+
const discovery = new AWSDiscovery(region);
|
|
463
|
+
|
|
464
|
+
const config = {
|
|
465
|
+
vpc: AppDefinition.vpc || {},
|
|
466
|
+
encryption: AppDefinition.encryption || {},
|
|
467
|
+
ssm: AppDefinition.ssm || {}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
discoveredResources = await discovery.discoverResources(config);
|
|
471
|
+
|
|
472
|
+
console.log('✅ AWS discovery completed successfully!');
|
|
473
|
+
if (discoveredResources.defaultVpcId) {
|
|
474
|
+
console.log(` VPC: ${discoveredResources.defaultVpcId}`);
|
|
475
|
+
}
|
|
476
|
+
if (discoveredResources.privateSubnetIds) {
|
|
477
|
+
console.log(` Subnets: ${discoveredResources.privateSubnetIds.join(', ')}`);
|
|
478
|
+
}
|
|
479
|
+
if (discoveredResources.defaultSecurityGroupId) {
|
|
480
|
+
console.log(` Security Group: ${discoveredResources.defaultSecurityGroupId}`);
|
|
481
|
+
}
|
|
482
|
+
if (discoveredResources.defaultKmsKeyId) {
|
|
483
|
+
console.log(` KMS Key: ${discoveredResources.defaultKmsKeyId}`);
|
|
484
|
+
}
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error('❌ AWS discovery failed:', error.message);
|
|
487
|
+
throw new Error(`AWS discovery failed: ${error.message}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
442
491
|
const definition = {
|
|
443
492
|
frameworkVersion: '>=3.17.0',
|
|
444
493
|
service: AppDefinition.name || 'create-frigg-app',
|
|
@@ -456,6 +505,14 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
456
505
|
environment: {
|
|
457
506
|
STAGE: '${opt:stage}',
|
|
458
507
|
AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
|
|
508
|
+
// Add discovered resources to environment if available
|
|
509
|
+
...(discoveredResources.defaultVpcId && { AWS_DISCOVERY_VPC_ID: discoveredResources.defaultVpcId }),
|
|
510
|
+
...(discoveredResources.defaultSecurityGroupId && { AWS_DISCOVERY_SECURITY_GROUP_ID: discoveredResources.defaultSecurityGroupId }),
|
|
511
|
+
...(discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds[0] && { AWS_DISCOVERY_SUBNET_ID_1: discoveredResources.privateSubnetIds[0] }),
|
|
512
|
+
...(discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds[1] && { AWS_DISCOVERY_SUBNET_ID_2: discoveredResources.privateSubnetIds[1] }),
|
|
513
|
+
...(discoveredResources.publicSubnetId && { AWS_DISCOVERY_PUBLIC_SUBNET_ID: discoveredResources.publicSubnetId }),
|
|
514
|
+
...(discoveredResources.defaultRouteTableId && { AWS_DISCOVERY_ROUTE_TABLE_ID: discoveredResources.defaultRouteTableId }),
|
|
515
|
+
...(discoveredResources.defaultKmsKeyId && { AWS_DISCOVERY_KMS_KEY_ID: discoveredResources.defaultKmsKeyId }),
|
|
459
516
|
},
|
|
460
517
|
iamRoleStatements: [
|
|
461
518
|
{
|
|
@@ -666,7 +723,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
666
723
|
|
|
667
724
|
// Configure KMS grants with discovered default key
|
|
668
725
|
definition.custom.kmsGrants = {
|
|
669
|
-
kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
|
|
726
|
+
kmsKeyId: discoveredResources.defaultKmsKeyId || '${env:AWS_DISCOVERY_KMS_KEY_ID}'
|
|
670
727
|
};
|
|
671
728
|
}
|
|
672
729
|
|
|
@@ -720,11 +777,11 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
720
777
|
// VPC configuration using discovered or explicitly provided resources
|
|
721
778
|
const vpcConfig = {
|
|
722
779
|
securityGroupIds: AppDefinition.vpc.securityGroupIds ||
|
|
723
|
-
['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
|
|
724
|
-
subnetIds: AppDefinition.vpc.subnetIds ||
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
780
|
+
(discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}']),
|
|
781
|
+
subnetIds: AppDefinition.vpc.subnetIds ||
|
|
782
|
+
(discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds.length >= 2 ?
|
|
783
|
+
[discoveredResources.privateSubnetIds[0], discoveredResources.privateSubnetIds[1]] :
|
|
784
|
+
['${env:AWS_DISCOVERY_SUBNET_ID_1}', '${env:AWS_DISCOVERY_SUBNET_ID_2}'])
|
|
728
785
|
};
|
|
729
786
|
|
|
730
787
|
// Set VPC config for Lambda functions
|
|
@@ -746,7 +803,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
746
803
|
Type: 'AWS::EC2::NatGateway',
|
|
747
804
|
Properties: {
|
|
748
805
|
AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
|
|
749
|
-
SubnetId: '${env:AWS_DISCOVERY_PUBLIC_SUBNET_ID}', // Discovery finds public subnet
|
|
806
|
+
SubnetId: discoveredResources.publicSubnetId || '${env:AWS_DISCOVERY_PUBLIC_SUBNET_ID}', // Discovery finds public subnet
|
|
750
807
|
Tags: [
|
|
751
808
|
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' }
|
|
752
809
|
]
|
|
@@ -757,7 +814,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
757
814
|
definition.resources.Resources.FriggLambdaRouteTable = {
|
|
758
815
|
Type: 'AWS::EC2::RouteTable',
|
|
759
816
|
Properties: {
|
|
760
|
-
VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
|
|
817
|
+
VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
|
|
761
818
|
Tags: [
|
|
762
819
|
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' }
|
|
763
820
|
]
|
|
@@ -777,7 +834,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
777
834
|
definition.resources.Resources.FriggSubnet1RouteAssociation = {
|
|
778
835
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
779
836
|
Properties: {
|
|
780
|
-
SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_1}',
|
|
837
|
+
SubnetId: discoveredResources.privateSubnetIds?.[0] || '${env:AWS_DISCOVERY_SUBNET_ID_1}',
|
|
781
838
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
782
839
|
}
|
|
783
840
|
};
|
|
@@ -785,7 +842,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
785
842
|
definition.resources.Resources.FriggSubnet2RouteAssociation = {
|
|
786
843
|
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
787
844
|
Properties: {
|
|
788
|
-
SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_2}',
|
|
845
|
+
SubnetId: discoveredResources.privateSubnetIds?.[1] || '${env:AWS_DISCOVERY_SUBNET_ID_2}',
|
|
789
846
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
790
847
|
}
|
|
791
848
|
};
|
|
@@ -795,7 +852,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
795
852
|
definition.resources.Resources.VPCEndpointS3 = {
|
|
796
853
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
797
854
|
Properties: {
|
|
798
|
-
VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
|
|
855
|
+
VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
|
|
799
856
|
ServiceName: 'com.amazonaws.${self:provider.region}.s3',
|
|
800
857
|
VpcEndpointType: 'Gateway',
|
|
801
858
|
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
|
|
@@ -805,7 +862,7 @@ const composeServerlessDefinition = (AppDefinition) => {
|
|
|
805
862
|
definition.resources.Resources.VPCEndpointDynamoDB = {
|
|
806
863
|
Type: 'AWS::EC2::VPCEndpoint',
|
|
807
864
|
Properties: {
|
|
808
|
-
VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
|
|
865
|
+
VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
|
|
809
866
|
ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
|
|
810
867
|
VpcEndpointType: 'Gateway',
|
|
811
868
|
RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
|
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.398.
|
|
4
|
+
"version": "2.0.0--canary.398.90b58c8.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.835.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.835.0",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"@babel/eslint-parser": "^7.18.9",
|
|
10
10
|
"@babel/parser": "^7.25.3",
|
|
11
11
|
"@babel/traverse": "^7.25.3",
|
|
12
|
-
"@friggframework/test": "2.0.0--canary.398.
|
|
12
|
+
"@friggframework/test": "2.0.0--canary.398.90b58c8.0",
|
|
13
13
|
"@hapi/boom": "^10.0.1",
|
|
14
14
|
"@inquirer/prompts": "^5.3.8",
|
|
15
15
|
"axios": "^1.7.2",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"serverless-http": "^2.7.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@friggframework/eslint-config": "2.0.0--canary.398.
|
|
35
|
-
"@friggframework/prettier-config": "2.0.0--canary.398.
|
|
34
|
+
"@friggframework/eslint-config": "2.0.0--canary.398.90b58c8.0",
|
|
35
|
+
"@friggframework/prettier-config": "2.0.0--canary.398.90b58c8.0",
|
|
36
36
|
"prettier": "^2.7.1",
|
|
37
37
|
"serverless": "3.39.0",
|
|
38
38
|
"serverless-dotenv-plugin": "^6.0.0",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "90b58c8e19b569bca169038b097a8b7358ca3268"
|
|
68
68
|
}
|