cloudformation: Support RoleARN for create and update stack (#807)

Signed-off-by: Andrew Harris <andrew.harris@getbraintree.com>
This commit is contained in:
Jesse Szwedko 2017-01-18 19:59:47 -08:00 committed by Steve Pulec
parent f68b2963db
commit e1260bca06
3 changed files with 30 additions and 5 deletions

View File

@ -11,7 +11,7 @@ from .exceptions import ValidationError
class FakeStack(object): 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.stack_id = stack_id
self.name = name self.name = name
self.template = template self.template = template
@ -19,6 +19,7 @@ class FakeStack(object):
self.parameters = parameters self.parameters = parameters
self.region_name = region_name self.region_name = region_name
self.notification_arns = notification_arns if notification_arns else [] self.notification_arns = notification_arns if notification_arns else []
self.role_arn = role_arn
self.tags = tags if tags else {} self.tags = tags if tags else {}
self.events = [] self.events = []
self._add_stack_event("CREATE_IN_PROGRESS", resource_status_reason="User Initiated") self._add_stack_event("CREATE_IN_PROGRESS", resource_status_reason="User Initiated")
@ -77,13 +78,14 @@ class FakeStack(object):
def stack_outputs(self): def stack_outputs(self):
return self.output_map.values() 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._add_stack_event("UPDATE_IN_PROGRESS", resource_status_reason="User Initiated")
self.template = template self.template = template
self.resource_map.update(json.loads(template)) self.resource_map.update(json.loads(template))
self.output_map = self._create_output_map() self.output_map = self._create_output_map()
self._add_stack_event("UPDATE_COMPLETE") self._add_stack_event("UPDATE_COMPLETE")
self.status = "UPDATE_COMPLETE" self.status = "UPDATE_COMPLETE"
self.role_arn = role_arn
def delete(self): def delete(self):
self._add_stack_event("DELETE_IN_PROGRESS", resource_status_reason="User Initiated") self._add_stack_event("DELETE_IN_PROGRESS", resource_status_reason="User Initiated")
@ -111,7 +113,7 @@ class CloudFormationBackend(BaseBackend):
self.stacks = {} self.stacks = {}
self.deleted_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) stack_id = generate_stack_id(name)
new_stack = FakeStack( new_stack = FakeStack(
stack_id=stack_id, stack_id=stack_id,
@ -121,6 +123,7 @@ class CloudFormationBackend(BaseBackend):
region_name=region_name, region_name=region_name,
notification_arns=notification_arns, notification_arns=notification_arns,
tags=tags, tags=tags,
role_arn=role_arn,
) )
self.stacks[stack_id] = new_stack self.stacks[stack_id] = new_stack
return new_stack return new_stack
@ -154,9 +157,9 @@ class CloudFormationBackend(BaseBackend):
if stack.name == name_or_stack_id: if stack.name == name_or_stack_id:
return stack return stack
def update_stack(self, name, template): def update_stack(self, name, template, role_arn=None):
stack = self.get_stack(name) stack = self.get_stack(name)
stack.update(template) stack.update(template, role_arn)
return stack return stack
def list_stack_resources(self, stack_name_or_id): def list_stack_resources(self, stack_name_or_id):

View File

@ -27,6 +27,7 @@ class CloudFormationResponse(BaseResponse):
stack_name = self._get_param('StackName') stack_name = self._get_param('StackName')
stack_body = self._get_param('TemplateBody') stack_body = self._get_param('TemplateBody')
template_url = self._get_param('TemplateURL') template_url = self._get_param('TemplateURL')
role_arn = self._get_param('RoleARN')
parameters_list = self._get_list_prefix("Parameters.member") parameters_list = self._get_list_prefix("Parameters.member")
tags = dict((item['key'], item['value']) for item in self._get_list_prefix("Tags.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, region_name=self.region,
notification_arns=stack_notification_arns, notification_arns=stack_notification_arns,
tags=tags, tags=tags,
role_arn=role_arn,
) )
if self.request_json: if self.request_json:
return json.dumps({ return json.dumps({
@ -131,6 +133,7 @@ class CloudFormationResponse(BaseResponse):
def update_stack(self): def update_stack(self):
stack_name = self._get_param('StackName') stack_name = self._get_param('StackName')
role_arn = self._get_param('RoleARN')
if self._get_param('UsePreviousTemplate') == "true": if self._get_param('UsePreviousTemplate') == "true":
stack_body = self.cloudformation_backend.get_stack(stack_name).template stack_body = self.cloudformation_backend.get_stack(stack_name).template
else: else:
@ -143,6 +146,7 @@ class CloudFormationResponse(BaseResponse):
stack = self.cloudformation_backend.update_stack( stack = self.cloudformation_backend.update_stack(
name=stack_name, name=stack_name,
template=stack_body, template=stack_body,
role_arn=role_arn,
) )
if self.request_json: if self.request_json:
stack_body = { stack_body = {
@ -227,6 +231,9 @@ DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResponse>
</member> </member>
{% endfor %} {% endfor %}
</Parameters> </Parameters>
{% if stack.role_arn %}
<RoleARN>{{ stack.role_arn }}</RoleARN>
{% endif %}
<Tags> <Tags>
{% for tag_key, tag_value in stack.tags.items() %} {% for tag_key, tag_value in stack.tags.items() %}
<member> <member>

View File

@ -104,6 +104,19 @@ def test_create_stack_with_notification_arn():
'arn:aws:sns:us-east-1:123456789012:fake-queue') '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_cloudformation
@mock_s3 @mock_s3
def test_create_stack_from_s3_url(): def test_create_stack_from_s3_url():
@ -240,6 +253,7 @@ def test_describe_updated_stack():
cf_conn.update_stack( cf_conn.update_stack(
StackName="test_stack", StackName="test_stack",
RoleARN='arn:aws:iam::123456789012:role/moto',
TemplateBody=dummy_update_template_json) TemplateBody=dummy_update_template_json)
stack = cf_conn.describe_stacks(StackName="test_stack")['Stacks'][0] 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['StackId'].should.equal(stack['StackId'])
stack_by_id['StackName'].should.equal("test_stack") stack_by_id['StackName'].should.equal("test_stack")
stack_by_id['StackStatus'].should.equal("UPDATE_COMPLETE") stack_by_id['StackStatus'].should.equal("UPDATE_COMPLETE")
stack_by_id['RoleARN'].should.equal('arn:aws:iam::123456789012:role/moto')
@mock_cloudformation @mock_cloudformation