From e1260bca06b8ac4cf8ecf72b0f4d3234b166b2b6 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Wed, 18 Jan 2017 19:59:47 -0800 Subject: [PATCH] cloudformation: Support RoleARN for create and update stack (#807) Signed-off-by: Andrew Harris --- moto/cloudformation/models.py | 13 ++++++++----- moto/cloudformation/responses.py | 7 +++++++ .../test_cloudformation_stack_crud_boto3.py | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index d9d09410d..1f091251b 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -11,7 +11,7 @@ from .exceptions import ValidationError class FakeStack(object): - def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None, tags=None): + def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None): self.stack_id = stack_id self.name = name self.template = template @@ -19,6 +19,7 @@ class FakeStack(object): self.parameters = parameters self.region_name = region_name self.notification_arns = notification_arns if notification_arns else [] + self.role_arn = role_arn self.tags = tags if tags else {} self.events = [] self._add_stack_event("CREATE_IN_PROGRESS", resource_status_reason="User Initiated") @@ -77,13 +78,14 @@ class FakeStack(object): def stack_outputs(self): return self.output_map.values() - def update(self, template): + def update(self, template, role_arn=None): self._add_stack_event("UPDATE_IN_PROGRESS", resource_status_reason="User Initiated") self.template = template self.resource_map.update(json.loads(template)) self.output_map = self._create_output_map() self._add_stack_event("UPDATE_COMPLETE") self.status = "UPDATE_COMPLETE" + self.role_arn = role_arn def delete(self): self._add_stack_event("DELETE_IN_PROGRESS", resource_status_reason="User Initiated") @@ -111,7 +113,7 @@ class CloudFormationBackend(BaseBackend): self.stacks = {} self.deleted_stacks = {} - def create_stack(self, name, template, parameters, region_name, notification_arns=None, tags=None): + def create_stack(self, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None): stack_id = generate_stack_id(name) new_stack = FakeStack( stack_id=stack_id, @@ -121,6 +123,7 @@ class CloudFormationBackend(BaseBackend): region_name=region_name, notification_arns=notification_arns, tags=tags, + role_arn=role_arn, ) self.stacks[stack_id] = new_stack return new_stack @@ -154,9 +157,9 @@ class CloudFormationBackend(BaseBackend): if stack.name == name_or_stack_id: return stack - def update_stack(self, name, template): + def update_stack(self, name, template, role_arn=None): stack = self.get_stack(name) - stack.update(template) + stack.update(template, role_arn) return stack def list_stack_resources(self, stack_name_or_id): diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index 9cab62a63..d16b3560c 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -27,6 +27,7 @@ class CloudFormationResponse(BaseResponse): stack_name = self._get_param('StackName') stack_body = self._get_param('TemplateBody') template_url = self._get_param('TemplateURL') + role_arn = self._get_param('RoleARN') parameters_list = self._get_list_prefix("Parameters.member") tags = dict((item['key'], item['value']) for item in self._get_list_prefix("Tags.member")) @@ -47,6 +48,7 @@ class CloudFormationResponse(BaseResponse): region_name=self.region, notification_arns=stack_notification_arns, tags=tags, + role_arn=role_arn, ) if self.request_json: return json.dumps({ @@ -131,6 +133,7 @@ class CloudFormationResponse(BaseResponse): def update_stack(self): stack_name = self._get_param('StackName') + role_arn = self._get_param('RoleARN') if self._get_param('UsePreviousTemplate') == "true": stack_body = self.cloudformation_backend.get_stack(stack_name).template else: @@ -143,6 +146,7 @@ class CloudFormationResponse(BaseResponse): stack = self.cloudformation_backend.update_stack( name=stack_name, template=stack_body, + role_arn=role_arn, ) if self.request_json: stack_body = { @@ -227,6 +231,9 @@ DESCRIBE_STACKS_TEMPLATE = """ {% endfor %} + {% if stack.role_arn %} + {{ stack.role_arn }} + {% endif %} {% for tag_key, tag_value in stack.tags.items() %} diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index 4197d2628..97c3e864a 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -104,6 +104,19 @@ def test_create_stack_with_notification_arn(): 'arn:aws:sns:us-east-1:123456789012:fake-queue') +@mock_cloudformation +def test_create_stack_with_role_arn(): + cf = boto3.resource('cloudformation', region_name='us-east-1') + cf.create_stack( + StackName="test_stack_with_notifications", + TemplateBody=dummy_template_json, + RoleARN='arn:aws:iam::123456789012:role/moto', + ) + + stack = list(cf.stacks.all())[0] + stack.role_arn.should.equal('arn:aws:iam::123456789012:role/moto') + + @mock_cloudformation @mock_s3 def test_create_stack_from_s3_url(): @@ -240,6 +253,7 @@ def test_describe_updated_stack(): cf_conn.update_stack( StackName="test_stack", + RoleARN='arn:aws:iam::123456789012:role/moto', TemplateBody=dummy_update_template_json) stack = cf_conn.describe_stacks(StackName="test_stack")['Stacks'][0] @@ -248,6 +262,7 @@ def test_describe_updated_stack(): stack_by_id['StackId'].should.equal(stack['StackId']) stack_by_id['StackName'].should.equal("test_stack") stack_by_id['StackStatus'].should.equal("UPDATE_COMPLETE") + stack_by_id['RoleARN'].should.equal('arn:aws:iam::123456789012:role/moto') @mock_cloudformation