deep-package-manager
Version:
DEEP Package Manager
327 lines (278 loc) • 9.12 kB
JavaScript
/**
* Created by CCristi on 2/6/17.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.LambdaService = undefined;
var _AbstractService = require('./AbstractService');
var _Instance = require('../Instance');
var _deepCore = require('deep-core');
var _deepCore2 = _interopRequireDefault(_deepCore);
var _LambdaCompiler = require('./Helpers/LambdaCompiler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class LambdaService extends _AbstractService.AbstractService {
/**
* @param {Object[]} args
*/
constructor(...args) {
super(...args);
this._lambda = null;
}
/**
* @returns {String}
*/
name() {
return _deepCore2.default.AWS.Service.LAMBDA;
}
/**
* @param {Object} lambda
*/
set lambda(lambda) {
this._lambda = lambda;
}
/**
* @param {String} bucket
* @param {Date|String|Number} dateTime
* @returns {*}
*/
startS3BackFillLambda(bucket, dateTime = Date.now()) {
return this._lambda.invoke({
FunctionName: this.s3BackFillFunctionName,
InvocationType: 'Event',
LogType: 'None',
Payload: JSON.stringify({
Bucket: bucket,
MaxDateTime: dateTime
})
}).promise();
}
/**
* @param {String} table
* @returns {Promise}
*/
startTableBackFillLambda(table) {
return this._lambda.invoke({
FunctionName: this.backFillStartFunctionName,
InvocationType: 'Event',
LogType: 'None',
Payload: JSON.stringify({
Table: table,
Hash: this.replication.hashCode + Date.now()
})
}).promise();
}
/**
* @param {String[]} tables
* @returns {Promise}
*/
stopDynamoDBStreamTriggerFunctions(tables) {
return this._lambda.listEventSourceMappings({
FunctionName: this.replicationFunctionName
}).promise().then(response => {
let events = response.EventSourceMappings;
let eventUUIDs = [];
for (let event of events) {
let tableName = event.EventSourceArn.replace(
//arn:aws:dynamodb:us-east-1:566686064739:table/DeepDevNamecb3200e6/stream/2017-02-13T13:59:36.071
/^arn\:aws\:dynamodb\:[\d\w\-]+\:\d+\:table\/([\w\d\-]+)\/.+$/i, '$1');
if (tables.indexOf(tableName) !== -1) {
eventUUIDs.push(event.UUID);
}
}
return Promise.all(eventUUIDs.map(uuid => {
return this._lambda.deleteEventSourceMapping({
UUID: uuid
}).promise();
}));
});
}
/**
* @param {Object} ignoreGlobs
* @returns {Promise}
*/
injectEnvVarsIntoS3ReplicationLambdas(ignoreGlobs) {
return Promise.all([this.s3ReplicationFunctionName, this.s3BackFillFunctionName].map(functionName => {
return this._lambda.getFunction({
FunctionName: functionName
}).promise().then(functionMetadata => {
let functionCfg = functionMetadata.Configuration;
this._deleteMetadataKeysFromLambdaConfig(functionCfg);
Object.assign(functionCfg, {
Environment: {
Variables: Object.assign({}, ignoreGlobs, this.replication.s3Service.buildBucketReplicationMap())
}
});
return this._lambda.updateFunctionConfiguration(functionCfg).promise().then(() => functionCfg);
});
}));
}
/**
* @param {String} dynamoDbStreamArn
* @param {Number} batchSize
* @returns {Promise}
*/
createDynamoDBStreamTriggerFunction(dynamoDbStreamArn, batchSize = 100) {
return this._lambda.getFunction({
FunctionName: this.replicationFunctionName
}).promise().then(functionMetadata => {
let functionCfg = functionMetadata.Configuration;
this._deleteMetadataKeysFromLambdaConfig(functionCfg);
Object.assign(functionCfg, {
Environment: {
Variables: this.replication.dynamoDbService.buildTableReplicationMap()
}
});
return this._lambda.updateFunctionConfiguration(functionCfg).promise().then(() => functionCfg);
}).then(functionCfg => {
let payload = {
EventSourceArn: dynamoDbStreamArn,
FunctionName: functionCfg.FunctionName,
BatchSize: batchSize,
Enabled: true,
StartingPosition: 'TRIM_HORIZON'
};
return this._lambda.createEventSourceMapping(payload).promise().catch(e => {
if (this._isFalsePositive(e)) {
return Promise.resolve({});
}
throw e;
});
});
}
/**
* @returns {String}
*/
get replicationFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['replication-stream'];
}
/**
* @returns {String}
*/
get backFillStartFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['replication-start'];
}
/**
* @returns {String}
*/
get s3ReplicationFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['replication-s3-notification'];
}
/**
* @returns {String}
*/
get s3BackFillFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['replication-s3-backfill'];
}
/**
* @returns {String}
*/
get cloudFrontTrafficManagerFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['cloudfront-traffic-manager'];
}
/**
* @returns {String}
*/
get cloudFrontResponseEnhancerFunctionName() {
return this.blueConfig().names[_Instance.Instance.BLUE_GREEN_MS_IDENTIFIER]['cloudfront-response-enhancer'];
}
/**
* @param {Error} error
* @returns {Boolean}
* @private
*/
_isFalsePositive(error) {
return ['ResourceConflictException'].indexOf(error.code) !== -1;
}
/**
* @param {String} lambdaArn
* @param {String} s3BucketName
* @returns {Promise}
*/
addS3InvokePermission(lambdaArn, s3BucketName) {
let payload = {
Action: 'lambda:InvokeFunction',
Principal: _deepCore2.default.AWS.Service.identifier(_deepCore2.default.AWS.Service.SIMPLE_STORAGE_SERVICE),
FunctionName: lambdaArn,
StatementId: `Replicate_${this.replication.hashCode}_${s3BucketName.replace(/[^\w\d]/g, '-')}`,
SourceArn: `arn:aws:s3:::${s3BucketName}`
};
return this._lambda.addPermission(payload).promise().catch(e => {
if (this._isFalsePositive(e)) {
return Promise.resolve({});
}
throw e;
});
}
/**
* @param {String} functionName
* @returns {String}
*/
generateLambdaArn(functionName) {
let config = this.replication.blueConfig;
let region = config.aws.region;
let awsAccountId = config.awsAccountId;
return `arn:aws:lambda:${region}:${awsAccountId}:function:${functionName}`;
}
/**
* @param {Object} lambdaConfig
* @private
*/
_deleteMetadataKeysFromLambdaConfig(lambdaConfig) {
delete lambdaConfig.CodeSize;
delete lambdaConfig.CodeSha256;
delete lambdaConfig.KMSKeyArn;
delete lambdaConfig.FunctionArn;
delete lambdaConfig.LastModified;
delete lambdaConfig.Version;
delete lambdaConfig.VpcConfig;
}
/**
* @param {String} functionName
* @param {String} cloudfrontDistributionId
* @param {String[]} _actionStack
* @returns {Promise}
*/
addLambdaEdgeInvokePermission(functionName, cloudfrontDistributionId, _actionStack = ['Get', 'Invoke']) {
if (_actionStack.length === 0) {
return Promise.resolve();
}
let actionStackClone = [].concat(_actionStack);
let action = actionStackClone.shift();
let payload = {
Action: `${_deepCore2.default.AWS.Service.LAMBDA}:${action}Function`,
Principal: _deepCore2.default.AWS.Service.identifier(_deepCore2.default.AWS.Service.LAMBDA_EDGE),
FunctionName: this.generateLambdaArn(functionName),
StatementId: `traffic-balancer_${functionName}_${action}_${this.replication.hashCode}`
};
if (action === 'Invoke') {
payload.SourceArn = `arn:aws:cloudfront::${this.replication.blueConfig.awsAccountId}` + `:distribution/${cloudfrontDistributionId}`;
}
return this._lambda.addPermission(payload).promise().then(() => this.addLambdaEdgeInvokePermission(functionName, cloudfrontDistributionId, actionStackClone)).catch(e => {
if (this._isFalsePositive(e)) {
return Promise.resolve({});
}
throw e;
});
}
/**
* @param {String} functionName
* @param {Object} variables
* @returns {Promise}
*/
compileLambdaForCloudFront(functionName, variables) {
return this._lambda.getFunction({
FunctionName: functionName
}).promise().then(functionObj => {
let s3Location = functionObj.Code.Location;
return _LambdaCompiler.LambdaCompiler.fetchFromUrl(s3Location).then(compiler => compiler.addParameterBag(variables, 'EDGE-PARAMS').compile()).then(codeBuffer => {
return this._lambda.updateFunctionCode({
FunctionName: functionName,
ZipFile: codeBuffer
}).promise();
});
});
}
}
exports.LambdaService = LambdaService;