UNPKG

lambda-tools

Version:

Scripts for working with AWS Lambda backed microservices

148 lines (128 loc) 4.63 kB
'use strict'; const _ = require('lodash'); const AWS = require('aws-sdk'); const response = require('cfn-response'); const lambda = new AWS.Lambda(); // Helpers const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } /** * Get function configuration by its name * * @return {Promise} which resolves into the function configuration */ function getFunction(name) { console.log('Function name: ', name); return new Promise((resolve, reject) => { lambda.getFunctionConfiguration({ FunctionName: name }, function(err, data) { if (err) return reject(err); resolve(data); }); }); } /** * Get all function versions * * @return {Promise} which resolves into an array of version configurations */ function getFunctionVersions(name, marker) { // Grab functions console.log('Function versions: ', name); return new Promise((resolve, reject) => { lambda.listVersionsByFunction({ FunctionName: name, Marker: marker }, function(err, data) { if (err) return reject(err); // Check if we can grab even more if (data.NextMarker) { return getFunctionVersions(name, data.NextMarker).then((versions) => { resolve(data.Versions.concat(versions)); }); } resolve(data.Versions); }); }); } /** * Publish a new Lambda function version * * @return {Promise} which resolves into the newly published version */ function functionPublishVersion(name, description, hash) { console.log('Publishing function version: ', name); return new Promise((resolve, reject) => { lambda.publishVersion({ FunctionName: name, Description: description, CodeSha256: hash }, function(err, data) { if (err) return reject(err); resolve(data); }); }); } exports.handler = function(event, context) { // Determine the nature of this request const command = event.RequestType; // Log the event (this will help debugging) console.log('Handle ' + command + ' request'); // eslint-disable-line no-console console.log('Event', JSON.stringify(event, null, 4)); // eslint-disable-line no-console // Validate context if (!event) { console.error(new Error('Context MUST have an event')); return response.send(event, context, response.FAILED, {}); } const properties = event.ResourceProperties; if (!properties) { console.error(new Error('Context event must have a \'ResourceProperties\' key')); return response.send(event, context, response.FAILED, {}); } // Ignore delete event if (command === 'Delete') { console.log('Ignore delete event'); return response.send(event, context, response.SUCCESS, {}); } // Artificial delay to prevent deploys from hitting internal AWS API limits delay(getRandomInt(15000)).then(() => { // Grab the function first return getFunction(properties.FunctionName).then(function(fn) { // Then grab all versions return getFunctionVersions(fn.FunctionName).then((versions) => { // Try to find one that matches the function configuration versions = versions.filter((version) => { // Ignore the latest pseudo-version return version.Version !== '$LATEST'; }); const trimmedFn = _.pick(fn, 'LastModified'); return { fn: fn, version: _.find(versions, trimmedFn) }; }); }); }).then((results) => { return delay(getRandomInt(5000)).then(function() { return results; }); }).then((results) => { const fn = results.fn; const version = results.version; if (!version) { console.log('Publish a new version'); return functionPublishVersion(fn.FunctionName, properties.Description, properties.CodeSha256); } return version; }).then((version) => { // Make sure the Arn property is populated version.Arn = version.FunctionArn; response.send(event, context, response.SUCCESS, version, version.FunctionArn); }).catch((err) => { console.error('Error', err); response.send(event, context, response.FAILED, {}); }); };