CF : Added support for get template summary (#3179)

* CF : Added support for get template summary

* Linting

Co-authored-by: Bert Blommers <info@bertblommers.nl>
This commit is contained in:
usmangani1 2020-07-27 18:38:01 +05:30 committed by GitHub
parent 03dd55d39d
commit bc1674cb19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 9 deletions

View File

@ -10,6 +10,31 @@ from moto.s3 import s3_backend
from moto.core import ACCOUNT_ID
from .models import cloudformation_backends
from .exceptions import ValidationError
from .utils import yaml_tag_constructor
def get_template_summary_response_from_template(template_body):
def get_resource_types(template_dict):
resources = {}
for key, value in template_dict.items():
if key == "Resources":
resources = value
resource_types = []
for key, value in resources.items():
resource_types.append(value["Type"])
return resource_types
yaml.add_multi_constructor("", yaml_tag_constructor)
try:
template_dict = yaml.load(template_body, Loader=yaml.Loader)
except (yaml.parser.ParserError, yaml.scanner.ScannerError):
template_dict = json.loads(template_body)
resources_types = get_resource_types(template_dict)
template_dict["resourceTypes"] = resources_types
return template_dict
class CloudFormationResponse(BaseResponse):
@ -269,6 +294,20 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
return template.render(stack=stack)
def get_template_summary(self):
stack_name = self._get_param("StackName")
template_url = self._get_param("TemplateURL")
stack_body = self._get_param("TemplateBody")
if stack_name:
stack_body = self.cloudformation_backend.get_stack(stack_name).template
elif template_url:
stack_body = self._get_stack_from_s3_url(template_url)
template_summary = get_template_summary_response_from_template(stack_body)
template = self.response_template(GET_TEMPLATE_SUMMARY_TEMPLATE)
return template.render(template_summary=template_summary)
def update_stack(self):
stack_name = self._get_param("StackName")
role_arn = self._get_param("RoleARN")
@ -743,7 +782,6 @@ DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResponse>
</DescribeStacksResult>
</DescribeStacksResponse>"""
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
<DescribeStackResourceResult>
<StackResourceDetail>
@ -758,7 +796,6 @@ DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
</DescribeStackResourceResult>
</DescribeStackResourceResponse>"""
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
<DescribeStackResourcesResult>
<StackResources>
@ -777,7 +814,6 @@ DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
</DescribeStackResourcesResult>
</DescribeStackResourcesResponse>"""
DESCRIBE_STACK_EVENTS_RESPONSE = """<DescribeStackEventsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
<DescribeStackEventsResult>
<StackEvents>
@ -802,7 +838,6 @@ DESCRIBE_STACK_EVENTS_RESPONSE = """<DescribeStackEventsResponse xmlns="http://c
</ResponseMetadata>
</DescribeStackEventsResponse>"""
LIST_CHANGE_SETS_RESPONSE = """<ListChangeSetsResponse>
<ListChangeSetsResult>
<Summaries>
@ -823,7 +858,6 @@ LIST_CHANGE_SETS_RESPONSE = """<ListChangeSetsResponse>
</ListChangeSetsResult>
</ListChangeSetsResponse>"""
LIST_STACKS_RESPONSE = """<ListStacksResponse>
<ListStacksResult>
<StackSummaries>
@ -840,7 +874,6 @@ LIST_STACKS_RESPONSE = """<ListStacksResponse>
</ListStacksResult>
</ListStacksResponse>"""
LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
<ListStackResourcesResult>
<StackResourceSummaries>
@ -860,7 +893,6 @@ LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
</ResponseMetadata>
</ListStackResourcesResponse>"""
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
<GetTemplateResult>
<TemplateBody>{{ stack.template }}</TemplateBody>
@ -870,7 +902,6 @@ GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
</ResponseMetadata>
</GetTemplateResponse>"""
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
<ResponseMetadata>
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
@ -878,7 +909,6 @@ DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
</DeleteStackResponse>
"""
LIST_EXPORTS_RESPONSE = """<ListExportsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
<ListExportsResult>
<Exports>
@ -1139,3 +1169,19 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
</ListStackSetOperationResultsResponse>
"""
)
GET_TEMPLATE_SUMMARY_TEMPLATE = """<GetTemplateSummaryResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
<GetTemplateSummaryResult>
<Description>{{ template_summary.Description }}</Description>
{% for resource in template_summary.resourceTypes %}
<ResourceTypes>
<ResourceType>{{ resource }}</ResourceType>
</ResourceTypes>
{% endfor %}
<Version>{{ template_summary.AWSTemplateFormatVersion }}</Version>
</GetTemplateSummaryResult>
<ResponseMetadata>
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
</ResponseMetadata>
</GetTemplateSummaryResponse>
"""

View File

@ -35,6 +35,14 @@ dummy_template = {
},
}
dummy_template3 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 3",
"Resources": {
"VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"}
},
}
dummy_template_yaml = """---
AWSTemplateFormatVersion: 2010-09-09
Description: Stack1 with yaml template
@ -668,6 +676,48 @@ def test_boto3_create_stack_with_short_form_func_yaml():
)
@mock_s3
@mock_cloudformation
def test_get_template_summary():
s3 = boto3.client("s3")
s3_conn = boto3.resource("s3", region_name="us-east-1")
conn = boto3.client("cloudformation", region_name="us-east-1")
result = conn.get_template_summary(TemplateBody=json.dumps(dummy_template3))
result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
result["Version"].should.equal("2010-09-09")
result["Description"].should.equal("Stack 3")
conn.create_stack(StackName="test_stack", TemplateBody=json.dumps(dummy_template3))
result = conn.get_template_summary(StackName="test_stack")
result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
result["Version"].should.equal("2010-09-09")
result["Description"].should.equal("Stack 3")
s3_conn.create_bucket(Bucket="foobar")
s3_conn.Object("foobar", "template-key").put(Body=json.dumps(dummy_template3))
key_url = s3.generate_presigned_url(
ClientMethod="get_object", Params={"Bucket": "foobar", "Key": "template-key"}
)
conn.create_stack(StackName="stack_from_url", TemplateURL=key_url)
result = conn.get_template_summary(TemplateURL=key_url)
result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
result["Version"].should.equal("2010-09-09")
result["Description"].should.equal("Stack 3")
conn = boto3.client("cloudformation", region_name="us-east-1")
result = conn.get_template_summary(TemplateBody=dummy_template_yaml)
result["ResourceTypes"].should.equal(["AWS::EC2::Instance"])
result["Version"].should.equal("2010-09-09")
result["Description"].should.equal("Stack1 with yaml template")
@mock_cloudformation
def test_boto3_create_stack_with_ref_yaml():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")