Adding cloudformation-validate. Cfn-lint does the heavy lifting.
This commit is contained in:
parent
71a054af92
commit
b66965e6e8
6 changed files with 181 additions and 1 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue