Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Support for referencing custom configuration in CodeDeploy #848

Merged
merged 8 commits into from
Apr 16, 2019
3 changes: 2 additions & 1 deletion docs/safe_lambda_deployments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ They work as follows:
- **AllAtOnce**: This is an instant shifting of 100% of traffic to new version. This is useful if you want to run
run pre/post hooks but don't want a gradual deployment. If you have a pipeline, you can set Beta/Gamma stages to
deploy instantly because the speed of deployments matter more than safety here.

- **Custom**: Aside from Above mentioned Configurations, Custom Codedeploy configuration are also supported.
(Example. Type: CustomCodeDeployConfiguration)

PreTraffic & PostTraffic Hooks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@
from samtranslator.model.codedeploy import CodeDeployApplication
from samtranslator.model.codedeploy import CodeDeployDeploymentGroup
from samtranslator.model.iam import IAMRole
from samtranslator.model.intrinsics import fnSub
from samtranslator.model.intrinsics import fnSub, is_instrinsic
from samtranslator.model.update_policy import UpdatePolicy
from samtranslator.translator.arn_generator import ArnGenerator
import copy

CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID = 'CodeDeployServiceRole'
CODEDEPLOY_APPLICATION_LOGICAL_ID = 'ServerlessDeploymentApplication'
CODEDEPLOY_PREDEFINED_CONFIGURATIONS_LIST = ["Canary10Percent5Minutes",
"Canary10Percent10Minutes",
"Canary10Percent15Minutes",
"Canary10Percent30Minutes",
"Linear10PercentEvery1Minute",
"Linear10PercentEvery2Minutes",
"Linear10PercentEvery3Minutes",
"Linear10PercentEvery10Minutes",
"AllAtOnce"
]


class DeploymentPreferenceCollection(object):
Expand Down Expand Up @@ -95,6 +106,7 @@ def deployment_group(self, function_logical_id):
:param function_logical_id: logical_id of the function this deployment group belongs to
:return: CodeDeployDeploymentGroup resource
"""

deployment_preference = self.get(function_logical_id)

deployment_group = CodeDeployDeploymentGroup(self.deployment_group_logical_id(function_logical_id))
Expand All @@ -109,8 +121,10 @@ def deployment_group(self, function_logical_id):
'Events': ['DEPLOYMENT_FAILURE',
'DEPLOYMENT_STOP_ON_ALARM',
'DEPLOYMENT_STOP_ON_REQUEST']}
deployment_group.DeploymentConfigName = fnSub("CodeDeployDefault.Lambda${ConfigName}",
{"ConfigName": deployment_preference.deployment_type})

deployment_group.DeploymentConfigName = self._replace_deployment_types(copy.deepcopy(
deployment_preference.deployment_type))

deployment_group.DeploymentStyle = {'DeploymentType': 'BLUE_GREEN',
'DeploymentOption': 'WITH_TRAFFIC_CONTROL'}

Expand All @@ -120,6 +134,20 @@ def deployment_group(self, function_logical_id):

return deployment_group

def _replace_deployment_types(self, value):
if isinstance(value, list):
for i in range(len(value)):
value[i] = self._replace_deployment_types(value[i])
return value
elif is_instrinsic(value):
for (k, v) in value.items():
value[k] = self._replace_deployment_types(v)
return value
else:
if value in CODEDEPLOY_PREDEFINED_CONFIGURATIONS_LIST:
return fnSub("CodeDeployDefault.Lambda${ConfigName}", {"ConfigName": value})
return value

def update_policy(self, function_logical_id):
deployment_preference = self.get(function_logical_id)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Resources:
MinimalFunction:
Type: 'AWS::Serverless::Function'
Properties:
CodeUri: s3://sam-demo-bucket/hello.zip
Handler: hello.handler
Runtime: python2.7
AutoPublishAlias: live
DeploymentPreference:
Type: TestDeploymentConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
EnvType:
Default: dev
Type: String
Conditions:
IsDevEnv: !Equals [!Ref EnvType, dev]
IsDevEnv2: !Equals [!Ref EnvType, prod]
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs8.10
CodeUri: s3://bucket/key
AutoPublishAlias: live
DeploymentPreference:
Type: !If [IsDevEnv, !If [IsDevEnv2, AllAtOnce, TestCustomDeploymentConfig], Canary10Percent15Minutes]
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ Resources:
Runtime: python2.7
AutoPublishAlias: live
DeploymentPreference:
Type: Linear10PercentEvery3Minute
Type: Linear10PercentEvery3Minutes
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Resources:
Runtime: python2.7
AutoPublishAlias: livewithdeploymentwithhooksandalarms
DeploymentPreference:
Type: Linear10PercentEvery2Minute
Type: Linear10PercentEvery2Minutes
Hooks:
PreTraffic: !Ref MySanityTestFunction
PostTraffic: !Ref MyValidationTestFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,11 @@ def test_deployment_group_with_minimal_parameters(self):
'Events': ['DEPLOYMENT_FAILURE',
'DEPLOYMENT_STOP_ON_ALARM',
'DEPLOYMENT_STOP_ON_REQUEST']}
deployment_type = 'deployment_type'
expected_deployment_group.DeploymentConfigName = {
'Fn::Sub': [
'CodeDeployDefault.Lambda${ConfigName}',
{
'ConfigName': deployment_type
'ConfigName': self.deployment_type_global
}
]
}
Expand All @@ -77,12 +76,70 @@ def test_deployment_group_with_minimal_parameters(self):
expected_deployment_group.ServiceRoleArn = {'Fn::GetAtt': [CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID, 'Arn']}

deployment_preference_collection = DeploymentPreferenceCollection()
deployment_preference_collection.add(self.function_logical_id, {'Type': deployment_type})
deployment_preference_collection.add(self.function_logical_id, {'Type': self.deployment_type_global})
deployment_group = deployment_preference_collection.deployment_group(self.function_logical_id)

self.assertEqual(deployment_group.to_dict(),
expected_deployment_group.to_dict())

@patch('boto3.session.Session.region_name', 'ap-southeast-1')
def test_deployment_preference_with_codedeploy_custom_configuration(self):
deployment_type = "TestDeploymentConfiguration"
deployment_preference_collection = DeploymentPreferenceCollection()
deployment_preference_collection.add(self.function_logical_id, {'Type': deployment_type})
deployment_group = deployment_preference_collection.deployment_group(self.function_logical_id)

self.assertEqual(deployment_type, deployment_group.DeploymentConfigName)

@patch('boto3.session.Session.region_name', 'ap-southeast-1')
def test_deployment_preference_with_codedeploy_predifined_configuration(self):
deployment_type = "Canary10Percent5Minutes"
expected_deployment_config_name = {
'Fn::Sub': [
'CodeDeployDefault.Lambda${ConfigName}',
{
'ConfigName': deployment_type
}
]
}
deployment_preference_collection = DeploymentPreferenceCollection()
deployment_preference_collection.add(self.function_logical_id, {'Type': deployment_type})
deployment_group = deployment_preference_collection.deployment_group(self.function_logical_id)

print(deployment_group.DeploymentConfigName)
self.assertEqual(expected_deployment_config_name, deployment_group.DeploymentConfigName)

@patch('boto3.session.Session.region_name', 'ap-southeast-1')
def test_deployment_preference_with_conditional_custom_configuration(self):
deployment_type = {'Fn::If': ['IsDevEnv', {'Fn::If':
['IsDevEnv1', 'AllAtOnce', 'TestDeploymentConfiguration']},
'Canary10Percent15Minutes']}

expected_deployment_config_name = {'Fn::If':
['IsDevEnv', {'Fn::If':
['IsDevEnv1', {'Fn::Sub': [
'CodeDeployDefault.Lambda${ConfigName}',
{
'ConfigName': 'AllAtOnce'
}
]
},
'TestDeploymentConfiguration']},
{'Fn::Sub': [
'CodeDeployDefault.Lambda${ConfigName}',
{
'ConfigName': 'Canary10Percent15Minutes'
}
]
}
]
}
deployment_preference_collection = DeploymentPreferenceCollection()
deployment_preference_collection.add(self.function_logical_id, {'Type': deployment_type})
deployment_group = deployment_preference_collection.deployment_group(self.function_logical_id)
print(deployment_group.DeploymentConfigName)
self.assertEqual(expected_deployment_config_name, deployment_group.DeploymentConfigName)

@patch('boto3.session.Session.region_name', 'ap-southeast-1')
def test_deployment_group_with_all_parameters(self):
expected_deployment_group = CodeDeployDeploymentGroup(self.function_logical_id + 'DeploymentGroup')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"Resources": {
"MinimalFunctionDeploymentGroup": {
"Type": "AWS::CodeDeploy::DeploymentGroup",
"Properties": {
"ApplicationName": {
"Ref": "ServerlessDeploymentApplication"
},
"AutoRollbackConfiguration": {
"Enabled": true,
"Events": [
"DEPLOYMENT_FAILURE",
"DEPLOYMENT_STOP_ON_ALARM",
"DEPLOYMENT_STOP_ON_REQUEST"
]
},
"ServiceRoleArn": {
"Fn::GetAtt": [
"CodeDeployServiceRole",
"Arn"
]
},
"DeploymentConfigName": "TestDeploymentConfiguration",
"DeploymentStyle": {
"DeploymentType": "BLUE_GREEN",
"DeploymentOption": "WITH_TRAFFIC_CONTROL"
}
}
},
"MinimalFunctionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
]
}
}
},
"MinimalFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Handler": "hello.handler",
"Role": {
"Fn::GetAtt": [
"MinimalFunctionRole",
"Arn"
]
},
"Runtime": "python2.7",
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
}
]
}
},
"CodeDeployServiceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda"
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"codedeploy.amazonaws.com"
]
}
}
]
}
}
},
"ServerlessDeploymentApplication": {
"Type": "AWS::CodeDeploy::Application",
"Properties": {
"ComputePlatform": "Lambda"
}
},
"MinimalFunctionVersion640128d35d": {
"DeletionPolicy": "Retain",
"Type": "AWS::Lambda::Version",
"Properties": {
"FunctionName": {
"Ref": "MinimalFunction"
}
}
},
"MinimalFunctionAliaslive": {
"Type": "AWS::Lambda::Alias",
"UpdatePolicy": {
"CodeDeployLambdaAliasUpdate": {
"ApplicationName": {
"Ref": "ServerlessDeploymentApplication"
},
"DeploymentGroupName": {
"Ref": "MinimalFunctionDeploymentGroup"
}
}
},
"Properties": {
"FunctionVersion": {
"Fn::GetAtt": [
"MinimalFunctionVersion640128d35d",
"Version"
]
},
"FunctionName": {
"Ref": "MinimalFunction"
},
"Name": "live"
}
}
}
}
Loading