Rewrite deprecated CloudFormation tests (#3842)

This commit is contained in:
Bert Blommers 2021-10-04 13:47:40 +00:00 committed by GitHub
parent c5f2a40245
commit e9a4100324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2336 additions and 66 deletions

View File

@ -1,11 +1,8 @@
import boto3
import json
import sure # noqa
from moto import (
mock_autoscaling,
mock_cloudformation,
mock_ec2,
)
from moto import mock_autoscaling, mock_cloudformation, mock_ec2, mock_elb
from .utils import setup_networking
from tests import EXAMPLE_AMI_ID
@ -276,3 +273,194 @@ Outputs:
lt["LaunchTemplateId"].should.be.equal(launch_template_id)
lt["LaunchTemplateName"].should.be.equal("test_launch_template_new")
lt["Version"].should.be.equal("1")
@mock_autoscaling
@mock_elb
@mock_cloudformation
@mock_ec2
def test_autoscaling_group_with_elb():
web_setup_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-as-group": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": ["us-east-1a"],
"LaunchConfigurationName": {"Ref": "my-launch-config"},
"MinSize": "2",
"MaxSize": "2",
"DesiredCapacity": "2",
"LoadBalancerNames": [{"Ref": "my-elb"}],
"Tags": [
{
"Key": "propagated-test-tag",
"Value": "propagated-test-tag-value",
"PropagateAtLaunch": True,
},
{
"Key": "not-propagated-test-tag",
"Value": "not-propagated-test-tag-value",
"PropagateAtLaunch": False,
},
],
},
},
"my-launch-config": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": EXAMPLE_AMI_ID,
"InstanceType": "t2.medium",
"UserData": "some user data",
},
},
"my-elb": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"AvailabilityZones": ["us-east-1a"],
"Listeners": [
{
"LoadBalancerPort": "80",
"InstancePort": "80",
"Protocol": "HTTP",
}
],
"LoadBalancerName": "my-elb",
"HealthCheck": {
"Target": "HTTP:80",
"HealthyThreshold": "3",
"UnhealthyThreshold": "5",
"Interval": "30",
"Timeout": "5",
},
},
},
},
}
web_setup_template_json = json.dumps(web_setup_template)
cf = boto3.client("cloudformation", region_name="us-east-1")
ec2 = boto3.client("ec2", region_name="us-east-1")
elb = boto3.client("elb", region_name="us-east-1")
client = boto3.client("autoscaling", region_name="us-east-1")
cf.create_stack(StackName="web_stack", TemplateBody=web_setup_template_json)
autoscale_group = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
autoscale_group["LaunchConfigurationName"].should.contain("my-launch-config")
autoscale_group["LoadBalancerNames"].should.equal(["my-elb"])
# Confirm the Launch config was actually created
client.describe_launch_configurations()[
"LaunchConfigurations"
].should.have.length_of(1)
# Confirm the ELB was actually created
elb.describe_load_balancers()["LoadBalancerDescriptions"].should.have.length_of(1)
resources = cf.list_stack_resources(StackName="web_stack")["StackResourceSummaries"]
as_group_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::AutoScaling::AutoScalingGroup"
][0]
as_group_resource["PhysicalResourceId"].should.contain("my-as-group")
launch_config_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::AutoScaling::LaunchConfiguration"
][0]
launch_config_resource["PhysicalResourceId"].should.contain("my-launch-config")
elb_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::ElasticLoadBalancing::LoadBalancer"
][0]
elb_resource["PhysicalResourceId"].should.contain("my-elb")
# confirm the instances were created with the right tags
reservations = ec2.describe_instances()["Reservations"]
reservations.should.have.length_of(1)
reservations[0]["Instances"].should.have.length_of(2)
for instance in reservations[0]["Instances"]:
tag_keys = [t["Key"] for t in instance["Tags"]]
tag_keys.should.contain("propagated-test-tag")
tag_keys.should_not.contain("not-propagated-test-tag")
@mock_autoscaling
@mock_cloudformation
@mock_ec2
def test_autoscaling_group_update():
asg_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-as-group": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": ["us-west-1a"],
"LaunchConfigurationName": {"Ref": "my-launch-config"},
"MinSize": "2",
"MaxSize": "2",
"DesiredCapacity": "2",
},
},
"my-launch-config": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": EXAMPLE_AMI_ID,
"InstanceType": "t2.medium",
"UserData": "some user data",
},
},
},
}
asg_template_json = json.dumps(asg_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
ec2 = boto3.client("ec2", region_name="us-west-1")
client = boto3.client("autoscaling", region_name="us-west-1")
cf.create_stack(StackName="asg_stack", TemplateBody=asg_template_json)
asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
asg["MinSize"].should.equal(2)
asg["MaxSize"].should.equal(2)
asg["DesiredCapacity"].should.equal(2)
asg_template["Resources"]["my-as-group"]["Properties"]["MaxSize"] = 3
asg_template["Resources"]["my-as-group"]["Properties"]["Tags"] = [
{
"Key": "propagated-test-tag",
"Value": "propagated-test-tag-value",
"PropagateAtLaunch": True,
},
{
"Key": "not-propagated-test-tag",
"Value": "not-propagated-test-tag-value",
"PropagateAtLaunch": False,
},
]
asg_template_json = json.dumps(asg_template)
cf.update_stack(StackName="asg_stack", TemplateBody=asg_template_json)
asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
asg["MinSize"].should.equal(2)
asg["MaxSize"].should.equal(3)
asg["DesiredCapacity"].should.equal(2)
# confirm the instances were created with the right tags
reservations = ec2.describe_instances()["Reservations"]
running_instance_count = 0
for res in reservations:
for instance in res["Instances"]:
if instance["State"]["Name"] == "running":
running_instance_count += 1
instance["Tags"].should.contain(
{"Key": "propagated-test-tag", "Value": "propagated-test-tag-value"}
)
tag_keys = [t["Key"] for t in instance["Tags"]]
tag_keys.should_not.contain("not-propagated-test-tag")
running_instance_count.should.equal(2)

View File

@ -4,7 +4,6 @@ import os
import json
import boto
import boto3
import boto.dynamodb2
import boto.iam
import boto.s3
@ -15,7 +14,6 @@ from freezegun import freeze_time
import sure # noqa
import pytest
from moto.core import ACCOUNT_ID
from moto import (
mock_cloudformation_deprecated,
@ -79,6 +77,7 @@ dummy_template_json3 = json.dumps(dummy_template3)
dummy_template_json4 = json.dumps(dummy_template4)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_create_stack():
conn = boto.connect_cloudformation()
@ -127,6 +126,7 @@ def test_create_stack_with_other_region():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_route53_deprecated
def test_create_stack_hosted_zone_by_id():
@ -168,6 +168,7 @@ def test_create_stack_hosted_zone_by_id():
assert stack.list_resources()
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_creating_stacks_across_regions():
west1_conn = boto.cloudformation.connect_to_region("us-west-1")
@ -180,6 +181,7 @@ def test_creating_stacks_across_regions():
list(west2_conn.describe_stacks()).should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_sns_deprecated
@mock_sqs_deprecated
@ -246,6 +248,7 @@ def test_create_stack_with_notification_arn():
msg.should.have.key("UnsubscribeURL")
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_s3_deprecated
def test_create_stack_from_s3_url():
@ -275,6 +278,7 @@ def test_create_stack_from_s3_url():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_describe_stack_by_name():
conn = boto.connect_cloudformation()
@ -284,6 +288,7 @@ def test_describe_stack_by_name():
stack.stack_name.should.equal("test_stack")
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_describe_stack_by_stack_id():
conn = boto.connect_cloudformation()
@ -296,6 +301,7 @@ def test_describe_stack_by_stack_id():
@mock_dynamodb2_deprecated
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_delete_stack_dynamo_template():
conn = boto.connect_cloudformation()
@ -308,6 +314,7 @@ def test_delete_stack_dynamo_template():
db_conn.list_tables()["TableNames"].should.have.length_of(0)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_describe_deleted_stack():
conn = boto.connect_cloudformation()
@ -322,6 +329,7 @@ def test_describe_deleted_stack():
stack_by_id.stack_status.should.equal("DELETE_COMPLETE")
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_get_template_by_name():
conn = boto.connect_cloudformation()
@ -342,6 +350,7 @@ def test_get_template_by_name():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_list_stacks():
conn = boto.connect_cloudformation()
@ -353,6 +362,7 @@ def test_list_stacks():
stacks[0].template_description.should.equal("Stack 1")
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_list_stacks_with_filter():
conn = boto.connect_cloudformation()
@ -366,6 +376,7 @@ def test_list_stacks_with_filter():
stacks.should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_delete_stack_by_name():
conn = boto.connect_cloudformation()
@ -376,6 +387,7 @@ def test_delete_stack_by_name():
conn.describe_stacks().should.have.length_of(0)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_delete_stack_by_id():
conn = boto.connect_cloudformation()
@ -390,6 +402,7 @@ def test_delete_stack_by_id():
conn.describe_stacks(stack_id).should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_delete_stack_with_resource_missing_delete_attr():
conn = boto.connect_cloudformation()
@ -400,6 +413,7 @@ def test_delete_stack_with_resource_missing_delete_attr():
conn.describe_stacks().should.have.length_of(0)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_bad_describe_stack():
conn = boto.connect_cloudformation()
@ -407,6 +421,7 @@ def test_bad_describe_stack():
conn.describe_stacks("bad_stack")
# Has boto3 equivalent
@mock_cloudformation_deprecated()
def test_cloudformation_params():
dummy_template = {
@ -435,6 +450,7 @@ def test_cloudformation_params():
param.value.should.equal("testing123")
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_cloudformation_params_conditions_and_resources_are_distinct():
dummy_template = {
@ -471,6 +487,7 @@ def test_cloudformation_params_conditions_and_resources_are_distinct():
]
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_stack_tags():
conn = boto.connect_cloudformation()
@ -484,6 +501,7 @@ def test_stack_tags():
dict(stack.tags).should.equal({"foo": "bar", "baz": "bleh"})
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_update_stack():
conn = boto.connect_cloudformation()
@ -507,6 +525,7 @@ def test_update_stack():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_update_stack_with_previous_template():
conn = boto.connect_cloudformation()
@ -529,6 +548,7 @@ def test_update_stack_with_previous_template():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_update_stack_with_parameters():
dummy_template = {
@ -559,6 +579,7 @@ def test_update_stack_with_parameters():
assert stack.parameters[0].value == "192.168.0.1/16"
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_update_stack_replace_tags():
conn = boto.connect_cloudformation()
@ -575,6 +596,7 @@ def test_update_stack_replace_tags():
dict(stack.tags).should.equal({"foo": "baz"})
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_update_stack_when_rolled_back():
conn = boto.connect_cloudformation()
@ -594,6 +616,7 @@ def test_update_stack_when_rolled_back():
ex.status.should.equal(400)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_describe_stack_events_shows_create_update_and_delete():
conn = boto.connect_cloudformation()
@ -647,6 +670,7 @@ def test_describe_stack_events_shows_create_update_and_delete():
err.status.should.equal(400)
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_create_stack_lambda_and_dynamodb():
conn = boto.connect_cloudformation()
@ -714,6 +738,7 @@ def test_create_stack_lambda_and_dynamodb():
assert len(resources) == 4
# Has boto3 equivalent
@mock_cloudformation_deprecated
def test_create_stack_kinesis():
conn = boto.connect_cloudformation()

View File

@ -3,12 +3,14 @@ from __future__ import unicode_literals
import json
from collections import OrderedDict
from datetime import datetime, timedelta
import os
import pytz
import boto3
from botocore.exceptions import ClientError
import pytest
from unittest import SkipTest
from moto import (
mock_cloudformation,
@ -17,9 +19,14 @@ from moto import (
mock_sns,
mock_sqs,
mock_ec2,
mock_iam,
mock_lambda,
)
from moto import settings
from moto.core import ACCOUNT_ID
from moto.cloudformation import cloudformation_backends
from .test_cloudformation_stack_crud import dummy_template_json2, dummy_template_json4
from tests import EXAMPLE_AMI_ID
dummy_template = {
@ -223,6 +230,20 @@ dummy_redrive_template_json = json.dumps(dummy_redrive_template)
@mock_cloudformation
@mock_ec2
def test_create_stack():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
stack = cf_conn.describe_stacks()["Stacks"][0]
stack.should.have.key("StackName").equal("test_stack")
template = cf_conn.get_template(StackName="test_stack")["TemplateBody"]
template.should.equal(dummy_template)
@mock_cloudformation
@mock_ec2
def test_boto3_describe_stack_instances():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
cf_conn.create_stack_set(
@ -1560,15 +1581,39 @@ def test_describe_updated_stack():
stack_by_id["RoleARN"].should.equal("arn:aws:iam::{}:role/moto".format(ACCOUNT_ID))
stack_by_id["Tags"].should.equal([{"Key": "foo", "Value": "baz"}])
# Verify the updated template is persisted
template = cf_conn.get_template(StackName="test_stack")["TemplateBody"]
template.should.equal(dummy_update_template)
@mock_cloudformation
def test_update_stack_with_previous_template():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
cf_conn.update_stack(StackName="test_stack", UsePreviousTemplate=True)
stack = cf_conn.describe_stacks(StackName="test_stack")["Stacks"][0]
stack["StackName"].should.equal("test_stack")
stack["StackStatus"].should.equal("UPDATE_COMPLETE")
# Verify the original template is persisted
template = cf_conn.get_template(StackName="test_stack")["TemplateBody"]
template.should.equal(dummy_template)
@mock_cloudformation
def test_bad_describe_stack():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
with pytest.raises(ClientError):
with pytest.raises(ClientError) as exc:
cf_conn.describe_stacks(StackName="non_existent_stack")
err = exc.value.response["Error"]
err.should.have.key("Code").being.equal("ValidationError")
err.should.have.key("Message").being.equal(
"Stack with id non_existent_stack does not exist"
)
@mock_cloudformation()
@mock_cloudformation
def test_cloudformation_params():
dummy_template_with_params = {
"AWSTemplateFormatVersion": "2010-09-09",
@ -1597,6 +1642,119 @@ def test_cloudformation_params():
param["ParameterValue"].should.equal("testing123")
@mock_cloudformation
@mock_ec2
def test_update_stack_with_parameters():
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack",
"Resources": {
"VPC": {
"Properties": {"CidrBlock": {"Ref": "Bar"}},
"Type": "AWS::EC2::VPC",
}
},
"Parameters": {"Bar": {"Type": "String"}},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-east-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[{"ParameterKey": "Bar", "ParameterValue": "192.168.0.0/16"}],
)
cf.update_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[{"ParameterKey": "Bar", "ParameterValue": "192.168.0.1/16"}],
)
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
stack["Parameters"].should.have.length_of(1)
stack["Parameters"][0].should.equal(
{"ParameterKey": "Bar", "ParameterValue": "192.168.0.1/16"}
)
@mock_cloudformation
@mock_ec2
def test_update_stack_replace_tags():
cf = boto3.client("cloudformation", region_name="us-east-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=dummy_template_json,
Tags=[{"Key": "foo", "Value": "bar"}],
)
cf.update_stack(
StackName="test_stack",
TemplateBody=dummy_template_json,
Tags=[{"Key": "foo", "Value": "baz"}],
)
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
stack["StackStatus"].should.equal("UPDATE_COMPLETE")
stack["Tags"].should.equal([{"Key": "foo", "Value": "baz"}])
@mock_cloudformation
def test_update_stack_when_rolled_back():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant manipulate backend in server mode")
cf = boto3.client("cloudformation", region_name="us-east-1")
stack = cf.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
stack_id = stack["StackId"]
cloudformation_backends["us-east-1"].stacks[stack_id].status = "ROLLBACK_COMPLETE"
with pytest.raises(ClientError) as ex:
cf.update_stack(StackName="test_stack", TemplateBody=dummy_template_json)
err = ex.value.response["Error"]
err.should.have.key("Code").being.equal("ValidationError")
err.should.have.key("Message").match(
r"Stack:arn:aws:cloudformation:us-east-1:123456789:stack/test_stack/[a-z0-9-]+ is in ROLLBACK_COMPLETE state and can not be updated."
)
@mock_cloudformation
@mock_ec2
def test_cloudformation_params_conditions_and_resources_are_distinct():
template_with_conditions = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 1",
"Conditions": {
"FooEnabled": {"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]},
"FooDisabled": {
"Fn::Not": [{"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]}]
},
},
"Parameters": {
"FooEnabled": {"Type": "String", "AllowedValues": ["true", "false"]}
},
"Resources": {
"Bar": {
"Properties": {"CidrBlock": "192.168.0.0/16"},
"Condition": "FooDisabled",
"Type": "AWS::EC2::VPC",
}
},
}
template_with_conditions = json.dumps(template_with_conditions)
cf = boto3.client("cloudformation", region_name="us-east-1")
cf.create_stack(
StackName="test_stack1",
TemplateBody=template_with_conditions,
Parameters=[{"ParameterKey": "FooEnabled", "ParameterValue": "true"}],
)
stack = cf.describe_stacks(StackName="test_stack1")["Stacks"][0]
resources = cf.list_stack_resources(StackName="test_stack1")[
"StackResourceSummaries"
]
assert not [
resource for resource in resources if resource["LogicalResourceId"] == "Bar"
]
@mock_cloudformation
def test_stack_tags():
tags = [{"Key": "foo", "Value": "bar"}, {"Key": "baz", "Value": "bleh"}]
@ -1791,3 +1949,91 @@ def test_delete_stack_dynamo_template():
table_desc = dynamodb_client.list_tables()
len(table_desc.get("TableNames")).should.equal(0)
conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json4)
@mock_dynamodb2
@mock_cloudformation
@mock_lambda
def test_create_stack_lambda_and_dynamodb():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
cf = boto3.client("cloudformation", region_name="us-east-1")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack Lambda Test 1",
"Parameters": {},
"Resources": {
"func1": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"S3Bucket": "bucket_123", "S3Key": "key_123"},
"FunctionName": "func1",
"Handler": "handler.handler",
"Role": get_role_name(),
"Runtime": "python2.7",
"Description": "descr",
"MemorySize": 12345,
},
},
"func1version": {
"Type": "AWS::Lambda::Version",
"Properties": {"FunctionName": {"Ref": "func1"}},
},
"tab1": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "tab1",
"KeySchema": [{"AttributeName": "attr1", "KeyType": "HASH"}],
"AttributeDefinitions": [
{"AttributeName": "attr1", "AttributeType": "string"}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 10,
},
"StreamSpecification": {"StreamViewType": "KEYS_ONLY"},
},
},
"func1mapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"Properties": {
"FunctionName": {"Ref": "func1"},
"EventSourceArn": {"Fn::GetAtt": ["tab1", "StreamArn"]},
"StartingPosition": "0",
"BatchSize": 100,
"Enabled": True,
},
},
},
}
validate_s3_before = os.environ.get("VALIDATE_LAMBDA_S3", "")
try:
os.environ["VALIDATE_LAMBDA_S3"] = "false"
cf.create_stack(
StackName="test_stack_lambda", TemplateBody=json.dumps(template),
)
finally:
os.environ["VALIDATE_LAMBDA_S3"] = validate_s3_before
resources = cf.list_stack_resources(StackName="test_stack_lambda")[
"StackResourceSummaries"
]
resources.should.have.length_of(4)
resource_types = [r["ResourceType"] for r in resources]
resource_types.should.contain("AWS::Lambda::Function")
resource_types.should.contain("AWS::Lambda::Version")
resource_types.should.contain("AWS::DynamoDB::Table")
resource_types.should.contain("AWS::Lambda::EventSourceMapping")
def get_role_name():
with mock_iam():
iam = boto3.client("iam", region_name="us-east-1")
try:
return iam.get_role(RoleName="my-role")["Role"]["Arn"]
except ClientError:
return iam.create_role(
RoleName="my-role",
AssumeRolePolicyDocument="some policy",
Path="/my-path/",
)["Role"]["Arn"]

View File

@ -21,6 +21,8 @@ import boto.sqs
import boto.vpc
import boto3
import sure # noqa
import pytest
from copy import deepcopy
from string import Template
from moto import (
@ -44,6 +46,7 @@ from moto import (
mock_route53_deprecated,
mock_s3,
mock_sns_deprecated,
mock_sqs,
mock_sqs_deprecated,
mock_elbv2,
)
@ -65,6 +68,7 @@ from tests.test_cloudformation.fixtures import (
)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
def test_stack_sqs_integration():
sqs_template = {
@ -88,6 +92,7 @@ def test_stack_sqs_integration():
queue.physical_resource_id.should.equal("my-queue")
# Has boto3 equivalent
@mock_cloudformation_deprecated()
def test_stack_list_resources():
sqs_template = {
@ -112,6 +117,7 @@ def test_stack_list_resources():
queue.physical_resource_id.should.equal("my-queue")
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack():
@ -147,6 +153,7 @@ def test_update_stack():
)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack_and_remove_resource():
@ -176,6 +183,7 @@ def test_update_stack_and_remove_resource():
queues.should.have.length_of(0)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack_and_add_resource():
@ -205,6 +213,7 @@ def test_update_stack_and_add_resource():
queues.should.have.length_of(1)
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_stack_ec2_integration():
@ -233,6 +242,7 @@ def test_stack_ec2_integration():
instance.physical_resource_id.should.equal(ec2_instance.id)
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
@ -277,6 +287,7 @@ def test_stack_elb_integration_with_attached_ec2_instances():
list(load_balancer.availability_zones).should.equal(["us-east-1"])
# Has boto3 equivalent
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
def test_stack_elb_integration_with_health_check():
@ -322,6 +333,7 @@ def test_stack_elb_integration_with_health_check():
health_check.unhealthy_threshold.should.equal(2)
# Has boto3 equivalent
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
def test_stack_elb_integration_with_update():
@ -363,6 +375,7 @@ def test_stack_elb_integration_with_update():
load_balancer.availability_zones[0].should.equal("us-west-1b")
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_redshift_deprecated()
@mock_cloudformation_deprecated()
@ -409,6 +422,7 @@ def test_redshift_stack():
group.rules[0].grants[0].cidr_ip.should.equal("10.0.0.1/16")
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_stack_security_groups():
@ -485,6 +499,7 @@ def test_stack_security_groups():
rule2.grants[0].group_id.should.equal(other_group.id)
# Has boto3 equivalent
@mock_autoscaling_deprecated()
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
@ -599,6 +614,7 @@ def test_autoscaling_group_with_elb():
instance.tags.keys().should_not.contain("not-propagated-test-tag")
# Has boto3 equivalent
@mock_autoscaling_deprecated()
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@ -672,6 +688,7 @@ def test_autoscaling_group_update():
running_instance_count.should.equal(2)
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_vpc_single_instance_in_subnet():
@ -727,56 +744,7 @@ def test_vpc_single_instance_in_subnet():
eip_resource.physical_resource_id.should.equal(eip.public_ip)
@mock_cloudformation()
@mock_ec2()
@mock_rds2()
def test_rds_db_parameter_groups():
ec2_conn = boto3.client("ec2", region_name="us-west-1")
ec2_conn.create_security_group(
GroupName="application", Description="Our Application Group"
)
template_json = json.dumps(rds_mysql_with_db_parameter_group.template)
cf_conn = boto3.client("cloudformation", "us-west-1")
cf_conn.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[
{"ParameterKey": key, "ParameterValue": value}
for key, value in [
("DBInstanceIdentifier", "master_db"),
("DBName", "my_db"),
("DBUser", "my_user"),
("DBPassword", "my_password"),
("DBAllocatedStorage", "20"),
("DBInstanceClass", "db.m1.medium"),
("EC2SecurityGroup", "application"),
("MultiAZ", "true"),
]
],
)
rds_conn = boto3.client("rds", region_name="us-west-1")
db_parameter_groups = rds_conn.describe_db_parameter_groups()
len(db_parameter_groups["DBParameterGroups"]).should.equal(1)
db_parameter_group_name = db_parameter_groups["DBParameterGroups"][0][
"DBParameterGroupName"
]
found_cloudformation_set_parameter = False
for db_parameter in rds_conn.describe_db_parameters(
DBParameterGroupName=db_parameter_group_name
)["Parameters"]:
if (
db_parameter["ParameterName"] == "BACKLOG_QUEUE_LIMIT"
and db_parameter["ParameterValue"] == "2048"
):
found_cloudformation_set_parameter = True
found_cloudformation_set_parameter.should.equal(True)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_rds_deprecated()
@ -819,6 +787,7 @@ def test_rds_mysql_with_read_replica():
security_group.ec2_groups[0].name.should.equal("application")
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_rds_deprecated()
@ -847,6 +816,7 @@ def test_rds_mysql_with_read_replica_in_vpc():
subnet_group.description.should.equal("my db subnet group")
# Has boto3 equivalent
@mock_autoscaling_deprecated()
@mock_iam_deprecated()
@mock_cloudformation_deprecated()
@ -1002,6 +972,7 @@ def test_iam_roles():
{r.physical_resource_id for r in role_resources}.should.equal(set(role_names))
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_single_instance_with_ebs_volume():
@ -1033,6 +1004,7 @@ def test_single_instance_with_ebs_volume():
ebs_volumes[0].physical_resource_id.should.equal(volume.id)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
def test_create_template_without_required_param():
template_json = json.dumps(single_instance_with_ebs_volume.template)
@ -1042,6 +1014,18 @@ def test_create_template_without_required_param():
).should.throw(BotoServerError)
@mock_cloudformation
def test_create_template_without_required_param_boto3():
template_json = json.dumps(single_instance_with_ebs_volume.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
with pytest.raises(ClientError) as ex:
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
err = ex.value.response["Error"]
err.should.have.key("Code").equal("Missing Parameter")
err.should.have.key("Message").equal("Missing parameter KeyName")
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_classic_eip():
@ -1059,6 +1043,7 @@ def test_classic_eip():
cfn_eip.physical_resource_id.should.equal(eip.public_ip)
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_vpc_eip():
@ -1076,6 +1061,7 @@ def test_vpc_eip():
cfn_eip.physical_resource_id.should.equal(eip.public_ip)
# Has boto3 equivalent
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_fn_join():
@ -1090,6 +1076,21 @@ def test_fn_join():
fn_join_output.value.should.equal("test eip:{0}".format(eip.public_ip))
@mock_ec2
@mock_cloudformation
def test_fn_join_boto3():
template_json = json.dumps(fn_join.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
eip = ec2.describe_addresses()["Addresses"][0]
stack = cf.describe_stacks()["Stacks"][0]
fn_join_output = stack["Outputs"][0]
fn_join_output["OutputValue"].should.equal("test eip:{0}".format(eip["PublicIp"]))
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_conditional_resources():
@ -1128,6 +1129,43 @@ def test_conditional_resources():
list(sqs_conn.get_all_queues()).should.have.length_of(1)
@mock_cloudformation
@mock_sqs
def test_conditional_resources_boto3():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"EnvType": {"Description": "Environment type.", "Type": "String"}
},
"Conditions": {"CreateQueue": {"Fn::Equals": [{"Ref": "EnvType"}, "prod"]}},
"Resources": {
"QueueGroup": {
"Condition": "CreateQueue",
"Type": "AWS::SQS::Queue",
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(
StackName="test_stack_without_queue",
TemplateBody=sqs_template_json,
Parameters=[{"ParameterKey": "EnvType", "ParameterValue": "staging"}],
)
sqs = boto3.client("sqs", region_name="us-west-1")
sqs.list_queues().shouldnt.have.key("QueueUrls")
cf.create_stack(
StackName="test_stack_with_queue",
TemplateBody=sqs_template_json,
Parameters=[{"ParameterKey": "EnvType", "ParameterValue": "prod"}],
)
sqs.list_queues()["QueueUrls"].should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_conditional_if_handling():
@ -1173,6 +1211,51 @@ def test_conditional_if_handling():
ec2_instance.image_id.should.equal(EXAMPLE_AMI_ID)
@mock_cloudformation
@mock_ec2
def test_conditional_if_handling_boto3():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Conditions": {"EnvEqualsPrd": {"Fn::Equals": [{"Ref": "ENV"}, "prd"]}},
"Parameters": {
"ENV": {
"Default": "dev",
"Description": "Deployment environment for the stack (dev/prd)",
"Type": "String",
}
},
"Description": "Stack 1",
"Resources": {
"App1": {
"Properties": {
"ImageId": {
"Fn::If": ["EnvEqualsPrd", EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2]
}
},
"Type": "AWS::EC2::Instance",
}
},
}
dummy_template_json = json.dumps(dummy_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
ec2_instance["ImageId"].should.equal(EXAMPLE_AMI_ID2)
cf = boto3.client("cloudformation", region_name="us-west-2")
cf.create_stack(
StackName="test_stack",
TemplateBody=dummy_template_json,
Parameters=[{"ParameterKey": "ENV", "ParameterValue": "prd"}],
)
ec2 = boto3.client("ec2", region_name="us-west-2")
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
ec2_instance["ImageId"].should.equal(EXAMPLE_AMI_ID)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_cloudformation_mapping():
@ -1217,6 +1300,49 @@ def test_cloudformation_mapping():
ec2_instance.image_id.should.equal(EXAMPLE_AMI_ID)
@mock_cloudformation
@mock_ec2
def test_cloudformation_mapping_boto3():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"RegionMap": {
"us-east-1": {"32": EXAMPLE_AMI_ID, "64": "n/a"},
"us-west-1": {"32": EXAMPLE_AMI_ID2, "64": "n/a"},
"eu-west-1": {"32": "n/a", "64": "n/a"},
"ap-southeast-1": {"32": "n/a", "64": "n/a"},
"ap-northeast-1": {"32": "n/a", "64": "n/a"},
}
},
"Resources": {
"WebServer": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": {
"Fn::FindInMap": ["RegionMap", {"Ref": "AWS::Region"}, "32"]
},
"InstanceType": "m1.small",
},
}
},
}
dummy_template_json = json.dumps(dummy_template)
cf = boto3.client("cloudformation", region_name="us-east-1")
cf.create_stack(StackName="test_stack1", TemplateBody=dummy_template_json)
ec2 = boto3.client("ec2", region_name="us-east-1")
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
ec2_instance["ImageId"].should.equal(EXAMPLE_AMI_ID)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack1", TemplateBody=dummy_template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
ec2_instance["ImageId"].should.equal(EXAMPLE_AMI_ID2)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
def test_route53_roundrobin():
@ -1259,6 +1385,7 @@ def test_route53_roundrobin():
output.value.should.equal("arn:aws:route53:::hostedzone/{0}".format(zone_id))
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_route53_deprecated()
@ -1292,6 +1419,7 @@ def test_route53_ec2_instance_with_public_ip():
record_set1.resource_records[0].should.equal("10.0.0.25")
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
def test_route53_associate_health_check():
@ -1330,6 +1458,7 @@ def test_route53_associate_health_check():
record_set.health_check.should.equal(health_check_id)
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
def test_route53_with_update():
@ -1353,10 +1482,11 @@ def test_route53_with_update():
record_set = rrsets[0]
record_set.resource_records.should.equal(["my.example.com"])
route53_health_check.template["Resources"]["myDNSRecord"]["Properties"][
"ResourceRecords"
] = ["my_other.example.com"]
template_json = json.dumps(route53_health_check.template)
template = deepcopy(route53_health_check.template)
template["Resources"]["myDNSRecord"]["Properties"]["ResourceRecords"] = [
"my_other.example.com"
]
template_json = json.dumps(template)
cf_conn.update_stack("test_stack", template_body=template_json)
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
@ -1374,6 +1504,7 @@ def test_route53_with_update():
record_set.resource_records.should.equal(["my_other.example.com"])
# Has boto3 equivalent
@mock_cloudformation_deprecated()
@mock_sns_deprecated()
def test_sns_topic():
@ -1424,6 +1555,7 @@ def test_sns_topic():
topic_arn_output.value.should.equal(topic_arn)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_vpc_gateway_attachment_creation_should_attach_itself_to_vpc():
@ -1461,6 +1593,7 @@ def test_vpc_gateway_attachment_creation_should_attach_itself_to_vpc():
igws.should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_vpc_peering_creation():
@ -1485,6 +1618,7 @@ def test_vpc_peering_creation():
peering_connections.should.have.length_of(1)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_multiple_security_group_ingress_separate_from_security_group_by_id():
@ -1538,6 +1672,7 @@ def test_multiple_security_group_ingress_separate_from_security_group_by_id():
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_ingress_separate_from_security_group_by_id():
@ -1585,6 +1720,7 @@ def test_security_group_ingress_separate_from_security_group_by_id():
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_ingress_separate_from_security_group_by_id_using_vpc():
@ -1642,6 +1778,7 @@ def test_security_group_ingress_separate_from_security_group_by_id_using_vpc():
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_with_update():
@ -1676,6 +1813,7 @@ def test_security_group_with_update():
security_group.vpc_id.should.equal(vpc2.id)
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_subnets_should_be_created_with_availability_zone():
@ -1702,6 +1840,7 @@ def test_subnets_should_be_created_with_availability_zone():
subnet.availability_zone.should.equal("us-west-1b")
# Has boto3 equivalent
@mock_cloudformation_deprecated
@mock_datapipeline_deprecated
def test_datapipeline():

View File

@ -0,0 +1,70 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_datapipeline
@mock_cloudformation
@mock_datapipeline
def test_datapipeline():
dp_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"dataPipeline": {
"Properties": {
"Activate": "true",
"Name": "testDataPipeline",
"PipelineObjects": [
{
"Fields": [
{
"Key": "failureAndRerunMode",
"StringValue": "CASCADE",
},
{"Key": "scheduleType", "StringValue": "cron"},
{"Key": "schedule", "RefValue": "DefaultSchedule"},
{
"Key": "pipelineLogUri",
"StringValue": "s3://bucket/logs",
},
{"Key": "type", "StringValue": "Default"},
],
"Id": "Default",
"Name": "Default",
},
{
"Fields": [
{
"Key": "startDateTime",
"StringValue": "1970-01-01T01:00:00",
},
{"Key": "period", "StringValue": "1 Day"},
{"Key": "type", "StringValue": "Schedule"},
],
"Id": "DefaultSchedule",
"Name": "RunOnce",
},
],
"PipelineTags": [],
},
"Type": "AWS::DataPipeline::Pipeline",
}
},
}
cf = boto3.client("cloudformation", region_name="us-east-1")
template_json = json.dumps(dp_template)
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
dp = boto3.client("datapipeline", region_name="us-east-1")
data_pipelines = dp.list_pipelines()["pipelineIdList"]
data_pipelines.should.have.length_of(1)
data_pipelines[0]["name"].should.equal("testDataPipeline")
stack_resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
stack_resources.should.have.length_of(1)
stack_resources[0]["PhysicalResourceId"].should.equal(data_pipelines[0]["id"])

View File

@ -0,0 +1,51 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_dynamodb2
template_create_table = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"myDynamoDBTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"AttributeDefinitions": [
{"AttributeName": "Name", "AttributeType": "S"},
{"AttributeName": "Age", "AttributeType": "S"},
],
"KeySchema": [
{"AttributeName": "Name", "KeyType": "HASH"},
{"AttributeName": "Age", "KeyType": "RANGE"},
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
"TableName": "Person",
},
}
},
}
@mock_dynamodb2
@mock_cloudformation
def test_delete_stack_dynamo_template_boto3():
conn = boto3.client("cloudformation", region_name="us-east-1")
dynamodb_client = boto3.client("dynamodb", region_name="us-east-1")
conn.create_stack(
StackName="test_stack", TemplateBody=json.dumps(template_create_table)
)
table_desc = dynamodb_client.list_tables()
len(table_desc.get("TableNames")).should.equal(1)
conn.delete_stack(StackName="test_stack")
table_desc = dynamodb_client.list_tables()
len(table_desc.get("TableNames")).should.equal(0)
conn.create_stack(
StackName="test_stack", TemplateBody=json.dumps(template_create_table)
)

View File

@ -1,16 +1,124 @@
from botocore.exceptions import ClientError
from moto import mock_cloudformation_deprecated, mock_ec2_deprecated
from moto import mock_cloudformation, mock_ec2
from tests import EXAMPLE_AMI_ID
from tests.test_cloudformation.fixtures import ec2_classic_eip
from tests.test_cloudformation.fixtures import single_instance_with_ebs_volume
from tests.test_cloudformation.fixtures import vpc_eip
from tests.test_cloudformation.fixtures import vpc_eni
from tests.test_cloudformation.fixtures import vpc_single_instance_in_subnet
import boto
import boto.ec2
import boto.cloudformation
import boto.vpc
import boto3
import json
import pytest
import sure # noqa
template_vpc = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Create VPC",
"Resources": {
"VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"}
},
}
@mock_ec2
@mock_cloudformation
def test_vpc_single_instance_in_subnet():
template_json = json.dumps(vpc_single_instance_in_subnet.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[{"ParameterKey": "KeyName", "ParameterValue": "my_key"}],
)
ec2 = boto3.client("ec2", region_name="us-west-1")
vpc = ec2.describe_vpcs(Filters=[{"Name": "cidrBlock", "Values": ["10.0.0.0/16"]}])[
"Vpcs"
][0]
vpc["CidrBlock"].should.equal("10.0.0.0/16")
ec2.describe_internet_gateways()["InternetGateways"].should.have.length_of(1)
subnet = ec2.describe_subnets(
Filters=[{"Name": "vpcId", "Values": [vpc["VpcId"]]}]
)["Subnets"][0]
subnet["VpcId"].should.equal(vpc["VpcId"])
ec2 = boto3.client("ec2", region_name="us-west-1")
reservation = ec2.describe_instances()["Reservations"][0]
instance = reservation["Instances"][0]
instance["Tags"].should.contain({"Key": "Foo", "Value": "Bar"})
# Check that the EIP is attached the the EC2 instance
eip = ec2.describe_addresses()["Addresses"][0]
eip["Domain"].should.equal("vpc")
eip["InstanceId"].should.equal(instance["InstanceId"])
security_group = ec2.describe_security_groups(
Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
)["SecurityGroups"][0]
security_group["VpcId"].should.equal(vpc["VpcId"])
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
vpc["Tags"].should.contain({"Key": "Application", "Value": stack["StackId"]})
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
vpc_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::VPC"
][0]
vpc_resource["PhysicalResourceId"].should.equal(vpc["VpcId"])
subnet_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::Subnet"
][0]
subnet_resource["PhysicalResourceId"].should.equal(subnet["SubnetId"])
eip_resource = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::EIP"
][0]
eip_resource["PhysicalResourceId"].should.equal(eip["PublicIp"])
@mock_cloudformation
@mock_ec2
def test_delete_stack_with_resource_missing_delete_attr():
cf = boto3.client("cloudformation", region_name="us-east-1")
ec2 = boto3.client("ec2", region_name="us-east-1")
name = "test_stack"
cf.create_stack(StackName=name, TemplateBody=json.dumps(template_vpc))
cf.describe_stacks(StackName=name)["Stacks"].should.have.length_of(1)
ec2.describe_vpcs()["Vpcs"].should.have.length_of(2)
cf.delete_stack(
StackName=name
) # should succeed, despite the fact that the resource itself cannot be deleted
with pytest.raises(ClientError) as exc:
cf.describe_stacks(StackName=name)
err = exc.value.response["Error"]
err.should.have.key("Code").equals("ValidationError")
err.should.have.key("Message").equals("Stack with id test_stack does not exist")
# We still have two VPCs, as the VPC-object does not have a delete-method yet
ec2.describe_vpcs()["Vpcs"].should.have.length_of(2)
# Has boto3 equivalent
@mock_ec2_deprecated
@mock_cloudformation_deprecated
def test_elastic_network_interfaces_cloudformation():
@ -35,6 +143,34 @@ def test_elastic_network_interfaces_cloudformation():
outputs["ENIIpAddress"].should.equal(eni.private_ip_addresses[0].private_ip_address)
@mock_ec2
@mock_cloudformation
def test_elastic_network_interfaces_cloudformation_boto3():
template = vpc_eni.template
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
eni = ec2.describe_network_interfaces()["NetworkInterfaces"][0]
eni["PrivateIpAddresses"].should.have.length_of(1)
private_ip_address = eni["PrivateIpAddresses"][0]["PrivateIpAddress"]
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
cfn_eni = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::NetworkInterface"
][0]
cfn_eni["PhysicalResourceId"].should.equal(eni["NetworkInterfaceId"])
outputs = cf.describe_stacks(StackName="test_stack")["Stacks"][0]["Outputs"]
outputs.should.contain(
{"OutputKey": "ENIIpAddress", "OutputValue": private_ip_address}
)
@mock_ec2
@mock_cloudformation
def test_volume_size_through_cloudformation():
@ -63,7 +199,15 @@ def test_volume_size_through_cloudformation():
}
template_json = json.dumps(volume_template)
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
instances = ec2.describe_instances()
resource = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
][0]
resource.should.have.key("LogicalResourceId").being.equal("testInstance")
resource.should.have.key("PhysicalResourceId").shouldnt.be.none
resource.should.have.key("ResourceType").being.equal("AWS::EC2::Instance")
instances = ec2.describe_instances(InstanceIds=[resource["PhysicalResourceId"]])
volume = instances["Reservations"][0]["Instances"][0]["BlockDeviceMappings"][0][
"Ebs"
]
@ -72,6 +216,7 @@ def test_volume_size_through_cloudformation():
volumes["Volumes"][0]["Size"].should.equal(50)
# Has boto3 equivalent
@mock_ec2_deprecated
@mock_cloudformation_deprecated
def test_subnet_tags_through_cloudformation():
@ -102,3 +247,405 @@ def test_subnet_tags_through_cloudformation():
subnet = vpc_conn.get_all_subnets(filters={"cidrBlock": "10.0.0.0/24"})[0]
subnet.tags["foo"].should.equal("bar")
subnet.tags["blah"].should.equal("baz")
@mock_ec2
@mock_cloudformation
def test_subnet_tags_through_cloudformation_boto3():
ec2 = boto3.client("ec2", region_name="us-west-1")
ec2_res = boto3.resource("ec2", region_name="us-west-1")
vpc = ec2_res.create_vpc(CidrBlock="10.0.0.0/16")
subnet_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"testSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": vpc.id,
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": "us-west-1b",
"Tags": [
{"Key": "foo", "Value": "bar"},
{"Key": "blah", "Value": "baz"},
],
},
}
},
}
template_json = json.dumps(subnet_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
subnet = ec2.describe_subnets(
Filters=[{"Name": "cidrBlock", "Values": ["10.0.0.0/24"]}]
)["Subnets"][0]
subnet["Tags"].should.contain({"Key": "foo", "Value": "bar"})
subnet["Tags"].should.contain({"Key": "blah", "Value": "baz"})
@mock_ec2
@mock_cloudformation
def test_single_instance_with_ebs_volume():
template_json = json.dumps(single_instance_with_ebs_volume.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[{"ParameterKey": "KeyName", "ParameterValue": "key_name"}],
)
ec2 = boto3.client("ec2", region_name="us-west-1")
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
volumes = ec2.describe_volumes()["Volumes"]
# Grab the mounted drive
volume = [
volume for volume in volumes if volume["Attachments"][0]["Device"] == "/dev/sdh"
][0]
volume["State"].should.equal("in-use")
volume["Attachments"][0]["InstanceId"].should.equal(ec2_instance["InstanceId"])
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
ebs_volumes = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::Volume"
]
ebs_volumes[0]["PhysicalResourceId"].should.equal(volume["VolumeId"])
@mock_ec2
@mock_cloudformation
def test_classic_eip():
template_json = json.dumps(ec2_classic_eip.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
eip = ec2.describe_addresses()["Addresses"][0]
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
cfn_eip = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::EIP"
][0]
cfn_eip["PhysicalResourceId"].should.equal(eip["PublicIp"])
@mock_ec2
@mock_cloudformation
def test_vpc_eip():
template_json = json.dumps(vpc_eip.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
eip = ec2.describe_addresses()["Addresses"][0]
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
cfn_eip = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::EC2::EIP"
][0]
cfn_eip["PhysicalResourceId"].should.equal(eip["PublicIp"])
@mock_cloudformation
@mock_ec2
def test_vpc_gateway_attachment_creation_should_attach_itself_to_vpc():
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"internetgateway": {"Type": "AWS::EC2::InternetGateway"},
"testvpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsHostnames": "true",
"EnableDnsSupport": "true",
"InstanceTenancy": "default",
},
},
"vpcgatewayattachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"InternetGatewayId": {"Ref": "internetgateway"},
"VpcId": {"Ref": "testvpc"},
},
},
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
vpc = ec2.describe_vpcs(Filters=[{"Name": "cidrBlock", "Values": ["10.0.0.0/16"]}])[
"Vpcs"
][0]
igws = ec2.describe_internet_gateways(
Filters=[{"Name": "attachment.vpc-id", "Values": [vpc["VpcId"]]}]
)["InternetGateways"]
igws.should.have.length_of(1)
@mock_cloudformation
@mock_ec2
def test_vpc_peering_creation():
ec2 = boto3.resource("ec2", region_name="us-west-1")
ec2_client = boto3.client("ec2", region_name="us-west-1")
vpc_source = ec2.create_vpc(CidrBlock="10.0.0.0/16")
peer_vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"vpcpeeringconnection": {
"Type": "AWS::EC2::VPCPeeringConnection",
"Properties": {"PeerVpcId": peer_vpc.id, "VpcId": vpc_source.id},
}
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
peering_connections = ec2_client.describe_vpc_peering_connections()[
"VpcPeeringConnections"
]
peering_connections.should.have.length_of(1)
@mock_cloudformation
@mock_ec2
def test_multiple_security_group_ingress_separate_from_security_group_by_id():
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group1": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"Tags": [{"Key": "sg-name", "Value": "sg1"}],
},
},
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"Tags": [{"Key": "sg-name", "Value": "sg2"}],
},
},
"test-sg-ingress": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": {
"GroupId": {"Ref": "test-security-group1"},
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "8080",
"SourceSecurityGroupId": {"Ref": "test-security-group2"},
},
},
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
ec2 = boto3.client("ec2", region_name="us-west-1")
security_group1 = get_secgroup_by_tag(ec2, "sg1")
security_group2 = get_secgroup_by_tag(ec2, "sg2")
security_group1["IpPermissions"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal(
security_group2["GroupId"]
)
security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp")
security_group1["IpPermissions"][0]["FromPort"].should.equal(80)
security_group1["IpPermissions"][0]["ToPort"].should.equal(8080)
@mock_cloudformation
@mock_ec2
def test_security_group_ingress_separate_from_security_group_by_id():
ec2 = boto3.client("ec2", region_name="us-west-1")
ec2.create_security_group(
GroupName="test-security-group1", Description="test security group"
)
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"Tags": [{"Key": "sg-name", "Value": "sg2"}],
},
},
"test-sg-ingress": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": {
"GroupName": "test-security-group1",
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "8080",
"SourceSecurityGroupId": {"Ref": "test-security-group2"},
},
},
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
security_group1 = ec2.describe_security_groups(GroupNames=["test-security-group1"])[
"SecurityGroups"
][0]
security_group2 = get_secgroup_by_tag(ec2, "sg2")
security_group1["IpPermissions"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal(
security_group2["GroupId"]
)
security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp")
security_group1["IpPermissions"][0]["FromPort"].should.equal(80)
security_group1["IpPermissions"][0]["ToPort"].should.equal(8080)
@mock_cloudformation
@mock_ec2
def test_security_group_ingress_separate_from_security_group_by_id_using_vpc():
ec2 = boto3.resource("ec2", region_name="us-west-1")
ec2_client = boto3.client("ec2", region_name="us-west-1")
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group1": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"VpcId": vpc.id,
"Tags": [{"Key": "sg-name", "Value": "sg1"}],
},
},
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"VpcId": vpc.id,
"Tags": [{"Key": "sg-name", "Value": "sg2"}],
},
},
"test-sg-ingress": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": {
"GroupId": {"Ref": "test-security-group1"},
"VpcId": vpc.id,
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "8080",
"SourceSecurityGroupId": {"Ref": "test-security-group2"},
},
},
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
security_group1 = get_secgroup_by_tag(ec2_client, "sg1")
security_group2 = get_secgroup_by_tag(ec2_client, "sg2")
security_group1["IpPermissions"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"].should.have.length_of(1)
security_group1["IpPermissions"][0]["UserIdGroupPairs"][0]["GroupId"].should.equal(
security_group2["GroupId"]
)
security_group1["IpPermissions"][0]["IpProtocol"].should.equal("tcp")
security_group1["IpPermissions"][0]["FromPort"].should.equal(80)
security_group1["IpPermissions"][0]["ToPort"].should.equal(8080)
@mock_cloudformation
@mock_ec2
def test_security_group_with_update():
ec2 = boto3.resource("ec2", region_name="us-west-1")
ec2_client = boto3.client("ec2", region_name="us-west-1")
vpc1 = ec2.create_vpc(CidrBlock="10.0.0.0/16")
vpc2 = ec2.create_vpc(CidrBlock="10.1.0.0/16")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"VpcId": vpc1.id,
"Tags": [{"Key": "sg-name", "Value": "sg"}],
},
}
},
}
template_json = json.dumps(template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
security_group = get_secgroup_by_tag(ec2_client, "sg")
security_group["VpcId"].should.equal(vpc1.id)
template["Resources"]["test-security-group"]["Properties"]["VpcId"] = vpc2.id
template_json = json.dumps(template)
cf.update_stack(StackName="test_stack", TemplateBody=template_json)
security_group = get_secgroup_by_tag(ec2_client, "sg")
security_group["VpcId"].should.equal(vpc2.id)
@mock_cloudformation
@mock_ec2
def test_subnets_should_be_created_with_availability_zone():
ec2 = boto3.resource("ec2", region_name="us-west-1")
ec2_client = boto3.client("ec2", region_name="us-west-1")
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"testSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": vpc.id,
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": "us-west-1b",
},
}
},
}
cf = boto3.client("cloudformation", region_name="us-west-1")
template_json = json.dumps(subnet_template)
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
subnet = ec2_client.describe_subnets(
Filters=[{"Name": "cidrBlock", "Values": ["10.0.0.0/24"]}]
)["Subnets"][0]
subnet["AvailabilityZone"].should.equal("us-west-1b")
def get_secgroup_by_tag(ec2, sg_):
return ec2.describe_security_groups(
Filters=[{"Name": "tag:sg-name", "Values": [sg_]}]
)["SecurityGroups"][0]

View File

@ -1,6 +1,8 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_ec2
from tests import EXAMPLE_AMI_ID
SEC_GROUP_INGRESS = """{
@ -85,6 +87,44 @@ SEC_GROUP_INGRESS_WITHOUT_DESC = """{
}
"""
SEC_GROUP_SOURCE = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-security-group": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {"GroupDescription": "My other group"},
},
"Ec2Instance2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"SecurityGroups": [{"Ref": "InstanceSecurityGroup"}],
"ImageId": EXAMPLE_AMI_ID,
},
},
"InstanceSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "My security group",
"Tags": [{"Key": "bar", "Value": "baz"}],
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "123.123.123.123/32",
},
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "8000",
"SourceSecurityGroupId": {"Ref": "my-security-group"},
},
],
},
},
},
}
@mock_cloudformation
@mock_ec2
@ -137,3 +177,46 @@ def test_security_group_ingress_without_description():
len(group["IpPermissions"]).should.be(1)
ingress = group["IpPermissions"][0]
ingress["IpRanges"].should.equal([{"CidrIp": "10.0.0.0/8"}])
@mock_ec2
@mock_cloudformation
def test_stack_security_groups():
template = json.dumps(SEC_GROUP_SOURCE)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(
StackName="security_group_stack",
TemplateBody=template,
Tags=[{"Key": "foo", "Value": "bar"}],
)
ec2 = boto3.client("ec2", region_name="us-west-1")
instance_group = ec2.describe_security_groups(
Filters=[{"Name": "description", "Values": ["My security group"]}]
)["SecurityGroups"][0]
instance_group.should.have.key("Description").equal("My security group")
instance_group.should.have.key("Tags")
instance_group["Tags"].should.contain({"Key": "bar", "Value": "baz"})
instance_group["Tags"].should.contain({"Key": "foo", "Value": "bar"})
other_group = ec2.describe_security_groups(
Filters=[{"Name": "description", "Values": ["My other group"]}]
)["SecurityGroups"][0]
ec2_instance = ec2.describe_instances()["Reservations"][0]["Instances"][0]
ec2_instance["NetworkInterfaces"][0]["Groups"][0]["GroupId"].should.equal(
instance_group["GroupId"]
)
rule1, rule2 = instance_group["IpPermissions"]
int(rule1["ToPort"]).should.equal(22)
int(rule1["FromPort"]).should.equal(22)
rule1["IpRanges"][0]["CidrIp"].should.equal("123.123.123.123/32")
rule1["IpProtocol"].should.equal("tcp")
int(rule2["ToPort"]).should.equal(8000)
int(rule2["FromPort"]).should.equal(80)
rule2["IpProtocol"].should.equal("tcp")
rule2["UserIdGroupPairs"][0]["GroupId"].should.equal(other_group["GroupId"])

View File

@ -0,0 +1,141 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_ec2, mock_elb
from tests import EXAMPLE_AMI_ID
@mock_ec2
@mock_elb
@mock_cloudformation
def test_stack_elb_integration_with_attached_ec2_instances():
elb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"Instances": [{"Ref": "Ec2Instance1"}],
"LoadBalancerName": "test-elb",
"AvailabilityZones": ["us-east-1"],
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
},
},
"Ec2Instance1": {
"Type": "AWS::EC2::Instance",
"Properties": {"ImageId": EXAMPLE_AMI_ID, "UserData": "some user data"},
},
},
}
elb_template_json = json.dumps(elb_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="elb_stack", TemplateBody=elb_template_json)
elb = boto3.client("elb", region_name="us-west-1")
load_balancer = elb.describe_load_balancers()["LoadBalancerDescriptions"][0]
ec2 = boto3.client("ec2", region_name="us-west-1")
reservations = ec2.describe_instances()["Reservations"][0]
ec2_instance = reservations["Instances"][0]
load_balancer["Instances"][0]["InstanceId"].should.equal(ec2_instance["InstanceId"])
load_balancer["AvailabilityZones"].should.equal(["us-east-1"])
@mock_elb
@mock_cloudformation
def test_stack_elb_integration_with_health_check():
elb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"LoadBalancerName": "test-elb",
"AvailabilityZones": ["us-west-1"],
"HealthCheck": {
"HealthyThreshold": "3",
"Interval": "5",
"Target": "HTTP:80/healthcheck",
"Timeout": "4",
"UnhealthyThreshold": "2",
},
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
},
}
},
}
elb_template_json = json.dumps(elb_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="elb_stack", TemplateBody=elb_template_json)
elb = boto3.client("elb", region_name="us-west-1")
load_balancer = elb.describe_load_balancers()["LoadBalancerDescriptions"][0]
health_check = load_balancer["HealthCheck"]
health_check.should.have.key("HealthyThreshold").equal(3)
health_check.should.have.key("Interval").equal(5)
health_check.should.have.key("Target").equal("HTTP:80/healthcheck")
health_check.should.have.key("Timeout").equal(4)
health_check.should.have.key("UnhealthyThreshold").equal(2)
@mock_elb
@mock_cloudformation
def test_stack_elb_integration_with_update():
elb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"LoadBalancerName": "test-elb",
"AvailabilityZones": ["us-west-1a"],
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
"Policies": {"Ref": "AWS::NoValue"},
},
}
},
}
elb_template_json = json.dumps(elb_template)
# when
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="elb_stack", TemplateBody=elb_template_json)
# then
elb = boto3.client("elb", region_name="us-west-1")
load_balancer = elb.describe_load_balancers()["LoadBalancerDescriptions"][0]
load_balancer["AvailabilityZones"].should.equal(["us-west-1a"])
# when
elb_template["Resources"]["MyELB"]["Properties"]["AvailabilityZones"] = [
"us-west-1b"
]
elb_template_json = json.dumps(elb_template)
cf.update_stack(StackName="elb_stack", TemplateBody=elb_template_json)
# then
load_balancer = elb.describe_load_balancers()["LoadBalancerDescriptions"][0]
load_balancer["AvailabilityZones"].should.equal(["us-west-1b"])

View File

@ -1,12 +1,14 @@
import boto3
import json
import yaml
import sure # noqa
import pytest
from botocore.exceptions import ClientError
from moto import mock_iam, mock_cloudformation, mock_s3, mock_sts
from moto.core import ACCOUNT_ID
from moto import mock_autoscaling, mock_iam, mock_cloudformation, mock_s3, mock_sts
from tests import EXAMPLE_AMI_ID
TEMPLATE_MINIMAL_ROLE = """
@ -60,6 +62,7 @@ Resources:
- !Ref RootRole
"""
# AWS::IAM::User Tests
@mock_iam
@mock_cloudformation
@ -1495,3 +1498,165 @@ def test_iam_cloudformation_create_role_and_instance_profile():
cf_client.delete_stack(StackName=stack_name)
iam_client.list_roles()["Roles"].should.have.length_of(0)
@mock_autoscaling
@mock_iam
@mock_cloudformation
def test_iam_roles():
iam_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-launch-config": {
"Properties": {
"IamInstanceProfile": {"Ref": "my-instance-profile-with-path"},
"ImageId": EXAMPLE_AMI_ID,
"InstanceType": "t2.medium",
},
"Type": "AWS::AutoScaling::LaunchConfiguration",
},
"my-instance-profile-with-path": {
"Properties": {
"Path": "my-path",
"Roles": [{"Ref": "my-role-with-path"}],
},
"Type": "AWS::IAM::InstanceProfile",
},
"my-instance-profile-no-path": {
"Properties": {"Roles": [{"Ref": "my-role-no-path"}]},
"Type": "AWS::IAM::InstanceProfile",
},
"my-role-with-path": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
"Principal": {"Service": ["ec2.amazonaws.com"]},
}
]
},
"Path": "/my-path/",
"Policies": [
{
"PolicyDocument": {
"Statement": [
{
"Action": [
"ec2:CreateTags",
"ec2:DescribeInstances",
"ec2:DescribeTags",
],
"Effect": "Allow",
"Resource": ["*"],
}
],
"Version": "2012-10-17",
},
"PolicyName": "EC2_Tags",
},
{
"PolicyDocument": {
"Statement": [
{
"Action": ["sqs:*"],
"Effect": "Allow",
"Resource": ["*"],
}
],
"Version": "2012-10-17",
},
"PolicyName": "SQS",
},
],
},
"Type": "AWS::IAM::Role",
},
"my-role-no-path": {
"Properties": {
"RoleName": "my-role-no-path-name",
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
"Principal": {"Service": ["ec2.amazonaws.com"]},
}
]
},
},
"Type": "AWS::IAM::Role",
},
},
}
iam_template_json = json.dumps(iam_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=iam_template_json)
iam = boto3.client("iam", region_name="us-west-1")
role_results = iam.list_roles()["Roles"]
role_name_to_id = {}
role_names = []
for role_result in role_results:
role = iam.get_role(RoleName=role_result["RoleName"])["Role"]
role_names.append(role["RoleName"])
# Role name is not specified, so randomly generated - can't check exact name
if "with-path" in role["RoleName"]:
role_name_to_id["with-path"] = role["RoleId"]
role["Path"].should.equal("/my-path/")
else:
role_name_to_id["no-path"] = role["RoleId"]
role["RoleName"].should.equal("my-role-no-path-name")
role["Path"].should.equal("/")
instance_profile_responses = iam.list_instance_profiles()["InstanceProfiles"]
instance_profile_responses.should.have.length_of(2)
instance_profile_names = []
for instance_profile_response in instance_profile_responses:
instance_profile = iam.get_instance_profile(
InstanceProfileName=instance_profile_response["InstanceProfileName"]
)["InstanceProfile"]
instance_profile_names.append(instance_profile["InstanceProfileName"])
instance_profile["InstanceProfileName"].should.contain("my-instance-profile")
if "with-path" in instance_profile["InstanceProfileName"]:
instance_profile["Path"].should.equal("my-path")
instance_profile["Roles"][0]["RoleId"].should.equal(
role_name_to_id["with-path"]
)
else:
instance_profile["InstanceProfileName"].should.contain("no-path")
instance_profile["Roles"][0]["RoleId"].should.equal(
role_name_to_id["no-path"]
)
instance_profile["Path"].should.equal("/")
autoscale = boto3.client("autoscaling", region_name="us-west-1")
launch_config = autoscale.describe_launch_configurations()["LaunchConfigurations"][
0
]
launch_config.should.have.key("IamInstanceProfile").should.contain(
"my-instance-profile-with-path"
)
resources = cf.list_stack_resources(StackName="test_stack")[
"StackResourceSummaries"
]
instance_profile_resources = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::IAM::InstanceProfile"
]
{ip["PhysicalResourceId"] for ip in instance_profile_resources}.should.equal(
set(instance_profile_names)
)
role_resources = [
resource
for resource in resources
if resource["ResourceType"] == "AWS::IAM::Role"
]
{r["PhysicalResourceId"] for r in role_resources}.should.equal(set(role_names))

View File

@ -2,6 +2,8 @@ import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_ec2, mock_rds2
from tests.test_cloudformation.fixtures import rds_mysql_with_db_parameter_group
from tests.test_cloudformation.fixtures import rds_mysql_with_read_replica
@mock_ec2
@ -112,3 +114,137 @@ def test_create_dbsecuritygroup_via_cf():
created = result[0]
created["DBSecurityGroupDescription"].should.equal("my sec group")
@mock_cloudformation
@mock_ec2
@mock_rds2
def test_rds_db_parameter_groups():
ec2_conn = boto3.client("ec2", region_name="us-west-1")
ec2_conn.create_security_group(
GroupName="application", Description="Our Application Group"
)
template_json = json.dumps(rds_mysql_with_db_parameter_group.template)
cf_conn = boto3.client("cloudformation", "us-west-1")
cf_conn.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[
{"ParameterKey": key, "ParameterValue": value}
for key, value in [
("DBInstanceIdentifier", "master_db"),
("DBName", "my_db"),
("DBUser", "my_user"),
("DBPassword", "my_password"),
("DBAllocatedStorage", "20"),
("DBInstanceClass", "db.m1.medium"),
("EC2SecurityGroup", "application"),
("MultiAZ", "true"),
]
],
)
rds_conn = boto3.client("rds", region_name="us-west-1")
db_parameter_groups = rds_conn.describe_db_parameter_groups()
db_parameter_groups["DBParameterGroups"].should.have.length_of(1)
db_parameter_group_name = db_parameter_groups["DBParameterGroups"][0][
"DBParameterGroupName"
]
found_cloudformation_set_parameter = False
for db_parameter in rds_conn.describe_db_parameters(
DBParameterGroupName=db_parameter_group_name
)["Parameters"]:
if (
db_parameter["ParameterName"] == "BACKLOG_QUEUE_LIMIT"
and db_parameter["ParameterValue"] == "2048"
):
found_cloudformation_set_parameter = True
found_cloudformation_set_parameter.should.equal(True)
@mock_cloudformation
@mock_ec2
@mock_rds2
def test_rds_mysql_with_read_replica():
ec2_conn = boto3.client("ec2", region_name="us-west-1")
ec2_conn.create_security_group(
GroupName="application", Description="Our Application Group"
)
template_json = json.dumps(rds_mysql_with_read_replica.template)
cf = boto3.client("cloudformation", "us-west-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[
{"ParameterKey": "DBInstanceIdentifier", "ParameterValue": "master_db"},
{"ParameterKey": "DBName", "ParameterValue": "my_db"},
{"ParameterKey": "DBUser", "ParameterValue": "my_user"},
{"ParameterKey": "DBPassword", "ParameterValue": "my_password"},
{"ParameterKey": "DBAllocatedStorage", "ParameterValue": "20"},
{"ParameterKey": "DBInstanceClass", "ParameterValue": "db.m1.medium"},
{"ParameterKey": "EC2SecurityGroup", "ParameterValue": "application"},
{"ParameterKey": "MultiAZ", "ParameterValue": "true"},
],
)
rds = boto3.client("rds", region_name="us-west-1")
primary = rds.describe_db_instances(DBInstanceIdentifier="master_db")[
"DBInstances"
][0]
primary.should.have.key("MasterUsername").equal("my_user")
primary.should.have.key("AllocatedStorage").equal(20)
primary.should.have.key("DBInstanceClass").equal("db.m1.medium")
primary.should.have.key("MultiAZ").equal(True)
primary.should.have.key("ReadReplicaDBInstanceIdentifiers").being.length_of(1)
replica_id = primary["ReadReplicaDBInstanceIdentifiers"][0]
replica = rds.describe_db_instances(DBInstanceIdentifier=replica_id)["DBInstances"][
0
]
replica.should.have.key("DBInstanceClass").equal("db.m1.medium")
security_group_name = primary["DBSecurityGroups"][0]["DBSecurityGroupName"]
security_group = rds.describe_db_security_groups(
DBSecurityGroupName=security_group_name
)["DBSecurityGroups"][0]
security_group["EC2SecurityGroups"][0]["EC2SecurityGroupName"].should.equal(
"application"
)
@mock_cloudformation
@mock_ec2
@mock_rds2
def test_rds_mysql_with_read_replica_in_vpc():
template_json = json.dumps(rds_mysql_with_read_replica.template)
cf = boto3.client("cloudformation", "eu-central-1")
cf.create_stack(
StackName="test_stack",
TemplateBody=template_json,
Parameters=[
{"ParameterKey": "DBInstanceIdentifier", "ParameterValue": "master_db"},
{"ParameterKey": "DBName", "ParameterValue": "my_db"},
{"ParameterKey": "DBUser", "ParameterValue": "my_user"},
{"ParameterKey": "DBPassword", "ParameterValue": "my_password"},
{"ParameterKey": "DBAllocatedStorage", "ParameterValue": "20"},
{"ParameterKey": "DBInstanceClass", "ParameterValue": "db.m1.medium"},
{"ParameterKey": "MultiAZ", "ParameterValue": "true"},
],
)
rds = boto3.client("rds", region_name="eu-central-1")
primary = rds.describe_db_instances(DBInstanceIdentifier="master_db")[
"DBInstances"
][0]
subnet_group_name = primary["DBSubnetGroup"]["DBSubnetGroupName"]
subnet_group = rds.describe_db_subnet_groups(DBSubnetGroupName=subnet_group_name)[
"DBSubnetGroups"
][0]
subnet_group.should.have.key("DBSubnetGroupDescription").equal("my db subnet group")

View File

@ -0,0 +1,52 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_ec2, mock_redshift
from tests.test_cloudformation.fixtures import redshift
@mock_ec2
@mock_redshift
@mock_cloudformation
def test_redshift_stack():
redshift_template_json = json.dumps(redshift.template)
ec2 = boto3.client("ec2", region_name="us-west-2")
cf = boto3.client("cloudformation", region_name="us-west-2")
cf.create_stack(
StackName="redshift_stack",
TemplateBody=redshift_template_json,
Parameters=[
{"ParameterKey": "DatabaseName", "ParameterValue": "mydb"},
{"ParameterKey": "ClusterType", "ParameterValue": "multi-node"},
{"ParameterKey": "NumberOfNodes", "ParameterValue": "2"},
{"ParameterKey": "NodeType", "ParameterValue": "dw1.xlarge"},
{"ParameterKey": "MasterUsername", "ParameterValue": "myuser"},
{"ParameterKey": "MasterUserPassword", "ParameterValue": "mypass"},
{"ParameterKey": "InboundTraffic", "ParameterValue": "10.0.0.1/16"},
{"ParameterKey": "PortNumber", "ParameterValue": "5439"},
],
)
redshift_conn = boto3.client("redshift", region_name="us-west-2")
cluster_res = redshift_conn.describe_clusters()
clusters = cluster_res["Clusters"]
clusters.should.have.length_of(1)
cluster = clusters[0]
cluster["DBName"].should.equal("mydb")
cluster["NumberOfNodes"].should.equal(2)
cluster["NodeType"].should.equal("dw1.xlarge")
cluster["MasterUsername"].should.equal("myuser")
cluster["Endpoint"]["Port"].should.equal(5439)
cluster["VpcSecurityGroups"].should.have.length_of(1)
security_group_id = cluster["VpcSecurityGroups"][0]["VpcSecurityGroupId"]
groups = ec2.describe_security_groups(GroupIds=[security_group_id])[
"SecurityGroups"
]
groups.should.have.length_of(1)
group = groups[0]
group["IpPermissions"].should.have.length_of(1)
group["IpPermissions"][0]["IpRanges"][0]["CidrIp"].should.equal("10.0.0.1/16")

View File

@ -11,6 +11,7 @@ from moto import mock_s3
from tests import EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2
@mock_rds2
@mock_ec2
@mock_resourcegroupstaggingapi
def test_get_resources_ec2():

View File

@ -0,0 +1,226 @@
import boto3
import json
import sure # noqa
from copy import deepcopy
from moto import mock_cloudformation, mock_ec2, mock_route53
from tests.test_cloudformation.fixtures import route53_ec2_instance_with_public_ip
from tests.test_cloudformation.fixtures import route53_health_check
from tests.test_cloudformation.fixtures import route53_roundrobin
template_hosted_zone = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 1",
"Parameters": {},
"Resources": {
"Bar": {
"Type": "AWS::Route53::HostedZone",
"Properties": {"Name": "foo.bar.baz"},
}
},
}
template_record_set = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 2",
"Parameters": {"ZoneId": {"Type": "String"}},
"Resources": {
"Foo": {
"Properties": {
"HostedZoneId": {"Ref": "ZoneId"},
"RecordSets": [
{
"Name": "test.vpc.internal",
"Type": "A",
"SetIdentifier": "test1",
"Weight": 50,
}
],
},
"Type": "AWS::Route53::RecordSetGroup",
}
},
}
@mock_cloudformation
@mock_route53
def test_create_stack_hosted_zone_by_id():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
conn = boto3.client("route53", region_name="us-east-1")
# when creating a hosted zone via CF
cf_conn.create_stack(
StackName="test_stack1", TemplateBody=json.dumps(template_hosted_zone)
)
# then a hosted zone should exist
zone = conn.list_hosted_zones()["HostedZones"][0]
zone.should.have.key("Name").equal("foo.bar.baz")
zone.should.have.key("ResourceRecordSetCount").equal(0)
# when adding a record set to this zone
cf_conn.create_stack(
StackName="test_stack2",
TemplateBody=json.dumps(template_record_set),
Parameters=[{"ParameterKey": "ZoneId", "ParameterValue": zone["Id"]}],
)
# then the hosted zone should have a record
updated_zone = conn.list_hosted_zones()["HostedZones"][0]
updated_zone.should.have.key("Id").equal(zone["Id"])
updated_zone.should.have.key("Name").equal("foo.bar.baz")
updated_zone.should.have.key("ResourceRecordSetCount").equal(1)
@mock_cloudformation
@mock_route53
def test_route53_roundrobin():
cf = boto3.client("cloudformation", region_name="us-west-1")
route53 = boto3.client("route53", region_name="us-west-1")
template_json = json.dumps(route53_roundrobin.template)
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
zones = route53.list_hosted_zones()["HostedZones"]
zones.should.have.length_of(1)
zone_id = zones[0]["Id"].split("/")[2]
rrsets = route53.list_resource_record_sets(HostedZoneId=zone_id)[
"ResourceRecordSets"
]
rrsets.should.have.length_of(2)
record_set1 = rrsets[0]
record_set1["Name"].should.equal("test_stack.us-west-1.my_zone.")
record_set1["SetIdentifier"].should.equal("test_stack AWS")
record_set1["Type"].should.equal("CNAME")
record_set1["TTL"].should.equal(900)
record_set1["Weight"].should.equal(3)
record_set1["ResourceRecords"][0]["Value"].should.equal("aws.amazon.com")
record_set2 = rrsets[1]
record_set2["Name"].should.equal("test_stack.us-west-1.my_zone.")
record_set2["SetIdentifier"].should.equal("test_stack Amazon")
record_set2["Type"].should.equal("CNAME")
record_set2["TTL"].should.equal(900)
record_set2["Weight"].should.equal(1)
record_set2["ResourceRecords"][0]["Value"].should.equal("www.amazon.com")
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
output = stack["Outputs"][0]
output["OutputKey"].should.equal("DomainName")
output["OutputValue"].should.equal(
"arn:aws:route53:::hostedzone/{0}".format(zone_id)
)
@mock_cloudformation
@mock_ec2
@mock_route53
def test_route53_ec2_instance_with_public_ip():
route53 = boto3.client("route53", region_name="us-west-1")
ec2 = boto3.client("ec2", region_name="us-west-1")
template_json = json.dumps(route53_ec2_instance_with_public_ip.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
instance_id = ec2.describe_instances()["Reservations"][0]["Instances"][0][
"InstanceId"
]
zones = route53.list_hosted_zones()["HostedZones"]
zones.should.have.length_of(1)
zone_id = zones[0]["Id"].split("/")[2]
rrsets = route53.list_resource_record_sets(HostedZoneId=zone_id)[
"ResourceRecordSets"
]
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set["Name"].should.equal("{0}.us-west-1.my_zone.".format(instance_id))
record_set.shouldnt.have.key("SetIdentifier")
record_set["Type"].should.equal("A")
record_set["TTL"].should.equal(900)
record_set.shouldnt.have.key("Weight")
record_set["ResourceRecords"][0]["Value"].should.equal("10.0.0.25")
@mock_cloudformation
@mock_route53
def test_route53_associate_health_check():
route53 = boto3.client("route53", region_name="us-west-1")
template_json = json.dumps(route53_health_check.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
checks = route53.list_health_checks()["HealthChecks"]
checks.should.have.length_of(1)
check = checks[0]
health_check_id = check["Id"]
config = check["HealthCheckConfig"]
config["FailureThreshold"].should.equal(3)
config["IPAddress"].should.equal("10.0.0.4")
config["Port"].should.equal(80)
config["RequestInterval"].should.equal(10)
config["ResourcePath"].should.equal("/")
config["Type"].should.equal("HTTP")
zones = route53.list_hosted_zones()["HostedZones"]
zones.should.have.length_of(1)
zone_id = zones[0]["Id"].split("/")[2]
rrsets = route53.list_resource_record_sets(HostedZoneId=zone_id)[
"ResourceRecordSets"
]
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set["HealthCheckId"].should.equal(health_check_id)
@mock_cloudformation
@mock_route53
def test_route53_with_update():
route53 = boto3.client("route53", region_name="us-west-1")
template_json = json.dumps(route53_health_check.template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
zones = route53.list_hosted_zones()["HostedZones"]
zones.should.have.length_of(1)
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
rrsets = route53.list_resource_record_sets(HostedZoneId=zone_id)[
"ResourceRecordSets"
]
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set["ResourceRecords"][0]["Value"].should.equal("my.example.com")
# given
template = deepcopy(route53_health_check.template)
template["Resources"]["myDNSRecord"]["Properties"]["ResourceRecords"] = [
"my_other.example.com"
]
template_json = json.dumps(template)
# when
cf.update_stack(StackName="test_stack", TemplateBody=template_json)
# then
zones = route53.list_hosted_zones()["HostedZones"]
zones.should.have.length_of(1)
zone_id = zones[0]["Id"].split("/")[2]
rrsets = route53.list_resource_record_sets(HostedZoneId=zone_id)[
"ResourceRecordSets"
]
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set["ResourceRecords"][0]["Value"].should.equal("my_other.example.com")

View File

@ -0,0 +1,53 @@
import boto3
import json
import sure # noqa
from moto import mock_cloudformation, mock_sns
@mock_cloudformation
@mock_sns
def test_sns_topic():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MySNSTopic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"Subscription": [
{"Endpoint": "https://example.com", "Protocol": "https"}
],
"TopicName": "my_topics",
},
}
},
"Outputs": {
"topic_name": {"Value": {"Fn::GetAtt": ["MySNSTopic", "TopicName"]}},
"topic_arn": {"Value": {"Ref": "MySNSTopic"}},
},
}
template_json = json.dumps(dummy_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=template_json)
sns = boto3.client("sns", region_name="us-west-1")
topics = sns.list_topics()["Topics"]
topics.should.have.length_of(1)
topic_arn = topics[0]["TopicArn"]
topic_arn.should.contain("my_topics")
subscriptions = sns.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(1)
subscription = subscriptions[0]
subscription["TopicArn"].should.equal(topic_arn)
subscription["Protocol"].should.equal("https")
subscription["SubscriptionArn"].should.contain(topic_arn)
subscription["Endpoint"].should.equal("https://example.com")
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
topic_name_output = [x for x in stack["Outputs"] if x["OutputKey"] == "topic_name"][
0
]
topic_name_output["OutputValue"].should.equal("my_topics")
topic_arn_output = [x for x in stack["Outputs"] if x["OutputKey"] == "topic_arn"][0]
topic_arn_output["OutputValue"].should.equal(topic_arn)

View File

@ -1,6 +1,21 @@
import boto3
from moto import mock_sqs, mock_cloudformation
import json
import sure # noqa
from moto import mock_sqs, mock_cloudformation
from moto.core import ACCOUNT_ID
simple_queue = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
simple_queue_json = json.dumps(simple_queue)
sqs_template_with_tags = """
{
"AWSTemplateFormatVersion": "2010-09-09",
@ -24,6 +39,43 @@ sqs_template_with_tags = """
}"""
@mock_sqs
@mock_cloudformation
def test_describe_stack_subresources():
res = boto3.resource("cloudformation", region_name="us-east-1")
cf = boto3.client("cloudformation", region_name="us-east-1")
client = boto3.client("sqs", region_name="us-east-1")
cf.create_stack(StackName="test_sqs", TemplateBody=simple_queue_json)
queue_url = client.list_queues()["QueueUrls"][0]
queue_url.should.contain("{}/{}".format(ACCOUNT_ID, "my-queue"))
stack = res.Stack("test_sqs")
for s in stack.resource_summaries.all():
s.resource_type.should.equal("AWS::SQS::Queue")
s.logical_id.should.equal("QueueGroup")
s.physical_resource_id.should.equal("my-queue")
@mock_sqs
@mock_cloudformation
def test_list_stack_resources():
cf = boto3.client("cloudformation", region_name="us-east-1")
client = boto3.client("sqs", region_name="us-east-1")
cf.create_stack(StackName="test_sqs", TemplateBody=simple_queue_json)
queue_url = client.list_queues()["QueueUrls"][0]
queue_url.should.contain("{}/{}".format(ACCOUNT_ID, "my-queue"))
queue = cf.list_stack_resources(StackName="test_sqs")["StackResourceSummaries"][0]
queue.should.have.key("ResourceType").equal("AWS::SQS::Queue")
queue.should.have.key("LogicalResourceId").should.equal("QueueGroup")
queue.should.have.key("PhysicalResourceId").should.equal("my-queue")
@mock_sqs
@mock_cloudformation
def test_create_from_cloudformation_json_with_tags():
@ -36,3 +88,98 @@ def test_create_from_cloudformation_json_with_tags():
queue_tags = client.list_queue_tags(QueueUrl=queue_url)["Tags"]
queue_tags.should.equal({"keyname1": "value1", "keyname2": "value2"})
@mock_cloudformation
@mock_sqs
def test_update_stack():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=sqs_template_json)
client = boto3.client("sqs", region_name="us-west-1")
queues = client.list_queues()["QueueUrls"]
queues.should.have.length_of(1)
attrs = client.get_queue_attributes(QueueUrl=queues[0], AttributeNames=["All"])[
"Attributes"
]
attrs["VisibilityTimeout"].should.equal("60")
# when updating
sqs_template["Resources"]["QueueGroup"]["Properties"]["VisibilityTimeout"] = 100
sqs_template_json = json.dumps(sqs_template)
cf.update_stack(StackName="test_stack", TemplateBody=sqs_template_json)
# then the attribute should be updated
queues = client.list_queues()["QueueUrls"]
queues.should.have.length_of(1)
attrs = client.get_queue_attributes(QueueUrl=queues[0], AttributeNames=["All"])[
"Attributes"
]
attrs["VisibilityTimeout"].should.equal("100")
@mock_cloudformation
@mock_sqs
def test_update_stack_and_remove_resource():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=sqs_template_json)
client = boto3.client("sqs", region_name="us-west-1")
client.list_queues()["QueueUrls"].should.have.length_of(1)
sqs_template["Resources"].pop("QueueGroup")
sqs_template_json = json.dumps(sqs_template)
cf.update_stack(StackName="test_stack", TemplateBody=sqs_template_json)
client.list_queues().shouldnt.have.key(
"QueueUrls"
) # No queues exist, so the key is not passed through
@mock_cloudformation
@mock_sqs
def test_update_stack_and_add_resource():
sqs_template = {"AWSTemplateFormatVersion": "2010-09-09", "Resources": {}}
sqs_template_json = json.dumps(sqs_template)
cf = boto3.client("cloudformation", region_name="us-west-1")
cf.create_stack(StackName="test_stack", TemplateBody=sqs_template_json)
client = boto3.client("sqs", region_name="us-west-1")
client.list_queues().shouldnt.have.key("QueueUrls")
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
cf.update_stack(StackName="test_stack", TemplateBody=sqs_template_json)
client.list_queues()["QueueUrls"].should.have.length_of(1)