@friggframework/devtools 2.0.0--canary.428.3bab734.0 → 2.0.0--canary.428.08c3f6f.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.
|
@@ -60,15 +60,13 @@ const getAppEnvironmentVars = (AppDefinition) => {
|
|
|
60
60
|
|
|
61
61
|
if (envKeys.length > 0) {
|
|
62
62
|
console.log(
|
|
63
|
-
` Found ${
|
|
64
|
-
envKeys.length
|
|
63
|
+
` Found ${envKeys.length
|
|
65
64
|
} environment variables: ${envKeys.join(', ')}`
|
|
66
65
|
);
|
|
67
66
|
}
|
|
68
67
|
if (skippedKeys.length > 0) {
|
|
69
68
|
console.log(
|
|
70
|
-
` ⚠️ Skipped ${
|
|
71
|
-
skippedKeys.length
|
|
69
|
+
` ⚠️ Skipped ${skippedKeys.length
|
|
72
70
|
} reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
|
|
73
71
|
);
|
|
74
72
|
}
|
|
@@ -231,6 +229,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
231
229
|
Key: 'Name',
|
|
232
230
|
Value: '${self:service}-${self:provider.stage}-vpc',
|
|
233
231
|
},
|
|
232
|
+
{
|
|
233
|
+
Key: 'ManagedBy',
|
|
234
|
+
Value: 'Frigg',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
Key: 'Service',
|
|
238
|
+
Value: '${self:service}',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
Key: 'Stage',
|
|
242
|
+
Value: '${self:provider.stage}',
|
|
243
|
+
},
|
|
234
244
|
],
|
|
235
245
|
},
|
|
236
246
|
},
|
|
@@ -244,6 +254,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
244
254
|
Key: 'Name',
|
|
245
255
|
Value: '${self:service}-${self:provider.stage}-igw',
|
|
246
256
|
},
|
|
257
|
+
{
|
|
258
|
+
Key: 'ManagedBy',
|
|
259
|
+
Value: 'Frigg',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
Key: 'Service',
|
|
263
|
+
Value: '${self:service}',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
Key: 'Stage',
|
|
267
|
+
Value: '${self:provider.stage}',
|
|
268
|
+
},
|
|
247
269
|
],
|
|
248
270
|
},
|
|
249
271
|
},
|
|
@@ -270,6 +292,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
270
292
|
Key: 'Name',
|
|
271
293
|
Value: '${self:service}-${self:provider.stage}-public-subnet',
|
|
272
294
|
},
|
|
295
|
+
{
|
|
296
|
+
Key: 'ManagedBy',
|
|
297
|
+
Value: 'Frigg',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
Key: 'Service',
|
|
301
|
+
Value: '${self:service}',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
Key: 'Stage',
|
|
305
|
+
Value: '${self:provider.stage}',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
Key: 'Type',
|
|
309
|
+
Value: 'Public',
|
|
310
|
+
},
|
|
273
311
|
],
|
|
274
312
|
},
|
|
275
313
|
},
|
|
@@ -286,6 +324,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
286
324
|
Key: 'Name',
|
|
287
325
|
Value: '${self:service}-${self:provider.stage}-private-subnet-1',
|
|
288
326
|
},
|
|
327
|
+
{
|
|
328
|
+
Key: 'ManagedBy',
|
|
329
|
+
Value: 'Frigg',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
Key: 'Service',
|
|
333
|
+
Value: '${self:service}',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
Key: 'Stage',
|
|
337
|
+
Value: '${self:provider.stage}',
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
Key: 'Type',
|
|
341
|
+
Value: 'Private',
|
|
342
|
+
},
|
|
289
343
|
],
|
|
290
344
|
},
|
|
291
345
|
},
|
|
@@ -302,6 +356,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
302
356
|
Key: 'Name',
|
|
303
357
|
Value: '${self:service}-${self:provider.stage}-private-subnet-2',
|
|
304
358
|
},
|
|
359
|
+
{
|
|
360
|
+
Key: 'ManagedBy',
|
|
361
|
+
Value: 'Frigg',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
Key: 'Service',
|
|
365
|
+
Value: '${self:service}',
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
Key: 'Stage',
|
|
369
|
+
Value: '${self:provider.stage}',
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
Key: 'Type',
|
|
373
|
+
Value: 'Private',
|
|
374
|
+
},
|
|
305
375
|
],
|
|
306
376
|
},
|
|
307
377
|
},
|
|
@@ -316,6 +386,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
316
386
|
Key: 'Name',
|
|
317
387
|
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
318
388
|
},
|
|
389
|
+
{
|
|
390
|
+
Key: 'ManagedBy',
|
|
391
|
+
Value: 'Frigg',
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
Key: 'Service',
|
|
395
|
+
Value: '${self:service}',
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
Key: 'Stage',
|
|
399
|
+
Value: '${self:provider.stage}',
|
|
400
|
+
},
|
|
319
401
|
],
|
|
320
402
|
},
|
|
321
403
|
DependsOn: 'FriggVPCGatewayAttachment',
|
|
@@ -334,6 +416,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
334
416
|
Key: 'Name',
|
|
335
417
|
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
336
418
|
},
|
|
419
|
+
{
|
|
420
|
+
Key: 'ManagedBy',
|
|
421
|
+
Value: 'Frigg',
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
Key: 'Service',
|
|
425
|
+
Value: '${self:service}',
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
Key: 'Stage',
|
|
429
|
+
Value: '${self:provider.stage}',
|
|
430
|
+
},
|
|
337
431
|
],
|
|
338
432
|
},
|
|
339
433
|
},
|
|
@@ -348,6 +442,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
348
442
|
Key: 'Name',
|
|
349
443
|
Value: '${self:service}-${self:provider.stage}-public-rt',
|
|
350
444
|
},
|
|
445
|
+
{
|
|
446
|
+
Key: 'ManagedBy',
|
|
447
|
+
Value: 'Frigg',
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
Key: 'Service',
|
|
451
|
+
Value: '${self:service}',
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
Key: 'Stage',
|
|
455
|
+
Value: '${self:provider.stage}',
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
Key: 'Type',
|
|
459
|
+
Value: 'Public',
|
|
460
|
+
},
|
|
351
461
|
],
|
|
352
462
|
},
|
|
353
463
|
},
|
|
@@ -382,6 +492,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
382
492
|
Key: 'Name',
|
|
383
493
|
Value: '${self:service}-${self:provider.stage}-private-rt',
|
|
384
494
|
},
|
|
495
|
+
{
|
|
496
|
+
Key: 'ManagedBy',
|
|
497
|
+
Value: 'Frigg',
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
Key: 'Service',
|
|
501
|
+
Value: '${self:service}',
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
Key: 'Stage',
|
|
505
|
+
Value: '${self:provider.stage}',
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
Key: 'Type',
|
|
509
|
+
Value: 'Private',
|
|
510
|
+
},
|
|
385
511
|
],
|
|
386
512
|
},
|
|
387
513
|
},
|
|
@@ -462,6 +588,18 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
462
588
|
Key: 'Name',
|
|
463
589
|
Value: '${self:service}-${self:provider.stage}-lambda-sg',
|
|
464
590
|
},
|
|
591
|
+
{
|
|
592
|
+
Key: 'ManagedBy',
|
|
593
|
+
Value: 'Frigg',
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
Key: 'Service',
|
|
597
|
+
Value: '${self:service}',
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
Key: 'Stage',
|
|
601
|
+
Value: '${self:provider.stage}',
|
|
602
|
+
},
|
|
465
603
|
],
|
|
466
604
|
},
|
|
467
605
|
},
|
|
@@ -550,6 +688,22 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
550
688
|
Key: 'Name',
|
|
551
689
|
Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg',
|
|
552
690
|
},
|
|
691
|
+
{
|
|
692
|
+
Key: 'ManagedBy',
|
|
693
|
+
Value: 'Frigg',
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
Key: 'Service',
|
|
697
|
+
Value: '${self:service}',
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
Key: 'Stage',
|
|
701
|
+
Value: '${self:provider.stage}',
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
Key: 'Type',
|
|
705
|
+
Value: 'VPCEndpoint',
|
|
706
|
+
},
|
|
553
707
|
],
|
|
554
708
|
},
|
|
555
709
|
};
|
|
@@ -572,6 +726,7 @@ const createVPCInfrastructure = (AppDefinition) => {
|
|
|
572
726
|
* @returns {Object} Complete serverless framework configuration
|
|
573
727
|
*/
|
|
574
728
|
const composeServerlessDefinition = async (AppDefinition) => {
|
|
729
|
+
console.log('composeServerlessDefinition', AppDefinition);
|
|
575
730
|
// Store discovered resources
|
|
576
731
|
let discoveredResources = {};
|
|
577
732
|
|
|
@@ -942,10 +1097,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
942
1097
|
Resource: '*',
|
|
943
1098
|
Condition: {
|
|
944
1099
|
StringEquals: {
|
|
945
|
-
'kms:ViaService': `lambda.${
|
|
946
|
-
process.env.AWS_REGION ||
|
|
1100
|
+
'kms:ViaService': `lambda.${process.env.AWS_REGION ||
|
|
947
1101
|
'us-east-1'
|
|
948
|
-
|
|
1102
|
+
}.amazonaws.com`,
|
|
949
1103
|
},
|
|
950
1104
|
},
|
|
951
1105
|
},
|
|
@@ -982,7 +1136,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
982
1136
|
// No key found and createIfNoneFound is not enabled - error
|
|
983
1137
|
throw new Error(
|
|
984
1138
|
'KMS field-level encryption is enabled but no KMS key was found. ' +
|
|
985
|
-
|
|
1139
|
+
'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
|
|
986
1140
|
);
|
|
987
1141
|
}
|
|
988
1142
|
}
|
|
@@ -1009,6 +1163,100 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1009
1163
|
}
|
|
1010
1164
|
}
|
|
1011
1165
|
|
|
1166
|
+
/**
|
|
1167
|
+
* Heals VPC configuration issues by fixing common misconfigurations
|
|
1168
|
+
* @param {Object} discoveredResources - Resources discovered from AWS
|
|
1169
|
+
* @param {Object} AppDefinition - Application definition with VPC settings
|
|
1170
|
+
* @returns {Object} Healing report with actions taken and recommendations
|
|
1171
|
+
*/
|
|
1172
|
+
const healVPCConfiguration = (discoveredResources, AppDefinition) => {
|
|
1173
|
+
const healingReport = {
|
|
1174
|
+
healed: [],
|
|
1175
|
+
warnings: [],
|
|
1176
|
+
errors: [],
|
|
1177
|
+
recommendations: []
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
// Only heal if selfHeal is explicitly enabled
|
|
1181
|
+
if (!AppDefinition.vpc?.selfHeal) {
|
|
1182
|
+
return healingReport;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
console.log('🔧 Self-healing mode enabled - checking for VPC misconfigurations...');
|
|
1186
|
+
|
|
1187
|
+
// Check NAT Gateway placement
|
|
1188
|
+
if (discoveredResources.natGatewayInPrivateSubnet) {
|
|
1189
|
+
healingReport.warnings.push(
|
|
1190
|
+
`NAT Gateway ${discoveredResources.natGatewayInPrivateSubnet} is in a private subnet`
|
|
1191
|
+
);
|
|
1192
|
+
healingReport.recommendations.push(
|
|
1193
|
+
'NAT Gateway should be recreated in a public subnet for proper internet connectivity'
|
|
1194
|
+
);
|
|
1195
|
+
|
|
1196
|
+
// Mark that we need to create a new NAT Gateway
|
|
1197
|
+
discoveredResources.needsNewNatGateway = true;
|
|
1198
|
+
healingReport.healed.push('Marked NAT Gateway for recreation in public subnet');
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// Check if EIP is already associated
|
|
1202
|
+
if (discoveredResources.elasticIpAlreadyAssociated) {
|
|
1203
|
+
healingReport.warnings.push(
|
|
1204
|
+
`Elastic IP ${discoveredResources.existingElasticIp} is already associated`
|
|
1205
|
+
);
|
|
1206
|
+
|
|
1207
|
+
// In self-heal mode, we'll try to reuse or create a new one
|
|
1208
|
+
if (discoveredResources.existingNatGatewayId) {
|
|
1209
|
+
healingReport.healed.push(
|
|
1210
|
+
'Will reuse existing NAT Gateway instead of creating a new one'
|
|
1211
|
+
);
|
|
1212
|
+
discoveredResources.reuseExistingNatGateway = true;
|
|
1213
|
+
} else {
|
|
1214
|
+
healingReport.healed.push(
|
|
1215
|
+
'Will allocate a new Elastic IP for NAT Gateway'
|
|
1216
|
+
);
|
|
1217
|
+
discoveredResources.allocateNewElasticIp = true;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Check route table associations
|
|
1222
|
+
if (discoveredResources.privateSubnetsWithWrongRoutes) {
|
|
1223
|
+
healingReport.warnings.push(
|
|
1224
|
+
`Found ${discoveredResources.privateSubnetsWithWrongRoutes.length} private subnets with incorrect routes`
|
|
1225
|
+
);
|
|
1226
|
+
healingReport.healed.push(
|
|
1227
|
+
'Route tables will be corrected during deployment'
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// Check for orphaned resources
|
|
1232
|
+
if (discoveredResources.orphanedElasticIps?.length > 0) {
|
|
1233
|
+
healingReport.warnings.push(
|
|
1234
|
+
`Found ${discoveredResources.orphanedElasticIps.length} orphaned Elastic IPs`
|
|
1235
|
+
);
|
|
1236
|
+
healingReport.recommendations.push(
|
|
1237
|
+
'Consider releasing orphaned Elastic IPs to avoid charges'
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// Log healing report
|
|
1242
|
+
if (healingReport.healed.length > 0) {
|
|
1243
|
+
console.log('✅ Self-healing actions:');
|
|
1244
|
+
healingReport.healed.forEach(action => console.log(` - ${action}`));
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
if (healingReport.warnings.length > 0) {
|
|
1248
|
+
console.log('⚠️ Issues detected:');
|
|
1249
|
+
healingReport.warnings.forEach(warning => console.log(` - ${warning}`));
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if (healingReport.recommendations.length > 0) {
|
|
1253
|
+
console.log('💡 Recommendations:');
|
|
1254
|
+
healingReport.recommendations.forEach(rec => console.log(` - ${rec}`));
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return healingReport;
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1012
1260
|
// VPC Configuration based on App Definition
|
|
1013
1261
|
if (AppDefinition.vpc?.enable === true) {
|
|
1014
1262
|
// Add VPC-related IAM permissions
|
|
@@ -1024,103 +1272,383 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1024
1272
|
Resource: '*',
|
|
1025
1273
|
});
|
|
1026
1274
|
|
|
1027
|
-
//
|
|
1028
|
-
if (
|
|
1029
|
-
|
|
1030
|
-
const vpcConfig = {};
|
|
1275
|
+
// Run healing if enabled and we have discovered resources
|
|
1276
|
+
if (discoveredResources && Object.keys(discoveredResources).length > 0) {
|
|
1277
|
+
const healingReport = healVPCConfiguration(discoveredResources, AppDefinition);
|
|
1031
1278
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
} else {
|
|
1036
|
-
// Use auto-created security group
|
|
1037
|
-
vpcConfig.securityGroupIds = [
|
|
1038
|
-
{ Ref: 'FriggLambdaSecurityGroup' },
|
|
1039
|
-
];
|
|
1279
|
+
// If healing failed critically, throw an error unless selfHeal is true
|
|
1280
|
+
if (healingReport.errors.length > 0 && !AppDefinition.vpc?.selfHeal) {
|
|
1281
|
+
throw new Error(`VPC configuration errors detected: ${healingReport.errors.join(', ')}`);
|
|
1040
1282
|
}
|
|
1283
|
+
}
|
|
1041
1284
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
{ Ref: 'FriggPrivateSubnet2' },
|
|
1050
|
-
];
|
|
1051
|
-
}
|
|
1285
|
+
// STEP 1: Determine VPC (create, discover, or use existing)
|
|
1286
|
+
const vpcManagement = AppDefinition.vpc.management || 'discover';
|
|
1287
|
+
let vpcId = null;
|
|
1288
|
+
let vpcConfig = {
|
|
1289
|
+
securityGroupIds: [],
|
|
1290
|
+
subnetIds: []
|
|
1291
|
+
};
|
|
1052
1292
|
|
|
1053
|
-
|
|
1054
|
-
definition.provider.vpc = vpcConfig;
|
|
1293
|
+
console.log(`VPC Management Mode: ${vpcManagement}`);
|
|
1055
1294
|
|
|
1056
|
-
|
|
1295
|
+
// First, establish VPC context
|
|
1296
|
+
if (vpcManagement === 'create-new') {
|
|
1297
|
+
// Create new VPC infrastructure
|
|
1057
1298
|
const vpcResources = createVPCInfrastructure(AppDefinition);
|
|
1058
1299
|
Object.assign(definition.resources.Resources, vpcResources);
|
|
1300
|
+
vpcId = { Ref: 'FriggVPC' }; // Reference to created VPC
|
|
1301
|
+
|
|
1302
|
+
// Default security group for new VPC
|
|
1303
|
+
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds || [
|
|
1304
|
+
{ Ref: 'FriggLambdaSecurityGroup' }
|
|
1305
|
+
];
|
|
1306
|
+
} else if (vpcManagement === 'use-existing') {
|
|
1307
|
+
// Use explicitly provided VPC
|
|
1308
|
+
if (!AppDefinition.vpc.vpcId) {
|
|
1309
|
+
throw new Error('VPC management is set to "use-existing" but no vpcId was provided');
|
|
1310
|
+
}
|
|
1311
|
+
vpcId = AppDefinition.vpc.vpcId;
|
|
1312
|
+
// Use provided security groups or try to discover default security group for the VPC
|
|
1313
|
+
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds ||
|
|
1314
|
+
(discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : []);
|
|
1059
1315
|
} else {
|
|
1060
|
-
//
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1316
|
+
// Discover VPC
|
|
1317
|
+
if (!discoveredResources.defaultVpcId) {
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
'VPC discovery failed: No VPC found. ' +
|
|
1320
|
+
'Either set vpc.management to "create-new" or provide vpc.vpcId with "use-existing".'
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
vpcId = discoveredResources.defaultVpcId;
|
|
1324
|
+
vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds ||
|
|
1325
|
+
(discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : []);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// STEP 2: Handle Subnet Management (independent of VPC management)
|
|
1329
|
+
// When creating a new VPC, default to creating subnets unless explicitly specified
|
|
1330
|
+
const defaultSubnetManagement = vpcManagement === 'create-new' ? 'create' : 'discover';
|
|
1331
|
+
const subnetManagement = AppDefinition.vpc.subnets?.management || defaultSubnetManagement;
|
|
1332
|
+
console.log(`Subnet Management Mode: ${subnetManagement}`);
|
|
1333
|
+
|
|
1334
|
+
// Ensure we have a valid VPC ID for subnet operations
|
|
1335
|
+
const effectiveVpcId = vpcId || discoveredResources.defaultVpcId;
|
|
1336
|
+
if (!effectiveVpcId) {
|
|
1337
|
+
throw new Error('Cannot manage subnets without a VPC ID');
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Subnet decision tree
|
|
1341
|
+
if (subnetManagement === 'create') {
|
|
1342
|
+
// Create new subnets in the VPC (either new or existing)
|
|
1343
|
+
console.log('Creating new subnets...');
|
|
1344
|
+
|
|
1345
|
+
// Determine VpcId based on VPC management mode
|
|
1346
|
+
const subnetVpcId = vpcManagement === 'create-new' ? { Ref: 'FriggVPC' } : effectiveVpcId;
|
|
1347
|
+
|
|
1348
|
+
// Generate CIDR blocks based on VPC type
|
|
1349
|
+
// For new VPC: use Fn::Cidr to generate from 10.0.0.0/16
|
|
1350
|
+
// For existing VPC: use safer high-range /24 blocks less likely to conflict
|
|
1351
|
+
let subnet1Cidr, subnet2Cidr, publicSubnetCidr;
|
|
1352
|
+
|
|
1353
|
+
if (vpcManagement === 'create-new') {
|
|
1354
|
+
// Use Fn::Cidr to generate 3 /24 subnets from the VPC CIDR
|
|
1355
|
+
// This creates [10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24]
|
|
1356
|
+
const generatedCidrs = {
|
|
1357
|
+
'Fn::Cidr': ['10.0.0.0/16', 3, 8] // 3 subnets with /24 (256-8=248 bits)
|
|
1358
|
+
};
|
|
1359
|
+
subnet1Cidr = { 'Fn::Select': [0, generatedCidrs] }; // 10.0.0.0/24
|
|
1360
|
+
subnet2Cidr = { 'Fn::Select': [1, generatedCidrs] }; // 10.0.1.0/24
|
|
1361
|
+
publicSubnetCidr = { 'Fn::Select': [2, generatedCidrs] }; // 10.0.2.0/24
|
|
1362
|
+
} else {
|
|
1363
|
+
// For existing VPCs, use high-range /24 blocks less likely to conflict
|
|
1364
|
+
// These are in the 172.31.x.x range for default VPC or high ranges for custom VPCs
|
|
1365
|
+
subnet1Cidr = '172.31.240.0/24';
|
|
1366
|
+
subnet2Cidr = '172.31.241.0/24';
|
|
1367
|
+
publicSubnetCidr = '172.31.250.0/24';
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Create private subnets
|
|
1371
|
+
definition.resources.Resources.FriggPrivateSubnet1 = {
|
|
1372
|
+
Type: 'AWS::EC2::Subnet',
|
|
1373
|
+
Properties: {
|
|
1374
|
+
VpcId: subnetVpcId,
|
|
1375
|
+
CidrBlock: subnet1Cidr,
|
|
1376
|
+
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
1377
|
+
Tags: [
|
|
1378
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-1' },
|
|
1379
|
+
{ Key: 'Type', Value: 'Private' },
|
|
1380
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1381
|
+
]
|
|
1382
|
+
}
|
|
1077
1383
|
};
|
|
1078
1384
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1385
|
+
definition.resources.Resources.FriggPrivateSubnet2 = {
|
|
1386
|
+
Type: 'AWS::EC2::Subnet',
|
|
1387
|
+
Properties: {
|
|
1388
|
+
VpcId: subnetVpcId,
|
|
1389
|
+
CidrBlock: subnet2Cidr,
|
|
1390
|
+
AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
|
|
1391
|
+
Tags: [
|
|
1392
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-2' },
|
|
1393
|
+
{ Key: 'Type', Value: 'Private' },
|
|
1394
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1395
|
+
]
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
// Create public subnet for NAT
|
|
1400
|
+
definition.resources.Resources.FriggPublicSubnet = {
|
|
1401
|
+
Type: 'AWS::EC2::Subnet',
|
|
1402
|
+
Properties: {
|
|
1403
|
+
VpcId: subnetVpcId,
|
|
1404
|
+
CidrBlock: publicSubnetCidr,
|
|
1405
|
+
MapPublicIpOnLaunch: true,
|
|
1406
|
+
AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
|
|
1407
|
+
Tags: [
|
|
1408
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-public' },
|
|
1409
|
+
{ Key: 'Type', Value: 'Public' },
|
|
1410
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1411
|
+
]
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1085
1414
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1415
|
+
vpcConfig.subnetIds = [
|
|
1416
|
+
{ Ref: 'FriggPrivateSubnet1' },
|
|
1417
|
+
{ Ref: 'FriggPrivateSubnet2' }
|
|
1418
|
+
];
|
|
1088
1419
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1420
|
+
// IMPORTANT: Create route tables even without NAT Gateway management
|
|
1421
|
+
// Otherwise subnets won't have proper routing
|
|
1422
|
+
if (!AppDefinition.vpc.natGateway || AppDefinition.vpc.natGateway.management === 'discover') {
|
|
1423
|
+
// Need to ensure public subnet has IGW route
|
|
1424
|
+
if (vpcManagement === 'create-new' || !discoveredResources.internetGatewayId) {
|
|
1425
|
+
// Create or reference IGW for public subnet
|
|
1426
|
+
if (!definition.resources.Resources.FriggInternetGateway) {
|
|
1427
|
+
definition.resources.Resources.FriggInternetGateway = {
|
|
1428
|
+
Type: 'AWS::EC2::InternetGateway',
|
|
1429
|
+
Properties: {
|
|
1430
|
+
Tags: [
|
|
1431
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' },
|
|
1432
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1433
|
+
]
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1093
1436
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1437
|
+
definition.resources.Resources.FriggIGWAttachment = {
|
|
1438
|
+
Type: 'AWS::EC2::VPCGatewayAttachment',
|
|
1439
|
+
Properties: {
|
|
1440
|
+
VpcId: subnetVpcId,
|
|
1441
|
+
InternetGatewayId: { Ref: 'FriggInternetGateway' }
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Create public route table with IGW route
|
|
1448
|
+
definition.resources.Resources.FriggPublicRouteTable = {
|
|
1449
|
+
Type: 'AWS::EC2::RouteTable',
|
|
1450
|
+
Properties: {
|
|
1451
|
+
VpcId: subnetVpcId,
|
|
1452
|
+
Tags: [
|
|
1453
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' },
|
|
1454
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1455
|
+
]
|
|
1456
|
+
}
|
|
1100
1457
|
};
|
|
1101
1458
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1459
|
+
definition.resources.Resources.FriggPublicRoute = {
|
|
1460
|
+
Type: 'AWS::EC2::Route',
|
|
1461
|
+
DependsOn: vpcManagement === 'create-new' ? 'FriggIGWAttachment' : undefined,
|
|
1462
|
+
Properties: {
|
|
1463
|
+
RouteTableId: { Ref: 'FriggPublicRouteTable' },
|
|
1464
|
+
DestinationCidrBlock: '0.0.0.0/0',
|
|
1465
|
+
GatewayId: discoveredResources.internetGatewayId || { Ref: 'FriggInternetGateway' }
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
// Associate public subnet with public route table
|
|
1470
|
+
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation = {
|
|
1471
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1472
|
+
Properties: {
|
|
1473
|
+
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
1474
|
+
RouteTableId: { Ref: 'FriggPublicRouteTable' }
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
|
|
1478
|
+
// Create private route table for Lambda subnets
|
|
1479
|
+
definition.resources.Resources.FriggLambdaRouteTable = {
|
|
1480
|
+
Type: 'AWS::EC2::RouteTable',
|
|
1481
|
+
Properties: {
|
|
1482
|
+
VpcId: subnetVpcId,
|
|
1483
|
+
Tags: [
|
|
1484
|
+
{ Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' },
|
|
1485
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1486
|
+
]
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
// Associate private subnets with route table
|
|
1491
|
+
definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation = {
|
|
1492
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1493
|
+
Properties: {
|
|
1494
|
+
SubnetId: { Ref: 'FriggPrivateSubnet1' },
|
|
1495
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation = {
|
|
1500
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1501
|
+
Properties: {
|
|
1502
|
+
SubnetId: { Ref: 'FriggPrivateSubnet2' },
|
|
1503
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' }
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
} else if (subnetManagement === 'use-existing') {
|
|
1508
|
+
// Use explicitly provided subnet IDs
|
|
1509
|
+
if (!AppDefinition.vpc.subnets?.ids || AppDefinition.vpc.subnets.ids.length < 2) {
|
|
1510
|
+
throw new Error(
|
|
1511
|
+
'Subnet management is "use-existing" but less than 2 subnet IDs provided. ' +
|
|
1512
|
+
'Provide at least 2 subnet IDs in vpc.subnets.ids.'
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
vpcConfig.subnetIds = AppDefinition.vpc.subnets.ids;
|
|
1516
|
+
} else {
|
|
1517
|
+
// Discover mode (default)
|
|
1518
|
+
vpcConfig.subnetIds =
|
|
1519
|
+
AppDefinition.vpc.subnets?.ids?.length > 0
|
|
1520
|
+
? AppDefinition.vpc.subnets.ids
|
|
1521
|
+
: (discoveredResources.privateSubnetId1 &&
|
|
1522
|
+
discoveredResources.privateSubnetId2
|
|
1523
|
+
? [
|
|
1524
|
+
discoveredResources.privateSubnetId1,
|
|
1525
|
+
discoveredResources.privateSubnetId2,
|
|
1526
|
+
]
|
|
1527
|
+
: []);
|
|
1528
|
+
|
|
1529
|
+
if (vpcConfig.subnetIds.length < 2) {
|
|
1530
|
+
if (AppDefinition.vpc.selfHeal) {
|
|
1531
|
+
console.log('No subnets found but self-heal enabled - creating minimal subnet setup');
|
|
1532
|
+
// Fall back to creating subnets
|
|
1533
|
+
subnetManagement = 'create';
|
|
1534
|
+
// Recursion would be complex here, so just set flag
|
|
1535
|
+
discoveredResources.createSubnets = true;
|
|
1536
|
+
} else {
|
|
1537
|
+
throw new Error(
|
|
1538
|
+
'No subnets discovered and subnets.management is "discover". ' +
|
|
1539
|
+
'Either enable vpc.selfHeal, set subnets.management to "create", or provide subnet IDs.'
|
|
1106
1540
|
);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1107
1544
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1545
|
+
// Set VPC config for Lambda functions only if we have valid subnet IDs
|
|
1546
|
+
if (
|
|
1547
|
+
vpcConfig.subnetIds.length >= 2 &&
|
|
1548
|
+
vpcConfig.securityGroupIds.length > 0
|
|
1549
|
+
) {
|
|
1550
|
+
definition.provider.vpc = vpcConfig;
|
|
1551
|
+
|
|
1552
|
+
// ALWAYS manage NAT Gateway through CloudFormation for self-healing
|
|
1553
|
+
// This ensures NAT Gateway is always in the correct subnet with proper configuration
|
|
1554
|
+
|
|
1555
|
+
console.log('AppDefinition.vpc.natGateway', AppDefinition.vpc.natGateway);
|
|
1556
|
+
const natGatewayManagement =
|
|
1557
|
+
AppDefinition.vpc.natGateway?.management || 'discover';
|
|
1558
|
+
console.log('natGatewayManagement', natGatewayManagement);
|
|
1559
|
+
let needsNewNatGateway =
|
|
1560
|
+
natGatewayManagement === 'createAndManage' ||
|
|
1561
|
+
discoveredResources.needsNewNatGateway === true; // Use healing flag
|
|
1562
|
+
|
|
1563
|
+
console.log('needsNewNatGateway', needsNewNatGateway);
|
|
1121
1564
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1565
|
+
// Remove unused helper function - validation is done in discovery
|
|
1566
|
+
|
|
1567
|
+
// Variables to track NAT Gateway and EIP reuse
|
|
1568
|
+
let reuseExistingNatGateway = false;
|
|
1569
|
+
let useExistingEip = false;
|
|
1570
|
+
|
|
1571
|
+
if (needsNewNatGateway) {
|
|
1572
|
+
// Always create new dedicated resources in create mode to avoid confusion with existing ones
|
|
1573
|
+
console.log(
|
|
1574
|
+
'Create mode: Creating dedicated EIP, public subnet, and NAT Gateway...'
|
|
1575
|
+
);
|
|
1576
|
+
|
|
1577
|
+
// Check if we can reuse existing NAT Gateway and EIP to avoid conflicts
|
|
1578
|
+
|
|
1579
|
+
// Check if we have a Frigg-managed NAT Gateway that we can reuse
|
|
1580
|
+
if (discoveredResources.existingNatGatewayId &&
|
|
1581
|
+
discoveredResources.existingElasticIpAllocationId) {
|
|
1582
|
+
// We have both NAT Gateway and EIP
|
|
1583
|
+
console.log('Found existing Frigg-managed NAT Gateway and EIP');
|
|
1584
|
+
|
|
1585
|
+
// CRITICAL: Check if NAT Gateway is in correct (public) subnet
|
|
1586
|
+
if (!discoveredResources.natGatewayInPrivateSubnet) {
|
|
1587
|
+
// NAT Gateway is properly configured, reuse it
|
|
1588
|
+
console.log('✅ Existing NAT Gateway is in PUBLIC subnet, will reuse it');
|
|
1589
|
+
reuseExistingNatGateway = true;
|
|
1590
|
+
} else {
|
|
1591
|
+
// NAT Gateway is in PRIVATE subnet - NEVER reuse it
|
|
1592
|
+
console.log('❌ NAT Gateway is in PRIVATE subnet - MUST create new one in PUBLIC subnet');
|
|
1593
|
+
|
|
1594
|
+
if (AppDefinition.vpc.selfHeal) {
|
|
1595
|
+
console.log('Self-heal enabled: Creating new NAT Gateway in PUBLIC subnet');
|
|
1596
|
+
// Force creation of new NAT in public subnet
|
|
1597
|
+
reuseExistingNatGateway = false;
|
|
1598
|
+
// Cannot reuse the EIP since it's associated with wrong NAT
|
|
1599
|
+
useExistingEip = false;
|
|
1600
|
+
// Mark for cleanup recommendations
|
|
1601
|
+
discoveredResources.needsCleanup = true;
|
|
1602
|
+
} else {
|
|
1603
|
+
throw new Error(
|
|
1604
|
+
'CRITICAL: NAT Gateway is in PRIVATE subnet (will not work!). ' +
|
|
1605
|
+
'Enable vpc.selfHeal to auto-fix or set natGateway.management to "createAndManage".'
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
} else if (discoveredResources.existingElasticIpAllocationId &&
|
|
1610
|
+
!discoveredResources.existingNatGatewayId) {
|
|
1611
|
+
// We have an EIP but no NAT Gateway - can reuse the EIP
|
|
1612
|
+
console.log('Found orphaned EIP, will reuse it for new NAT Gateway in PUBLIC subnet');
|
|
1613
|
+
useExistingEip = true;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Skip all resource creation if reusing existing NAT Gateway
|
|
1617
|
+
if (reuseExistingNatGateway) {
|
|
1618
|
+
console.log('Reusing existing NAT Gateway - skipping resource creation');
|
|
1619
|
+
// The existing NAT Gateway will be used for routing
|
|
1620
|
+
// No new resources need to be created
|
|
1621
|
+
} else {
|
|
1622
|
+
// Only create EIP if we're not reusing an existing one
|
|
1623
|
+
if (!useExistingEip) {
|
|
1624
|
+
definition.resources.Resources.FriggNATGatewayEIP = {
|
|
1625
|
+
Type: 'AWS::EC2::EIP',
|
|
1626
|
+
Properties: {
|
|
1627
|
+
Domain: 'vpc',
|
|
1628
|
+
Tags: [
|
|
1629
|
+
{
|
|
1630
|
+
Key: 'Name',
|
|
1631
|
+
Value: '${self:service}-${self:provider.stage}-nat-eip',
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
Key: 'ManagedBy',
|
|
1635
|
+
Value: 'Frigg',
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
Key: 'Service',
|
|
1639
|
+
Value: '${self:service}',
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
Key: 'Stage',
|
|
1643
|
+
Value: '${self:provider.stage}',
|
|
1644
|
+
},
|
|
1645
|
+
],
|
|
1646
|
+
},
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// Create public subnet if needed (for NAT Gateway placement)
|
|
1651
|
+
if (!discoveredResources.publicSubnetId || discoveredResources.createPublicSubnet) {
|
|
1124
1652
|
console.log(
|
|
1125
1653
|
'No public subnet found, creating one for NAT Gateway placement...'
|
|
1126
1654
|
);
|
|
@@ -1128,28 +1656,28 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1128
1656
|
// Check if Internet Gateway exists or create one
|
|
1129
1657
|
if (!discoveredResources.internetGatewayId) {
|
|
1130
1658
|
definition.resources.Resources.FriggInternetGateway =
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1659
|
+
{
|
|
1660
|
+
Type: 'AWS::EC2::InternetGateway',
|
|
1661
|
+
Properties: {
|
|
1662
|
+
Tags: [
|
|
1663
|
+
{
|
|
1664
|
+
Key: 'Name',
|
|
1665
|
+
Value: '${self:service}-${self:provider.stage}-igw',
|
|
1666
|
+
},
|
|
1667
|
+
],
|
|
1668
|
+
},
|
|
1669
|
+
};
|
|
1142
1670
|
|
|
1143
1671
|
definition.resources.Resources.FriggIGWAttachment =
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
},
|
|
1672
|
+
{
|
|
1673
|
+
Type: 'AWS::EC2::VPCGatewayAttachment',
|
|
1674
|
+
Properties: {
|
|
1675
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1676
|
+
InternetGatewayId: {
|
|
1677
|
+
Ref: 'FriggInternetGateway',
|
|
1151
1678
|
},
|
|
1152
|
-
}
|
|
1679
|
+
},
|
|
1680
|
+
};
|
|
1153
1681
|
}
|
|
1154
1682
|
|
|
1155
1683
|
// Create a small public subnet for NAT Gateway
|
|
@@ -1209,65 +1737,114 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1209
1737
|
|
|
1210
1738
|
// Associate public subnet with public route table
|
|
1211
1739
|
definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
},
|
|
1740
|
+
{
|
|
1741
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1742
|
+
Properties: {
|
|
1743
|
+
SubnetId: { Ref: 'FriggPublicSubnet' },
|
|
1744
|
+
RouteTableId: {
|
|
1745
|
+
Ref: 'FriggPublicRouteTable',
|
|
1219
1746
|
},
|
|
1220
|
-
}
|
|
1747
|
+
},
|
|
1748
|
+
};
|
|
1221
1749
|
}
|
|
1222
1750
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1751
|
+
// Create NAT Gateway only if not reusing existing one
|
|
1752
|
+
definition.resources.Resources.FriggNATGateway = {
|
|
1753
|
+
Type: 'AWS::EC2::NatGateway',
|
|
1754
|
+
Properties: {
|
|
1755
|
+
AllocationId: useExistingEip ?
|
|
1756
|
+
discoveredResources.existingElasticIpAllocationId :
|
|
1757
|
+
{
|
|
1758
|
+
'Fn::GetAtt': [
|
|
1759
|
+
'FriggNATGatewayEIP',
|
|
1760
|
+
'AllocationId',
|
|
1761
|
+
],
|
|
1762
|
+
},
|
|
1763
|
+
SubnetId: discoveredResources.publicSubnetId || {
|
|
1764
|
+
Ref: 'FriggPublicSubnet',
|
|
1765
|
+
},
|
|
1766
|
+
Tags: [
|
|
1767
|
+
{
|
|
1768
|
+
Key: 'Name',
|
|
1769
|
+
Value: '${self:service}-${self:provider.stage}-nat-gateway',
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
Key: 'ManagedBy',
|
|
1773
|
+
Value: 'Frigg',
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
Key: 'Service',
|
|
1777
|
+
Value: '${self:service}',
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
Key: 'Stage',
|
|
1781
|
+
Value: '${self:provider.stage}',
|
|
1782
|
+
},
|
|
1231
1783
|
],
|
|
1232
1784
|
},
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
} else if (natGatewayManagement === 'discover' || natGatewayManagement === 'useExisting') {
|
|
1788
|
+
// Discover or use existing NAT Gateway
|
|
1789
|
+
if (natGatewayManagement === 'useExisting' && AppDefinition.vpc.natGateway?.id) {
|
|
1790
|
+
// Use explicitly provided NAT Gateway ID
|
|
1791
|
+
console.log(`Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`);
|
|
1792
|
+
discoveredResources.existingNatGatewayId = AppDefinition.vpc.natGateway.id;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (discoveredResources.existingNatGatewayId) {
|
|
1796
|
+
console.log('discoveredResources.existingNatGatewayId', discoveredResources.existingNatGatewayId);
|
|
1797
|
+
|
|
1798
|
+
// CRITICAL: Verify NAT Gateway is in PUBLIC subnet
|
|
1799
|
+
if (discoveredResources.natGatewayInPrivateSubnet) {
|
|
1800
|
+
// NAT is in PRIVATE subnet - CANNOT use it
|
|
1801
|
+
console.log('❌ CRITICAL: NAT Gateway is in PRIVATE subnet - Internet connectivity will NOT work!');
|
|
1802
|
+
|
|
1803
|
+
if (AppDefinition.vpc.selfHeal === true) {
|
|
1804
|
+
console.log('Self-heal enabled: Will create new NAT Gateway in PUBLIC subnet');
|
|
1805
|
+
// Force creation of new NAT Gateway in public subnet
|
|
1806
|
+
needsNewNatGateway = true;
|
|
1807
|
+
discoveredResources.existingNatGatewayId = null; // Don't use the misconfigured NAT
|
|
1808
|
+
// Ensure we have a public subnet for the NAT
|
|
1809
|
+
if (!discoveredResources.publicSubnetId) {
|
|
1810
|
+
console.log('No public subnet found - will create one for NAT Gateway');
|
|
1811
|
+
discoveredResources.createPublicSubnet = true;
|
|
1812
|
+
}
|
|
1813
|
+
} else {
|
|
1814
|
+
throw new Error(
|
|
1815
|
+
'CRITICAL: NAT Gateway is in PRIVATE subnet and will NOT provide internet connectivity! ' +
|
|
1816
|
+
'Options: 1) Enable vpc.selfHeal to auto-create proper NAT, ' +
|
|
1817
|
+
'2) Set natGateway.management to "createAndManage", or ' +
|
|
1818
|
+
'3) Manually fix the NAT Gateway placement.'
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
} else {
|
|
1822
|
+
// NAT is correctly in public subnet
|
|
1823
|
+
console.log('✅ NAT Gateway is correctly placed in PUBLIC subnet');
|
|
1824
|
+
}
|
|
1261
1825
|
} else {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1826
|
+
// No existing NAT Gateway found
|
|
1827
|
+
if (natGatewayManagement === 'useExisting') {
|
|
1828
|
+
throw new Error(
|
|
1829
|
+
'NAT Gateway management set to "useExisting" but no NAT Gateway found. ' +
|
|
1830
|
+
'Either provide natGateway.id or change management to "discover" or "createAndManage".'
|
|
1831
|
+
);
|
|
1832
|
+
} else if (AppDefinition.vpc.selfHeal === true) {
|
|
1833
|
+
// Self-healing enabled, create a new NAT Gateway
|
|
1834
|
+
console.log('No NAT Gateway found but self-healing enabled - creating new NAT Gateway in PUBLIC subnet');
|
|
1835
|
+
needsNewNatGateway = true;
|
|
1836
|
+
// Ensure we have a public subnet for the NAT
|
|
1837
|
+
if (!discoveredResources.publicSubnetId) {
|
|
1838
|
+
console.log('No public subnet found - will create one for NAT Gateway');
|
|
1839
|
+
discoveredResources.createPublicSubnet = true;
|
|
1840
|
+
}
|
|
1841
|
+
} else {
|
|
1842
|
+
throw new Error(
|
|
1843
|
+
'No existing NAT Gateway found in discovery mode. ' +
|
|
1844
|
+
'Set natGateway.management to "createAndManage" to create a new NAT Gateway.'
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1265
1847
|
}
|
|
1266
|
-
} else {
|
|
1267
|
-
// No NAT and not in create mode: Error out to prevent isolated subnets
|
|
1268
|
-
throw new Error(
|
|
1269
|
-
'No existing NAT Gateway found and createAndManage not enabled. Update appDefinition.vpc.natGateway.method or ensure discovery finds a valid NAT.'
|
|
1270
|
-
);
|
|
1271
1848
|
}
|
|
1272
1849
|
|
|
1273
1850
|
// Always add route table and routes (referencing the NAT, whether new or existing)
|
|
@@ -1286,47 +1863,96 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1286
1863
|
},
|
|
1287
1864
|
};
|
|
1288
1865
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1866
|
+
// Determine which NAT Gateway ID to use for routing
|
|
1867
|
+
let natGatewayIdForRoute;
|
|
1868
|
+
|
|
1869
|
+
if (reuseExistingNatGateway) {
|
|
1870
|
+
// Use the existing NAT Gateway that we're reusing
|
|
1871
|
+
natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
|
|
1872
|
+
} else if (needsNewNatGateway && !reuseExistingNatGateway) {
|
|
1873
|
+
// Reference the new NAT Gateway being created
|
|
1874
|
+
natGatewayIdForRoute = { Ref: 'FriggNATGateway' };
|
|
1875
|
+
} else if (discoveredResources.existingNatGatewayId) {
|
|
1876
|
+
// Use the existing NAT Gateway ID
|
|
1877
|
+
natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
|
|
1878
|
+
} else if (AppDefinition.vpc.natGateway?.id) {
|
|
1879
|
+
// Use explicitly provided NAT Gateway ID
|
|
1880
|
+
natGatewayIdForRoute = AppDefinition.vpc.natGateway.id;
|
|
1881
|
+
} else if (AppDefinition.vpc.selfHeal === true) {
|
|
1882
|
+
// Self-healing enabled but no NAT Gateway - skip NAT route
|
|
1883
|
+
natGatewayIdForRoute = null;
|
|
1884
|
+
} else {
|
|
1885
|
+
throw new Error(
|
|
1886
|
+
'Unable to determine NAT Gateway ID for routing. ' +
|
|
1887
|
+
'Please check your configuration.'
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// Only create NAT route if we have a NAT Gateway
|
|
1892
|
+
if (natGatewayIdForRoute) {
|
|
1893
|
+
definition.resources.Resources.FriggNATRoute = {
|
|
1291
1894
|
Type: 'AWS::EC2::Route',
|
|
1292
1895
|
Properties: {
|
|
1293
1896
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1294
1897
|
DestinationCidrBlock: '0.0.0.0/0',
|
|
1295
|
-
NatGatewayId:
|
|
1898
|
+
NatGatewayId: natGatewayIdForRoute,
|
|
1296
1899
|
},
|
|
1297
1900
|
};
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
// Associate Lambda subnets with NAT Gateway route table
|
|
1904
|
+
// CRITICAL: This fixes the "NAT Gateway in private subnet" issue by ensuring correct routing
|
|
1905
|
+
if (AppDefinition.vpc.selfHeal === true) {
|
|
1906
|
+
console.log('✅ Self-healing: Ensuring subnets have correct route table associations');
|
|
1907
|
+
// In self-heal mode, we force the associations even if they might conflict
|
|
1908
|
+
// CloudFormation will automatically disassociate from old route table first
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
// Only create associations for discovered subnets (not for Refs)
|
|
1912
|
+
if (typeof vpcConfig.subnetIds[0] === 'string') {
|
|
1913
|
+
definition.resources.Resources.FriggSubnet1RouteAssociation = {
|
|
1914
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1301
1915
|
Properties: {
|
|
1916
|
+
SubnetId: vpcConfig.subnetIds[0],
|
|
1302
1917
|
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1303
|
-
DestinationCidrBlock: '0.0.0.0/0',
|
|
1304
|
-
NatGatewayId:
|
|
1305
|
-
discoveredResources.existingNatGatewayId,
|
|
1306
1918
|
},
|
|
1919
|
+
DependsOn: 'FriggLambdaRouteTable',
|
|
1307
1920
|
};
|
|
1308
1921
|
}
|
|
1309
1922
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
};
|
|
1923
|
+
if (typeof vpcConfig.subnetIds[1] === 'string') {
|
|
1924
|
+
definition.resources.Resources.FriggSubnet2RouteAssociation = {
|
|
1925
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1926
|
+
Properties: {
|
|
1927
|
+
SubnetId: vpcConfig.subnetIds[1],
|
|
1928
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1929
|
+
},
|
|
1930
|
+
DependsOn: 'FriggLambdaRouteTable',
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1321
1933
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1934
|
+
// If subnets are CloudFormation refs (newly created), associate them
|
|
1935
|
+
if (typeof vpcConfig.subnetIds[0] === 'object' && vpcConfig.subnetIds[0].Ref) {
|
|
1936
|
+
definition.resources.Resources.FriggNewSubnet1RouteAssociation = {
|
|
1937
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1938
|
+
Properties: {
|
|
1939
|
+
SubnetId: vpcConfig.subnetIds[0],
|
|
1940
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1941
|
+
},
|
|
1942
|
+
DependsOn: ['FriggLambdaRouteTable', vpcConfig.subnetIds[0].Ref],
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
if (typeof vpcConfig.subnetIds[1] === 'object' && vpcConfig.subnetIds[1].Ref) {
|
|
1947
|
+
definition.resources.Resources.FriggNewSubnet2RouteAssociation = {
|
|
1948
|
+
Type: 'AWS::EC2::SubnetRouteTableAssociation',
|
|
1949
|
+
Properties: {
|
|
1950
|
+
SubnetId: vpcConfig.subnetIds[1],
|
|
1951
|
+
RouteTableId: { Ref: 'FriggLambdaRouteTable' },
|
|
1952
|
+
},
|
|
1953
|
+
DependsOn: ['FriggLambdaRouteTable', vpcConfig.subnetIds[1].Ref],
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1330
1956
|
|
|
1331
1957
|
// Add VPC endpoints for AWS service optimization (optional but recommended)
|
|
1332
1958
|
if (AppDefinition.vpc.enableVPCEndpoints !== false) {
|
|
@@ -1363,30 +1989,30 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1363
1989
|
.VPCEndpointSecurityGroup
|
|
1364
1990
|
) {
|
|
1365
1991
|
definition.resources.Resources.VPCEndpointSecurityGroup =
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1992
|
+
{
|
|
1993
|
+
Type: 'AWS::EC2::SecurityGroup',
|
|
1994
|
+
Properties: {
|
|
1995
|
+
GroupDescription:
|
|
1996
|
+
'Security group for VPC endpoints',
|
|
1997
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
1998
|
+
SecurityGroupIngress: [
|
|
1999
|
+
{
|
|
2000
|
+
IpProtocol: 'tcp',
|
|
2001
|
+
FromPort: 443,
|
|
2002
|
+
ToPort: 443,
|
|
2003
|
+
CidrIp:
|
|
2004
|
+
discoveredResources.vpcCidr ||
|
|
2005
|
+
'10.0.0.0/16', // Dynamic VPC CIDR
|
|
2006
|
+
},
|
|
2007
|
+
],
|
|
2008
|
+
Tags: [
|
|
2009
|
+
{
|
|
2010
|
+
Key: 'Name',
|
|
2011
|
+
Value: '${self:service}-${self:provider.stage}-vpc-endpoints-sg',
|
|
2012
|
+
},
|
|
2013
|
+
],
|
|
2014
|
+
},
|
|
2015
|
+
};
|
|
1390
2016
|
}
|
|
1391
2017
|
|
|
1392
2018
|
definition.resources.Resources.VPCEndpointKMS = {
|
|
@@ -1407,130 +2033,129 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
1407
2033
|
// Also add Secrets Manager endpoint if using Secrets Manager
|
|
1408
2034
|
if (AppDefinition.secretsManager?.enable === true) {
|
|
1409
2035
|
definition.resources.Resources.VPCEndpointSecretsManager =
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
2036
|
+
{
|
|
2037
|
+
Type: 'AWS::EC2::VPCEndpoint',
|
|
2038
|
+
Properties: {
|
|
2039
|
+
VpcId: discoveredResources.defaultVpcId,
|
|
2040
|
+
ServiceName:
|
|
2041
|
+
'com.amazonaws.${self:provider.region}.secretsmanager',
|
|
2042
|
+
VpcEndpointType: 'Interface',
|
|
2043
|
+
SubnetIds: vpcConfig.subnetIds,
|
|
2044
|
+
SecurityGroupIds: [
|
|
2045
|
+
{ Ref: 'VPCEndpointSecurityGroup' },
|
|
2046
|
+
],
|
|
2047
|
+
PrivateDnsEnabled: true,
|
|
2048
|
+
},
|
|
2049
|
+
};
|
|
1424
2050
|
}
|
|
1425
2051
|
}
|
|
1426
2052
|
}
|
|
1427
2053
|
}
|
|
1428
2054
|
}
|
|
1429
2055
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
2056
|
+
// SSM Parameter Store Configuration based on App Definition
|
|
2057
|
+
if (AppDefinition.ssm?.enable === true) {
|
|
2058
|
+
// Add AWS Parameters and Secrets Lambda Extension layer
|
|
2059
|
+
definition.provider.layers = [
|
|
2060
|
+
'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11',
|
|
2061
|
+
];
|
|
1436
2062
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
2063
|
+
// Add SSM IAM permissions
|
|
2064
|
+
definition.provider.iamRoleStatements.push({
|
|
2065
|
+
Effect: 'Allow',
|
|
2066
|
+
Action: [
|
|
2067
|
+
'ssm:GetParameter',
|
|
2068
|
+
'ssm:GetParameters',
|
|
2069
|
+
'ssm:GetParametersByPath',
|
|
2070
|
+
],
|
|
2071
|
+
Resource: [
|
|
2072
|
+
'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*',
|
|
2073
|
+
],
|
|
2074
|
+
});
|
|
1449
2075
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
2076
|
+
// Add environment variable for SSM parameter prefix
|
|
2077
|
+
definition.provider.environment.SSM_PARAMETER_PREFIX =
|
|
2078
|
+
'/${self:service}/${self:provider.stage}';
|
|
2079
|
+
}
|
|
1454
2080
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
2081
|
+
// Add integration-specific functions and resources
|
|
2082
|
+
if (
|
|
2083
|
+
AppDefinition.integrations &&
|
|
2084
|
+
Array.isArray(AppDefinition.integrations)
|
|
2085
|
+
) {
|
|
2086
|
+
console.log(`Processing ${AppDefinition.integrations.length} integrations...`);
|
|
2087
|
+
for (const integration of AppDefinition.integrations) {
|
|
2088
|
+
if (
|
|
2089
|
+
!integration ||
|
|
2090
|
+
!integration.Definition ||
|
|
2091
|
+
!integration.Definition.name
|
|
2092
|
+
) {
|
|
2093
|
+
throw new Error(
|
|
2094
|
+
'Invalid integration: missing Definition or name'
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
const integrationName = integration.Definition.name;
|
|
1471
2098
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
},
|
|
2099
|
+
// Add function for the integration
|
|
2100
|
+
definition.functions[integrationName] = {
|
|
2101
|
+
handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
|
|
2102
|
+
events: [
|
|
2103
|
+
{
|
|
2104
|
+
httpApi: {
|
|
2105
|
+
path: `/api/${integrationName}-integration/{proxy+}`,
|
|
2106
|
+
method: 'ANY',
|
|
1481
2107
|
},
|
|
1482
|
-
|
|
1483
|
-
|
|
2108
|
+
},
|
|
2109
|
+
],
|
|
2110
|
+
};
|
|
1484
2111
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
integrationName.slice(1)
|
|
2112
|
+
// Add SQS Queue for the integration
|
|
2113
|
+
const queueReference = `${integrationName.charAt(0).toUpperCase() +
|
|
2114
|
+
integrationName.slice(1)
|
|
1489
2115
|
}Queue`;
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
},
|
|
2116
|
+
const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
|
|
2117
|
+
definition.resources.Resources[queueReference] = {
|
|
2118
|
+
Type: 'AWS::SQS::Queue',
|
|
2119
|
+
Properties: {
|
|
2120
|
+
QueueName: `\${self:custom.${queueReference}}`,
|
|
2121
|
+
MessageRetentionPeriod: 60,
|
|
2122
|
+
VisibilityTimeout: 1800, // 30 minutes
|
|
2123
|
+
RedrivePolicy: {
|
|
2124
|
+
maxReceiveCount: 1,
|
|
2125
|
+
deadLetterTargetArn: {
|
|
2126
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
1502
2127
|
},
|
|
1503
2128
|
},
|
|
1504
|
-
}
|
|
2129
|
+
},
|
|
2130
|
+
};
|
|
1505
2131
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
},
|
|
1517
|
-
batchSize: 1,
|
|
2132
|
+
// Add Queue Worker for the integration
|
|
2133
|
+
const queueWorkerName = `${integrationName}QueueWorker`;
|
|
2134
|
+
definition.functions[queueWorkerName] = {
|
|
2135
|
+
handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
|
|
2136
|
+
reservedConcurrency: 5,
|
|
2137
|
+
events: [
|
|
2138
|
+
{
|
|
2139
|
+
sqs: {
|
|
2140
|
+
arn: {
|
|
2141
|
+
'Fn::GetAtt': [queueReference, 'Arn'],
|
|
1518
2142
|
},
|
|
2143
|
+
batchSize: 1,
|
|
1519
2144
|
},
|
|
1520
|
-
],
|
|
1521
|
-
timeout: 600,
|
|
1522
|
-
};
|
|
1523
|
-
|
|
1524
|
-
// Add Queue URL for the integration to the ENVironment variables
|
|
1525
|
-
definition.provider.environment = {
|
|
1526
|
-
...definition.provider.environment,
|
|
1527
|
-
[`${integrationName.toUpperCase()}_QUEUE_URL`]: {
|
|
1528
|
-
Ref: queueReference,
|
|
1529
2145
|
},
|
|
1530
|
-
|
|
2146
|
+
],
|
|
2147
|
+
timeout: 600,
|
|
2148
|
+
};
|
|
1531
2149
|
|
|
1532
|
-
|
|
1533
|
-
|
|
2150
|
+
// Add Queue URL for the integration to the ENVironment variables
|
|
2151
|
+
definition.provider.environment = {
|
|
2152
|
+
...definition.provider.environment,
|
|
2153
|
+
[`${integrationName.toUpperCase()}_QUEUE_URL`]: {
|
|
2154
|
+
Ref: queueReference,
|
|
2155
|
+
},
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
definition.custom[queueReference] = queueName;
|
|
1534
2159
|
}
|
|
1535
2160
|
}
|
|
1536
2161
|
|