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 moto.core import BaseBackend, BaseModel
|
||||||
|
|
||||||
from .parsing import ResourceMap, OutputMap
|
from .parsing import ResourceMap, OutputMap
|
||||||
from .utils import generate_stack_id
|
from .utils import generate_stack_id, yaml_tag_constructor
|
||||||
from .exceptions import ValidationError
|
from .exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +74,7 @@ class FakeStack(BaseModel):
|
|||||||
))
|
))
|
||||||
|
|
||||||
def _parse_template(self):
|
def _parse_template(self):
|
||||||
|
yaml.add_multi_constructor('', yaml_tag_constructor)
|
||||||
try:
|
try:
|
||||||
self.template_dict = yaml.load(self.template)
|
self.template_dict = yaml.load(self.template)
|
||||||
except yaml.parser.ParserError:
|
except yaml.parser.ParserError:
|
||||||
|
@ -391,8 +391,7 @@ LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
|||||||
|
|
||||||
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
||||||
<GetTemplateResult>
|
<GetTemplateResult>
|
||||||
<TemplateBody>{{ stack.template }}
|
<TemplateBody>{{ stack.template }}</TemplateBody>
|
||||||
</TemplateBody>
|
|
||||||
</GetTemplateResult>
|
</GetTemplateResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
import uuid
|
import uuid
|
||||||
import six
|
import six
|
||||||
import random
|
import random
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
def generate_stack_id(stack_name):
|
def generate_stack_id(stack_name):
|
||||||
@ -13,3 +14,22 @@ def random_suffix():
|
|||||||
size = 12
|
size = 12
|
||||||
chars = list(range(10)) + ['A-Z']
|
chars = list(range(10)) + ['A-Z']
|
||||||
return ''.join(six.text_type(random.choice(chars)) for x in range(size))
|
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 = {
|
dummy_update_template = {
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
"AWSTemplateFormatVersion": "2010-09-09",
|
||||||
"Parameters": {
|
"Parameters": {
|
||||||
@ -110,6 +172,46 @@ def test_boto3_create_stack():
|
|||||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||||
dummy_template)
|
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
|
@mock_cloudformation
|
||||||
def test_creating_stacks_across_regions():
|
def test_creating_stacks_across_regions():
|
||||||
@ -150,7 +252,6 @@ def test_create_stack_with_role_arn():
|
|||||||
TemplateBody=dummy_template_json,
|
TemplateBody=dummy_template_json,
|
||||||
RoleARN='arn:aws:iam::123456789012:role/moto',
|
RoleARN='arn:aws:iam::123456789012:role/moto',
|
||||||
)
|
)
|
||||||
|
|
||||||
stack = list(cf.stacks.all())[0]
|
stack = list(cf.stacks.all())[0]
|
||||||
stack.role_arn.should.equal('arn:aws:iam::123456789012:role/moto')
|
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.cloudformation.parsing import resource_class_from_type, parse_condition, Export
|
||||||
from moto.sqs.models import Queue
|
from moto.sqs.models import Queue
|
||||||
from moto.s3.models import FakeBucket
|
from moto.s3.models import FakeBucket
|
||||||
|
from moto.cloudformation.utils import yaml_tag_constructor
|
||||||
from boto.cloudformation.stack import Output
|
from boto.cloudformation.stack import Output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dummy_template = {
|
dummy_template = {
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
"AWSTemplateFormatVersion": "2010-09-09",
|
||||||
|
|
||||||
@ -380,3 +383,47 @@ def test_import():
|
|||||||
|
|
||||||
queue = import_stack.resource_map['Queue']
|
queue = import_stack.resource_map['Queue']
|
||||||
queue.name.should.equal("value")
|
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