From 94c83f701f31bfa68763e958b1d9ba0507bbd4c6 Mon Sep 17 00:00:00 2001 From: Frank Schmid Date: Tue, 9 May 2017 23:59:58 +0200 Subject: [PATCH] Support CW events. Add SERVERLESS_ALIAS variable to aliased functions. --- README.md | 20 +++++++++---- lib/aliasRestructureStack.js | 56 ++++++++++++++++++++++++++++++++++++ lib/stackops/apiGateway.js | 6 +++- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6d55ac7..d667980 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,6 @@ hook into the deployment process. Additionally the new `alias` command is added to Serverless which offers some functionality for aliases. -## Interoperability - -Care has to be taken when using other plugins that modify the CF output too. -I will add configuration instructions in this section for these plugin combinations. - ## Deploy the default alias The default alias (for the stage) is deployed just by doing a standard stage @@ -226,6 +221,19 @@ the alias stack has been removed. The alias plugin is compatible with all standard Serverless commands and switches. For example, you can use `--noDeploy` and the plugin will behave accordingly. +## Interoperability + +Care has to be taken when using other plugins that modify the CF output too. +I will add configuration instructions in this section for these plugin combinations. + +### [serverless-plugin-warmup](https://github.com/FidelLimited/serverless-plugin-warmup) + +The warmup plugin will keep your Lambdas warm and reduce the cold start time +effectively. When using the plugin, it must be listed **before** the alias plugin +in the plugin list of _serverless.yml_. The warmup lambda created by the plugin +will be aliased too, so that the warmup plugin can be configured differently +per deployed alias. + ## Test it In case you wanna test how it behaves, I provided a predefined test service in @@ -307,6 +315,8 @@ and _serverless.service.provider.deployedAliasTemplates[]_. * 0.5.0-alpha1 Fixes a bug with deploying event sources introduced with 0.4.0 Use new event model introduced in SLS 1.12. Needs SLS 1.12 or greater from now on. + Add support for CW events. + Set SERVERLESS_ALIAS environment variable on deployed functions. * 0.4.0-alpha1 APIG support fixed. Support external IAM roles. BREAKING. * 0.3.4-alpha1 Bugfixes. IAM policy consolitaion. Show master alias information. * 0.3.3-alpha1 Bugfixes. Allow manual resource overrides. Allow methods attached to APIG root resource. diff --git a/lib/aliasRestructureStack.js b/lib/aliasRestructureStack.js index 1da36c8..cd9b4bc 100644 --- a/lib/aliasRestructureStack.js +++ b/lib/aliasRestructureStack.js @@ -91,6 +91,12 @@ module.exports = { } }; + // Set SERVERLESS_ALIAS environment variable + _.forOwn(stageStack.Resources, resource => { + if (resource.Type === 'AWS::Lambda::Function') { + _.set(resource, 'Properties.Environment.Variables.SERVERLESS_ALIAS', this._alias); + } + }); const versions = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Version' ])); if (!_.isEmpty(versions)) { @@ -313,6 +319,55 @@ module.exports = { return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]); }, + aliasHandleCWEvents(currentTemplate, aliasStackTemplates, currentAliasStackTemplate) { + + const stageStack = this._serverless.service.provider.compiledCloudFormationTemplate; + const aliasStack = this._serverless.service.provider.compiledCloudFormationAliasTemplate; + + const cwEvents = _.assign({}, _.pickBy(_.get(stageStack, 'Resources', {}), [ 'Type', 'AWS::Events::Rule' ])); + const cwEventLambdaPermissions = + _.assign({}, + _.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]), + ['Properties.Principal', 'events.amazonaws.com'])); + + _.forOwn(cwEvents, (cwEvent, name) => { + // Reference alias as FunctionName + const targetRefs = utils.findAllReferences(_.get(cwEvent, 'Properties.Targets')); + cwEvent.DependsOn = cwEvent.DependsOn || []; + _.forEach(targetRefs, ref => { + const functionName = _.replace(ref.ref, /LambdaFunction$/, ''); + _.set(cwEvent.Properties.Targets, ref.path, { Ref: `${functionName}Alias` }); + cwEvent.DependsOn.push(`${functionName}Alias`); + }); + + // Remove mapping from stage stack + delete stageStack.Resources[name]; + }); + + // Move event subscriptions to alias stack + _.defaults(aliasStack.Resources, cwEvents); + + // Adjust permission to reference the function aliases + _.forOwn(cwEventLambdaPermissions, (permission, name) => { + const targetFunctionRef = utils.findAllReferences(_.get(permission, 'Properties.FunctionName')); + const functionName = _.replace(targetFunctionRef[0].ref, /LambdaFunction$/, ''); + + // Adjust references and alias permissions + permission.Properties.FunctionName = { Ref: `${functionName}Alias` }; + + // Add dependency on function alias + permission.DependsOn = [ `${functionName}Alias` ]; + + delete stageStack.Resources[name]; + }); + + // Add all alias stack owned resources + _.defaults(aliasStack.Resources, cwEventLambdaPermissions); + + // Forward inputs to the promise chain + return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]); + }, + aliasFinalize(currentTemplate, aliasStackTemplates, currentAliasStackTemplate) { const aliasStack = this._serverless.service.provider.compiledCloudFormationAliasTemplate; @@ -336,6 +391,7 @@ module.exports = { .spread(this.aliasHandleFunctions) .spread(this.aliasHandleApiGateway) .spread(this.aliasHandleEvents) + .spread(this.aliasHandleCWEvents) .spread(this.aliasFinalize) .then(() => BbPromise.resolve()); } diff --git a/lib/stackops/apiGateway.js b/lib/stackops/apiGateway.js index d6d0460..f95c366 100644 --- a/lib/stackops/apiGateway.js +++ b/lib/stackops/apiGateway.js @@ -95,7 +95,11 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac } // Fetch lambda permissions, methods and resources. These have to be updated later to allow the aliased functions. - const apiLambdaPermissions = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ])); + const apiLambdaPermissions = + _.assign({}, + _.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]), + ['Properties.Principal', 'apigateway.amazonaws.com'])); + const apiMethods = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Method' ])); //const apiResources = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Resource' ])); const aliases = _.assign({}, _.pickBy(aliasStack.Resources, [ 'Type', 'AWS::Lambda::Alias' ]));