Skip to content

Commit 7aacc65

Browse files
keetonianhawflau
authored andcommitted
Cover all condition cases, update tests
1 parent 1c2e550 commit 7aacc65

16 files changed

+2693
-92
lines changed

samtranslator/model/preferences/deployment_preference_collection.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
is_intrinsic_if,
1010
is_intrinsic_no_value,
1111
validate_intrinsic_if_items,
12+
make_combined_condition,
13+
ref,
14+
fnGetAtt
1215
)
1316
from samtranslator.model.update_policy import UpdatePolicy
1417
from samtranslator.translator.arn_generator import ArnGenerator
@@ -27,6 +30,7 @@
2730
"Linear10PercentEvery10Minutes",
2831
"AllAtOnce",
2932
]
33+
CODE_DEPLOY_CONDITION_NAME = "ServerlessCodeDeployCondition"
3034

3135

3236
class DeploymentPreferenceCollection(object):
@@ -41,12 +45,10 @@ class DeploymentPreferenceCollection(object):
4145

4246
def __init__(self):
4347
"""
44-
This collection stores an intenral dict of the deployment preferences for each function's
45-
deployment preference in the SAM Template.
48+
This collection stores an internal dict of the deployment preferences for each function's
49+
deployment preference in the SAM Template.
4650
"""
4751
self._resource_preferences = {}
48-
self.codedeploy_application = self._codedeploy_application()
49-
self.codedeploy_iam_role = self._codedeploy_iam_role()
5052

5153
def add(self, logical_id, deployment_preference_dict, condition=None):
5254
"""
@@ -93,20 +95,47 @@ def needs_resource_condition(self):
9395
If all preferences have a condition, all code deploy resources need to be conditionally created
9496
:return: True, if a condition needs to be created
9597
"""
96-
return all(preference.condition for preference in self._resource_preferences.values())
98+
# If there are any enabled deployment preferences without conditions, return false
99+
return self._resource_preferences and not any(
100+
not preference.condition and preference.enabled for preference in self._resource_preferences.values()
101+
)
102+
103+
def get_all_deployment_conditions(self):
104+
"""
105+
Returns a list of all conditions associated with the deployment preference resources
106+
:return: List of condition names
107+
"""
108+
conditions_set = set([preference.condition for preference in self._resource_preferences.values()])
109+
if None in conditions_set:
110+
# None can exist if there are disabled deployment preference(s)
111+
conditions_set.remove(None)
112+
return list(conditions_set)
113+
114+
def create_aggregate_deployment_condition(self):
115+
"""
116+
Creates an aggregate deployment condition if necessary
117+
:return: None if <2 conditions are found, otherwise a dictionary of new conditions to add to template
118+
"""
119+
return make_combined_condition(self.get_all_deployment_conditions(), CODE_DEPLOY_CONDITION_NAME)
97120

98121
def enabled_logical_ids(self):
99122
"""
100123
:return: only the logical id's for the deployment preferences in this collection which are enabled
101124
"""
102125
return [logical_id for logical_id, preference in self._resource_preferences.items() if preference.enabled]
103126

104-
def _codedeploy_application(self):
127+
def get_codedeploy_application(self):
105128
codedeploy_application_resource = CodeDeployApplication(CODEDEPLOY_APPLICATION_LOGICAL_ID)
106129
codedeploy_application_resource.ComputePlatform = "Lambda"
130+
if self.needs_resource_condition():
131+
conditions = self.get_all_deployment_conditions()
132+
condition_name = CODE_DEPLOY_CONDITION_NAME
133+
if len(conditions) <= 1:
134+
condition_name = conditions.pop()
135+
codedeploy_application_resource.set_resource_attribute("Condition", condition_name)
107136
return codedeploy_application_resource
108137

109-
def _codedeploy_iam_role(self):
138+
def get_codedeploy_iam_role(self):
110139
iam_role = IAMRole(CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID)
111140
iam_role.AssumeRolePolicyDocument = {
112141
"Version": "2012-10-17",
@@ -130,6 +159,12 @@ def _codedeploy_iam_role(self):
130159
ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSCodeDeployRoleForLambda")
131160
]
132161

162+
if self.needs_resource_condition():
163+
conditions = self.get_all_deployment_conditions()
164+
condition_name = CODE_DEPLOY_CONDITION_NAME
165+
if len(conditions) <= 1:
166+
condition_name = conditions.pop()
167+
iam_role.set_resource_attribute("Condition", condition_name)
133168
return iam_role
134169

135170
def deployment_group(self, function_logical_id):
@@ -147,7 +182,7 @@ def deployment_group(self, function_logical_id):
147182
except ValueError as e:
148183
raise InvalidResourceException(function_logical_id, str(e))
149184

150-
deployment_group.ApplicationName = self.codedeploy_application.get_runtime_attr("name")
185+
deployment_group.ApplicationName = ref(CODEDEPLOY_APPLICATION_LOGICAL_ID)
151186
deployment_group.AutoRollbackConfiguration = {
152187
"Enabled": True,
153188
"Events": ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST"],
@@ -159,7 +194,7 @@ def deployment_group(self, function_logical_id):
159194

160195
deployment_group.DeploymentStyle = {"DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL"}
161196

162-
deployment_group.ServiceRoleArn = self.codedeploy_iam_role.get_runtime_attr("arn")
197+
deployment_group.ServiceRoleArn = fnGetAtt(CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID, "Arn")
163198
if deployment_preference.role:
164199
deployment_group.ServiceRoleArn = deployment_preference.role
165200

@@ -253,7 +288,7 @@ def update_policy(self, function_logical_id):
253288
deployment_preference = self.get(function_logical_id)
254289

255290
return UpdatePolicy(
256-
self.codedeploy_application.get_runtime_attr("name"),
291+
ref(CODEDEPLOY_APPLICATION_LOGICAL_ID),
257292
self.deployment_group(function_logical_id).get_runtime_attr("name"),
258293
deployment_preference.pre_traffic_hook,
259294
deployment_preference.post_traffic_hook,

samtranslator/translator/translator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,14 @@ def translate(self, sam_template, parameter_values, feature_toggle=None, passthr
167167
document_errors.append(e)
168168

169169
if deployment_preference_collection.any_enabled():
170-
template["Resources"].update(deployment_preference_collection.codedeploy_application.to_dict())
170+
template["Resources"].update(deployment_preference_collection.get_codedeploy_application().to_dict())
171+
if deployment_preference_collection.needs_resource_condition():
172+
new_conditions = deployment_preference_collection.create_aggregate_deployment_condition()
173+
if new_conditions:
174+
template.get("Conditions").update(new_conditions)
171175

172176
if not deployment_preference_collection.can_skip_service_role():
173-
template["Resources"].update(deployment_preference_collection.codedeploy_iam_role.to_dict())
177+
template["Resources"].update(deployment_preference_collection.get_codedeploy_iam_role().to_dict())
174178

175179
for logical_id in deployment_preference_collection.enabled_logical_ids():
176180
try:

tests/translator/input/function_with_deployment_no_service_role.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
Conditions:
2+
Condition1:
3+
Fn::Equals:
4+
- true
5+
- true
6+
17
Globals:
28
Function:
39
AutoPublishAlias: live
@@ -15,6 +21,7 @@ Resources:
1521
Runtime: python2.7
1622

1723
OtherFunction:
24+
Condition: Condition1
1825
Type: 'AWS::Serverless::Function'
1926
Properties:
2027
CodeUri: s3://sam-demo-bucket/hello.zip
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Parameters:
2+
FnName:
3+
Type: String
4+
ProvisionedConcurrency:
5+
Type: String
6+
Default: 10
7+
EnableAliasProvisionedConcurrency:
8+
Type: String
9+
AllowedValues:
10+
- true
11+
- false
12+
Default: true
13+
Conditions:
14+
AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true]
15+
FunctionCondition: !Equals [true, true]
16+
Resources:
17+
MinimalFunction:
18+
Type: 'AWS::Serverless::Function'
19+
Condition: FunctionCondition
20+
Properties:
21+
CodeUri: s3://sam-demo-bucket/hello.zip
22+
Handler: hello.handler
23+
Runtime: python2.7
24+
AutoPublishAlias: live
25+
DeploymentPreference:
26+
Type: Linear10PercentEvery3Minutes
27+
ProvisionedConcurrencyConfig: !If
28+
- AliasProvisionedConcurrencyEnabled
29+
- ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency
30+
- !Ref 'AWS::NoValue'
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
Conditions:
2+
Condition1:
3+
Fn::Equals:
4+
- true
5+
- true
6+
Condition2:
7+
Fn::Equals:
8+
- true
9+
- false
10+
Condition3:
11+
Fn::Equals:
12+
- true
13+
- false
14+
15+
Parameters:
16+
MyFalseParameter:
17+
Type: String
18+
Default: False
19+
20+
Resources:
21+
MinimalFunction:
22+
Condition: Condition1
23+
Type: 'AWS::Serverless::Function'
24+
Properties:
25+
CodeUri: s3://sam-demo-bucket/hello.zip
26+
Handler: hello.handler
27+
Runtime: python2.7
28+
AutoPublishAlias: live
29+
DeploymentPreference:
30+
Type: Linear10PercentEvery2Minutes
31+
32+
MinimalFunctionWithMinimalDeploymentPreference:
33+
Type: 'AWS::Serverless::Function'
34+
Condition: Condition2
35+
Properties:
36+
CodeUri: s3://sam-demo-bucket/hello.zip
37+
Handler: hello.handler
38+
Runtime: python2.7
39+
AutoPublishAlias: livewithdeployment
40+
DeploymentPreference:
41+
Type: Canary10Percent5Minutes
42+
43+
MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms:
44+
Type: 'AWS::Serverless::Function'
45+
Condition: Condition2
46+
Properties:
47+
CodeUri: s3://sam-demo-bucket/hello.zip
48+
Handler: hello.handler
49+
Runtime: python2.7
50+
AutoPublishAlias: livewithdeploymentwithhooksandalarms
51+
DeploymentPreference:
52+
Type: Linear10PercentEvery2Minutes
53+
Hooks:
54+
PreTraffic: !Ref MySanityTestFunction
55+
PostTraffic: !Ref MyValidationTestFunction
56+
Alarms:
57+
- !Ref MyCloudWatchAlarm
58+
59+
MySanityTestFunction:
60+
Type: 'AWS::Serverless::Function'
61+
Condition: Condition3
62+
Properties:
63+
Handler: hello.handler
64+
Runtime: python2.7
65+
CodeUri: s3://my-bucket/mySanityTestFunction.zip
66+
DeploymentPreference:
67+
Enabled: False
68+
69+
MyValidationTestFunction:
70+
Type: 'AWS::Serverless::Function'
71+
Properties:
72+
Handler: hello.handler
73+
Runtime: python2.7
74+
CodeUri: s3://my-bucket/myValidationTestFunction.zip
75+
DeploymentPreference:
76+
Enabled: !Ref MyFalseParameter
77+
78+
MyCloudWatchAlarm:
79+
Type: AWS::CloudWatch::Alarm
80+
Properties:
81+
ComparisonOperator: GreaterThanThreshold
82+
EvaluationPeriods: 1
83+
MetricName: MyMetric
84+
Namespace: AWS/EC2
85+
Period: 300
86+
Threshold: 10

tests/translator/model/preferences/test_deployment_preference_collection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def setup_method(self, method):
1818
self.post_traffic_host_global = "post_traffic_function_ref"
1919
self.pre_traffic_hook_global = "pre_traffic_function_ref"
2020
self.function_logical_id = "FunctionLogicalId"
21+
self.condition = "CodeDeployCondition"
2122

2223
@patch("boto3.session.Session.region_name", "ap-southeast-1")
2324
def test_when_no_global_dict_each_local_deployment_preference_requires_parameters(self):
@@ -37,7 +38,7 @@ def test_codedeploy_application(self):
3738
expected_codedeploy_application_resource.ComputePlatform = "Lambda"
3839

3940
self.assertEqual(
40-
DeploymentPreferenceCollection().codedeploy_application.to_dict(),
41+
DeploymentPreferenceCollection().get_codedeploy_application().to_dict(),
4142
expected_codedeploy_application_resource.to_dict(),
4243
)
4344

@@ -59,7 +60,7 @@ def test_codedeploy_iam_role(self):
5960
]
6061

6162
self.assertEqual(
62-
DeploymentPreferenceCollection().codedeploy_iam_role.to_dict(), expected_codedeploy_iam_role.to_dict()
63+
DeploymentPreferenceCollection().get_codedeploy_iam_role().to_dict(), expected_codedeploy_iam_role.to_dict()
6364
)
6465

6566
@patch("boto3.session.Session.region_name", "ap-southeast-1")

0 commit comments

Comments
 (0)