@kumologica/builder
Version:
Kumologica build and deploy module
293 lines (257 loc) • 7.29 kB
JavaScript
const jsonata = require('jsonata');
const apiTrigger = require('./triggers/api');
const sqsTrigger = require('./triggers/sqs');
class TerraformTemplate {
constructor() {}
createTemplate(provider, params, flow) {
switch (provider) {
case "aws":
return this.createAWSTemplate(params, flow);
break;
default:
throw new Error (`provider: <${provider}> not supported yet;`);
}
}
/*
* Creates cloud formation template file for current lambda:
* It is composed of:
* - lambda definition
* - lambda's role
* - lambda's policy
* - if event subriptions confirmed then event source mappings for:
* - dynamodb stream
* - sns
* - kinesis
* - if flow nodes are provided then
* - role added for each downstream aws node
*
* params:
* functionName
* zipFileName
* roleName
* deploymentBucketName
* description
* runtime
* timeout
* memory
* reservedConcurrency
* tracingConfig
* roleArn
* environment
* tags
* kmsKeyArn
* vpcConfig
* deadLetterConfig
* fileSystemConfigs
*/
createAWSTemplate(params, flow) {
let ts = new Date();
let template = {
terraform: {
required_providers: {
aws: {
source: "hashicorp/aws"
}
}
},
data: {
aws_iam_policy_document: {}
},
resource: {
aws_lambda_function: {},
aws_iam_role: {}
},
output: {
LambdaArn: {
Description: 'The Arn of the cloud action flow lambda.',
value: '${aws_lambda_function.lambda.arn}'
}
}
};
let lambda = {
function_name: params.functionName,
s3_bucket: params.deploymentBucketName,
s3_key: params.zipFileName,
handler: "lambda.handler",
runtime: params.runtime || "nodejs12.x",
timeout: params.timeout || 3,
memory_size: params.memory || 128,
environment: {
variables: {}
},
tags: {
"Kumologica-ts":`${ts.toISOString()}`
}
};
template.resource.aws_lambda_function.lambda = lambda;
template.resource.aws_iam_role.lambda_role = {
name: params.roleName,
assume_role_policy: {
effect: 'Allow',
principal: {
service: 'lambda.amazonaws.com'
},
action: 'sts:AssumeRole'
}
};
/*
Path: '/',
Policies: [
{
PolicyName: 'AWSLambdaBasicExecutionPolicy',
PolicyDocument: {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Action: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents'
],
Resource: '*'
}
]
}
}
]
}
};*/
if (params.description) {
lambda.description = params.description;
}
if (params.tracingConfig) {
lambda.tracing_config = {
mode: params.tracingConfig.Mode
};
}
if (params.environment) {
lambda.environment.variables = params.environment;
}
if (params.tags) {
//tags = {
// Name = "efs_for_lambda"
//}
lambda.tags = {};
params.tags.forEach(function(tag, index) {
lambda.tags[tag.key] = tag.value;
});
}
if (params.roleArn) {
lambda.role = params.roleArn;
}
if (params.reservedConcurrency) {
lambda.reserved_concurrent_executions = params.reservedConcurrency;
}
if (params.kmsKeyArn) {
lambda.kms_key_arn = params.kmsKeyArn;
}
if (params.vpcConfig) {
lambda.vpc_config = {
subnet_ids: params.vpcConfig.SubnetIds,
security_group_ids: SecurityGroupIds
}
}
if (params.deadLetterConfig) {
lambda.dead_letter_config = {
target_arn: params.deadLetterConfig.TargetArn
};
}
if (params.fileSystemConfigs) {
params.fileSystemConfigs.forEach(function(c, index) {
lambda.file_system_config = {
arn: c.Arn,
local_mount_path: c.LocalMountPath
}
});
}
if (params.layers) {
lambda.layers = [JSON.parse(params.layers)]
}
// end
// adding inbound services permissions, only for dynamodb stream, sqs and kinesis
if (params.events) {
for (var i = 0; i < params.events.length; i++) {
if (['dynamodb', 'sqs', 'kinesis'].includes(params.events[i].source)) {
var mappingName = `${params.events[i].source}Mapping${i}`;
template.resource[mappingName] = this.eventSourceMapping(params.events[i]);
}
}
}
if (params.role) {
template.resource.aws_lambda_function.lambda.role = params.role;
} else {
const AWSCFTemplate = require('./cf');
const cf = new AWSCFTemplate();
const cfTemplate = cf.createCfTemplate(params, flow);
let policyStatements = this.convertPolicy(cfTemplate.Resources.LambdaRole.Properties.Policies);
if (params.tracingConfig) {
policyStatements.push({
effect: 'Allow',
actions: [
'xray:PutTraceSegments',
'xray:PutTelemetryRecords'
],
resources: ['*']
});
}
template.data.aws_iam_policy_document[`${params.roleName}-policy`] = {
statement: policyStatements
};
}
//template.resource.aws_lambda_function.lambda.role = "${aws_iam_role.lambda_role.arn}";
//if (params.triggers) {
// template.Resources = Object.assign(template.Resources, this.handleTriggers(params));
//}
return template;
}
// converts cf policy into tf policy syntax
convertPolicy(policies) {
let response = [];
policies.forEach(function(policy, i) {
policy.PolicyDocument.Statement.forEach(function(s, i) {
response.push({
actions: s.Action,
resources: s.Resource,
effect: s.Effect
}
)
})
})
return response;
}
// Method iterates through triggers and sets up relevant resources:
// event mappings or specific resource artefacts
handleTriggers(params) {
let resources = {};
for (let i=0; i<params.triggers.length; i++) {
switch(Object.keys(params.triggers[i])[0]) {
case 'api':
resources = Object.assign(resources, apiTrigger.trigger(params.triggers[i].api));
break;
case 'sqs':
resources = Object.assign(resources, sqsTrigger.trigger(params.functionName, params.triggers[i]));
break;
}
}
return resources;
}
eventSourceMapping(event) {
let mapping = {
aws_lambda_event_source_mapping: {
event_source_mapping: {
event_source_arn: event.stream,
enabled: true,
function_name: "${aws_lambda_function.Lambda.arn}",
batch_size: event.batchSize,
maximum_batching_window_in_seconds: event.batchWindow,
}
}
};
if (event.startingPosition) {
mapping.aws_lambda_event_source_mapping.event_source_mapping.starting_position = event.startingPosition;
}
return mapping;
}
}
module.exports = TerraformTemplate;