From 3210a94ef9615367d16feb82e1dec4e55f549869 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 20 Dec 2019 13:47:29 +0100 Subject: [PATCH] fix(AWS Lambda): Workaround AWS issue related to alias redeployments Addresses #7059 --- lib/plugins/aws/lib/naming.js | 9 +++ .../aws/package/compile/functions/index.js | 77 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/lib/plugins/aws/lib/naming.js b/lib/plugins/aws/lib/naming.js index 6a37087d9..77b0f0d59 100644 --- a/lib/plugins/aws/lib/naming.js +++ b/lib/plugins/aws/lib/naming.js @@ -163,6 +163,15 @@ module.exports = { '' )}`; }, + getCodeDeployApplicationLogicalId() { + return 'CodeDeployApplication'; + }, + getCodeDeployDeploymentGroupLogicalId() { + return 'CodeDeployDeploymentGroup'; + }, + getCodeDeployRoleLogicalId() { + return 'CodeDeployRole'; + }, getLambdaProvisionedConcurrencyAliasLogicalId(functionName) { return `${this.getNormalizedFunctionName(functionName)}ProvConcLambdaAlias`; }, diff --git a/lib/plugins/aws/package/compile/functions/index.js b/lib/plugins/aws/package/compile/functions/index.js index bdad7d6ff..9fcf3f779 100644 --- a/lib/plugins/aws/package/compile/functions/index.js +++ b/lib/plugins/aws/package/compile/functions/index.js @@ -542,6 +542,83 @@ class AwsCompileFunctions { cfTemplate.Resources[ this.provider.naming.getLambdaProvisionedConcurrencyAliasLogicalId(functionName) ] = aliasResource; + + // Note: Following setup is a temporary workaround for a known AWS issue of + // "Alias with weights can not be used with Provisioned Concurrency" error being + // thrown when switching version for same alias which has provisioned concurrency configured + // AWS works currently on a fix, and we were informed it'll be released in the near future + + const codeDeployApplicationLogicalId = this.provider.naming.getCodeDeployApplicationLogicalId(); + const codeDeployDeploymentGroupLogicalId = this.provider.naming.getCodeDeployDeploymentGroupLogicalId(); + aliasResource.UpdatePolicy = { + CodeDeployLambdaAliasUpdate: { + ApplicationName: { Ref: codeDeployApplicationLogicalId }, + DeploymentGroupName: { Ref: codeDeployDeploymentGroupLogicalId }, + }, + }; + if (!cfTemplate.Resources[codeDeployApplicationLogicalId]) { + const codeDeployApplicationResource = { + Type: 'AWS::CodeDeploy::Application', + Properties: { + ComputePlatform: 'Lambda', + }, + }; + const codeDeployDeploymentGroupResource = { + Type: 'AWS::CodeDeploy::DeploymentGroup', + Properties: { + ApplicationName: { Ref: codeDeployApplicationLogicalId }, + AutoRollbackConfiguration: { + Enabled: true, + Events: [ + 'DEPLOYMENT_FAILURE', + 'DEPLOYMENT_STOP_ON_ALARM', + 'DEPLOYMENT_STOP_ON_REQUEST', + ], + }, + DeploymentConfigName: { + 'Fn::Sub': ['CodeDeployDefault.Lambda${ConfigName}', { ConfigName: 'AllAtOnce' }], + }, + DeploymentStyle: { + DeploymentType: 'BLUE_GREEN', + DeploymentOption: 'WITH_TRAFFIC_CONTROL', + }, + }, + }; + Object.assign(cfTemplate.Resources, { + [codeDeployApplicationLogicalId]: codeDeployApplicationResource, + [codeDeployDeploymentGroupLogicalId]: codeDeployDeploymentGroupResource, + }); + + if (this.serverless.service.provider.cfnRole) { + codeDeployDeploymentGroupResource.Properties.ServiceRoleArn = this.serverless.service.provider.cfnRole; + } else { + const codeDeployRoleLogicalId = this.provider.naming.getCodeDeployRoleLogicalId(); + const codeDeployRoleResource = { + Type: 'AWS::IAM::Role', + Properties: { + ManagedPolicyArns: [ + 'arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda', + ], + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: ['sts:AssumeRole'], + Effect: 'Allow', + Principal: { + Service: ['codedeploy.amazonaws.com'], + }, + }, + ], + }, + }, + }; + codeDeployDeploymentGroupResource.Properties.ServiceRoleArn = { + 'Fn::GetAtt': [codeDeployRoleLogicalId, 'Arn'], + }; + cfTemplate.Resources[codeDeployRoleLogicalId] = codeDeployRoleResource; + } + } }); }); }