@kumologica/sdk 3.2.0-beta8 → 3.2.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,28 +1,19 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const AWS = require('aws-sdk');
4
- const AdmZip = require('adm-zip');
5
- const util = require('util');
6
- const exec = util.promisify(require('child_process').exec);
7
- const rimraf = require('rimraf');
8
- const { codegen } = require('@kumologica/builder');
9
- const AWSCFTemplate = require('./cf');
10
4
  const CAAWSCloudWatch = require('./ca-cloudwatch-api');
11
- const CAAWSIot = require('./ca-iot-api');
12
5
  const CAS3 = require('./ca-s3-api');
13
6
  const CASNS = require('./ca-sns-api');
14
7
  const CASQS = require('./ca-sqs-api');
15
8
  const CAAWSApiGateway = require('./ca-apigw-api');
16
9
  const CADynamoDb = require('./ca-dynamodb-api');
17
- const CAElb = require('./ca-elb-api');
18
10
  const CAEvents = require('./ca-events-api');
19
- const CAAlexa = require('./ca-alexa-api');
20
- const CACodeCommit = require('./ca-codecommit-api');
21
11
  const AWSProfile = require('./aws-profile');
22
- const KLIAM = require('./kl-iam-api');
23
12
  const { openFileOnEditor } = require('../utils/editor');
13
+ const { exp } = require('@kumologica/builder');
14
+ const { build } = require('@kumologica/builder');
24
15
 
25
- /**
16
+ /**
26
17
  * sample events:
27
18
  * https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html
28
19
  */
@@ -45,7 +36,6 @@ class AWSDeployer {
45
36
 
46
37
  this.cw = false;
47
38
 
48
- this.iot = new CAAWSIot(this.log.bind(this));
49
39
  this.apiGateway = new CAAWSApiGateway(this.log.bind(this));
50
40
  this.dynamoDb = new CADynamoDb(this.log.bind(this));
51
41
  this.cf = new AWS.CloudFormation();
@@ -53,15 +43,10 @@ class AWSDeployer {
53
43
  this.sns = new CASNS(this.log.bind(this));
54
44
  this.sqs = new CASQS(this.log.bind(this));
55
45
  this.lambdaApi = new AWS.Lambda();
56
- this.cfTemplate = new AWSCFTemplate();
57
46
  this.cwLogs = new CAAWSCloudWatch(this.logCloud.bind(this));
58
47
  this.cwevents = new AWS.CloudWatchEvents();
59
48
  this.cognito = new AWS.CognitoIdentityServiceProvider();
60
- this.elb = new CAElb(this.log.bind(this));
61
49
  this.events = new CAEvents(this.log.bind(this));
62
- this.alexa = new CAAlexa(this.log.bind(this));
63
- this.codecommit = new CACodeCommit(this.log.bind(this));
64
- this.iam = new KLIAM(this.log.bind(this));
65
50
  }
66
51
 
67
52
  chalk(c, t) {
@@ -107,23 +92,9 @@ class AWSDeployer {
107
92
  }
108
93
 
109
94
  prepare(projectDir, flowFileName, originaLambdaName) {
110
- const deployDir = projectDir + '/deploy';
111
95
 
96
+ const deployDir = path.join(projectDir, "build");
112
97
  const flowName = flowFileName.replace('.json', '');
113
-
114
- // setup deployment directory
115
- fs.ensureDirSync(deployDir);
116
- fs.emptyDirSync(deployDir);
117
- fs.ensureDirSync(deployDir + '/node_modules');
118
-
119
- // copy project files
120
- codegen.generateProjectCode(
121
- deployDir,
122
- flowFileName
123
- );
124
-
125
- this.processPackageJson(projectDir, deployDir);
126
-
127
98
  const zipFileName = `${flowName}/lambda${Date.now()}.zip`;
128
99
  const lambdaName = this.sanitizeLambdaName(originaLambdaName);
129
100
 
@@ -141,34 +112,6 @@ class AWSDeployer {
141
112
  };
142
113
  }
143
114
 
144
- processPackageJson(projectDir, deployDir) {
145
-
146
- const pckg = this.loadJsonFile(path.join(projectDir, 'package.json'));
147
-
148
- // ensure dependencies contain runtime
149
- if (!pckg.dependencies["@kumologica/runtime"]) {
150
- pckg.dependencies["@kumologica/runtime"] = `^${pckg.version.substr(0, 1)}`;
151
- this.createFile(deployDir, 'package.json', JSON.stringify(pckg));
152
-
153
- } else {
154
- fs.copySync(
155
- path.join(projectDir, 'package.json'),
156
- path.join(deployDir, 'package.json')
157
- );
158
- }
159
-
160
- if (pckg.files) {
161
- pckg.files.forEach(f => {
162
-
163
- if (!["node_modules/**/*", "lambda.js" ].includes(f)) {
164
- fs.copySync(
165
- path.join(projectDir, f),
166
- path.join(deployDir, f)
167
- );
168
- }
169
- });
170
- }
171
- }
172
115
 
173
116
  /**
174
117
  *
@@ -210,7 +153,6 @@ class AWSDeployer {
210
153
  }
211
154
 
212
155
  const deploymentStart = Date.now();
213
- var lambdaArn;
214
156
 
215
157
  this.log(`Deployment to AWS Started:`);
216
158
  this.log(` ${this.chalk('#F5DEB3', 'flow:')} ${this.chalk('whiteBright', projectInfo.projectFlowName)}`, false);
@@ -234,7 +176,8 @@ class AWSDeployer {
234
176
  settings.deploymentBucketName = await this.prepareS3Bucket(
235
177
  settings.flowName
236
178
  );
237
- await this.buildLambda(settings);
179
+
180
+ await this.buildLambda(settings, projectInfo);
238
181
 
239
182
  const nodes = this.loadJsonFile(
240
183
  path.join(projectInfo.projectDir, projectInfo.projectFlowName)
@@ -246,9 +189,29 @@ class AWSDeployer {
246
189
  settings,
247
190
  params,
248
191
  deploymentStart,
249
- nodes
192
+ nodes,
193
+ projectInfo
250
194
  );
251
- lambdaArn = stackDetails.Stacks[0].Outputs[0].OutputValue;
195
+ /*
196
+ "Outputs": [
197
+ {
198
+ "OutputKey": "LambdaArn",
199
+ "OutputValue": "arn:aws:lambda:ap-southeast-2:174842903734:function:sockets-flow",
200
+ "Description": "The Arn of the kumologica flow lambda."
201
+ },
202
+ {
203
+ "OutputKey": "RootResourceId",
204
+ "OutputValue": "qb9cfkwah9",
205
+ "Description": "The root resource id of rest api gateway created in this script."
206
+ },
207
+ {
208
+ "OutputKey": "RestApiId",
209
+ "OutputValue": "h1eihbg3gb",
210
+ "Description": "The id of rest api gateway created in this script."
211
+ }
212
+ */
213
+
214
+ const res = this.parseStackOutput(stackDetails);
252
215
 
253
216
  this.log(`${this.chalk('greenBright', 'Deployment successful.')}`);
254
217
  this.log(
@@ -256,104 +219,122 @@ class AWSDeployer {
256
219
  stackDetails.Stacks[0].StackId
257
220
  )}`,
258
221
  false
259
- );
260
- this.log(` ${this.chalk('#F5DEB3', 'LambdaArn:')} ${this.chalk('whiteBright',lambdaArn)}`, false);
222
+ );
223
+ this.log(` ${this.chalk('#F5DEB3', 'LambdaArn:')} ${this.chalk('whiteBright',res.LambdaArn)}`, false);
261
224
  this.log('', false);
262
225
 
226
+ this.printSignature(flowListeners, params, res);
227
+
263
228
  // this call to be moved out to the terminals panel
264
229
  /*if (this.cw == false) {
265
230
  this.cwLogs.startCWLogs(settings.functionName, AWS.config.region);
266
231
  this.cw = true;
267
232
  }*/
268
- } catch (error) {
269
- this.log(`${this.chalk('redBright', 'Deployment failed.')}`);
270
- this.log(` ${this.chalk('redBright', error)}`);
271
- } finally {
272
- //this.s3.deleteS3Bucket(deploymentBucketName);
273
- }
274
233
 
275
- let response = {};
276
- if (lambdaArn) {
277
- try {
278
- response = await this.runTriggers(params, settings, lambdaArn);
279
- if (response) {
280
- const url = `https://${response.apiId}.execute-api.${AWS.config.region}.amazonaws.com/${response.stage}`;
281
- this.printSignature(url, flowListeners, params);
282
- }
283
- } catch (error) {
284
- this.log(`${this.chalk('redBright', 'Trigger creation failed.')}`);
285
- this.log(` ${this.chalk('redBright', error)}`);
286
- }
287
- }
288
- return response;
234
+ } catch (error) {
235
+ this.log(`${this.chalk('redBright', 'Deployment failed.')}`);
236
+ this.log(` ${this.chalk('redBright', error)}`);
237
+ } finally {
238
+ }
239
+
240
+ }
241
+
242
+ parseStackOutput(stackDetails) {
243
+ let res = {};
244
+ stackDetails.Stacks[0].Outputs.forEach(o => {
245
+ res[o.OutputKey] = o.OutputValue;
246
+ });
247
+ return res;
289
248
  }
290
249
 
291
- printSignature(url, s, p) {
292
- this.log(' ')
293
-
294
- if (s.api && s.api.length > 0) {
295
- this.log('API Gateway:');
296
- s.api.forEach(a => this.log(` ${(' ' + a.verb.toUpperCase()).slice(-6)} ${url}${a.url}`), this);
297
- this.log('');
298
- }
250
+ printSignature(s, p, output) {
251
+ this.log(' ')
299
252
 
300
- if (s.dynamodb && s.dynamodb.length > 0) {
301
- this.log('DynamoDB Streams:');
302
- s.dynamodb.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
303
- this.log('');
304
- }
253
+ if (s.api && s.api.length > 0) {
254
+ const _api = p.events.find(e => e.source === "api");
305
255
 
306
- if (s.s3 && s.s3.length > 0) {
307
- this.log('S3:');
308
- s.s3.forEach(a => this.log(` ${a.eventType} ${a.bucket}`), this);
309
- this.log('');
310
- }
311
-
312
- if (s.sns && s.sns.length > 0) {
313
- this.log('SNS Topics:');
314
- s.sns.forEach(a => this.log(` ${a.topic}`), this);
315
- this.log('');
316
- }
256
+ if (_api) {
257
+ this.log('API Gateway:');
317
258
 
318
- if (s.iot && s.iot.length > 0) {
319
- this.log('IOT:');
320
- s.iot.forEach(a => this.log(` ${a.rule} ${a.query}`), this);
321
- this.log('');
322
- }
259
+ let apiId = _api.api;
260
+ let stage = _api.stage;
323
261
 
324
- if (s.alb && s.alb.length > 0) {
325
- this.log('Application Load Balancer:');
326
- s.alb.forEach(a => this.log(` ${a.alb} ${a.listener}`), this);
327
- this.log('');
262
+ if (_api.api === "create new") {
263
+ apiId = output.RestApiId || "unknown api gateway";
328
264
  }
329
265
 
330
- if (p.events && p.events.length > 0) {
331
- const sqs = p.events.filter(i => i.source == 'sqs');
332
- if (sqs && sqs.length > 0) {
333
- this.log('SQS URL:');
334
- sqs.forEach(a => this.log(` ${a.url}`), this);
335
- this.log('');
336
- }
337
- }
266
+ let url = `https://${apiId}.execute-api.${AWS.config.region}.amazonaws.com/${stage}`;
267
+ s.api.forEach(a => this.log(` ${(' ' + a.verb.toUpperCase()).slice(-6)} ${url}${a.url}`), this);
268
+ this.log('');
269
+ }
270
+ }
338
271
 
339
- if (s.alexa && s.alexa.length > 0) {
340
- this.log('Alexa Skills:');
341
- s.alexa.forEach(a => this.log(` ${a.skillId}`), this);
342
- this.log('');
343
- }
272
+ if (s.dynamodb && s.dynamodb.length > 0) {
273
+ const _ddb = p.events.find(e => e.source === "dynamodb");
274
+ if (_ddb) {
275
+ this.log('DynamoDB Streams:');
276
+ s.dynamodb.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
277
+ this.log('');
278
+ }
279
+ }
344
280
 
345
- if (s.kinesis && s.kinesis.length > 0) {
346
- this.log('Kinesis:');
347
- s.kinesis.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
348
- this.log('');
349
- }
281
+ if (s.s3 && s.s3.length > 0) {
282
+ const _s3 = p.events.find(e => e.source === "s3");
283
+ if (_s3) {
284
+ this.log('S3:');
285
+ s.s3.forEach(a => this.log(` ${a.eventType} ${a.bucket}`), this);
286
+ this.log('');
287
+ }
288
+ }
350
289
 
351
- if (s.cwevents && s.cwevents.length > 0) {
352
- this.log('Cloud Watch Events Rules:');
353
- s.cwevents.forEach(a => this.log(` ${a.rule}`), this);
354
- this.log('');
355
- }
290
+ if (s.sns && s.sns.length > 0) {
291
+ const _sns = p.events.find(e => e.source === "sns");
292
+ if (_sns) {
293
+ this.log('SNS Topics:');
294
+ s.sns.forEach(a => this.log(` ${a.topic}`), this);
295
+ this.log('');
296
+ }
297
+ }
298
+
299
+ if (s.iot && s.iot.length > 0) {
300
+ this.log('IOT:');
301
+ s.iot.forEach(a => this.log(` ${a.rule} ${a.query}`), this);
302
+ this.log('');
303
+ }
304
+
305
+ if (s.alb && s.alb.length > 0) {
306
+ this.log('Application Load Balancer:');
307
+ s.alb.forEach(a => this.log(` ${a.alb} ${a.listener}`), this);
308
+ this.log('');
309
+ }
310
+
311
+ if (p.events && p.events.length > 0) {
312
+ const sqs = p.events.filter(i => i.source == 'sqs');
313
+ if (sqs && sqs.length > 0) {
314
+ this.log('SQS URL:');
315
+ sqs.forEach(a => this.log(` ${a.url}`), this);
316
+ this.log('');
356
317
  }
318
+ }
319
+
320
+ if (s.kinesis && s.kinesis.length > 0) {
321
+ const _k = p.events.find(e => e.source === "kinesis");
322
+ if (_k) {
323
+ this.log('Kinesis:');
324
+ s.kinesis.forEach(a => this.log(` ${a.startingPosition} ${a.stream}`), this);
325
+ this.log('');
326
+ }
327
+ }
328
+
329
+ if (s.cwevents && s.cwevents.length > 0) {
330
+ const _ev = p.events.find(e => e.source === "event");
331
+ if (_ev) {
332
+ this.log('Cloud Watch Events Rules:');
333
+ s.cwevents.forEach(a => this.log(` ${a.rule}`), this);
334
+ this.log('');
335
+ }
336
+ }
337
+ }
357
338
 
358
339
  processFlow(flow) {
359
340
 
@@ -421,6 +402,105 @@ class AWSDeployer {
421
402
  };
422
403
  }
423
404
 
405
+ mapParams(params) {
406
+ let vpcConf;
407
+ if (params.vpcConfig && params.vpcConfig.sn && params.vpcConfig.sn.length > 0
408
+ && params.vpcConfig.sg && params.vpcConfig.sg.length > 0) {
409
+ let sn = params.vpcConfig.sn.split(",").map(s => s.trim());
410
+ let sg = params.vpcConfig.sg.split(",").map(s => s.trim());
411
+
412
+ vpcConf = JSON.stringify({
413
+ SubnetIds: sn,
414
+ SecurityGroupIds: sg
415
+ });
416
+ }
417
+
418
+ let tags;
419
+ if ( params.tags && params.tags.length > 0) {
420
+ tags = {};
421
+ params.tags.forEach(t => tags[t.key] = t.value)
422
+ tags = JSON.stringify(tags);
423
+ }
424
+
425
+ let env;
426
+ if ( params.environment && params.environment.length > 0) {
427
+ env = {
428
+ Variables: {}
429
+ };
430
+ params.environment.forEach(e => env.Variables[e.key] = e.value)
431
+ env = JSON.stringify(env);
432
+ }
433
+
434
+ let tracing;
435
+ if (params.xRay) {
436
+ tracing = '{"Mode":"Active"}';
437
+ }
438
+ // KL_TRIGGERS: '[{"api": {"apiId": "CHANGE_IT_TO_API_GATEWAY_ID", "parentId": "CHANGE_IT_TO_PARENT_ID", "stage": "test", "resource": "accounts"}}, {"event": {"expression": "cron(0 1 * * ? *)", "reference": "1am", "name": "CliBuildDemoEvent1am"}}, {"event": {"expression": "rate(1 minute)", "reference": "5min", "name": "CliBuildDemoEvent5min"}}]'
439
+
440
+ // "events":[{"source":"api","api":"qyhtzl0n39","stage":"test","parentId":"xfdg32","resource":"sample","authorizerId":"fdr3"}]
441
+ // "events":[{"source":"api","api":"qyhtzl0n39","stage":"test","parentId":"xfdg32","resource":"sample","authorizerId":"fdr3"},
442
+ //{"source":"dynamodb","stream":"arn:aws:dynamodb:ap-southeast-2:174842903734:table/contact_us/stream/2021-04-25T15:02:15.028","batchSize":"23","batchWindow":"3","startingPosition":"LATEST"},
443
+ //{"source":"sqs","stream":"https://sqs.ap-southeast-2.amazonaws.com/174842903734/devq2","batchSize":"32"},
444
+ //{"source":"kinesis","stream":"arn:aws:dynamodb:ap-southeast-2:174842903734:table/contact_us/stream/2021-04-25T15:02:15.028","batchSize":"34","batchWindow":"33","startingPosition":"LATEST"},
445
+ // {"source":"cwevents","rule":"arn:aws:events:ap-southeast-2:174842903734:rule/Test"},
446
+ //{"source":"sns","topic":"arn:aws:sns:ap-southeast-2:174842903734:t1"},
447
+ //{"source":"s3","bucket":"kumoexcelstore","eventType":"s3:ObjectCreated:*","prefix":"cc","suffix":"fd"}],"vpcConfig":{"sg":"","sn":""}}
448
+ ///Users/wojtek/Development/kumologica/sdk/kumologica/packages/sdk/src/app/lib/github/index.js:114
449
+ let triggers;
450
+ if (params.events) {
451
+ triggers = [];
452
+ params.events.forEach(e => {
453
+ switch(e.source) {
454
+ case "api": triggers.push({"api": {"apiId": e.api, "stage": e.stage, "parentId": e.parentId, "resource": e.resource, "authorizerId": e.authorizerId}});
455
+ break;
456
+ case "websocket": triggers.push({"websocket": {"apiId": e.apiId, "stage": e.stage}});
457
+ break;
458
+ case "sns": triggers.push({"sns": {"topicArn": e.topic}});
459
+ break;
460
+ case "sqs": triggers.push({"sqs": {"queueArn": e.queueArn, "batchSize": e.batchSize}});
461
+ break;
462
+ case "dynamodb": triggers.push({"dynamodb": {"streamArn": e.stream, "startingPosition": e.startingPosition, "batchSize": e.batchSize, "batchWindow": e.batchWindow}});
463
+ break;
464
+ case "kinesis": triggers.push({"kinesis": {"streamArn": e.stream, "startingPosition": e.startingPosition, "batchSize": e.batchSize, "batchWindow": e.batchWindow}});
465
+ break;
466
+ case "s3": triggers.push({"s3": {"bucket": e.bucket, "eventType": e.eventType, "prefix": e.prefix, "suffix": e.suffix}});
467
+ break;
468
+ case "cwevents": triggers.push({"event": {"expression": e.expression, "reference": e.reference, "name": e.name, "state": e.state}});
469
+ break;
470
+ }
471
+ })
472
+ triggers = JSON.stringify(triggers);
473
+ };
474
+
475
+ let args = {
476
+ "zip-file-name": params.zipFileName,
477
+ "bucket-name": params.bucketName,
478
+ "lambda-name": params.functionName,
479
+ "memory-size": params.memory,
480
+ "description": params.description,
481
+ "environment": env,
482
+ "tags": tags,
483
+ "timeout": params.timeout,
484
+ "triggers": triggers,
485
+ "vpc-config": vpcConf,
486
+ "role-arn": params.role,
487
+ "tracing-config": tracing,
488
+ "reserved-concurrency": params.reservedConcurrency
489
+ // not present
490
+ // "role-name": params.roleName,
491
+ // "layers": params.layers,
492
+ // "policy": params.policy,
493
+ // "strict-mode": params.strictMode,
494
+ // "log-retention-days": params.logRetentionDays,
495
+ // "file-system-configs": params.fileSystemConfigs,
496
+ // "dead-letter-config": params.deadLetterConfig,
497
+ // "runtime": params.runtime,
498
+ // "architectures": params.architectures,
499
+ // "kms-key-arn": params.kmsKeyArn,
500
+ }
501
+ return args;
502
+ }
503
+
424
504
  async generateScript(projectInfo, params, profile) {
425
505
  if (!profile) {
426
506
  throw new Error(
@@ -440,34 +520,23 @@ class AWSDeployer {
440
520
  this.log(` ${this.chalk('#F5DEB3', 'AWS region:')} ${this.chalk('whiteBright', AWS.config.region)}`, false);
441
521
 
442
522
  const settings = this.prepare(projectInfo.projectDir, projectInfo.projectFlowName, params.functionName, params.description);
443
- const nodes = await this.prepareNodes(this.loadJsonFile(path.join(projectInfo.projectDir, projectInfo.projectFlowName)));
444
523
 
445
524
  settings.deploymentBucketName = await this.prepareS3Bucket(
446
525
  settings.flowName
447
526
  );
448
527
 
449
- const lambdaCfTemplate = JSON.stringify(
450
- this.cfTemplate.createCfTemplate(
451
- params,
452
- settings,
453
- nodes,
454
- AWS.config.region
455
- ),
456
- null,
457
- 2
458
- );
528
+ // NEW
529
+ const args = this.mapParams(params);
530
+ args.region = AWS.config.region;
531
+ args["bucket-name"] = settings.deploymentBucketName;
532
+ args["project-directory"] = projectInfo.projectDir;
533
+ args["flow-file-name"] = projectInfo.projectFlowName;
534
+ args["zip-file-name"] = settings.zipFileName;
535
+
536
+ console.log(`args: ${JSON.stringify(args)}`);
537
+ const scriptFileName = exp("cloudformation", "aws", args);
538
+ this.log(`Cloudformation script has been created: ${scriptFileName}`);
459
539
 
460
- this.createFile(
461
- settings.deployDir,
462
- `cf-${projectInfo.projectFlowName}`,
463
- lambdaCfTemplate
464
- );
465
-
466
- const scriptFileName = path.join(
467
- settings.deployDir,
468
- `cf-${projectInfo.projectFlowName}`
469
- );
470
- this.log(`Cloud formation script created: ${scriptFileName}`);
471
540
  openFileOnEditor(scriptFileName);
472
541
  } catch (Error) {
473
542
  this.log(`${this.chalk('redBright', 'Script creation failed.')}`);
@@ -476,49 +545,16 @@ class AWSDeployer {
476
545
  }
477
546
  }
478
547
 
479
-
480
548
  /*
481
549
  * Builds lambda and zips all sources.
482
- */
483
- async buildLambda(settings) {
484
- this.log('Building lambda...');
485
-
486
- const npmCmd = `npm install --production --prefix "${settings.deployDir}"`;
487
- const response = await exec(npmCmd, { cwd: settings.deployDir });
488
-
489
- if (response) {
490
- this.log('', false);
491
- const lines = response.stdout.split('\n');
492
- for (var i = 0; i < lines.length; i++) {
493
- if (lines[i]) {
494
- this.log(lines[i], false);
495
- }
496
- }
497
- this.log('', false);
498
- }
499
-
500
- this.log('Excluding optional dependencies...');
501
-
502
- // for npm 8 on windows is creating symlink that causes recursions and zip to fail
503
- const splitdir = path.dirname(settings.deployDir).split(path.sep);
504
- const linkname = splitdir[splitdir.length-1];
505
- try {
506
- fs.unlinkSync(path.join(settings.deployDir, 'node_modules', linkname));
507
- } catch (e) {
508
- // ignore ENOENT
509
- if (e.code !== "ENOENT") {
510
- throw e;
511
- }
512
- }
513
-
514
- // Remove aws-sdk library as it is provided by aws nodejs runtime.
515
- // Reducing the resulting lambda zip file by more than 70%
516
- rimraf.sync(path.join(settings.deployDir, 'node_modules', 'aws-sdk'));
517
-
518
- this.log('Zipping lambda...');
519
- const zip = new AdmZip();
520
- zip.addLocalFolder(settings.deployDir);
521
- zip.writeZip(path.join(settings.deployDir, settings.zipFileName));
550
+ */
551
+ async buildLambda(settings, projectInfo) {
552
+ let args = {};
553
+ args["project-directory"] = projectInfo.projectDir;
554
+ args["flow-file-name"] = projectInfo.projectFlowName;
555
+ args["zip-file-name"] = settings.zipFileName;
556
+
557
+ await build("aws", args);
522
558
  }
523
559
 
524
560
  /*
@@ -526,7 +562,7 @@ class AWSDeployer {
526
562
  * and executes create or update stack.
527
563
  * Returns stack id
528
564
  */
529
- async executeStack(settings, params, deploymentStartTimestamp, nodes) {
565
+ async executeStack(settings, params, deploymentStartTimestamp, nodes, projectInfo) {
530
566
  this.log(`Uploading zip file to bucket...`);
531
567
  var body = fs.createReadStream(
532
568
  path.join(settings.deployDir, settings.zipFileName)
@@ -535,19 +571,21 @@ class AWSDeployer {
535
571
  await this.s3.upload({
536
572
  Bucket: settings.deploymentBucketName,
537
573
  Key: settings.zipFileName,
538
- Body: body,
574
+ Body: body
539
575
  });
540
576
 
541
- params = await this.prepareParams(params);
542
- nodes = await this.prepareNodes(nodes);
543
-
544
- const lambdaCfTemplate = this.cfTemplate.createCfTemplate(
545
- params,
546
- settings,
547
- nodes,
548
- AWS.config.region
549
- );
550
-
577
+ const args = this.mapParams(params);
578
+ args.region = AWS.config.region;
579
+ args["bucket-name"] = settings.deploymentBucketName;
580
+ args["project-directory"] = projectInfo.projectDir;
581
+ args["flow-file-name"] = projectInfo.projectFlowName;
582
+ args["zip-file-name"] = settings.zipFileName;
583
+ args.skipPrepare = "true"; // no need for cloudformation prepare
584
+
585
+ const scriptFileName = exp("cloudformation", "aws", args);
586
+
587
+ const lambdaCfTemplate = this.loadJsonFile(scriptFileName);
588
+
551
589
  const stackParams = {
552
590
  StackName: settings.stackName,
553
591
  Capabilities: ['CAPABILITY_NAMED_IAM'],
@@ -563,7 +601,9 @@ class AWSDeployer {
563
601
  await this.cf
564
602
  .waitFor('stackCreateComplete', { StackName: stackParams.StackName })
565
603
  .promise();
604
+
566
605
  } catch (error) {
606
+
567
607
  if (error.code == 'AlreadyExistsException') {
568
608
  stack = await this.cf.updateStack(stackParams).promise();
569
609
  this.log('Waiting for stack update complete...');
@@ -575,6 +615,7 @@ class AWSDeployer {
575
615
  })
576
616
  .promise();
577
617
  } catch (error) {
618
+
578
619
  await this.describeStackEvents(
579
620
  stackParams.StackName,
580
621
  deploymentStartTimestamp
@@ -593,137 +634,6 @@ class AWSDeployer {
593
634
  return this.describeStack(stackParams.StackName);
594
635
  }
595
636
 
596
- /**
597
- * Function links additional details for parameters that are not needed
598
- * in ui but are required by cloud formation
599
- *
600
- * @param {*} params
601
- */
602
- async prepareParams(params) {
603
- for (var i = 0; i < params.events.length; i++) {
604
- if (params.events[i].source === 'sqs') {
605
- if (!params.events[i].stream) {
606
- throw Error(`Missing Trigger Parameter: SQS Queue Url.`);
607
- }
608
- params.events[i].url = params.events[i].stream;
609
- params.events[i].stream = await this.sqs.getQueueArn(
610
- params.events[i].stream
611
- );
612
- } else if (params.events[i].source === 'codecommit') {
613
- if (!params.events[i].repository) {
614
- throw Error(`Missing Trigger Parameter: CodeCommit Repository.`);
615
- }
616
- const repository = await this.codecommit.getRepository(
617
- params.events[i].repository
618
- );
619
- params.events[i].repositoryArn = repository.Arn;
620
- params.events[i].account = repository.accountId;
621
- }
622
- }
623
- return params;
624
- }
625
-
626
- async prepareNodes(nodes) {
627
- for (var i = 0; i < nodes.length; i++) {
628
- if (nodes[i].type === 'SQS') {
629
- if (!nodes[i].QueueUrl) {
630
- throw Error(`Missing Node Parameter: SQS Queue Url.`);
631
- }
632
- nodes[i].QueueArn = await this.sqs.getQueueArn(nodes[i].QueueUrl);
633
- }
634
- }
635
- return nodes;
636
- }
637
-
638
- async runTriggers(params, settings, lambdaArn) {
639
- let response = {};
640
-
641
- if (params.events) {
642
- this.log('Adding flow triggers ...');
643
-
644
- for (var i = 0; i < params.events.length; i++) {
645
- if (params.events[i].source === 's3') {
646
- await this.addS3Event(
647
- params.events[i],
648
- settings.functionName,
649
- lambdaArn
650
- );
651
- } else if (params.events[i].source === 'sns') {
652
- await this.addSNSEvent(
653
- params.events[i],
654
- settings.functionName,
655
- lambdaArn
656
- );
657
- } else if (params.events[i].source === 'cwevents') {
658
- await this.addCWEvent(
659
- params.events[i],
660
- settings.functionName,
661
- lambdaArn
662
- );
663
- } else if (params.events[i].source === 'cwlogs') {
664
- await this.addCWLogsEvent(
665
- params.events[i],
666
- settings.functionName,
667
- lambdaArn
668
- );
669
- } else if (params.events[i].source === 'cognito') {
670
- await this.addCognitoEvent(
671
- params.events[i],
672
- settings.functionName,
673
- lambdaArn
674
- );
675
- } else if (params.events[i].source === 'alexa') {
676
- await this.addAlexaEvent(
677
- params.events[i],
678
- settings.functionName,
679
- lambdaArn
680
- );
681
- } else if (params.events[i].source === 'codecommit') {
682
- await this.addCodeCommitEvent(
683
- params.events[i],
684
- settings.functionName,
685
- lambdaArn
686
- );
687
- } else if (params.events[i].source === 'iot') {
688
- var rule = await this.iot.addTrigger(
689
- params.events[i],
690
- settings.functionName,
691
- lambdaArn
692
- );
693
- if (rule) {
694
- await this.addLambdaPermission(
695
- 'iot.amazonaws.com',
696
- settings.functionName,
697
- null,
698
- rule.ruleArn
699
- );
700
- }
701
- } else if (params.events[i].source === 'api') {
702
- const newApiId = await this.apiGateway.addTrigger(
703
- params.events[i],
704
- settings.functionName,
705
- lambdaArn
706
- );
707
-
708
- response.apiId = newApiId? newApiId: params.events[i].api;
709
- response.stage = params.events[i].stage;
710
-
711
- //arn:aws:lambda:ap-southeast-2:640233474616:function:kumologica-aws-inbound-test-flow-lambda
712
- const lambdaArnParts = lambdaArn.split(':');
713
- const apiArn = `arn:aws:execute-api:${lambdaArnParts[3]}:${lambdaArnParts[4]}:${params.events[i].api}/*/*/{proxy+}`;
714
- await this.addLambdaPermission(
715
- 'apigateway.amazonaws.com',
716
- settings.functionName,
717
- null,
718
- apiArn
719
- );
720
- }
721
- }
722
- this.log(`${this.chalk('greenBright', 'All triggers added successfully:')}`);
723
- }
724
- return response;
725
- }
726
-
727
637
  async describeStack(stackName) {
728
638
  return this.cf.describeStacks({ StackName: stackName }).promise();
729
639
  }
@@ -733,10 +643,17 @@ class AWSDeployer {
733
643
  */
734
644
  async describeStackEvents(stackName, deploymentStartTimestamp) {
735
645
  this.log('Calling describe stack events...');
646
+ let events;
736
647
 
737
- const events = await this.cf
648
+ try {
649
+ events = await this.cf
738
650
  .describeStackEvents({ StackName: stackName })
739
651
  .promise();
652
+ } catch (error) {
653
+ //this.log(`de: ${error}`);
654
+ // may throw stack does not exists for the first ever call
655
+ }
656
+
740
657
  if (!events || events == undefined) {
741
658
  return;
742
659
  }
@@ -780,157 +697,6 @@ class AWSDeployer {
780
697
  fs.outputFileSync(path.join(baseDir, fileName), content, 'utf-8');
781
698
  }
782
699
 
783
- async addLambdaPermission(
784
- principal,
785
- lambdaName,
786
- sourceAccount,
787
- sourceArn,
788
- eventSourceToken
789
- ) {
790
- var request = {
791
- Action: 'lambda:InvokeFunction',
792
- FunctionName: lambdaName,
793
- Principal: principal,
794
- SourceAccount: sourceAccount,
795
- SourceArn: sourceArn,
796
- EventSourceToken: eventSourceToken,
797
- StatementId: `${lambdaName}-${Date.now()}`,
798
- };
799
-
800
- // wait for response, the subsequent calls require permissions to be present.
801
- await this.lambdaApi.addPermission(request).promise();
802
- }
803
-
804
- async addS3Event(event, lambdaName, lambdaArn) {
805
- if (!event.bucket) {
806
- throw Error(`Missing Trigger Parameter: Bucket Name.`);
807
- }
808
-
809
- this.log(
810
- `Setting lambda execution permissions for bucket ${event.bucket} ...`
811
- );
812
- await this.addLambdaPermission(
813
- 's3.amazonaws.com',
814
- lambdaName,
815
- event.sourceAccount,
816
- `arn:aws:s3:::${event.bucket}`
817
- );
818
-
819
- await this.s3.bucketNotificationConfiguration(
820
- lambdaArn,
821
- event.bucket,
822
- event.eventType,
823
- event.prefix,
824
- event.suffix
825
- );
826
- }
827
-
828
- async addAlexaEvent(event, lambdaName, lambdaArn) {
829
- this.log(`Setting configuration for alexa skill id ${event.skillID} ...`);
830
- await this.addLambdaPermission(
831
- 'alexa-appkit.amazon.com',
832
- lambdaName,
833
- null,
834
- null,
835
- event.skillID
836
- );
837
- }
838
-
839
- async addCodeCommitEvent(event, lambdaName, lambdaArn) {
840
- this.log(
841
- `Setting configuration for code commit repository ${event.repository} ...`
842
- );
843
-
844
- await this.addLambdaPermission(
845
- 'codecommit.amazon.com',
846
- lambdaName,
847
- event.account,
848
- event.repositoryArn
849
- );
850
-
851
- this.log('Adding permissions for lambda to call codecommit.');
852
- await this.codecommit.addLambdaPermissions(event, lambdaArn);
853
- }
854
-
855
- async addSNSEvent(event, lambdaName, lambdaArn) {
856
- await this.addLambdaPermission(
857
- 'sns.amazonaws.com',
858
- lambdaName,
859
- null,
860
- event.topic
861
- );
862
-
863
- this.log(`Setting notification configuration for topic ${event.topic} ...`);
864
- await this.sns.subscribe(event.topic, lambdaArn);
865
- }
866
-
867
- async addCWEvent(event, lambdaName, lambdaArn) {
868
- await this.addLambdaPermission(
869
- 'events.amazonaws.com',
870
- lambdaName,
871
- null,
872
- event.rule
873
- );
874
-
875
- const ruleName = event.rule.split('rule/').pop();
876
-
877
- this.log(
878
- `Setting notification configuration for cloudwatch event ${ruleName} ...`
879
- );
880
-
881
- var params = {
882
- Rule: ruleName,
883
- Targets: [
884
- {
885
- Arn: lambdaArn,
886
- Id: `${lambdaName}`,
887
- },
888
- ],
889
- };
890
-
891
- await this.cwevents.putTargets(params).promise();
892
- }
893
-
894
- /**
895
- * https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudWatchLogs.html#putSubscriptionFilter-property
896
- *
897
- * @param {*} event
898
- * @param {*} lambdaName
899
- * @param {*} lambdaArn
900
- */
901
- async addCWLogsEvent(event, lambdaName, lambdaArn) {
902
- //await this.addLambdaPermission("events.amazonaws.com",
903
- // lambdaName, null, event.rule);
904
-
905
- /*this.log(`Setting notification configuration for cloudwatch event ${ruleName} ...`);
906
- var params = {
907
- destinationArn: 'STRING_VALUE', // required
908
- filterName: 'STRING_VALUE', // required
909
- filterPattern: 'STRING_VALUE', // required
910
- logGroupName: 'STRING_VALUE', // required
911
- distribution: Random | ByLogStream,
912
- roleArn: 'STRING_VALUE'
913
- };*/
914
- await this.cwLogs.putSubscriptionFilter(params).promise();
915
- }
916
-
917
- async addCognitoEvent(event, lambdaName, lambdaArn) {
918
- await this.addLambdaPermission(
919
- 'cognito-idp.amazonaws.com',
920
- lambdaName,
921
- null,
922
- event.identityPool
923
- );
924
-
925
- const ruleName = event.rule.split('rule/').pop();
926
-
927
- // TODO: add cognito implementation
928
- //this.log(`Setting notification configuration for cloudwatch event ${ruleName} ...`);
929
- var params = {};
930
-
931
- await this.cognito.updateUserPool(params).promise();
932
- }
933
-
934
700
  async listServices(type, profile) {
935
701
  if (!profile) {
936
702
  throw new Error(
@@ -969,12 +735,6 @@ class AWSDeployer {
969
735
  case 'iot':
970
736
  res = await this.iot.listTopicRules();
971
737
  break;
972
- case 'alexa':
973
- res = await this.alexa.listSkills();
974
- break;
975
- case 'codecommit':
976
- res = await this.codecommit.listRepositories();
977
- break;
978
738
  case 'iamroles':
979
739
  res = await this.iam.listRoles();
980
740
  break;