2014-08-27 11:17:06 -04:00
|
|
|
from __future__ import unicode_literals
|
2014-03-27 19:12:53 -04:00
|
|
|
import uuid
|
2014-10-20 11:45:47 -04:00
|
|
|
import six
|
|
|
|
import random
|
2017-09-08 03:28:15 +09:00
|
|
|
import yaml
|
2018-11-02 16:04:17 -07:00
|
|
|
import os
|
2019-06-18 15:27:07 +02:00
|
|
|
import string
|
2018-11-02 16:04:17 -07:00
|
|
|
|
|
|
|
from cfnlint import decode, core
|
2019-12-16 21:05:29 -05:00
|
|
|
from moto.core import ACCOUNT_ID
|
2014-03-27 19:12:53 -04:00
|
|
|
|
2019-01-10 21:33:15 -08:00
|
|
|
def generate_stack_id(stack_name, region="us-east-1", account="123456789"):
|
2014-03-27 19:12:53 -04:00
|
|
|
random_id = uuid.uuid4()
|
2019-10-31 08:44:26 -07:00
|
|
|
return "arn:aws:cloudformation:{}:{}:stack/{}/{}".format(
|
|
|
|
region, account, stack_name, random_id
|
|
|
|
)
|
2014-10-20 11:45:47 -04:00
|
|
|
|
|
|
|
|
2017-12-11 01:23:35 -08:00
|
|
|
def generate_changeset_id(changeset_name, region_name):
|
|
|
|
random_id = uuid.uuid4()
|
2019-10-31 08:44:26 -07:00
|
|
|
return "arn:aws:cloudformation:{0}:123456789:changeSet/{1}/{2}".format(
|
|
|
|
region_name, changeset_name, random_id
|
|
|
|
)
|
2017-12-11 01:23:35 -08:00
|
|
|
|
|
|
|
|
2019-01-10 21:33:15 -08:00
|
|
|
def generate_stackset_id(stackset_name):
|
|
|
|
random_id = uuid.uuid4()
|
2019-10-31 08:44:26 -07:00
|
|
|
return "{}:{}".format(stackset_name, random_id)
|
2019-01-10 21:33:15 -08:00
|
|
|
|
|
|
|
|
|
|
|
def generate_stackset_arn(stackset_id, region_name):
|
2019-12-15 19:22:26 -05:00
|
|
|
return "arn:aws:cloudformation:{}:{}:stackset/{}".format(
|
|
|
|
region_name, ACCOUNT_ID, stackset_id
|
2019-10-31 08:44:26 -07:00
|
|
|
)
|
2019-01-10 21:33:15 -08:00
|
|
|
|
|
|
|
|
2014-10-20 11:45:47 -04:00
|
|
|
def random_suffix():
|
|
|
|
size = 12
|
2019-06-18 15:27:07 +02:00
|
|
|
chars = list(range(10)) + list(string.ascii_uppercase)
|
2019-10-31 08:44:26 -07:00
|
|
|
return "".join(six.text_type(random.choice(chars)) for x in range(size))
|
2017-09-08 03:28:15 +09:00
|
|
|
|
|
|
|
|
|
|
|
def yaml_tag_constructor(loader, tag, node):
|
|
|
|
"""convert shorthand intrinsic function to full name
|
|
|
|
"""
|
2019-10-31 08:44:26 -07:00
|
|
|
|
2017-09-08 03:28:15 +09:00
|
|
|
def _f(loader, tag, node):
|
2019-10-31 08:44:26 -07:00
|
|
|
if tag == "!GetAtt":
|
|
|
|
return node.value.split(".")
|
2017-09-08 03:28:15 +09:00
|
|
|
elif type(node) == yaml.SequenceNode:
|
|
|
|
return loader.construct_sequence(node)
|
|
|
|
else:
|
|
|
|
return node.value
|
|
|
|
|
2019-10-31 08:44:26 -07:00
|
|
|
if tag == "!Ref":
|
|
|
|
key = "Ref"
|
2017-09-08 03:28:15 +09:00
|
|
|
else:
|
2019-10-31 08:44:26 -07:00
|
|
|
key = "Fn::{}".format(tag[1:])
|
2017-09-08 03:28:15 +09:00
|
|
|
|
|
|
|
return {key: _f(loader, tag, node)}
|
2018-11-02 16:04:17 -07:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2019-10-31 08:44:26 -07:00
|
|
|
regions = ["us-east-1"]
|
2018-11-02 16:04:17 -07:00
|
|
|
|
|
|
|
# Process all the rules and gather the errors
|
2019-10-31 08:44:26 -07:00
|
|
|
matches = core.run_checks(abs_filename, template, rules, regions)
|
2018-11-02 16:04:17 -07:00
|
|
|
|
|
|
|
return matches
|