diff --git a/docs/safe_lambda_deployments.rst b/docs/safe_lambda_deployments.rst index 6f0b741270..437faa588e 100644 --- a/docs/safe_lambda_deployments.rst +++ b/docs/safe_lambda_deployments.rst @@ -166,7 +166,7 @@ resource: Runtime: nodejs8.10 FunctionName: 'CodeDeployHook_preTrafficHook' DeploymentPreference: - Enabled: false + Enabled: False Role: "" Environment: Variables: @@ -273,7 +273,7 @@ Hooks are extremely powerful because: FunctionName: 'CodeDeployHook_preTrafficHook' DeploymentPreference: - Enabled: false + Enabled: False Policies: - Version: "2012-10-17" Statement: diff --git a/examples/2016-10-31/lambda_safe_deployments/template.yaml b/examples/2016-10-31/lambda_safe_deployments/template.yaml index 49c31e7c7a..1079f8c591 100644 --- a/examples/2016-10-31/lambda_safe_deployments/template.yaml +++ b/examples/2016-10-31/lambda_safe_deployments/template.yaml @@ -38,7 +38,7 @@ Resources: Runtime: nodejs8.10 FunctionName: 'CodeDeployHook_preTrafficHook' DeploymentPreference: - Enabled: false + Enabled: False Environment: Variables: CurrentVersion: !Ref safeTest.Version diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index f2f56e7943..bf4cc101b7 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -421,10 +421,11 @@ def _construct_alias(self, name, function, version): def _validate_deployment_preference_and_add_update_policy(self, deployment_preference_collection, lambda_alias, intrinsics_resolver, mappings_resolver): if 'Enabled' in self.DeploymentPreference: - self.DeploymentPreference['Enabled'] = intrinsics_resolver.resolve_parameter_refs( - self.DeploymentPreference['Enabled']) - if isinstance(self.DeploymentPreference['Enabled'], dict): - raise InvalidResourceException(self.logical_id, "'Enabled' must be a boolean value") + # resolve intrinsics and mappings for Type + enabled = self.DeploymentPreference['Enabled'] + enabled = intrinsics_resolver.resolve_parameter_refs(enabled) + enabled = mappings_resolver.resolve_parameter_refs(enabled) + self.DeploymentPreference['Enabled'] = enabled if 'Type' in self.DeploymentPreference: # resolve intrinsics and mappings for Type diff --git a/samtranslator/sdk/parameter.py b/samtranslator/sdk/parameter.py index 350e463311..57deee442d 100644 --- a/samtranslator/sdk/parameter.py +++ b/samtranslator/sdk/parameter.py @@ -65,3 +65,14 @@ def add_pseudo_parameter_values(self): """ if 'AWS::Region' not in self.parameter_values: self.parameter_values['AWS::Region'] = boto3.session.Session().region_name + + if 'AWS::Partition' not in self.parameter_values: + region = boto3.session.Session().region_name + + # neither boto nor botocore has any way of returning the partition value yet + if region.startswith('cn-'): + self.parameter_values['AWS::Partition'] = 'aws-cn' + elif region.startswith('us-gov-'): + self.parameter_values['AWS::Partition'] = 'aws-us-gov' + else: + self.parameter_values['AWS::Partition'] = 'aws' diff --git a/tests/sdk/test_parameter.py b/tests/sdk/test_parameter.py index d2eade0381..10c749a658 100644 --- a/tests/sdk/test_parameter.py +++ b/tests/sdk/test_parameter.py @@ -114,7 +114,8 @@ def test_add_pseudo_parameter_values_aws_region(self): expected = { "Param1": "value1", - "AWS::Region": "ap-southeast-1" + "AWS::Region": "ap-southeast-1", + "AWS::Partition": "aws" } sam_parameter_values = SamParameterValues(parameter_values) @@ -128,7 +129,39 @@ def test_add_pseudo_parameter_values_aws_region_not_override(self): } expected = { - "AWS::Region": "value1" + "AWS::Region": "value1", + "AWS::Partition": "aws" + } + + sam_parameter_values = SamParameterValues(parameter_values) + sam_parameter_values.add_pseudo_parameter_values() + self.assertEqual(expected, sam_parameter_values.parameter_values) + + @patch('boto3.session.Session.region_name', 'us-gov-west-1') + def test_add_pseudo_parameter_values_aws_partition(self): + parameter_values = { + "Param1": "value1" + } + + expected = { + "Param1": "value1", + "AWS::Region": "us-gov-west-1", + "AWS::Partition": "aws-us-gov" + } + + sam_parameter_values = SamParameterValues(parameter_values) + sam_parameter_values.add_pseudo_parameter_values() + self.assertEqual(expected, sam_parameter_values.parameter_values) + + @patch('boto3.session.Session.region_name', 'us-gov-west-1') + def test_add_pseudo_parameter_values_aws_partition_not_override(self): + parameter_values = { + "AWS::Partition": "aws" + } + + expected = { + "AWS::Partition": "aws", + "AWS::Region": "us-gov-west-1" } sam_parameter_values = SamParameterValues(parameter_values) diff --git a/tests/translator/input/function_with_deployment_preference_all_parameters.yaml b/tests/translator/input/function_with_deployment_preference_all_parameters.yaml index 00fdce01a9..5b0311484b 100644 --- a/tests/translator/input/function_with_deployment_preference_all_parameters.yaml +++ b/tests/translator/input/function_with_deployment_preference_all_parameters.yaml @@ -7,7 +7,7 @@ Resources: Runtime: python2.7 AutoPublishAlias: live DeploymentPreference: - Enabled: true + Enabled: True Type: Linear10PercentEvery1Minute Hooks: PreTraffic: !Ref MySanityTestFunction @@ -22,7 +22,7 @@ Resources: Runtime: python2.7 CodeUri: s3://my-bucket/mySanityTestFunction.zip DeploymentPreference: - Enabled: false + Enabled: False MyValidationTestFunction: Type: 'AWS::Serverless::Function' @@ -31,7 +31,7 @@ Resources: Runtime: python2.7 CodeUri: s3://my-bucket/myValidationTestFunction.zip DeploymentPreference: - Enabled: false + Enabled: False MyCloudWatchAlarm: Type: AWS::CloudWatch::Alarm diff --git a/tests/translator/input/function_with_deployment_preference_multiple_combinations.yaml b/tests/translator/input/function_with_deployment_preference_multiple_combinations.yaml index 588983aa04..9cc5703d8a 100644 --- a/tests/translator/input/function_with_deployment_preference_multiple_combinations.yaml +++ b/tests/translator/input/function_with_deployment_preference_multiple_combinations.yaml @@ -39,7 +39,7 @@ Resources: Runtime: python2.7 CodeUri: s3://my-bucket/mySanityTestFunction.zip DeploymentPreference: - Enabled: false + Enabled: False MyValidationTestFunction: Type: 'AWS::Serverless::Function' @@ -48,7 +48,7 @@ Resources: Runtime: python2.7 CodeUri: s3://my-bucket/myValidationTestFunction.zip DeploymentPreference: - Enabled: false + Enabled: False MyCloudWatchAlarm: Type: AWS::CloudWatch::Alarm diff --git a/tests/translator/input/function_with_disabled_deployment_preference.yaml b/tests/translator/input/function_with_disabled_deployment_preference.yaml index 092fd455ed..26b7dc8e14 100644 --- a/tests/translator/input/function_with_disabled_deployment_preference.yaml +++ b/tests/translator/input/function_with_disabled_deployment_preference.yaml @@ -7,5 +7,5 @@ Resources: Runtime: python2.7 AutoPublishAlias: live DeploymentPreference: - Enabled: false - Type: AllAtOnce \ No newline at end of file + Enabled: False + Type: AllAtOnce diff --git a/tests/translator/test_function_resources.py b/tests/translator/test_function_resources.py index 2439a9a8ab..e89f76ee08 100644 --- a/tests/translator/test_function_resources.py +++ b/tests/translator/test_function_resources.py @@ -170,6 +170,7 @@ def test_sam_function_with_disabled_deployment_preference_does_not_add_update_po kwargs["managed_policy_map"] = {"a": "b"} kwargs["event_resources"] = [] kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock preference_collection = self._make_deployment_preference_collection() preference_collection.get.return_value = DeploymentPreference.from_dict(sam_func.logical_id, deploy_preference_dict) @@ -227,6 +228,8 @@ def test_sam_function_without_alias_allows_disabled_deployment_preference(self): kwargs["managed_policy_map"] = {"a": "b"} kwargs["event_resources"] = [] kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock + preference_collection = self._make_deployment_preference_collection() preference_collection.get.return_value = DeploymentPreference.from_dict(sam_func.logical_id, deploy_preference_dict) @@ -280,8 +283,9 @@ def test_sam_function_with_deployment_preference_intrinsic_ref_enabled_boolean_p self.assertTrue("UpdatePolicy" in list(aliases[0].values())[0]) self.assertEqual(list(aliases[0].values())[0]["UpdatePolicy"], self.update_policy().to_dict()) + @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch.object(SamFunction, "_get_resolved_alias_name") - def test_sam_function_with_deployment_preference_instrinsic_ref_enabled_dict_parameter(self, get_resolved_alias_name_mock): + def test_sam_function_with_deployment_preference_intrinsic_ref_enabled_dict_parameter(self, get_resolved_alias_name_mock): alias_name = "AliasName" enabled = {"Ref": "MyEnabledFlag"} deploy_preference_dict = {"Type": "LINEAR", "Enabled": enabled} @@ -302,13 +306,47 @@ def test_sam_function_with_deployment_preference_instrinsic_ref_enabled_dict_par kwargs["managed_policy_map"] = {"a": "b"} kwargs["event_resources"] = [] kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock deployment_preference_collection = self._make_deployment_preference_collection() kwargs['deployment_preference_collection'] = deployment_preference_collection - self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = {"key": "value"} + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = {"MyEnabledFlag": True} get_resolved_alias_name_mock.return_value = alias_name - with self.assertRaises(InvalidResourceException): - sam_func.to_cloudformation(**kwargs) + sam_func.to_cloudformation(**kwargs) + self.assertTrue(sam_func.DeploymentPreference['Enabled']) + + @patch('boto3.session.Session.region_name', 'ap-southeast-1') + @patch.object(SamFunction, "_get_resolved_alias_name") + def test_sam_function_with_deployment_preference_intrinsic_findinmap_enabled_dict_parameter(self, get_resolved_alias_name_mock): + alias_name = "AliasName" + enabled = {"Fn::FindInMap": ["FooMap", "FooKey", "Enabled"]} + deploy_preference_dict = {"Type": "LINEAR", "Enabled": enabled} + func = { + "Type": "AWS::Serverless::Function", + "Properties": { + "CodeUri": self.code_uri, + "Runtime": "nodejs4.3", + "Handler": "index.handler", + "AutoPublishAlias": alias_name, + "DeploymentPreference": deploy_preference_dict + } + } + + sam_func = SamFunction.from_dict(logical_id="foo", resource_dict=func) + + kwargs = dict() + kwargs["managed_policy_map"] = {"a": "b"} + kwargs["event_resources"] = [] + kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock + deployment_preference_collection = self._make_deployment_preference_collection() + kwargs['deployment_preference_collection'] = deployment_preference_collection + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = {"MyEnabledFlag": True} + self.mappings_resolver_mock.resolve_parameter_refs.return_value = True + get_resolved_alias_name_mock.return_value = alias_name + + sam_func.to_cloudformation(**kwargs) + self.assertTrue(sam_func.DeploymentPreference['Enabled']) @patch("samtranslator.translator.logical_id_generator.LogicalIdGenerator") def test_version_creation(self, LogicalIdGeneratorMock): diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index b29eb843b4..a8719bb156 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -779,6 +779,7 @@ def _do_transform(self, document, parameter_values=get_template_parameter_values class TestTemplateValidation(TestCase): + @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('botocore.client.ClientEndpointBridge._check_default_region', mock_get_region) def test_throws_when_resource_not_found(self): template = { @@ -790,6 +791,7 @@ def test_throws_when_resource_not_found(self): translator = Translator({}, sam_parser) translator.translate(template, {}) + @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('botocore.client.ClientEndpointBridge._check_default_region', mock_get_region) def test_throws_when_resource_is_empty(self): template = { @@ -802,6 +804,7 @@ def test_throws_when_resource_is_empty(self): translator.translate(template, {}) + @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('botocore.client.ClientEndpointBridge._check_default_region', mock_get_region) def test_throws_when_resource_is_not_dict(self): template = { @@ -814,6 +817,7 @@ def test_throws_when_resource_is_not_dict(self): translator.translate(template, {}) + @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('botocore.client.ClientEndpointBridge._check_default_region', mock_get_region) def test_throws_when_resources_not_all_dicts(self): template = { @@ -915,7 +919,7 @@ def test_transform_method_must_inject_plugins_when_creating_resources(self, resource_from_dict_mock.assert_called_with("MyTable", manifest["Resources"]["MyTable"], sam_plugins=sam_plugins_object_mock) - prepare_plugins_mock.assert_called_once_with(initial_plugins, {"AWS::Region": "ap-southeast-1"}) + prepare_plugins_mock.assert_called_once_with(initial_plugins, {"AWS::Region": "ap-southeast-1", "AWS::Partition": "aws"}) def get_policy_mock(): mock_policy_loader = MagicMock() diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index 0070e18b77..c991fa6002 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -782,7 +782,7 @@ Specifies the configurations to enable Safe Lambda Deployments. Read the [usage ```yaml DeploymentPreference: - Enabled: true + Enabled: True # Set to False to disable. Supports all intrinsics. Type: Linear10PercentEvery10Minutes Alarms: # A list of alarms that you want to monitor