Adding cloudformation-validate. Cfn-lint does the heavy lifting.

This commit is contained in:
adam davis 2018-11-02 16:04:17 -07:00
commit b66965e6e8
6 changed files with 181 additions and 1 deletions

View file

@ -13,6 +13,7 @@ from .utils import (
generate_changeset_id,
generate_stack_id,
yaml_tag_constructor,
validate_template_cfn_lint,
)
from .exceptions import ValidationError
@ -270,6 +271,9 @@ class CloudFormationBackend(BaseBackend):
next_token = str(token + 100) if len(all_exports) > token + 100 else None
return exports, next_token
def validate_template(self, template):
return validate_template_cfn_lint(template)
def _validate_export_uniqueness(self, stack):
new_stack_export_names = [x.name for x in stack.exports]
export_names = self.exports.keys()

View file

@ -1,6 +1,7 @@
from __future__ import unicode_literals
import json
import yaml
from six.moves.urllib.parse import urlparse
from moto.core.responses import BaseResponse
@ -294,6 +295,32 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(LIST_EXPORTS_RESPONSE)
return template.render(exports=exports, next_token=next_token)
def validate_template(self):
cfn_lint = self.cloudformation_backend.validate_template(self._get_param('TemplateBody'))
if cfn_lint:
raise ValidationError(cfn_lint[0].message)
description = ""
try:
description = json.loads(self._get_param('TemplateBody'))['Description']
except (ValueError, KeyError):
pass
try:
description = yaml.load(self._get_param('TemplateBody'))['Description']
except (yaml.ParserError, KeyError):
pass
template = self.response_template(VALIDATE_STACK_RESPONSE_TEMPLATE)
return template.render(description=description)
VALIDATE_STACK_RESPONSE_TEMPLATE = """<ValidateTemplateResponse>
<ValidateTemplateResult>
<Capabilities></Capabilities>
<CapabilitiesReason></CapabilitiesReason>
<DeclaredTransforms></DeclaredTransforms>
<Description>{{ description }}</Description>
<Parameters></Parameters>
</ValidateTemplateResult>
</ValidateTemplateResponse>"""
CREATE_STACK_RESPONSE_TEMPLATE = """<CreateStackResponse>
<CreateStackResult>

View file

@ -3,6 +3,9 @@ import uuid
import six
import random
import yaml
import os
from cfnlint import decode, core
def generate_stack_id(stack_name):
@ -38,3 +41,33 @@ def yaml_tag_constructor(loader, tag, node):
key = 'Fn::{}'.format(tag[1:])
return {key: _f(loader, tag, node)}
def validate_template_cfn_lint(template):
# Save the template to a temporary file -- cfn-lint requires a file
filename = "file.tmp"
with open(filename, "w") as file:
file.write(template)
abs_filename = os.path.abspath(filename)
# decode handles both yaml and json
template, matches = decode.decode(abs_filename, False)
# Set cfn-lint to info
core.configure_logging(None)
# Initialize the ruleset to be applied (no overrules, no excludes)
rules = core.get_rules([], [], [])
# Use us-east-1 region (spec file) for validation
regions = ['us-east-1']
# Process all the rules and gather the errors
matches = core.run_checks(
abs_filename,
template,
rules,
regions)
return matches