Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
5804441d38
104 changed files with 26177 additions and 18562 deletions
|
|
@ -1,34 +1,38 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "VPC ENI Test CloudFormation",
|
||||
"Resources": {
|
||||
"ENI": {
|
||||
"Type": "AWS::EC2::NetworkInterface",
|
||||
"Properties": {
|
||||
"SubnetId": {"Ref": "Subnet"}
|
||||
}
|
||||
},
|
||||
"Subnet": {
|
||||
"Type": "AWS::EC2::Subnet",
|
||||
"Properties": {
|
||||
"AvailabilityZone": "us-east-1a",
|
||||
"VpcId": {"Ref": "VPC"},
|
||||
"CidrBlock": "10.0.0.0/24"
|
||||
}
|
||||
},
|
||||
"VPC": {
|
||||
"Type": "AWS::EC2::VPC",
|
||||
"Properties": {
|
||||
"CidrBlock": "10.0.0.0/16"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"NinjaENI": {
|
||||
"Description": "Elastic IP mapping to Auto-Scaling Group",
|
||||
"Value": {"Ref": "ENI"}
|
||||
}
|
||||
}
|
||||
}
|
||||
from __future__ import unicode_literals
|
||||
|
||||
template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "VPC ENI Test CloudFormation",
|
||||
"Resources": {
|
||||
"ENI": {
|
||||
"Type": "AWS::EC2::NetworkInterface",
|
||||
"Properties": {
|
||||
"SubnetId": {"Ref": "Subnet"}
|
||||
}
|
||||
},
|
||||
"Subnet": {
|
||||
"Type": "AWS::EC2::Subnet",
|
||||
"Properties": {
|
||||
"AvailabilityZone": "us-east-1a",
|
||||
"VpcId": {"Ref": "VPC"},
|
||||
"CidrBlock": "10.0.0.0/24"
|
||||
}
|
||||
},
|
||||
"VPC": {
|
||||
"Type": "AWS::EC2::VPC",
|
||||
"Properties": {
|
||||
"CidrBlock": "10.0.0.0/16"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"NinjaENI": {
|
||||
"Description": "Elastic IP mapping to Auto-Scaling Group",
|
||||
"Value": {"Ref": "ENI"}
|
||||
},
|
||||
"ENIIpAddress": {
|
||||
"Description": "ENI's Private IP address",
|
||||
"Value": {"Fn::GetAtt": ["ENI", "PrimaryPrivateIpAddress"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,471 +1,471 @@
|
|||
from __future__ import unicode_literals
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from mock import patch
|
||||
import sure # noqa
|
||||
|
||||
from moto.cloudformation.exceptions import ValidationError
|
||||
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",
|
||||
|
||||
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
|
||||
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": "my-queue",
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
"S3Bucket": {
|
||||
"Type": "AWS::S3::Bucket",
|
||||
"DeletionPolicy": "Retain"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
name_type_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
|
||||
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
|
||||
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
output_dict = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Ref": "Queue"},
|
||||
"Description": "This is a description."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bad_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAtt": ["Queue", "InvalidAttribute"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_attribute_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAtt": ["Queue", "QueueName"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_availability_zones_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAZs": ""}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
split_select_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Select": [ "1", {"Fn::Split": [ "-", "123-myqueue" ] } ] },
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue1": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue-${!Literal}'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
"Queue2": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${Queue1.QueueName}'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export_value_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": "value",
|
||||
"Export": {"Name": 'queue-us-west-1'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import_value_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::ImportValue": 'queue-us-west-1'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputs_template = dict(list(dummy_template.items()) +
|
||||
list(output_dict.items()))
|
||||
bad_outputs_template = dict(
|
||||
list(dummy_template.items()) + list(bad_output.items()))
|
||||
get_attribute_outputs_template = dict(
|
||||
list(dummy_template.items()) + list(get_attribute_output.items()))
|
||||
get_availability_zones_template = dict(
|
||||
list(dummy_template.items()) + list(get_availability_zones_output.items()))
|
||||
|
||||
dummy_template_json = json.dumps(dummy_template)
|
||||
name_type_template_json = json.dumps(name_type_template)
|
||||
output_type_template_json = json.dumps(outputs_template)
|
||||
bad_output_template_json = json.dumps(bad_outputs_template)
|
||||
get_attribute_outputs_template_json = json.dumps(
|
||||
get_attribute_outputs_template)
|
||||
get_availability_zones_template_json = json.dumps(
|
||||
get_availability_zones_template)
|
||||
split_select_template_json = json.dumps(split_select_template)
|
||||
sub_template_json = json.dumps(sub_template)
|
||||
export_value_template_json = json.dumps(export_value_template)
|
||||
import_value_template_json = json.dumps(import_value_template)
|
||||
|
||||
|
||||
def test_parse_stack_resources():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=dummy_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(2)
|
||||
|
||||
queue = stack.resource_map['Queue']
|
||||
queue.should.be.a(Queue)
|
||||
queue.name.should.equal("my-queue")
|
||||
|
||||
bucket = stack.resource_map['S3Bucket']
|
||||
bucket.should.be.a(FakeBucket)
|
||||
bucket.physical_resource_id.should.equal(bucket.name)
|
||||
|
||||
|
||||
@patch("moto.cloudformation.parsing.logger")
|
||||
def test_missing_resource_logs(logger):
|
||||
resource_class_from_type("foobar")
|
||||
logger.warning.assert_called_with(
|
||||
'No Moto CloudFormation support for %s', 'foobar')
|
||||
|
||||
|
||||
def test_parse_stack_with_name_type_resource():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=name_type_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
list(stack.resource_map.keys())[0].should.equal('Queue')
|
||||
queue = list(stack.resource_map.values())[0]
|
||||
queue.should.be.a(Queue)
|
||||
|
||||
|
||||
def test_parse_stack_with_yaml_template():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=yaml.dump(name_type_template),
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
list(stack.resource_map.keys())[0].should.equal('Queue')
|
||||
queue = list(stack.resource_map.values())[0]
|
||||
queue.should.be.a(Queue)
|
||||
|
||||
|
||||
def test_parse_stack_with_outputs():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=output_type_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.description.should.equal("This is a description.")
|
||||
|
||||
|
||||
def test_parse_stack_with_get_attribute_outputs():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=get_attribute_outputs_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.value.should.equal("my-queue")
|
||||
|
||||
def test_parse_stack_with_get_attribute_kms():
|
||||
from .fixtures.kms_key import template
|
||||
|
||||
template_json = json.dumps(template)
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('KeyArn')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
|
||||
def test_parse_stack_with_get_availability_zones():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=get_availability_zones_template_json,
|
||||
parameters={},
|
||||
region_name='us-east-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.value.should.equal([ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d" ])
|
||||
|
||||
|
||||
def test_parse_stack_with_bad_get_attribute_outputs():
|
||||
FakeStack.when.called_with(
|
||||
"test_id", "test_stack", bad_output_template_json, {}, "us-west-1").should.throw(ValidationError)
|
||||
|
||||
|
||||
def test_parse_equals_condition():
|
||||
parse_condition(
|
||||
condition={"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
parse_condition(
|
||||
condition={"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
resources_map={"EnvType": "staging"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_parse_not_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Not": [{
|
||||
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
|
||||
}]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Not": [{
|
||||
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
|
||||
}]
|
||||
},
|
||||
resources_map={"EnvType": "staging"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
|
||||
def test_parse_and_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::And": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::And": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
|
||||
def test_parse_or_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Or": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Or": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_reference_other_conditions():
|
||||
parse_condition(
|
||||
condition={"Fn::Not": [{"Condition": "OtherCondition"}]},
|
||||
resources_map={},
|
||||
condition_map={"OtherCondition": True},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_parse_split_and_select():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=split_select_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
queue = stack.resource_map['Queue']
|
||||
queue.name.should.equal("myqueue")
|
||||
|
||||
|
||||
def test_sub():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=sub_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
queue1 = stack.resource_map['Queue1']
|
||||
queue2 = stack.resource_map['Queue2']
|
||||
queue2.name.should.equal(queue1.name)
|
||||
|
||||
|
||||
def test_import():
|
||||
export_stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=export_value_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
import_stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=import_value_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1',
|
||||
cross_stack_resources={export_stack.exports[0].value: export_stack.exports[0]})
|
||||
|
||||
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)
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from mock import patch
|
||||
import sure # noqa
|
||||
|
||||
from moto.cloudformation.exceptions import ValidationError
|
||||
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",
|
||||
|
||||
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
|
||||
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": "my-queue",
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
"S3Bucket": {
|
||||
"Type": "AWS::S3::Bucket",
|
||||
"DeletionPolicy": "Retain"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
name_type_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
|
||||
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
|
||||
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
output_dict = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Ref": "Queue"},
|
||||
"Description": "This is a description."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bad_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAtt": ["Queue", "InvalidAttribute"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_attribute_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAtt": ["Queue", "QueueName"]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_availability_zones_output = {
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": {"Fn::GetAZs": ""}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
split_select_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Select": [ "1", {"Fn::Split": [ "-", "123-myqueue" ] } ] },
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue1": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue-${!Literal}'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
"Queue2": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${Queue1.QueueName}'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export_value_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"Output1": {
|
||||
"Value": "value",
|
||||
"Export": {"Name": 'queue-us-west-1'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import_value_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"Queue": {
|
||||
"Type": "AWS::SQS::Queue",
|
||||
"Properties": {
|
||||
"QueueName": {"Fn::ImportValue": 'queue-us-west-1'},
|
||||
"VisibilityTimeout": 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputs_template = dict(list(dummy_template.items()) +
|
||||
list(output_dict.items()))
|
||||
bad_outputs_template = dict(
|
||||
list(dummy_template.items()) + list(bad_output.items()))
|
||||
get_attribute_outputs_template = dict(
|
||||
list(dummy_template.items()) + list(get_attribute_output.items()))
|
||||
get_availability_zones_template = dict(
|
||||
list(dummy_template.items()) + list(get_availability_zones_output.items()))
|
||||
|
||||
dummy_template_json = json.dumps(dummy_template)
|
||||
name_type_template_json = json.dumps(name_type_template)
|
||||
output_type_template_json = json.dumps(outputs_template)
|
||||
bad_output_template_json = json.dumps(bad_outputs_template)
|
||||
get_attribute_outputs_template_json = json.dumps(
|
||||
get_attribute_outputs_template)
|
||||
get_availability_zones_template_json = json.dumps(
|
||||
get_availability_zones_template)
|
||||
split_select_template_json = json.dumps(split_select_template)
|
||||
sub_template_json = json.dumps(sub_template)
|
||||
export_value_template_json = json.dumps(export_value_template)
|
||||
import_value_template_json = json.dumps(import_value_template)
|
||||
|
||||
|
||||
def test_parse_stack_resources():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=dummy_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(2)
|
||||
|
||||
queue = stack.resource_map['Queue']
|
||||
queue.should.be.a(Queue)
|
||||
queue.name.should.equal("my-queue")
|
||||
|
||||
bucket = stack.resource_map['S3Bucket']
|
||||
bucket.should.be.a(FakeBucket)
|
||||
bucket.physical_resource_id.should.equal(bucket.name)
|
||||
|
||||
|
||||
@patch("moto.cloudformation.parsing.logger")
|
||||
def test_missing_resource_logs(logger):
|
||||
resource_class_from_type("foobar")
|
||||
logger.warning.assert_called_with(
|
||||
'No Moto CloudFormation support for %s', 'foobar')
|
||||
|
||||
|
||||
def test_parse_stack_with_name_type_resource():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=name_type_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
list(stack.resource_map.keys())[0].should.equal('Queue')
|
||||
queue = list(stack.resource_map.values())[0]
|
||||
queue.should.be.a(Queue)
|
||||
|
||||
|
||||
def test_parse_stack_with_yaml_template():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=yaml.dump(name_type_template),
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
list(stack.resource_map.keys())[0].should.equal('Queue')
|
||||
queue = list(stack.resource_map.values())[0]
|
||||
queue.should.be.a(Queue)
|
||||
|
||||
|
||||
def test_parse_stack_with_outputs():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=output_type_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.description.should.equal("This is a description.")
|
||||
|
||||
|
||||
def test_parse_stack_with_get_attribute_outputs():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=get_attribute_outputs_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.value.should.equal("my-queue")
|
||||
|
||||
def test_parse_stack_with_get_attribute_kms():
|
||||
from .fixtures.kms_key import template
|
||||
|
||||
template_json = json.dumps(template)
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('KeyArn')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
|
||||
def test_parse_stack_with_get_availability_zones():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=get_availability_zones_template_json,
|
||||
parameters={},
|
||||
region_name='us-east-1')
|
||||
|
||||
stack.output_map.should.have.length_of(1)
|
||||
list(stack.output_map.keys())[0].should.equal('Output1')
|
||||
output = list(stack.output_map.values())[0]
|
||||
output.should.be.a(Output)
|
||||
output.value.should.equal([ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d" ])
|
||||
|
||||
|
||||
def test_parse_stack_with_bad_get_attribute_outputs():
|
||||
FakeStack.when.called_with(
|
||||
"test_id", "test_stack", bad_output_template_json, {}, "us-west-1").should.throw(ValidationError)
|
||||
|
||||
|
||||
def test_parse_equals_condition():
|
||||
parse_condition(
|
||||
condition={"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
parse_condition(
|
||||
condition={"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
resources_map={"EnvType": "staging"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_parse_not_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Not": [{
|
||||
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
|
||||
}]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Not": [{
|
||||
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
|
||||
}]
|
||||
},
|
||||
resources_map={"EnvType": "staging"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
|
||||
def test_parse_and_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::And": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::And": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
|
||||
def test_parse_or_condition():
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Or": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(True)
|
||||
|
||||
parse_condition(
|
||||
condition={
|
||||
"Fn::Or": [
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
{"Fn::Equals": [{"Ref": "EnvType"}, "staging"]},
|
||||
]
|
||||
},
|
||||
resources_map={"EnvType": "prod"},
|
||||
condition_map={},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_reference_other_conditions():
|
||||
parse_condition(
|
||||
condition={"Fn::Not": [{"Condition": "OtherCondition"}]},
|
||||
resources_map={},
|
||||
condition_map={"OtherCondition": True},
|
||||
).should.equal(False)
|
||||
|
||||
|
||||
def test_parse_split_and_select():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=split_select_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
stack.resource_map.should.have.length_of(1)
|
||||
queue = stack.resource_map['Queue']
|
||||
queue.name.should.equal("myqueue")
|
||||
|
||||
|
||||
def test_sub():
|
||||
stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=sub_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
|
||||
queue1 = stack.resource_map['Queue1']
|
||||
queue2 = stack.resource_map['Queue2']
|
||||
queue2.name.should.equal(queue1.name)
|
||||
|
||||
|
||||
def test_import():
|
||||
export_stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=export_value_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1')
|
||||
import_stack = FakeStack(
|
||||
stack_id="test_id",
|
||||
name="test_stack",
|
||||
template=import_value_template_json,
|
||||
parameters={},
|
||||
region_name='us-west-1',
|
||||
cross_stack_resources={export_stack.exports[0].value: export_stack.exports[0]})
|
||||
|
||||
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, Loader=yaml.Loader)
|
||||
template_dict = yaml.load(template, Loader=yaml.Loader)
|
||||
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)
|
||||
|
|
|
|||
115
tests/test_cloudformation/test_validate.py
Normal file
115
tests/test_cloudformation/test_validate.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
from collections import OrderedDict
|
||||
import json
|
||||
import yaml
|
||||
import os
|
||||
import boto3
|
||||
from nose.tools import raises
|
||||
import botocore
|
||||
|
||||
|
||||
from moto.cloudformation.exceptions import ValidationError
|
||||
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
|
||||
from moto import mock_cloudformation, mock_s3, mock_sqs, mock_ec2
|
||||
|
||||
json_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "Stack 1",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# One resource is required
|
||||
json_bad_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "Stack 1"
|
||||
}
|
||||
|
||||
dummy_template_json = json.dumps(json_template)
|
||||
dummy_bad_template_json = json.dumps(json_bad_template)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_json_validate_successful():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
response = cf_conn.validate_template(
|
||||
TemplateBody=dummy_template_json,
|
||||
)
|
||||
assert response['Description'] == "Stack 1"
|
||||
assert response['Parameters'] == []
|
||||
assert response['ResponseMetadata']['HTTPStatusCode'] == 200
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_json_invalid_missing_resource():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
try:
|
||||
cf_conn.validate_template(
|
||||
TemplateBody=dummy_bad_template_json,
|
||||
)
|
||||
assert False
|
||||
except botocore.exceptions.ClientError as e:
|
||||
assert str(e) == 'An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack' \
|
||||
' with id Missing top level item Resources to file module does not exist'
|
||||
assert True
|
||||
|
||||
|
||||
yaml_template = """
|
||||
AWSTemplateFormatVersion: '2010-09-09'
|
||||
Description: Simple CloudFormation Test Template
|
||||
Resources:
|
||||
S3Bucket:
|
||||
Type: AWS::S3::Bucket
|
||||
Properties:
|
||||
AccessControl: PublicRead
|
||||
BucketName: cf-test-bucket-1
|
||||
"""
|
||||
|
||||
yaml_bad_template = """
|
||||
AWSTemplateFormatVersion: '2010-09-09'
|
||||
Description: Simple CloudFormation Test Template
|
||||
"""
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_yaml_validate_successful():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
response = cf_conn.validate_template(
|
||||
TemplateBody=yaml_template,
|
||||
)
|
||||
assert response['Description'] == "Simple CloudFormation Test Template"
|
||||
assert response['Parameters'] == []
|
||||
assert response['ResponseMetadata']['HTTPStatusCode'] == 200
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_yaml_invalid_missing_resource():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
try:
|
||||
cf_conn.validate_template(
|
||||
TemplateBody=yaml_bad_template,
|
||||
)
|
||||
assert False
|
||||
except botocore.exceptions.ClientError as e:
|
||||
assert str(e) == 'An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack' \
|
||||
' with id Missing top level item Resources to file module does not exist'
|
||||
assert True
|
||||
Loading…
Add table
Add a link
Reference in a new issue