handle short form function in cfn yaml template (#1103)
This commit is contained in:
parent
0d122ef86f
commit
2f6f42a183
@ -9,7 +9,7 @@ from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
from .parsing import ResourceMap, OutputMap
|
||||
from .utils import generate_stack_id
|
||||
from .utils import generate_stack_id, yaml_tag_constructor
|
||||
from .exceptions import ValidationError
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ class FakeStack(BaseModel):
|
||||
))
|
||||
|
||||
def _parse_template(self):
|
||||
yaml.add_multi_constructor('', yaml_tag_constructor)
|
||||
try:
|
||||
self.template_dict = yaml.load(self.template)
|
||||
except yaml.parser.ParserError:
|
||||
|
@ -391,8 +391,7 @@ LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
||||
|
||||
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
||||
<GetTemplateResult>
|
||||
<TemplateBody>{{ stack.template }}
|
||||
</TemplateBody>
|
||||
<TemplateBody>{{ stack.template }}</TemplateBody>
|
||||
</GetTemplateResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
import uuid
|
||||
import six
|
||||
import random
|
||||
import yaml
|
||||
|
||||
|
||||
def generate_stack_id(stack_name):
|
||||
@ -13,3 +14,22 @@ def random_suffix():
|
||||
size = 12
|
||||
chars = list(range(10)) + ['A-Z']
|
||||
return ''.join(six.text_type(random.choice(chars)) for x in range(size))
|
||||
|
||||
|
||||
def yaml_tag_constructor(loader, tag, node):
|
||||
"""convert shorthand intrinsic function to full name
|
||||
"""
|
||||
def _f(loader, tag, node):
|
||||
if tag == '!GetAtt':
|
||||
return node.value.split('.')
|
||||
elif type(node) == yaml.SequenceNode:
|
||||
return loader.construct_sequence(node)
|
||||
else:
|
||||
return node.value
|
||||
|
||||
if tag == '!Ref':
|
||||
key = 'Ref'
|
||||
else:
|
||||
key = 'Fn::{}'.format(tag[1:])
|
||||
|
||||
return {key: _f(loader, tag, node)}
|
||||
|
@ -39,6 +39,68 @@ dummy_template = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dummy_template_yaml = """---
|
||||
AWSTemplateFormatVersion: 2010-09-09
|
||||
Description: Stack1 with yaml template
|
||||
Resources:
|
||||
EC2Instance1:
|
||||
Type: AWS::EC2::Instance
|
||||
Properties:
|
||||
ImageId: ami-d3adb33f
|
||||
KeyName: dummy
|
||||
InstanceType: t2.micro
|
||||
Tags:
|
||||
- Key: Description
|
||||
Value: Test tag
|
||||
- Key: Name
|
||||
Value: Name tag for tests
|
||||
"""
|
||||
|
||||
|
||||
dummy_template_yaml_with_short_form_func = """---
|
||||
AWSTemplateFormatVersion: 2010-09-09
|
||||
Description: Stack1 with yaml template
|
||||
Resources:
|
||||
EC2Instance1:
|
||||
Type: AWS::EC2::Instance
|
||||
Properties:
|
||||
ImageId: ami-d3adb33f
|
||||
KeyName: !Join [ ":", [ du, m, my ] ]
|
||||
InstanceType: t2.micro
|
||||
Tags:
|
||||
- Key: Description
|
||||
Value: Test tag
|
||||
- Key: Name
|
||||
Value: Name tag for tests
|
||||
"""
|
||||
|
||||
|
||||
dummy_template_yaml_with_ref = """---
|
||||
AWSTemplateFormatVersion: 2010-09-09
|
||||
Description: Stack1 with yaml template
|
||||
Parameters:
|
||||
TagDescription:
|
||||
Type: String
|
||||
TagName:
|
||||
Type: String
|
||||
|
||||
Resources:
|
||||
EC2Instance1:
|
||||
Type: AWS::EC2::Instance
|
||||
Properties:
|
||||
ImageId: ami-d3adb33f
|
||||
KeyName: dummy
|
||||
InstanceType: t2.micro
|
||||
Tags:
|
||||
- Key: Description
|
||||
Value:
|
||||
Ref: TagDescription
|
||||
- Key: Name
|
||||
Value: !Ref TagName
|
||||
"""
|
||||
|
||||
|
||||
dummy_update_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Parameters": {
|
||||
@ -110,6 +172,46 @@ def test_boto3_create_stack():
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||
dummy_template)
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_create_stack_with_yaml():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
cf_conn.create_stack(
|
||||
StackName="test_stack",
|
||||
TemplateBody=dummy_template_yaml,
|
||||
)
|
||||
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||
dummy_template_yaml)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_create_stack_with_short_form_func_yaml():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
cf_conn.create_stack(
|
||||
StackName="test_stack",
|
||||
TemplateBody=dummy_template_yaml_with_short_form_func,
|
||||
)
|
||||
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||
dummy_template_yaml_with_short_form_func)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_create_stack_with_ref_yaml():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
params = [
|
||||
{'ParameterKey': 'TagDescription', 'ParameterValue': 'desc_ref'},
|
||||
{'ParameterKey': 'TagName', 'ParameterValue': 'name_ref'},
|
||||
]
|
||||
cf_conn.create_stack(
|
||||
StackName="test_stack",
|
||||
TemplateBody=dummy_template_yaml_with_ref,
|
||||
Parameters=params
|
||||
)
|
||||
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||
dummy_template_yaml_with_ref)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_creating_stacks_across_regions():
|
||||
@ -150,7 +252,6 @@ def test_create_stack_with_role_arn():
|
||||
TemplateBody=dummy_template_json,
|
||||
RoleARN='arn:aws:iam::123456789012:role/moto',
|
||||
)
|
||||
|
||||
stack = list(cf.stacks.all())[0]
|
||||
stack.role_arn.should.equal('arn:aws:iam::123456789012:role/moto')
|
||||
|
||||
|
@ -10,8 +10,11 @@ from moto.cloudformation.models import FakeStack
|
||||
from moto.cloudformation.parsing import resource_class_from_type, parse_condition, Export
|
||||
from moto.sqs.models import Queue
|
||||
from moto.s3.models import FakeBucket
|
||||
from moto.cloudformation.utils import yaml_tag_constructor
|
||||
from boto.cloudformation.stack import Output
|
||||
|
||||
|
||||
|
||||
dummy_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
|
||||
@ -380,3 +383,47 @@ def test_import():
|
||||
|
||||
queue = import_stack.resource_map['Queue']
|
||||
queue.name.should.equal("value")
|
||||
|
||||
|
||||
|
||||
def test_short_form_func_in_yaml_teamplate():
|
||||
template = """---
|
||||
KeyB64: !Base64 valueToEncode
|
||||
KeyRef: !Ref foo
|
||||
KeyAnd: !And
|
||||
- A
|
||||
- B
|
||||
KeyEquals: !Equals [A, B]
|
||||
KeyIf: !If [A, B, C]
|
||||
KeyNot: !Not [A]
|
||||
KeyOr: !Or [A, B]
|
||||
KeyFindInMap: !FindInMap [A, B, C]
|
||||
KeyGetAtt: !GetAtt A.B
|
||||
KeyGetAZs: !GetAZs A
|
||||
KeyImportValue: !ImportValue A
|
||||
KeyJoin: !Join [ ":", [A, B, C] ]
|
||||
KeySelect: !Select [A, B]
|
||||
KeySplit: !Split [A, B]
|
||||
KeySub: !Sub A
|
||||
"""
|
||||
yaml.add_multi_constructor('', yaml_tag_constructor)
|
||||
template_dict = yaml.load(template)
|
||||
key_and_expects = [
|
||||
['KeyRef', {'Ref': 'foo'}],
|
||||
['KeyB64', {'Fn::Base64': 'valueToEncode'}],
|
||||
['KeyAnd', {'Fn::And': ['A', 'B']}],
|
||||
['KeyEquals', {'Fn::Equals': ['A', 'B']}],
|
||||
['KeyIf', {'Fn::If': ['A', 'B', 'C']}],
|
||||
['KeyNot', {'Fn::Not': ['A']}],
|
||||
['KeyOr', {'Fn::Or': ['A', 'B']}],
|
||||
['KeyFindInMap', {'Fn::FindInMap': ['A', 'B', 'C']}],
|
||||
['KeyGetAtt', {'Fn::GetAtt': ['A', 'B']}],
|
||||
['KeyGetAZs', {'Fn::GetAZs': 'A'}],
|
||||
['KeyImportValue', {'Fn::ImportValue': 'A'}],
|
||||
['KeyJoin', {'Fn::Join': [ ":", [ 'A', 'B', 'C' ] ]}],
|
||||
['KeySelect', {'Fn::Select': ['A', 'B']}],
|
||||
['KeySplit', {'Fn::Split': ['A', 'B']}],
|
||||
['KeySub', {'Fn::Sub': 'A'}],
|
||||
]
|
||||
for k, v in key_and_expects:
|
||||
template_dict.should.have.key(k).which.should.be.equal(v)
|
||||
|
Loading…
Reference in New Issue
Block a user