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:
parent
03dd55d39d
commit
bc1674cb19
@ -10,6 +10,31 @@ from moto.s3 import s3_backend
|
|||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from .models import cloudformation_backends
|
from .models import cloudformation_backends
|
||||||
from .exceptions import ValidationError
|
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):
|
class CloudFormationResponse(BaseResponse):
|
||||||
@ -269,6 +294,20 @@ class CloudFormationResponse(BaseResponse):
|
|||||||
template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
|
template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
|
||||||
return template.render(stack=stack)
|
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):
|
def update_stack(self):
|
||||||
stack_name = self._get_param("StackName")
|
stack_name = self._get_param("StackName")
|
||||||
role_arn = self._get_param("RoleARN")
|
role_arn = self._get_param("RoleARN")
|
||||||
@ -743,7 +782,6 @@ DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResponse>
|
|||||||
</DescribeStacksResult>
|
</DescribeStacksResult>
|
||||||
</DescribeStacksResponse>"""
|
</DescribeStacksResponse>"""
|
||||||
|
|
||||||
|
|
||||||
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
||||||
<DescribeStackResourceResult>
|
<DescribeStackResourceResult>
|
||||||
<StackResourceDetail>
|
<StackResourceDetail>
|
||||||
@ -758,7 +796,6 @@ DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
|||||||
</DescribeStackResourceResult>
|
</DescribeStackResourceResult>
|
||||||
</DescribeStackResourceResponse>"""
|
</DescribeStackResourceResponse>"""
|
||||||
|
|
||||||
|
|
||||||
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
||||||
<DescribeStackResourcesResult>
|
<DescribeStackResourcesResult>
|
||||||
<StackResources>
|
<StackResources>
|
||||||
@ -777,7 +814,6 @@ DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
|||||||
</DescribeStackResourcesResult>
|
</DescribeStackResourcesResult>
|
||||||
</DescribeStackResourcesResponse>"""
|
</DescribeStackResourcesResponse>"""
|
||||||
|
|
||||||
|
|
||||||
DESCRIBE_STACK_EVENTS_RESPONSE = """<DescribeStackEventsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
DESCRIBE_STACK_EVENTS_RESPONSE = """<DescribeStackEventsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||||
<DescribeStackEventsResult>
|
<DescribeStackEventsResult>
|
||||||
<StackEvents>
|
<StackEvents>
|
||||||
@ -802,7 +838,6 @@ DESCRIBE_STACK_EVENTS_RESPONSE = """<DescribeStackEventsResponse xmlns="http://c
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</DescribeStackEventsResponse>"""
|
</DescribeStackEventsResponse>"""
|
||||||
|
|
||||||
|
|
||||||
LIST_CHANGE_SETS_RESPONSE = """<ListChangeSetsResponse>
|
LIST_CHANGE_SETS_RESPONSE = """<ListChangeSetsResponse>
|
||||||
<ListChangeSetsResult>
|
<ListChangeSetsResult>
|
||||||
<Summaries>
|
<Summaries>
|
||||||
@ -823,7 +858,6 @@ LIST_CHANGE_SETS_RESPONSE = """<ListChangeSetsResponse>
|
|||||||
</ListChangeSetsResult>
|
</ListChangeSetsResult>
|
||||||
</ListChangeSetsResponse>"""
|
</ListChangeSetsResponse>"""
|
||||||
|
|
||||||
|
|
||||||
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
||||||
<ListStacksResult>
|
<ListStacksResult>
|
||||||
<StackSummaries>
|
<StackSummaries>
|
||||||
@ -840,7 +874,6 @@ LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
|||||||
</ListStacksResult>
|
</ListStacksResult>
|
||||||
</ListStacksResponse>"""
|
</ListStacksResponse>"""
|
||||||
|
|
||||||
|
|
||||||
LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
||||||
<ListStackResourcesResult>
|
<ListStackResourcesResult>
|
||||||
<StackResourceSummaries>
|
<StackResourceSummaries>
|
||||||
@ -860,7 +893,6 @@ LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</ListStackResourcesResponse>"""
|
</ListStackResourcesResponse>"""
|
||||||
|
|
||||||
|
|
||||||
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
||||||
<GetTemplateResult>
|
<GetTemplateResult>
|
||||||
<TemplateBody>{{ stack.template }}</TemplateBody>
|
<TemplateBody>{{ stack.template }}</TemplateBody>
|
||||||
@ -870,7 +902,6 @@ GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</GetTemplateResponse>"""
|
</GetTemplateResponse>"""
|
||||||
|
|
||||||
|
|
||||||
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
|
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
|
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
|
||||||
@ -878,7 +909,6 @@ DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
|
|||||||
</DeleteStackResponse>
|
</DeleteStackResponse>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
LIST_EXPORTS_RESPONSE = """<ListExportsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
LIST_EXPORTS_RESPONSE = """<ListExportsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||||
<ListExportsResult>
|
<ListExportsResult>
|
||||||
<Exports>
|
<Exports>
|
||||||
@ -1139,3 +1169,19 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
|
|||||||
</ListStackSetOperationResultsResponse>
|
</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>
|
||||||
|
"""
|
||||||
|
@ -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 = """---
|
dummy_template_yaml = """---
|
||||||
AWSTemplateFormatVersion: 2010-09-09
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
Description: Stack1 with yaml template
|
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
|
@mock_cloudformation
|
||||||
def test_boto3_create_stack_with_ref_yaml():
|
def test_boto3_create_stack_with_ref_yaml():
|
||||||
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
||||||
|
Loading…
Reference in New Issue
Block a user