moto/tests/test_cloudformation/test_cloudformation_stack_integration.py

3392 lines
122 KiB
Python
Raw Normal View History

import json
import io
import zipfile
from decimal import Decimal
import boto
import boto.cloudformation
import boto.datapipeline
import boto.ec2
import boto.ec2.autoscale
import boto.ec2.elb
from boto.exception import BotoServerError
from botocore.exceptions import ClientError
import boto.iam
import boto.rds
import boto.redshift
2015-01-18 00:48:08 +00:00
import boto.sns
import boto.sqs
import boto.vpc
import boto3
2021-10-18 19:44:29 +00:00
import sure # noqa # pylint: disable=unused-import
import pytest
from copy import deepcopy
from string import Template
from moto import (
2017-02-16 03:35:45 +00:00
mock_autoscaling_deprecated,
mock_autoscaling,
mock_cloudformation,
2017-02-16 03:35:45 +00:00
mock_cloudformation_deprecated,
mock_datapipeline_deprecated,
mock_dynamodb2,
mock_ec2,
2017-02-16 03:35:45 +00:00
mock_ec2_deprecated,
mock_elb_deprecated,
2020-03-22 21:03:42 +00:00
mock_events,
2017-02-16 03:35:45 +00:00
mock_iam_deprecated,
mock_kms,
mock_lambda,
mock_logs,
2017-02-16 03:35:45 +00:00
mock_rds_deprecated,
mock_redshift_deprecated,
mock_route53_deprecated,
mock_s3,
2017-02-16 03:35:45 +00:00
mock_sns_deprecated,
mock_sqs,
2017-02-16 03:35:45 +00:00
mock_sqs_deprecated,
2019-10-31 15:44:26 +00:00
mock_elbv2,
)
from moto.core import ACCOUNT_ID
from tests import EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2
from tests.test_cloudformation.fixtures import (
ec2_classic_eip,
2015-01-11 21:15:08 +00:00
fn_join,
rds_mysql_with_read_replica,
redshift,
route53_ec2_instance_with_public_ip,
2015-01-18 00:06:43 +00:00
route53_health_check,
2015-01-17 15:17:25 +00:00
route53_roundrobin,
2015-01-11 21:15:08 +00:00
single_instance_with_ebs_volume,
2014-10-22 02:05:27 +00:00
vpc_eip,
2015-01-11 21:15:08 +00:00
vpc_single_instance_in_subnet,
)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
def test_stack_sqs_integration():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=sqs_template_json)
stack = conn.describe_stacks()[0]
queue = stack.describe_resources()[0]
2019-10-31 15:44:26 +00:00
queue.resource_type.should.equal("AWS::SQS::Queue")
queue.logical_resource_id.should.equal("QueueGroup")
queue.physical_resource_id.should.equal("my-queue")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
def test_stack_list_resources():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=sqs_template_json)
resources = conn.list_stack_resources("test_stack")
assert len(resources) == 1
queue = resources[0]
2019-10-31 15:44:26 +00:00
queue.resource_type.should.equal("AWS::SQS::Queue")
queue.logical_resource_id.should.equal("QueueGroup")
queue.physical_resource_id.should.equal("my-queue")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=sqs_template_json)
sqs_conn = boto.sqs.connect_to_region("us-west-1")
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
queues[0].get_attributes("VisibilityTimeout")["VisibilityTimeout"].should.equal(
"60"
)
2019-10-31 15:44:26 +00:00
sqs_template["Resources"]["QueueGroup"]["Properties"]["VisibilityTimeout"] = 100
sqs_template_json = json.dumps(sqs_template)
conn.update_stack("test_stack", sqs_template_json)
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
queues[0].get_attributes("VisibilityTimeout")["VisibilityTimeout"].should.equal(
"100"
)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack_and_remove_resource():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=sqs_template_json)
sqs_conn = boto.sqs.connect_to_region("us-west-1")
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
sqs_template["Resources"].pop("QueueGroup")
sqs_template_json = json.dumps(sqs_template)
conn.update_stack("test_stack", sqs_template_json)
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(0)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_update_stack_and_add_resource():
2019-10-31 15:44:26 +00:00
sqs_template = {"AWSTemplateFormatVersion": "2010-09-09", "Resources": {}}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=sqs_template_json)
sqs_conn = boto.sqs.connect_to_region("us-west-1")
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(0)
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn.update_stack("test_stack", sqs_template_json)
queues = sqs_conn.get_all_queues()
queues.should.have.length_of(1)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_stack_ec2_integration():
ec2_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"WebServerGroup": {
"Type": "AWS::EC2::Instance",
"Properties": {"ImageId": EXAMPLE_AMI_ID, "UserData": "some user data"},
2019-10-31 15:44:26 +00:00
}
},
}
ec2_template_json = json.dumps(ec2_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("ec2_stack", template_body=ec2_template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
stack = conn.describe_stacks()[0]
instance = stack.describe_resources()[0]
2019-10-31 15:44:26 +00:00
instance.resource_type.should.equal("AWS::EC2::Instance")
instance.logical_resource_id.should.contain("WebServerGroup")
instance.physical_resource_id.should.equal(ec2_instance.id)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
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",
2019-10-31 15:44:26 +00:00
"AvailabilityZones": ["us-east-1"],
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
2019-10-31 15:44:26 +00:00
},
},
"Ec2Instance1": {
"Type": "AWS::EC2::Instance",
"Properties": {"ImageId": EXAMPLE_AMI_ID, "UserData": "some user data"},
},
},
}
elb_template_json = json.dumps(elb_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("elb_stack", template_body=elb_template_json)
elb_conn = boto.ec2.elb.connect_to_region("us-west-1")
load_balancer = elb_conn.get_all_load_balancers()[0]
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
load_balancer.instances[0].id.should.equal(ec2_instance.id)
2019-10-31 15:44:26 +00:00
list(load_balancer.availability_zones).should.equal(["us-east-1"])
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
def test_stack_elb_integration_with_health_check():
elb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"LoadBalancerName": "test-elb",
2019-10-31 15:44:26 +00:00
"AvailabilityZones": ["us-west-1"],
"HealthCheck": {
"HealthyThreshold": "3",
"Interval": "5",
"Target": "HTTP:80/healthcheck",
"Timeout": "4",
"UnhealthyThreshold": "2",
},
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
2019-10-31 15:44:26 +00:00
},
}
},
}
elb_template_json = json.dumps(elb_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("elb_stack", template_body=elb_template_json)
elb_conn = boto.ec2.elb.connect_to_region("us-west-1")
load_balancer = elb_conn.get_all_load_balancers()[0]
health_check = load_balancer.health_check
health_check.healthy_threshold.should.equal(3)
health_check.interval.should.equal(5)
health_check.target.should.equal("HTTP:80/healthcheck")
health_check.timeout.should.equal(4)
health_check.unhealthy_threshold.should.equal(2)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
def test_stack_elb_integration_with_update():
elb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"LoadBalancerName": "test-elb",
2019-10-31 15:44:26 +00:00
"AvailabilityZones": ["us-west-1a"],
"Listeners": [
{
"InstancePort": "80",
"LoadBalancerPort": "80",
"Protocol": "HTTP",
}
],
"Policies": {"Ref": "AWS::NoValue"},
2019-10-31 15:44:26 +00:00
},
}
},
}
elb_template_json = json.dumps(elb_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("elb_stack", template_body=elb_template_json)
elb_conn = boto.ec2.elb.connect_to_region("us-west-1")
load_balancer = elb_conn.get_all_load_balancers()[0]
2019-10-31 15:44:26 +00:00
load_balancer.availability_zones[0].should.equal("us-west-1a")
2019-10-31 15:44:26 +00:00
elb_template["Resources"]["MyELB"]["Properties"]["AvailabilityZones"] = [
"us-west-1b"
]
elb_template_json = json.dumps(elb_template)
2019-10-31 15:44:26 +00:00
conn.update_stack("elb_stack", template_body=elb_template_json)
load_balancer = elb_conn.get_all_load_balancers()[0]
2019-10-31 15:44:26 +00:00
load_balancer.availability_zones[0].should.equal("us-west-1b")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_redshift_deprecated()
@mock_cloudformation_deprecated()
def test_redshift_stack():
redshift_template_json = json.dumps(redshift.template)
vpc_conn = boto.vpc.connect_to_region("us-west-2")
conn = boto.cloudformation.connect_to_region("us-west-2")
conn.create_stack(
"redshift_stack",
template_body=redshift_template_json,
parameters=[
("DatabaseName", "mydb"),
("ClusterType", "multi-node"),
("NumberOfNodes", 2),
("NodeType", "dw1.xlarge"),
("MasterUsername", "myuser"),
("MasterUserPassword", "mypass"),
("InboundTraffic", "10.0.0.1/16"),
("PortNumber", 5439),
2019-10-31 15:44:26 +00:00
],
)
redshift_conn = boto.redshift.connect_to_region("us-west-2")
cluster_res = redshift_conn.describe_clusters()
2019-10-31 15:44:26 +00:00
clusters = cluster_res["DescribeClustersResponse"]["DescribeClustersResult"][
"Clusters"
]
clusters.should.have.length_of(1)
cluster = clusters[0]
2019-10-31 15:44:26 +00:00
cluster["DBName"].should.equal("mydb")
cluster["NumberOfNodes"].should.equal(2)
cluster["NodeType"].should.equal("dw1.xlarge")
cluster["MasterUsername"].should.equal("myuser")
cluster["Port"].should.equal(5439)
cluster["VpcSecurityGroups"].should.have.length_of(1)
security_group_id = cluster["VpcSecurityGroups"][0]["VpcSecurityGroupId"]
groups = vpc_conn.get_all_security_groups(group_ids=[security_group_id])
groups.should.have.length_of(1)
group = groups[0]
group.rules.should.have.length_of(1)
group.rules[0].grants[0].cidr_ip.should.equal("10.0.0.1/16")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_stack_security_groups():
security_group_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-security-group": {
"Type": "AWS::EC2::SecurityGroup",
2019-10-31 15:44:26 +00:00
"Properties": {"GroupDescription": "My other group"},
},
"Ec2Instance2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"SecurityGroups": [{"Ref": "InstanceSecurityGroup"}],
"ImageId": EXAMPLE_AMI_ID,
2019-10-31 15:44:26 +00:00
},
},
"InstanceSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "My security group",
2019-10-31 15:44:26 +00:00
"Tags": [{"Key": "bar", "Value": "baz"}],
"SecurityGroupIngress": [
{
2019-10-31 15:44:26 +00:00
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "123.123.123.123/32",
},
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "8000",
"SourceSecurityGroupId": {"Ref": "my-security-group"},
},
],
2019-10-31 15:44:26 +00:00
},
},
},
}
security_group_template_json = json.dumps(security_group_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
"security_group_stack",
template_body=security_group_template_json,
2019-10-31 15:44:26 +00:00
tags={"foo": "bar"},
)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
2017-02-24 02:37:43 +00:00
instance_group = ec2_conn.get_all_security_groups(
2019-10-31 15:44:26 +00:00
filters={"description": ["My security group"]}
)[0]
2017-02-24 02:37:43 +00:00
other_group = ec2_conn.get_all_security_groups(
2019-10-31 15:44:26 +00:00
filters={"description": ["My other group"]}
)[0]
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
ec2_instance.groups[0].id.should.equal(instance_group.id)
instance_group.description.should.equal("My security group")
2019-10-31 15:44:26 +00:00
instance_group.tags.should.have.key("foo").which.should.equal("bar")
instance_group.tags.should.have.key("bar").which.should.equal("baz")
rule1, rule2 = instance_group.rules
int(rule1.to_port).should.equal(22)
int(rule1.from_port).should.equal(22)
rule1.grants[0].cidr_ip.should.equal("123.123.123.123/32")
2019-10-31 15:44:26 +00:00
rule1.ip_protocol.should.equal("tcp")
int(rule2.to_port).should.equal(8000)
int(rule2.from_port).should.equal(80)
2019-10-31 15:44:26 +00:00
rule2.ip_protocol.should.equal("tcp")
rule2.grants[0].group_id.should.equal(other_group.id)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_autoscaling_deprecated()
@mock_elb_deprecated()
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_autoscaling_group_with_elb():
web_setup_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-as-group": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
2020-04-08 14:14:39 +00:00
"AvailabilityZones": ["us-east-1a"],
"LaunchConfigurationName": {"Ref": "my-launch-config"},
"MinSize": "2",
"MaxSize": "2",
"DesiredCapacity": "2",
"LoadBalancerNames": [{"Ref": "my-elb"}],
"Tags": [
{
2019-10-31 15:44:26 +00:00
"Key": "propagated-test-tag",
"Value": "propagated-test-tag-value",
"PropagateAtLaunch": True,
},
{
"Key": "not-propagated-test-tag",
"Value": "not-propagated-test-tag-value",
2019-10-31 15:44:26 +00:00
"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": {
2020-04-08 14:14:39 +00:00
"AvailabilityZones": ["us-east-1a"],
2019-10-31 15:44:26 +00:00
"Listeners": [
{
"LoadBalancerPort": "80",
"InstancePort": "80",
"Protocol": "HTTP",
}
],
"LoadBalancerName": "my-elb",
"HealthCheck": {
"Target": "HTTP:80",
"HealthyThreshold": "3",
"UnhealthyThreshold": "5",
"Interval": "30",
"Timeout": "5",
},
},
},
2019-10-31 15:44:26 +00:00
},
}
web_setup_template_json = json.dumps(web_setup_template)
2020-04-08 14:14:39 +00:00
conn = boto.cloudformation.connect_to_region("us-east-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("web_stack", template_body=web_setup_template_json)
2020-04-08 14:14:39 +00:00
autoscale_conn = boto.ec2.autoscale.connect_to_region("us-east-1")
autoscale_group = autoscale_conn.get_all_groups()[0]
autoscale_group.launch_config_name.should.contain("my-launch-config")
2019-10-31 15:44:26 +00:00
autoscale_group.load_balancers[0].should.equal("my-elb")
# Confirm the Launch config was actually created
autoscale_conn.get_all_launch_configurations().should.have.length_of(1)
# Confirm the ELB was actually created
2020-04-08 14:14:39 +00:00
elb_conn = boto.ec2.elb.connect_to_region("us-east-1")
elb_conn.get_all_load_balancers().should.have.length_of(1)
stack = conn.describe_stacks()[0]
resources = stack.describe_resources()
2019-10-31 15:44:26 +00:00
as_group_resource = [
resource
for resource in resources
if resource.resource_type == "AWS::AutoScaling::AutoScalingGroup"
][0]
as_group_resource.physical_resource_id.should.contain("my-as-group")
2017-02-24 02:37:43 +00:00
launch_config_resource = [
2019-10-31 15:44:26 +00:00
resource
for resource in resources
if resource.resource_type == "AWS::AutoScaling::LaunchConfiguration"
][0]
launch_config_resource.physical_resource_id.should.contain("my-launch-config")
elb_resource = [
resource
for resource in resources
if resource.resource_type == "AWS::ElasticLoadBalancing::LoadBalancer"
][0]
elb_resource.physical_resource_id.should.contain("my-elb")
# confirm the instances were created with the right tags
2020-04-08 14:14:39 +00:00
ec2_conn = boto.ec2.connect_to_region("us-east-1")
reservations = ec2_conn.get_all_reservations()
len(reservations).should.equal(1)
reservation = reservations[0]
len(reservation.instances).should.equal(2)
for instance in reservation.instances:
2019-10-31 15:44:26 +00:00
instance.tags["propagated-test-tag"].should.equal("propagated-test-tag-value")
instance.tags.keys().should_not.contain("not-propagated-test-tag")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_autoscaling_deprecated()
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_autoscaling_group_update():
asg_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"my-as-group": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
2020-04-08 14:14:39 +00:00
"AvailabilityZones": ["us-west-1a"],
"LaunchConfigurationName": {"Ref": "my-launch-config"},
"MinSize": "2",
"MaxSize": "2",
2019-10-31 15:44:26 +00:00
"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)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("asg_stack", template_body=asg_template_json)
autoscale_conn = boto.ec2.autoscale.connect_to_region("us-west-1")
asg = autoscale_conn.get_all_groups()[0]
asg.min_size.should.equal(2)
asg.max_size.should.equal(2)
asg.desired_capacity.should.equal(2)
2019-10-31 15:44:26 +00:00
asg_template["Resources"]["my-as-group"]["Properties"]["MaxSize"] = 3
asg_template["Resources"]["my-as-group"]["Properties"]["Tags"] = [
{
2019-10-31 15:44:26 +00:00
"Key": "propagated-test-tag",
"Value": "propagated-test-tag-value",
"PropagateAtLaunch": True,
},
{
"Key": "not-propagated-test-tag",
"Value": "not-propagated-test-tag-value",
2019-10-31 15:44:26 +00:00
"PropagateAtLaunch": False,
},
]
asg_template_json = json.dumps(asg_template)
2019-10-31 15:44:26 +00:00
conn.update_stack("asg_stack", template_body=asg_template_json)
asg = autoscale_conn.get_all_groups()[0]
asg.min_size.should.equal(2)
asg.max_size.should.equal(3)
asg.desired_capacity.should.equal(2)
# confirm the instances were created with the right tags
2019-10-31 15:44:26 +00:00
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservations = ec2_conn.get_all_reservations()
running_instance_count = 0
for res in reservations:
for instance in res.instances:
2019-10-31 15:44:26 +00:00
if instance.state == "running":
running_instance_count += 1
2019-10-31 15:44:26 +00:00
instance.tags["propagated-test-tag"].should.equal(
"propagated-test-tag-value"
)
instance.tags.keys().should_not.contain("not-propagated-test-tag")
running_instance_count.should.equal(2)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_vpc_single_instance_in_subnet():
template_json = json.dumps(vpc_single_instance_in_subnet.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
2019-10-31 15:44:26 +00:00
"test_stack", template_body=template_json, parameters=[("KeyName", "my_key")]
)
vpc_conn = boto.vpc.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
vpc = vpc_conn.get_all_vpcs(filters={"cidrBlock": "10.0.0.0/16"})[0]
vpc.cidr_block.should.equal("10.0.0.0/16")
# Add this once we implement the endpoint
# vpc_conn.get_all_internet_gateways().should.have.length_of(1)
2019-10-31 15:44:26 +00:00
subnet = vpc_conn.get_all_subnets(filters={"vpcId": vpc.id})[0]
subnet.vpc_id.should.equal(vpc.id)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
instance = reservation.instances[0]
instance.tags["Foo"].should.equal("Bar")
# Check that the EIP is attached the the EC2 instance
eip = ec2_conn.get_all_addresses()[0]
2019-10-31 15:44:26 +00:00
eip.domain.should.equal("vpc")
eip.instance_id.should.equal(instance.id)
2019-10-31 15:44:26 +00:00
security_group = ec2_conn.get_all_security_groups(filters={"vpc_id": [vpc.id]})[0]
security_group.vpc_id.should.equal(vpc.id)
stack = conn.describe_stacks()[0]
2017-12-28 19:27:53 +00:00
2019-10-31 15:44:26 +00:00
vpc.tags.should.have.key("Application").which.should.equal(stack.stack_id)
2017-12-28 19:27:53 +00:00
resources = stack.describe_resources()
2017-02-24 02:37:43 +00:00
vpc_resource = [
2019-10-31 15:44:26 +00:00
resource for resource in resources if resource.resource_type == "AWS::EC2::VPC"
][0]
vpc_resource.physical_resource_id.should.equal(vpc.id)
2017-02-24 02:37:43 +00:00
subnet_resource = [
2019-10-31 15:44:26 +00:00
resource
for resource in resources
if resource.resource_type == "AWS::EC2::Subnet"
][0]
subnet_resource.physical_resource_id.should.equal(subnet.id)
2017-02-24 02:37:43 +00:00
eip_resource = [
2019-10-31 15:44:26 +00:00
resource for resource in resources if resource.resource_type == "AWS::EC2::EIP"
][0]
eip_resource.physical_resource_id.should.equal(eip.public_ip)
2017-02-24 02:37:43 +00:00
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_rds_deprecated()
2015-01-11 21:15:08 +00:00
def test_rds_mysql_with_read_replica():
ec2_conn = boto.ec2.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
ec2_conn.create_security_group("application", "Our Application Group")
2015-01-11 21:15:08 +00:00
template_json = json.dumps(rds_mysql_with_read_replica.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
"test_stack",
template_body=template_json,
parameters=[
("DBInstanceIdentifier", "master_db"),
("DBName", "my_db"),
("DBUser", "my_user"),
("DBPassword", "my_password"),
("DBAllocatedStorage", "20"),
("DBInstanceClass", "db.m1.medium"),
("EC2SecurityGroup", "application"),
("MultiAZ", "true"),
],
)
rds_conn = boto.rds.connect_to_region("us-west-1")
primary = rds_conn.get_all_dbinstances("master_db")[0]
primary.master_username.should.equal("my_user")
primary.allocated_storage.should.equal(20)
primary.instance_class.should.equal("db.m1.medium")
primary.multi_az.should.equal(True)
list(primary.read_replica_dbinstance_identifiers).should.have.length_of(1)
replica_id = primary.read_replica_dbinstance_identifiers[0]
replica = rds_conn.get_all_dbinstances(replica_id)[0]
replica.instance_class.should.equal("db.m1.medium")
security_group_name = primary.security_groups[0].name
security_group = rds_conn.get_all_dbsecurity_groups(security_group_name)[0]
security_group.ec2_groups[0].name.should.equal("application")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_rds_deprecated()
2015-01-11 21:15:08 +00:00
def test_rds_mysql_with_read_replica_in_vpc():
template_json = json.dumps(rds_mysql_with_read_replica.template)
conn = boto.cloudformation.connect_to_region("eu-central-1")
conn.create_stack(
"test_stack",
template_body=template_json,
parameters=[
("DBInstanceIdentifier", "master_db"),
("DBName", "my_db"),
("DBUser", "my_user"),
("DBPassword", "my_password"),
("DBAllocatedStorage", "20"),
("DBInstanceClass", "db.m1.medium"),
("MultiAZ", "true"),
],
)
rds_conn = boto.rds.connect_to_region("eu-central-1")
primary = rds_conn.get_all_dbinstances("master_db")[0]
subnet_group_name = primary.subnet_group.name
subnet_group = rds_conn.get_all_db_subnet_groups(subnet_group_name)[0]
subnet_group.description.should.equal("my db subnet group")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_autoscaling_deprecated()
@mock_iam_deprecated()
@mock_cloudformation_deprecated()
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",
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::AutoScaling::LaunchConfiguration",
},
"my-instance-profile-with-path": {
"Properties": {
"Path": "my-path",
"Roles": [{"Ref": "my-role-with-path"}],
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::IAM::InstanceProfile",
},
"my-instance-profile-no-path": {
2019-10-31 15:44:26 +00:00
"Properties": {"Roles": [{"Ref": "my-role-no-path"}]},
"Type": "AWS::IAM::InstanceProfile",
},
"my-role-with-path": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
2019-10-31 15:44:26 +00:00
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
2019-10-31 15:44:26 +00:00
"Principal": {"Service": ["ec2.amazonaws.com"]},
}
]
},
"Path": "/my-path/",
"Policies": [
{
"PolicyDocument": {
"Statement": [
{
"Action": [
"ec2:CreateTags",
"ec2:DescribeInstances",
2019-10-31 15:44:26 +00:00
"ec2:DescribeTags",
],
"Effect": "Allow",
2019-10-31 15:44:26 +00:00
"Resource": ["*"],
}
],
2019-10-31 15:44:26 +00:00
"Version": "2012-10-17",
},
2019-10-31 15:44:26 +00:00
"PolicyName": "EC2_Tags",
},
{
"PolicyDocument": {
"Statement": [
{
2019-10-31 15:44:26 +00:00
"Action": ["sqs:*"],
"Effect": "Allow",
2019-10-31 15:44:26 +00:00
"Resource": ["*"],
}
],
2019-10-31 15:44:26 +00:00
"Version": "2012-10-17",
},
2019-10-31 15:44:26 +00:00
"PolicyName": "SQS",
},
2019-10-31 15:44:26 +00:00
],
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::IAM::Role",
},
"my-role-no-path": {
"Properties": {
"RoleName": "my-role-no-path-name",
"AssumeRolePolicyDocument": {
"Statement": [
{
2019-10-31 15:44:26 +00:00
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
2019-10-31 15:44:26 +00:00
"Principal": {"Service": ["ec2.amazonaws.com"]},
}
]
2020-03-11 13:19:40 +00:00
},
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::IAM::Role",
},
},
}
iam_template_json = json.dumps(iam_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=iam_template_json)
iam_conn = boto.iam.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
role_results = iam_conn.list_roles()["list_roles_response"]["list_roles_result"][
"roles"
]
role_name_to_id = {}
role_names = []
for role_result in role_results:
role = iam_conn.get_role(role_result.role_name)
# Role name is not specified, so randomly generated - can't check exact name
if "with-path" in role.role_name:
2019-10-31 15:44:26 +00:00
role_name_to_id["with-path"] = role.role_id
role.path.should.equal("/my-path/")
else:
2019-10-31 15:44:26 +00:00
role_name_to_id["no-path"] = role.role_id
role.role_name.should.equal("my-role-no-path-name")
2019-10-31 15:44:26 +00:00
role.path.should.equal("/")
role_names.append(role.role_name)
instance_profile_responses = iam_conn.list_instance_profiles()[
2019-10-31 15:44:26 +00:00
"list_instance_profiles_response"
]["list_instance_profiles_result"]["instance_profiles"]
instance_profile_responses.should.have.length_of(2)
instance_profile_names = []
for instance_profile_response in instance_profile_responses:
2019-10-31 15:44:26 +00:00
instance_profile = iam_conn.get_instance_profile(
instance_profile_response.instance_profile_name
)
instance_profile_names.append(instance_profile.instance_profile_name)
2019-10-31 15:44:26 +00:00
instance_profile.instance_profile_name.should.contain("my-instance-profile")
if "with-path" in instance_profile.instance_profile_name:
instance_profile.path.should.equal("my-path")
2019-10-31 15:44:26 +00:00
instance_profile.role_id.should.equal(role_name_to_id["with-path"])
else:
2019-10-31 15:44:26 +00:00
instance_profile.instance_profile_name.should.contain("no-path")
instance_profile.role_id.should.equal(role_name_to_id["no-path"])
instance_profile.path.should.equal("/")
autoscale_conn = boto.ec2.autoscale.connect_to_region("us-west-1")
launch_config = autoscale_conn.get_all_launch_configurations()[0]
launch_config.instance_profile_name.should.contain("my-instance-profile-with-path")
stack = conn.describe_stacks()[0]
resources = stack.describe_resources()
instance_profile_resources = [
2019-10-31 15:44:26 +00:00
resource
for resource in resources
if resource.resource_type == "AWS::IAM::InstanceProfile"
]
{ip.physical_resource_id for ip in instance_profile_resources}.should.equal(
set(instance_profile_names)
)
role_resources = [
2019-10-31 15:44:26 +00:00
resource for resource in resources if resource.resource_type == "AWS::IAM::Role"
]
{r.physical_resource_id for r in role_resources}.should.equal(set(role_names))
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_single_instance_with_ebs_volume():
template_json = json.dumps(single_instance_with_ebs_volume.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
2019-10-31 15:44:26 +00:00
"test_stack", template_body=template_json, parameters=[("KeyName", "key_name")]
)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
volumes = ec2_conn.get_all_volumes()
# Grab the mounted drive
2019-10-31 15:44:26 +00:00
volume = [volume for volume in volumes if volume.attach_data.device == "/dev/sdh"][
0
]
volume.volume_state().should.equal("in-use")
volume.attach_data.instance_id.should.equal(ec2_instance.id)
stack = conn.describe_stacks()[0]
resources = stack.describe_resources()
2017-02-24 02:37:43 +00:00
ebs_volumes = [
2019-10-31 15:44:26 +00:00
resource
for resource in resources
if resource.resource_type == "AWS::EC2::Volume"
]
ebs_volumes[0].physical_resource_id.should.equal(volume.id)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
def test_create_template_without_required_param():
template_json = json.dumps(single_instance_with_ebs_volume.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack.when.called_with(
2019-10-31 15:44:26 +00:00
"test_stack", template_body=template_json
).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
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_classic_eip():
template_json = json.dumps(ec2_classic_eip.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack("test_stack", template_body=template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
eip = ec2_conn.get_all_addresses()[0]
stack = conn.describe_stacks()[0]
resources = stack.describe_resources()
2017-02-24 02:37:43 +00:00
cfn_eip = [
2019-10-31 15:44:26 +00:00
resource for resource in resources if resource.resource_type == "AWS::EC2::EIP"
][0]
cfn_eip.physical_resource_id.should.equal(eip.public_ip)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
def test_vpc_eip():
template_json = json.dumps(vpc_eip.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack("test_stack", template_body=template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
eip = ec2_conn.get_all_addresses()[0]
stack = conn.describe_stacks()[0]
resources = stack.describe_resources()
2017-02-24 02:37:43 +00:00
cfn_eip = [
2019-10-31 15:44:26 +00:00
resource for resource in resources if resource.resource_type == "AWS::EC2::EIP"
][0]
cfn_eip.physical_resource_id.should.equal(eip.public_ip)
2014-10-22 02:05:27 +00:00
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_ec2_deprecated()
@mock_cloudformation_deprecated()
2014-10-22 02:05:27 +00:00
def test_fn_join():
template_json = json.dumps(fn_join.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack("test_stack", template_body=template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
2014-10-22 02:05:27 +00:00
eip = ec2_conn.get_all_addresses()[0]
stack = conn.describe_stacks()[0]
fn_join_output = stack.outputs[0]
2019-10-31 15:44:26 +00:00
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
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_sqs_deprecated()
def test_conditional_resources():
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
2019-10-31 15:44:26 +00:00
"EnvType": {"Description": "Environment type.", "Type": "String"}
},
2019-10-31 15:44:26 +00:00
"Conditions": {"CreateQueue": {"Fn::Equals": [{"Ref": "EnvType"}, "prod"]}},
"Resources": {
"QueueGroup": {
"Condition": "CreateQueue",
"Type": "AWS::SQS::Queue",
2019-10-31 15:44:26 +00:00
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
}
},
}
sqs_template_json = json.dumps(sqs_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
"test_stack_without_queue",
template_body=sqs_template_json,
parameters=[("EnvType", "staging")],
)
sqs_conn = boto.sqs.connect_to_region("us-west-1")
list(sqs_conn.get_all_queues()).should.have.length_of(0)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack(
"test_stack_with_queue",
template_body=sqs_template_json,
parameters=[("EnvType", "prod")],
)
sqs_conn = boto.sqs.connect_to_region("us-west-1")
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
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_conditional_if_handling():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
2019-10-31 15:44:26 +00:00
"Conditions": {"EnvEqualsPrd": {"Fn::Equals": [{"Ref": "ENV"}, "prd"]}},
"Parameters": {
"ENV": {
"Default": "dev",
"Description": "Deployment environment for the stack (dev/prd)",
2019-10-31 15:44:26 +00:00
"Type": "String",
}
},
"Description": "Stack 1",
"Resources": {
"App1": {
"Properties": {
"ImageId": {
"Fn::If": ["EnvEqualsPrd", EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2]
2019-10-31 15:44:26 +00:00
}
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::EC2::Instance",
}
},
}
dummy_template_json = json.dumps(dummy_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack1", template_body=dummy_template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
ec2_instance.image_id.should.equal(EXAMPLE_AMI_ID2)
ec2_instance.terminate()
conn = boto.cloudformation.connect_to_region("us-west-2")
2017-02-24 02:37:43 +00:00
conn.create_stack(
2019-10-31 15:44:26 +00:00
"test_stack1", template_body=dummy_template_json, parameters=[("ENV", "prd")]
)
ec2_conn = boto.ec2.connect_to_region("us-west-2")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
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
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
def test_cloudformation_mapping():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"RegionMap": {
"us-east-1": {"32": EXAMPLE_AMI_ID, "64": EXAMPLE_AMI_ID2},
"us-west-1": {"32": EXAMPLE_AMI_ID, "64": EXAMPLE_AMI_ID2},
"eu-west-1": {"32": EXAMPLE_AMI_ID, "64": EXAMPLE_AMI_ID2},
"ap-southeast-1": {"32": EXAMPLE_AMI_ID, "64": EXAMPLE_AMI_ID2},
"ap-northeast-1": {"32": EXAMPLE_AMI_ID, "64": EXAMPLE_AMI_ID2},
}
},
"Resources": {
"WebServer": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": {
"Fn::FindInMap": ["RegionMap", {"Ref": "AWS::Region"}, "32"]
},
2019-10-31 15:44:26 +00:00
"InstanceType": "m1.small",
},
2019-10-31 15:44:26 +00:00
}
},
}
dummy_template_json = json.dumps(dummy_template)
conn = boto.cloudformation.connect_to_region("us-east-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack1", template_body=dummy_template_json)
ec2_conn = boto.ec2.connect_to_region("us-east-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
ec2_instance.image_id.should.equal(EXAMPLE_AMI_ID)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack1", template_body=dummy_template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
reservation = ec2_conn.get_all_reservations()[0]
ec2_instance = reservation.instances[0]
ec2_instance.image_id.should.equal(EXAMPLE_AMI_ID)
2015-01-17 15:17:25 +00:00
@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
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
2015-01-17 15:17:25 +00:00
def test_route53_roundrobin():
route53_conn = boto.connect_route53()
template_json = json.dumps(route53_roundrobin.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
stack = conn.create_stack("test_stack", template_body=template_json)
2015-01-17 15:17:25 +00:00
2019-10-31 15:44:26 +00:00
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
"HostedZones"
]
2015-01-17 15:17:25 +00:00
list(zones).should.have.length_of(1)
2019-10-31 15:44:26 +00:00
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
2015-01-17 15:17:25 +00:00
rrsets = route53_conn.get_all_rrsets(zone_id)
rrsets.hosted_zone_id.should.equal(zone_id)
rrsets.should.have.length_of(2)
record_set1 = rrsets[0]
2019-10-31 15:44:26 +00:00
record_set1.name.should.equal("test_stack.us-west-1.my_zone.")
2015-01-17 15:17:25 +00:00
record_set1.identifier.should.equal("test_stack AWS")
2019-10-31 15:44:26 +00:00
record_set1.type.should.equal("CNAME")
record_set1.ttl.should.equal("900")
record_set1.weight.should.equal("3")
2015-01-17 19:50:19 +00:00
record_set1.resource_records[0].should.equal("aws.amazon.com")
2015-01-17 15:17:25 +00:00
record_set2 = rrsets[1]
2019-10-31 15:44:26 +00:00
record_set2.name.should.equal("test_stack.us-west-1.my_zone.")
2015-01-17 15:17:25 +00:00
record_set2.identifier.should.equal("test_stack Amazon")
2019-10-31 15:44:26 +00:00
record_set2.type.should.equal("CNAME")
record_set2.ttl.should.equal("900")
record_set2.weight.should.equal("1")
2015-01-17 19:50:19 +00:00
record_set2.resource_records[0].should.equal("www.amazon.com")
stack = conn.describe_stacks()[0]
output = stack.outputs[0]
2019-10-31 15:44:26 +00:00
output.key.should.equal("DomainName")
output.value.should.equal("arn:aws:route53:::hostedzone/{0}".format(zone_id))
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_ec2_deprecated()
@mock_route53_deprecated()
def test_route53_ec2_instance_with_public_ip():
route53_conn = boto.connect_route53()
ec2_conn = boto.ec2.connect_to_region("us-west-1")
template_json = json.dumps(route53_ec2_instance_with_public_ip.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=template_json)
instance_id = ec2_conn.get_all_reservations()[0].instances[0].id
2019-10-31 15:44:26 +00:00
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
"HostedZones"
]
list(zones).should.have.length_of(1)
2019-10-31 15:44:26 +00:00
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
rrsets = route53_conn.get_all_rrsets(zone_id)
rrsets.should.have.length_of(1)
record_set1 = rrsets[0]
2019-10-31 15:44:26 +00:00
record_set1.name.should.equal("{0}.us-west-1.my_zone.".format(instance_id))
record_set1.identifier.should.equal(None)
2019-10-31 15:44:26 +00:00
record_set1.type.should.equal("A")
record_set1.ttl.should.equal("900")
record_set1.weight.should.equal(None)
record_set1.resource_records[0].should.equal("10.0.0.25")
2015-01-18 00:06:43 +00:00
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
2015-01-18 00:06:43 +00:00
def test_route53_associate_health_check():
route53_conn = boto.connect_route53()
template_json = json.dumps(route53_health_check.template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
conn.create_stack("test_stack", template_body=template_json)
2015-01-18 00:06:43 +00:00
2019-10-31 15:44:26 +00:00
checks = route53_conn.get_list_health_checks()["ListHealthChecksResponse"][
"HealthChecks"
]
2015-01-18 00:06:43 +00:00
list(checks).should.have.length_of(1)
check = checks[0]
2019-10-31 15:44:26 +00:00
health_check_id = check["Id"]
config = check["HealthCheckConfig"]
2015-01-18 00:06:43 +00:00
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")
2019-10-31 15:44:26 +00:00
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
"HostedZones"
]
2015-01-18 00:06:43 +00:00
list(zones).should.have.length_of(1)
2019-10-31 15:44:26 +00:00
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
2015-01-18 00:06:43 +00:00
rrsets = route53_conn.get_all_rrsets(zone_id)
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set.health_check.should.equal(health_check_id)
2015-01-18 00:48:08 +00:00
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_route53_deprecated()
def test_route53_with_update():
route53_conn = boto.connect_route53()
template_json = json.dumps(route53_health_check.template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
2019-10-31 15:44:26 +00:00
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
"HostedZones"
]
list(zones).should.have.length_of(1)
2019-10-31 15:44:26 +00:00
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
rrsets = route53_conn.get_all_rrsets(zone_id)
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set.resource_records.should.equal(["my.example.com"])
template = deepcopy(route53_health_check.template)
template["Resources"]["myDNSRecord"]["Properties"]["ResourceRecords"] = [
"my_other.example.com"
]
template_json = json.dumps(template)
2019-10-31 15:44:26 +00:00
cf_conn.update_stack("test_stack", template_body=template_json)
2019-10-31 15:44:26 +00:00
zones = route53_conn.get_all_hosted_zones()["ListHostedZonesResponse"][
"HostedZones"
]
list(zones).should.have.length_of(1)
2019-10-31 15:44:26 +00:00
zone_id = zones[0]["Id"]
zone_id = zone_id.split("/")
zone_id = zone_id[2]
rrsets = route53_conn.get_all_rrsets(zone_id)
rrsets.should.have.length_of(1)
record_set = rrsets[0]
record_set.resource_records.should.equal(["my_other.example.com"])
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated()
@mock_sns_deprecated()
2015-01-18 00:48:08 +00:00
def test_sns_topic():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MySNSTopic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"Subscription": [
2019-10-31 15:44:26 +00:00
{"Endpoint": "https://example.com", "Protocol": "https"}
2015-01-18 00:48:08 +00:00
],
"TopicName": "my_topics",
2019-10-31 15:44:26 +00:00
},
2015-01-18 00:48:08 +00:00
}
},
"Outputs": {
2019-10-31 15:44:26 +00:00
"topic_name": {"Value": {"Fn::GetAtt": ["MySNSTopic", "TopicName"]}},
"topic_arn": {"Value": {"Ref": "MySNSTopic"}},
},
2015-01-18 00:48:08 +00:00
}
template_json = json.dumps(dummy_template)
conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
stack = conn.create_stack("test_stack", template_body=template_json)
2015-01-18 00:48:08 +00:00
sns_conn = boto.sns.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
topics = sns_conn.get_all_topics()["ListTopicsResponse"]["ListTopicsResult"][
"Topics"
]
2015-01-18 00:48:08 +00:00
topics.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
topic_arn = topics[0]["TopicArn"]
2015-01-18 00:48:08 +00:00
topic_arn.should.contain("my_topics")
2017-02-24 02:37:43 +00:00
subscriptions = sns_conn.get_all_subscriptions()["ListSubscriptionsResponse"][
2019-10-31 15:44:26 +00:00
"ListSubscriptionsResult"
]["Subscriptions"]
2015-01-18 00:48:08 +00:00
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 = conn.describe_stacks()[0]
2019-10-31 15:44:26 +00:00
topic_name_output = [x for x in stack.outputs if x.key == "topic_name"][0]
2015-01-18 00:48:08 +00:00
topic_name_output.value.should.equal("my_topics")
2019-10-31 15:44:26 +00:00
topic_arn_output = [x for x in stack.outputs if x.key == "topic_arn"][0]
2015-01-18 00:48:08 +00:00
topic_arn_output.value.should.equal(topic_arn)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_vpc_gateway_attachment_creation_should_attach_itself_to_vpc():
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
2019-10-31 15:44:26 +00:00
"internetgateway": {"Type": "AWS::EC2::InternetGateway"},
"testvpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsHostnames": "true",
"EnableDnsSupport": "true",
2019-10-31 15:44:26 +00:00
"InstanceTenancy": "default",
},
},
"vpcgatewayattachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
2019-10-31 15:44:26 +00:00
"InternetGatewayId": {"Ref": "internetgateway"},
"VpcId": {"Ref": "testvpc"},
},
},
2019-10-31 15:44:26 +00:00
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
vpc_conn = boto.vpc.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
vpc = vpc_conn.get_all_vpcs(filters={"cidrBlock": "10.0.0.0/16"})[0]
igws = vpc_conn.get_all_internet_gateways(filters={"attachment.vpc-id": vpc.id})
igws.should.have.length_of(1)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_vpc_peering_creation():
vpc_conn = boto.vpc.connect_to_region("us-west-1")
vpc_source = vpc_conn.create_vpc("10.0.0.0/16")
peer_vpc = vpc_conn.create_vpc("10.1.0.0/16")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"vpcpeeringconnection": {
"Type": "AWS::EC2::VPCPeeringConnection",
2019-10-31 15:44:26 +00:00
"Properties": {"PeerVpcId": peer_vpc.id, "VpcId": vpc_source.id},
}
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
peering_connections = vpc_conn.get_all_vpc_peering_connections()
peering_connections.should.have.length_of(1)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
2015-03-14 22:55:44 +00:00
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",
2019-10-31 15:44:26 +00:00
"Tags": [{"Key": "sg-name", "Value": "sg1"}],
},
},
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
2019-10-31 15:44:26 +00:00
"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"},
2019-10-31 15:44:26 +00:00
},
},
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
ec2_conn = boto.ec2.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
security_group1 = ec2_conn.get_all_security_groups(filters={"tag:sg-name": "sg1"})[
0
]
security_group2 = ec2_conn.get_all_security_groups(filters={"tag:sg-name": "sg2"})[
0
]
security_group1.rules.should.have.length_of(1)
security_group1.rules[0].grants.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
security_group1.rules[0].grants[0].group_id.should.equal(security_group2.id)
security_group1.rules[0].ip_protocol.should.equal("tcp")
security_group1.rules[0].from_port.should.equal("80")
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_ingress_separate_from_security_group_by_id():
ec2_conn = boto.ec2.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
ec2_conn.create_security_group("test-security-group1", "test security group")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
2019-10-31 15:44:26 +00:00
"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"},
2019-10-31 15:44:26 +00:00
},
},
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
2017-02-24 02:37:43 +00:00
security_group1 = ec2_conn.get_all_security_groups(
2019-10-31 15:44:26 +00:00
groupnames=["test-security-group1"]
)[0]
security_group2 = ec2_conn.get_all_security_groups(filters={"tag:sg-name": "sg2"})[
0
]
security_group1.rules.should.have.length_of(1)
security_group1.rules[0].grants.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
security_group1.rules[0].grants[0].group_id.should.equal(security_group2.id)
security_group1.rules[0].ip_protocol.should.equal("tcp")
security_group1.rules[0].from_port.should.equal("80")
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_ingress_separate_from_security_group_by_id_using_vpc():
vpc_conn = boto.vpc.connect_to_region("us-west-1")
vpc = vpc_conn.create_vpc("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,
2019-10-31 15:44:26 +00:00
"Tags": [{"Key": "sg-name", "Value": "sg1"}],
},
},
"test-security-group2": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"VpcId": vpc.id,
2019-10-31 15:44:26 +00:00
"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"},
2019-10-31 15:44:26 +00:00
},
},
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
security_group1 = vpc_conn.get_all_security_groups(filters={"tag:sg-name": "sg1"})[
0
]
security_group2 = vpc_conn.get_all_security_groups(filters={"tag:sg-name": "sg2"})[
0
]
security_group1.rules.should.have.length_of(1)
security_group1.rules[0].grants.should.have.length_of(1)
2019-10-31 15:44:26 +00:00
security_group1.rules[0].grants[0].group_id.should.equal(security_group2.id)
security_group1.rules[0].ip_protocol.should.equal("tcp")
security_group1.rules[0].from_port.should.equal("80")
security_group1.rules[0].to_port.should.equal("8080")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_security_group_with_update():
vpc_conn = boto.vpc.connect_to_region("us-west-1")
vpc1 = vpc_conn.create_vpc("10.0.0.0/16")
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test-security-group": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "test security group",
"VpcId": vpc1.id,
2019-10-31 15:44:26 +00:00
"Tags": [{"Key": "sg-name", "Value": "sg"}],
},
2019-10-31 15:44:26 +00:00
}
},
}
template_json = json.dumps(template)
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
security_group = vpc_conn.get_all_security_groups(filters={"tag:sg-name": "sg"})[0]
security_group.vpc_id.should.equal(vpc1.id)
vpc2 = vpc_conn.create_vpc("10.1.0.0/16")
2019-10-31 15:44:26 +00:00
template["Resources"]["test-security-group"]["Properties"]["VpcId"] = vpc2.id
template_json = json.dumps(template)
2019-10-31 15:44:26 +00:00
cf_conn.update_stack("test_stack", template_body=template_json)
security_group = vpc_conn.get_all_security_groups(filters={"tag:sg-name": "sg"})[0]
security_group.vpc_id.should.equal(vpc2.id)
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_ec2_deprecated
def test_subnets_should_be_created_with_availability_zone():
2019-10-31 15:44:26 +00:00
vpc_conn = boto.vpc.connect_to_region("us-west-1")
vpc = vpc_conn.create_vpc("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",
2019-10-31 15:44:26 +00:00
},
}
2019-10-31 15:44:26 +00:00
},
}
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
template_json = json.dumps(subnet_template)
2019-10-31 15:44:26 +00:00
cf_conn.create_stack("test_stack", template_body=template_json)
subnet = vpc_conn.get_all_subnets(filters={"cidrBlock": "10.0.0.0/24"})[0]
subnet.availability_zone.should.equal("us-west-1b")
# Has boto3 equivalent
2017-02-16 03:35:45 +00:00
@mock_cloudformation_deprecated
@mock_datapipeline_deprecated
def test_datapipeline():
dp_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"dataPipeline": {
"Properties": {
"Activate": "true",
"Name": "testDataPipeline",
"PipelineObjects": [
{
"Fields": [
{
"Key": "failureAndRerunMode",
2019-10-31 15:44:26 +00:00
"StringValue": "CASCADE",
},
2019-10-31 15:44:26 +00:00
{"Key": "scheduleType", "StringValue": "cron"},
{"Key": "schedule", "RefValue": "DefaultSchedule"},
{
"Key": "pipelineLogUri",
2019-10-31 15:44:26 +00:00
"StringValue": "s3://bucket/logs",
},
2019-10-31 15:44:26 +00:00
{"Key": "type", "StringValue": "Default"},
],
"Id": "Default",
2019-10-31 15:44:26 +00:00
"Name": "Default",
},
{
"Fields": [
{
"Key": "startDateTime",
2019-10-31 15:44:26 +00:00
"StringValue": "1970-01-01T01:00:00",
},
2019-10-31 15:44:26 +00:00
{"Key": "period", "StringValue": "1 Day"},
{"Key": "type", "StringValue": "Schedule"},
],
"Id": "DefaultSchedule",
2019-10-31 15:44:26 +00:00
"Name": "RunOnce",
},
],
2019-10-31 15:44:26 +00:00
"PipelineTags": [],
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::DataPipeline::Pipeline",
}
2019-10-31 15:44:26 +00:00
},
}
cf_conn = boto.cloudformation.connect_to_region("us-east-1")
template_json = json.dumps(dp_template)
2019-10-31 15:44:26 +00:00
stack_id = cf_conn.create_stack("test_stack", template_body=template_json)
2019-10-31 15:44:26 +00:00
dp_conn = boto.datapipeline.connect_to_region("us-east-1")
data_pipelines = dp_conn.list_pipelines()
2019-10-31 15:44:26 +00:00
data_pipelines["pipelineIdList"].should.have.length_of(1)
data_pipelines["pipelineIdList"][0]["name"].should.equal("testDataPipeline")
stack_resources = cf_conn.list_stack_resources(stack_id)
stack_resources.should.have.length_of(1)
2017-02-24 02:37:43 +00:00
stack_resources[0].physical_resource_id.should.equal(
2019-10-31 15:44:26 +00:00
data_pipelines["pipelineIdList"][0]["id"]
)
2017-02-24 02:37:43 +00:00
@mock_cloudformation
@mock_lambda
def test_lambda_function():
2016-10-06 09:52:23 +00:00
# switch this to python as backend lambda only supports python execution.
lambda_code = """
def lambda_handler(event, context):
return {"event": event}
"""
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"lambdaTest": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
# CloudFormation expects a string as ZipFile, not a ZIP file base64-encoded
"ZipFile": {"Fn::Join": ["\n", lambda_code.splitlines()]}
},
"Handler": "index.lambda_handler",
"Description": "Test function",
"MemorySize": 128,
"Role": {"Fn::GetAtt": ["MyRole", "Arn"]},
"Runtime": "python2.7",
2019-10-31 15:44:26 +00:00
"Environment": {"Variables": {"TEST_ENV_KEY": "test-env-val"}},
"ReservedConcurrentExecutions": 10,
2019-10-31 15:44:26 +00:00
},
},
"MyRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
"Principal": {"Service": ["ec2.amazonaws.com"]},
}
]
}
},
},
2019-10-31 15:44:26 +00:00
},
}
template_json = json.dumps(template)
2019-10-31 15:44:26 +00:00
cf_conn = boto3.client("cloudformation", "us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=template_json)
2019-10-31 15:44:26 +00:00
conn = boto3.client("lambda", "us-east-1")
result = conn.list_functions()
2019-10-31 15:44:26 +00:00
result["Functions"].should.have.length_of(1)
result["Functions"][0]["Description"].should.equal("Test function")
result["Functions"][0]["Handler"].should.equal("index.lambda_handler")
2019-10-31 15:44:26 +00:00
result["Functions"][0]["MemorySize"].should.equal(128)
result["Functions"][0]["Runtime"].should.equal("python2.7")
result["Functions"][0]["Environment"].should.equal(
{"Variables": {"TEST_ENV_KEY": "test-env-val"}}
)
function_name = result["Functions"][0]["FunctionName"]
result = conn.get_function(FunctionName=function_name)
result["Concurrency"]["ReservedConcurrentExecutions"].should.equal(10)
response = conn.invoke(FunctionName=function_name)
result = json.loads(response["Payload"].read())
result.should.equal({"event": "{}"})
def _make_zipfile(func_str):
zip_output = io.BytesIO()
zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED)
zip_file.writestr("lambda_function.py", func_str)
zip_file.close()
zip_output.seek(0)
return zip_output.read()
@mock_cloudformation
@mock_s3
@mock_lambda
def test_lambda_layer():
# switch this to python as backend lambda only supports python execution.
layer_code = """
def lambda_handler(event, context):
return (event, context)
"""
region = "us-east-1"
bucket_name = "test_bucket"
s3_conn = boto3.client("s3", region)
s3_conn.create_bucket(Bucket=bucket_name)
zip_content = _make_zipfile(layer_code)
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"lambdaTest": {
"Type": "AWS::Lambda::LayerVersion",
"Properties": {
"Content": {"S3Bucket": bucket_name, "S3Key": "test.zip",},
"LayerName": "testLayer",
"Description": "Test Layer",
"CompatibleRuntimes": ["python2.7", "python3.6"],
"LicenseInfo": "MIT",
},
},
},
}
template_json = json.dumps(template)
cf_conn = boto3.client("cloudformation", region)
cf_conn.create_stack(StackName="test_stack", TemplateBody=template_json)
lambda_conn = boto3.client("lambda", region)
result = lambda_conn.list_layers()
layer_name = result["Layers"][0]["LayerName"]
result = lambda_conn.list_layer_versions(LayerName=layer_name)
result["LayerVersions"][0].pop("CreatedDate")
result["LayerVersions"].should.equal(
[
{
"Version": 1,
"LayerVersionArn": "arn:aws:lambda:{}:{}:layer:{}:1".format(
region, ACCOUNT_ID, layer_name
),
"CompatibleRuntimes": ["python2.7", "python3.6"],
"Description": "Test Layer",
"LicenseInfo": "MIT",
}
]
)
@mock_cloudformation
@mock_ec2
def test_nat_gateway():
2019-10-31 15:44:26 +00:00
ec2_conn = boto3.client("ec2", "us-east-1")
vpc_id = ec2_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"]
subnet_id = ec2_conn.create_subnet(CidrBlock="10.0.1.0/24", VpcId=vpc_id)["Subnet"][
"SubnetId"
]
route_table_id = ec2_conn.create_route_table(VpcId=vpc_id)["RouteTable"][
"RouteTableId"
]
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
2017-02-24 02:37:43 +00:00
"NAT": {
"DependsOn": "vpcgatewayattachment",
"Type": "AWS::EC2::NatGateway",
"Properties": {
"AllocationId": {"Fn::GetAtt": ["EIP", "AllocationId"]},
2019-10-31 15:44:26 +00:00
"SubnetId": subnet_id,
},
},
2019-10-31 15:44:26 +00:00
"EIP": {"Type": "AWS::EC2::EIP", "Properties": {"Domain": "vpc"}},
2017-02-24 02:37:43 +00:00
"Route": {
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": route_table_id,
"DestinationCidrBlock": "0.0.0.0/0",
2019-10-31 15:44:26 +00:00
"NatGatewayId": {"Ref": "NAT"},
},
},
2019-10-31 15:44:26 +00:00
"internetgateway": {"Type": "AWS::EC2::InternetGateway"},
"vpcgatewayattachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
2019-10-31 15:44:26 +00:00
"InternetGatewayId": {"Ref": "internetgateway"},
"VpcId": vpc_id,
},
2019-10-31 15:44:26 +00:00
},
},
}
2019-10-31 15:44:26 +00:00
cf_conn = boto3.client("cloudformation", "us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=json.dumps(template))
stack_resources = cf_conn.list_stack_resources(StackName="test_stack")
nat_gateway_resource = stack_resources.get("StackResourceSummaries")[0]
for resource in stack_resources["StackResourceSummaries"]:
if resource["ResourceType"] == "AWS::EC2::NatGateway":
nat_gateway_resource = resource
elif resource["ResourceType"] == "AWS::EC2::Route":
route_resource = resource
result = ec2_conn.describe_nat_gateways()
2019-10-31 15:44:26 +00:00
result["NatGateways"].should.have.length_of(1)
result["NatGateways"][0]["VpcId"].should.equal(vpc_id)
result["NatGateways"][0]["SubnetId"].should.equal(subnet_id)
result["NatGateways"][0]["State"].should.equal("available")
result["NatGateways"][0]["NatGatewayId"].should.equal(
nat_gateway_resource.get("PhysicalResourceId")
)
route_resource.get("PhysicalResourceId").should.contain("rtb-")
2017-02-24 02:37:43 +00:00
@mock_cloudformation()
@mock_kms()
def test_stack_kms():
kms_key_template = {
2019-10-31 15:44:26 +00:00
"Resources": {
"kmskey": {
"Properties": {
"Description": "A kms key",
"EnableKeyRotation": True,
"Enabled": True,
"KeyPolicy": "a policy",
},
2019-10-31 15:44:26 +00:00
"Type": "AWS::KMS::Key",
}
}
}
kms_key_template_json = json.dumps(kms_key_template)
2019-10-31 15:44:26 +00:00
cf_conn = boto3.client("cloudformation", "us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=kms_key_template_json)
2019-10-31 15:44:26 +00:00
kms_conn = boto3.client("kms", "us-east-1")
keys = kms_conn.list_keys()["Keys"]
len(keys).should.equal(1)
2019-10-31 15:44:26 +00:00
result = kms_conn.describe_key(KeyId=keys[0]["KeyId"])
2019-10-31 15:44:26 +00:00
result["KeyMetadata"]["Enabled"].should.equal(True)
result["KeyMetadata"]["KeyUsage"].should.equal("ENCRYPT_DECRYPT")
@mock_cloudformation()
@mock_ec2()
def test_stack_spot_fleet():
2019-10-31 15:44:26 +00:00
conn = boto3.client("ec2", "us-east-1")
2016-11-08 04:08:30 +00:00
2019-10-31 15:44:26 +00:00
vpc = conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
2017-02-24 02:37:43 +00:00
subnet = conn.create_subnet(
2019-10-31 15:44:26 +00:00
VpcId=vpc["VpcId"], CidrBlock="10.0.0.0/16", AvailabilityZone="us-east-1a"
)["Subnet"]
subnet_id = subnet["SubnetId"]
2016-11-08 04:08:30 +00:00
spot_fleet_template = {
2019-10-31 15:44:26 +00:00
"Resources": {
"SpotFleet": {
2017-02-24 02:37:43 +00:00
"Type": "AWS::EC2::SpotFleet",
"Properties": {
"SpotFleetRequestConfigData": {
"IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
2017-02-24 02:37:43 +00:00
"SpotPrice": "0.12",
"TargetCapacity": 6,
"AllocationStrategy": "diversified",
"LaunchSpecifications": [
{
"EbsOptimized": "false",
2019-10-31 15:44:26 +00:00
"InstanceType": "t2.small",
"ImageId": EXAMPLE_AMI_ID,
"SubnetId": subnet_id,
"WeightedCapacity": "2",
"SpotPrice": "0.13",
},
{
"EbsOptimized": "true",
2019-10-31 15:44:26 +00:00
"InstanceType": "t2.large",
"ImageId": EXAMPLE_AMI_ID,
"Monitoring": {"Enabled": "true"},
"SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id,
2019-10-31 15:44:26 +00:00
"IamInstanceProfile": {
2019-12-17 02:25:20 +00:00
"Arn": "arn:aws:iam::{}:role/fleet".format(
ACCOUNT_ID
)
2019-10-31 15:44:26 +00:00
},
"WeightedCapacity": "4",
"SpotPrice": "10.00",
2019-10-31 15:44:26 +00:00
},
],
2017-02-24 02:37:43 +00:00
}
2019-10-31 15:44:26 +00:00
},
}
}
}
spot_fleet_template_json = json.dumps(spot_fleet_template)
2019-10-31 15:44:26 +00:00
cf_conn = boto3.client("cloudformation", "us-east-1")
stack_id = cf_conn.create_stack(
2019-10-31 15:44:26 +00:00
StackName="test_stack", TemplateBody=spot_fleet_template_json
)["StackId"]
stack_resources = cf_conn.list_stack_resources(StackName=stack_id)
2019-10-31 15:44:26 +00:00
stack_resources["StackResourceSummaries"].should.have.length_of(1)
spot_fleet_id = stack_resources["StackResourceSummaries"][0]["PhysicalResourceId"]
2017-02-24 02:37:43 +00:00
spot_fleet_requests = conn.describe_spot_fleet_requests(
2019-10-31 15:44:26 +00:00
SpotFleetRequestIds=[spot_fleet_id]
)["SpotFleetRequestConfigs"]
len(spot_fleet_requests).should.equal(1)
spot_fleet_request = spot_fleet_requests[0]
2019-10-31 15:44:26 +00:00
spot_fleet_request["SpotFleetRequestState"].should.equal("active")
spot_fleet_config = spot_fleet_request["SpotFleetRequestConfig"]
2019-10-31 15:44:26 +00:00
spot_fleet_config["SpotPrice"].should.equal("0.12")
spot_fleet_config["TargetCapacity"].should.equal(6)
spot_fleet_config["IamFleetRole"].should.equal(
"arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
2019-10-31 15:44:26 +00:00
)
spot_fleet_config["AllocationStrategy"].should.equal("diversified")
spot_fleet_config["FulfilledCapacity"].should.equal(6.0)
2019-10-31 15:44:26 +00:00
len(spot_fleet_config["LaunchSpecifications"]).should.equal(2)
launch_spec = spot_fleet_config["LaunchSpecifications"][0]
2019-10-31 15:44:26 +00:00
launch_spec["EbsOptimized"].should.equal(False)
launch_spec["ImageId"].should.equal(EXAMPLE_AMI_ID)
2019-10-31 15:44:26 +00:00
launch_spec["InstanceType"].should.equal("t2.small")
launch_spec["SubnetId"].should.equal(subnet_id)
launch_spec["SpotPrice"].should.equal("0.13")
launch_spec["WeightedCapacity"].should.equal(2.0)
@mock_cloudformation()
@mock_ec2()
def test_stack_spot_fleet_should_figure_out_default_price():
2019-10-31 15:44:26 +00:00
conn = boto3.client("ec2", "us-east-1")
2019-10-31 15:44:26 +00:00
vpc = conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
subnet = conn.create_subnet(
2019-10-31 15:44:26 +00:00
VpcId=vpc["VpcId"], CidrBlock="10.0.0.0/16", AvailabilityZone="us-east-1a"
)["Subnet"]
subnet_id = subnet["SubnetId"]
spot_fleet_template = {
2019-10-31 15:44:26 +00:00
"Resources": {
"SpotFleet1": {
"Type": "AWS::EC2::SpotFleet",
"Properties": {
"SpotFleetRequestConfigData": {
"IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"TargetCapacity": 6,
"AllocationStrategy": "diversified",
"LaunchSpecifications": [
{
"EbsOptimized": "false",
2019-10-31 15:44:26 +00:00
"InstanceType": "t2.small",
"ImageId": EXAMPLE_AMI_ID,
"SubnetId": subnet_id,
"WeightedCapacity": "2",
},
{
"EbsOptimized": "true",
2019-10-31 15:44:26 +00:00
"InstanceType": "t2.large",
"ImageId": EXAMPLE_AMI_ID,
"Monitoring": {"Enabled": "true"},
"SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id,
2019-10-31 15:44:26 +00:00
"IamInstanceProfile": {
2019-12-17 02:25:20 +00:00
"Arn": "arn:aws:iam::{}:role/fleet".format(
ACCOUNT_ID
)
2019-10-31 15:44:26 +00:00
},
"WeightedCapacity": "4",
2019-10-31 15:44:26 +00:00
},
],
}
2019-10-31 15:44:26 +00:00
},
}
}
}
spot_fleet_template_json = json.dumps(spot_fleet_template)
2019-10-31 15:44:26 +00:00
cf_conn = boto3.client("cloudformation", "us-east-1")
stack_id = cf_conn.create_stack(
2019-10-31 15:44:26 +00:00
StackName="test_stack", TemplateBody=spot_fleet_template_json
)["StackId"]
stack_resources = cf_conn.list_stack_resources(StackName=stack_id)
2019-10-31 15:44:26 +00:00
stack_resources["StackResourceSummaries"].should.have.length_of(1)
spot_fleet_id = stack_resources["StackResourceSummaries"][0]["PhysicalResourceId"]
spot_fleet_requests = conn.describe_spot_fleet_requests(
2019-10-31 15:44:26 +00:00
SpotFleetRequestIds=[spot_fleet_id]
)["SpotFleetRequestConfigs"]
len(spot_fleet_requests).should.equal(1)
spot_fleet_request = spot_fleet_requests[0]
2019-10-31 15:44:26 +00:00
spot_fleet_request["SpotFleetRequestState"].should.equal("active")
spot_fleet_config = spot_fleet_request["SpotFleetRequestConfig"]
2019-10-31 15:44:26 +00:00
assert "SpotPrice" not in spot_fleet_config
len(spot_fleet_config["LaunchSpecifications"]).should.equal(2)
launch_spec1 = spot_fleet_config["LaunchSpecifications"][0]
launch_spec2 = spot_fleet_config["LaunchSpecifications"][1]
2019-10-31 15:44:26 +00:00
assert "SpotPrice" not in launch_spec1
assert "SpotPrice" not in launch_spec2
@mock_ec2
@mock_elbv2
@mock_cloudformation
def test_invalid_action_type_listener_rule():
invalid_listener_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"alb": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"Name": "myelbv2",
"Scheme": "internet-facing",
"Subnets": [{"Ref": "mysubnet"}],
},
},
"mytargetgroup1": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {"Name": "mytargetgroup1",},
},
"mytargetgroup2": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {"Name": "mytargetgroup2",},
},
"listener": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
"DefaultActions": [
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup1"}}
],
"LoadBalancerArn": {"Ref": "alb"},
"Port": "80",
"Protocol": "HTTP",
},
},
"rule": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [
{
"Type": "forward2",
"TargetGroupArn": {"Ref": "mytargetgroup2"},
}
],
"Conditions": [{"field": "path-pattern", "values": ["/*"]}],
"ListenerArn": {"Ref": "listener"},
"Priority": 2,
},
},
"myvpc": {
"Type": "AWS::EC2::VPC",
"Properties": {"CidrBlock": "10.0.0.0/16"},
},
"mysubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {"CidrBlock": "10.0.0.0/27", "VpcId": {"Ref": "myvpc"}},
},
},
}
listener_template_json = json.dumps(invalid_listener_template)
cfn_conn = boto3.client("cloudformation", "us-west-1")
cfn_conn.create_stack.when.called_with(
StackName="listener_stack", TemplateBody=listener_template_json
).should.throw(ClientError)
Add capability to update `AWS::ElasticLoadBalancingV2`(Listener and ListenerRule) resource (#4005) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check * Implement update_from_cloudformation_json for Listener and ListenerRule * Unwanted spaces * Linting issues * Add tests for code coverage Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-11 20:56:28 +00:00
@mock_ec2
@mock_elbv2
@mock_cloudformation
@mock_events
def test_update_stack_listener_and_rule():
initial_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"alb": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"Name": "myelbv2",
"Scheme": "internet-facing",
"Subnets": [{"Ref": "mysubnet"}],
"SecurityGroups": [{"Ref": "mysg"}],
"Type": "application",
"IpAddressType": "ipv4",
},
},
"mytargetgroup1": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {"Name": "mytargetgroup1",},
},
"mytargetgroup2": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {"Name": "mytargetgroup2",},
},
"listener": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
"DefaultActions": [
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup1"}}
],
"LoadBalancerArn": {"Ref": "alb"},
"Port": "80",
"Protocol": "HTTP",
},
},
"rule": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [
{
"Type": "forward",
"TargetGroupArn": {"Ref": "mytargetgroup2"},
}
],
"Conditions": [{"Field": "path-pattern", "Values": ["/*"]}],
"ListenerArn": {"Ref": "listener"},
"Priority": 2,
},
},
"myvpc": {
"Type": "AWS::EC2::VPC",
"Properties": {"CidrBlock": "10.0.0.0/16"},
},
"mysubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {"CidrBlock": "10.0.0.0/27", "VpcId": {"Ref": "myvpc"}},
},
"mysg": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupName": "mysg",
"GroupDescription": "test security group",
"VpcId": {"Ref": "myvpc"},
},
},
},
}
initial_template_json = json.dumps(initial_template)
cfn_conn = boto3.client("cloudformation", "us-west-1")
cfn_conn.create_stack(StackName="initial_stack", TemplateBody=initial_template_json)
elbv2_conn = boto3.client("elbv2", "us-west-1")
initial_template["Resources"]["rule"]["Properties"]["Conditions"][0][
"Field"
] = "host-header"
initial_template["Resources"]["rule"]["Properties"]["Conditions"][0]["Values"] = "*"
initial_template["Resources"]["listener"]["Properties"]["Port"] = 90
initial_template_json = json.dumps(initial_template)
cfn_conn.update_stack(StackName="initial_stack", TemplateBody=initial_template_json)
load_balancers = elbv2_conn.describe_load_balancers()["LoadBalancers"]
listeners = elbv2_conn.describe_listeners(
LoadBalancerArn=load_balancers[0]["LoadBalancerArn"]
)["Listeners"]
listeners[0]["Port"].should.equal(90)
listener_rule = elbv2_conn.describe_rules(ListenerArn=listeners[0]["ListenerArn"])[
"Rules"
]
listener_rule[0]["Conditions"].should.equal(
[{"Field": "host-header", "Values": ["*"],}]
)
@mock_ec2
@mock_elbv2
@mock_cloudformation
def test_stack_elbv2_resources_integration():
alb_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"albdns": {
"Description": "Load balanacer DNS",
"Value": {"Fn::GetAtt": ["alb", "DNSName"]},
},
"albname": {
"Description": "Load balancer name",
"Value": {"Fn::GetAtt": ["alb", "LoadBalancerName"]},
},
"canonicalhostedzoneid": {
"Description": "Load balancer canonical hosted zone ID",
"Value": {"Fn::GetAtt": ["alb", "CanonicalHostedZoneID"]},
},
},
"Resources": {
"alb": {
2019-10-31 15:44:26 +00:00
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"Name": "myelbv2",
"Scheme": "internet-facing",
"Subnets": [{"Ref": "mysubnet"}],
"SecurityGroups": [{"Ref": "mysg"}],
"Type": "application",
"IpAddressType": "ipv4",
},
},
"mytargetgroup1": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {
"HealthCheckIntervalSeconds": 30,
"HealthCheckPath": "/status",
"HealthCheckPort": 80,
"HealthCheckProtocol": "HTTP",
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 30,
"UnhealthyThresholdCount": 5,
2019-10-31 15:44:26 +00:00
"Matcher": {"HttpCode": "200,201"},
"Name": "mytargetgroup1",
"Port": 80,
"Protocol": "HTTP",
"TargetType": "instance",
2019-10-31 15:44:26 +00:00
"Targets": [{"Id": {"Ref": "ec2instance", "Port": 80}}],
"VpcId": {"Ref": "myvpc"},
},
},
"mytargetgroup2": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {
"HealthCheckIntervalSeconds": 30,
"HealthCheckPath": "/status",
"HealthCheckPort": 8080,
"HealthCheckProtocol": "HTTP",
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 30,
"UnhealthyThresholdCount": 5,
"Name": "mytargetgroup2",
"Port": 8080,
"Protocol": "HTTP",
"TargetType": "instance",
2019-10-31 15:44:26 +00:00
"Targets": [{"Id": {"Ref": "ec2instance", "Port": 8080}}],
"VpcId": {"Ref": "myvpc"},
},
},
"listener": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
2019-10-31 15:44:26 +00:00
"DefaultActions": [
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup1"}}
],
"LoadBalancerArn": {"Ref": "alb"},
"Port": "80",
2019-10-31 15:44:26 +00:00
"Protocol": "HTTP",
},
},
"rule": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
{
"Type": "forward",
"ForwardConfig": {
"TargetGroups": [
{
"TargetGroupArn": {"Ref": "mytargetgroup2"},
"Weight": 1,
},
{
"TargetGroupArn": {"Ref": "mytargetgroup1"},
"Weight": 2,
},
]
},
}
],
Add capability to update `AWS::ElasticLoadBalancingV2`(Listener and ListenerRule) resource (#4005) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check * Implement update_from_cloudformation_json for Listener and ListenerRule * Unwanted spaces * Linting issues * Add tests for code coverage Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-11 20:56:28 +00:00
"Conditions": [{"Field": "path-pattern", "Values": ["/*"]}],
"ListenerArn": {"Ref": "listener"},
"Priority": 2,
},
},
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
"rule2": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [
{"Type": "forward", "TargetGroupArn": {"Ref": "mytargetgroup2"}}
],
Add capability to update `AWS::ElasticLoadBalancingV2`(Listener and ListenerRule) resource (#4005) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check * Implement update_from_cloudformation_json for Listener and ListenerRule * Unwanted spaces * Linting issues * Add tests for code coverage Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-11 20:56:28 +00:00
"Conditions": [{"Field": "host-header", "Values": ["example.com"]}],
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
"ListenerArn": {"Ref": "listener"},
"Priority": 30,
},
},
"myvpc": {
"Type": "AWS::EC2::VPC",
2019-10-31 15:44:26 +00:00
"Properties": {"CidrBlock": "10.0.0.0/16"},
},
"mysubnet": {
"Type": "AWS::EC2::Subnet",
2019-10-31 15:44:26 +00:00
"Properties": {"CidrBlock": "10.0.0.0/27", "VpcId": {"Ref": "myvpc"}},
},
"mysg": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupName": "mysg",
"GroupDescription": "test security group",
2019-10-31 15:44:26 +00:00
"VpcId": {"Ref": "myvpc"},
},
},
"ec2instance": {
"Type": "AWS::EC2::Instance",
"Properties": {"ImageId": EXAMPLE_AMI_ID, "UserData": "some user data"},
},
},
}
alb_template_json = json.dumps(alb_template)
cfn_conn = boto3.client("cloudformation", "us-west-1")
2019-10-31 15:44:26 +00:00
cfn_conn.create_stack(StackName="elb_stack", TemplateBody=alb_template_json)
elbv2_conn = boto3.client("elbv2", "us-west-1")
2019-10-31 15:44:26 +00:00
load_balancers = elbv2_conn.describe_load_balancers()["LoadBalancers"]
len(load_balancers).should.equal(1)
2019-10-31 15:44:26 +00:00
load_balancers[0]["LoadBalancerName"].should.equal("myelbv2")
load_balancers[0]["Scheme"].should.equal("internet-facing")
load_balancers[0]["Type"].should.equal("application")
load_balancers[0]["IpAddressType"].should.equal("ipv4")
target_groups = sorted(
2019-10-31 15:44:26 +00:00
elbv2_conn.describe_target_groups()["TargetGroups"],
key=lambda tg: tg["TargetGroupName"],
) # sort to do comparison with indexes
len(target_groups).should.equal(2)
2019-10-31 15:44:26 +00:00
target_groups[0]["HealthCheckIntervalSeconds"].should.equal(30)
target_groups[0]["HealthCheckPath"].should.equal("/status")
target_groups[0]["HealthCheckPort"].should.equal("80")
target_groups[0]["HealthCheckProtocol"].should.equal("HTTP")
target_groups[0]["HealthCheckTimeoutSeconds"].should.equal(5)
target_groups[0]["HealthyThresholdCount"].should.equal(30)
target_groups[0]["UnhealthyThresholdCount"].should.equal(5)
target_groups[0]["Matcher"].should.equal({"HttpCode": "200,201"})
target_groups[0]["TargetGroupName"].should.equal("mytargetgroup1")
target_groups[0]["Port"].should.equal(80)
target_groups[0]["Protocol"].should.equal("HTTP")
target_groups[0]["TargetType"].should.equal("instance")
target_groups[1]["HealthCheckIntervalSeconds"].should.equal(30)
target_groups[1]["HealthCheckPath"].should.equal("/status")
target_groups[1]["HealthCheckPort"].should.equal("8080")
target_groups[1]["HealthCheckProtocol"].should.equal("HTTP")
target_groups[1]["HealthCheckTimeoutSeconds"].should.equal(5)
target_groups[1]["HealthyThresholdCount"].should.equal(30)
target_groups[1]["UnhealthyThresholdCount"].should.equal(5)
target_groups[1]["Matcher"].should.equal({"HttpCode": "200"})
target_groups[1]["TargetGroupName"].should.equal("mytargetgroup2")
target_groups[1]["Port"].should.equal(8080)
target_groups[1]["Protocol"].should.equal("HTTP")
target_groups[1]["TargetType"].should.equal("instance")
listeners = elbv2_conn.describe_listeners(
LoadBalancerArn=load_balancers[0]["LoadBalancerArn"]
)["Listeners"]
len(listeners).should.equal(1)
2019-10-31 15:44:26 +00:00
listeners[0]["LoadBalancerArn"].should.equal(load_balancers[0]["LoadBalancerArn"])
listeners[0]["Port"].should.equal(80)
listeners[0]["Protocol"].should.equal("HTTP")
listeners[0]["DefaultActions"].should.equal(
[{"Type": "forward", "TargetGroupArn": target_groups[0]["TargetGroupArn"]}]
)
listener_rule = elbv2_conn.describe_rules(ListenerArn=listeners[0]["ListenerArn"])[
"Rules"
]
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
len(listener_rule).should.equal(3)
listener_rule[0]["Priority"].should.equal("2")
listener_rule[0]["Actions"].should.equal(
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
[
{
"Type": "forward",
"ForwardConfig": {
"TargetGroups": [
{
"TargetGroupArn": target_groups[1]["TargetGroupArn"],
"Weight": 1,
},
{
"TargetGroupArn": target_groups[0]["TargetGroupArn"],
"Weight": 2,
},
]
},
}
Merge LocalStack changes into upstream moto (#4082) * fix OPTIONS requests on non-existing API GW integrations * add cloudformation models for API Gateway deployments * bump version * add backdoor to return CloudWatch metrics * Updating implementation coverage * Updating implementation coverage * add cloudformation models for API Gateway deployments * Updating implementation coverage * Updating implementation coverage * Implemented get-caller-identity returning real data depending on the access key used. * bump version * minor fixes * fix Number data_type for SQS message attribute * fix handling of encoding errors * bump version * make CF stack queryable before starting to initialize its resources * bump version * fix integration_method for API GW method integrations * fix undefined status in CF FakeStack * Fix apigateway issues with terraform v0.12.21 * resource_methods -> add handle for "DELETE" method * integrations -> fix issue that "httpMethod" wasn't included in body request (this value was set as the value from refer method resource) * bump version * Fix setting http method for API gateway integrations (#6) * bump version * remove duplicate methods * add storage class to S3 Key when completing multipart upload (#7) * fix SQS performance issues; bump version * add pagination to SecretsManager list-secrets (#9) * fix default parameter groups in RDS * fix adding S3 metadata headers with names containing dots (#13) * Updating implementation coverage * Updating implementation coverage * add cloudformation models for API Gateway deployments * Updating implementation coverage * Updating implementation coverage * Implemented get-caller-identity returning real data depending on the access key used. * make CF stack queryable before starting to initialize its resources * bump version * remove duplicate methods * fix adding S3 metadata headers with names containing dots (#13) * Update amis.json to support EKS AMI mocks (#15) * fix PascalCase for boolean value in ListMultipartUploads response (#17); fix _get_multi_param to parse nested list/dict query params * determine non-zero container exit code in Batch API * support filtering by dimensions in CW get_metric_statistics * fix storing attributes for ELBv2 Route entities; API GW refactorings for TF tests * add missing fields for API GW resources * fix error messages for Route53 (TF-compat) * various fixes for IAM resources (tf-compat) * minor fixes for API GW models (tf-compat) * minor fixes for API GW responses (tf-compat) * add s3 exception for bucket notification filter rule validation * change the way RESTErrors generate the response body and content-type header * fix lint errors and disable "black" syntax enforcement * remove return type hint in RESTError.get_body * add RESTError XML template for IAM exceptions * add support for API GW minimumCompressionSize * fix casing getting PrivateDnsEnabled API GW attribute * minor fixes for error responses * fix escaping special chars for IAM role descriptions (tf-compat) * minor fixes and tagging support for API GW and ELB v2 (tf-compat) * Merge branch 'master' into localstack * add "AlarmRule" attribute to enable support for composite CloudWatch metrics * fix recursive parsing of complex/nested query params * bump version * add API to delete S3 website configurations (#18) * use dict copy to allow parallelism and avoid concurrent modification exceptions in S3 * fix precondition check for etags in S3 (#19) * minor fix for user filtering in Cognito * fix API Gateway error response; avoid returning empty response templates (tf-compat) * support tags and tracingEnabled attribute for API GW stages * fix boolean value in S3 encryption response (#20) * fix connection arn structure * fix api destination arn structure * black format * release 2.0.3.37 * fix s3 exception tests see botocore/parsers.py:1002 where RequestId is removed from parsed * remove python 2 from build action * add test failure annotations in build action * fix events test arn comparisons * fix s3 encryption response test * return default value "0" if EC2 availableIpAddressCount is empty * fix extracting SecurityGroupIds for EC2 VPC endpoints * support deleting/updating API Gateway DomainNames * fix(events): Return empty string instead of null when no pattern is specified in EventPattern (tf-compat) (#22) * fix logic and revert CF changes to get tests running again (#21) * add support for EC2 customer gateway API (#25) * add support for EC2 Transit Gateway APIs (#24) * feat(logs): add `kmsKeyId` into `LogGroup` entity (#23) * minor change in ELBv2 logic to fix tests * feat(events): add APIs to describe and delete CloudWatch Events connections (#26) * add support for EC2 transit gateway route tables (#27) * pass transit gateway route table ID in Describe API, minor refactoring (#29) * add support for EC2 Transit Gateway Routes (#28) * fix region on ACM certificate import (#31) * add support for EC2 transit gateway attachments (#30) * add support for EC2 Transit Gateway VPN attachments (#32) * fix account ID for logs API * add support for DeleteOrganization API * feat(events): store raw filter representation for CloudWatch events patterns (tf-compat) (#36) * feat(events): add support to describe/update/delete CloudWatch API destinations (#35) * add Cognito UpdateIdentityPool, CW Logs PutResourcePolicy * feat(events): add support for tags in EventBus API (#38) * fix parameter validation for Batch compute environments (tf-compat) * revert merge conflicts in IMPLEMENTATION_COVERAGE.md * format code using black * restore original README; re-enable and fix CloudFormation tests * restore tests and old logic for CF stack parameters from SSM * parameterize RequestId/RequestID in response messages and revert related test changes * undo LocalStack-specific adaptations * minor fix * Update CodeCov config to reflect removal of Py2 * undo change related to CW metric filtering; add additional test for CW metric statistics with dimensions * Terraform - Extend whitelist of running tests Co-authored-by: acsbendi <acsbendi28@gmail.com> Co-authored-by: Phan Duong <duongpv@outlook.com> Co-authored-by: Thomas Rausch <thomas@thrau.at> Co-authored-by: Macwan Nevil <macnev2013@gmail.com> Co-authored-by: Dominik Schubert <dominik.schubert91@gmail.com> Co-authored-by: Gonzalo Saad <saad.gonzalo.ale@gmail.com> Co-authored-by: Mohit Alonja <monty16597@users.noreply.github.com> Co-authored-by: Miguel Gagliardo <migag9@gmail.com> Co-authored-by: Bert Blommers <info@bertblommers.nl>
2021-07-26 14:21:17 +00:00
],
[{"Type": "forward", "TargetGroupArn": target_groups[1]["TargetGroupArn"]}],
)
listener_rule[0]["Conditions"].should.equal(
[{"Field": "path-pattern", "Values": ["/*"]}]
)
Adding support for `ForwardConfig` property in `ListernRule` in CloudFormation (#3993) * Add ssm parsing support for cloudformation stacks * Start adding unit tests for ssm parameter parsing * Add tests for code update * Add tests to parse ssm parameters code * Fix black lint errors * Fix bug. * Need to specify region_name * region needs to be same * Use ssm_backends[region] instead of ssm_backend * StringList -> string * Linting * check if servermode tests are on * Typo * Added support for ListenerRule. Will remove cruft * Pushing latest * Something works * Put back ripped out code * Save point. Incase I need more validations * Revert "Save point. Incase I need more validations" This reverts commit dac4953335dd9335eddb7a91a63667bc3c17104c. * Fixed validations and some refactor * Fix formatting * Linting * Cannot refactor if I have to fix all tests * Remove exceptions for now. Will do in another PR * Remove validations. Will add in next PR * Fix broken tests. Almost.: * Fix all tests. Some sneaky for now. * Python2 making me write bad code * OrderedDict.move_to_end() does not work in python2 * Linting * Add more checks to field in conditions later. * Unwnated change in FakeListener * Revert "Unwnated change in FakeListener" This reverts commit 962c2fdfd76fce999de9feccf1dd1c3ec48c459f. * Add back default listener rule * Linting fix * Fix priority sorting * Add cloudformation test for edge case * Add validation for ForwardConfig in Action of ListernRule CF * use not in * set the priority template correctly * Check for boolean in condition * One more check Co-authored-by: Bert Blommers <bblommers@users.noreply.github.com>
2021-06-09 17:41:18 +00:00
listener_rule[1]["Priority"].should.equal("30")
listener_rule[1]["Actions"].should.equal(
[{"Type": "forward", "TargetGroupArn": target_groups[1]["TargetGroupArn"]}]
)
listener_rule[1]["Conditions"].should.equal(
[{"Field": "host-header", "Values": ["example.com"]}]
)
# test outputs
2019-10-31 15:44:26 +00:00
stacks = cfn_conn.describe_stacks(StackName="elb_stack")["Stacks"]
len(stacks).should.equal(1)
2019-10-31 15:44:26 +00:00
dns = list(
filter(lambda item: item["OutputKey"] == "albdns", stacks[0]["Outputs"])
)[0]
name = list(
filter(lambda item: item["OutputKey"] == "albname", stacks[0]["Outputs"])
)[0]
2019-10-31 15:44:26 +00:00
dns["OutputValue"].should.equal(load_balancers[0]["DNSName"])
name["OutputValue"].should.equal(load_balancers[0]["LoadBalancerName"])
@mock_dynamodb2
@mock_cloudformation
def test_stack_dynamodb_resources_integration():
dynamodb_template = {
2019-10-31 15:44:26 +00:00
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"myDynamoDBTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"AttributeDefinitions": [
{"AttributeName": "Album", "AttributeType": "S"},
{"AttributeName": "Artist", "AttributeType": "S"},
{"AttributeName": "Sales", "AttributeType": "N"},
{"AttributeName": "NumberOfSongs", "AttributeType": "N"},
],
"KeySchema": [
{"AttributeName": "Album", "KeyType": "HASH"},
{"AttributeName": "Artist", "KeyType": "RANGE"},
],
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5",
},
"TableName": "myTableName",
"GlobalSecondaryIndexes": [
{
"IndexName": "myGSI",
"KeySchema": [
{"AttributeName": "Sales", "KeyType": "HASH"},
{"AttributeName": "Artist", "KeyType": "RANGE"},
],
"Projection": {
"NonKeyAttributes": ["Album", "NumberOfSongs"],
"ProjectionType": "INCLUDE",
},
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5",
},
},
{
"IndexName": "myGSI2",
"KeySchema": [
{"AttributeName": "NumberOfSongs", "KeyType": "HASH"},
{"AttributeName": "Sales", "KeyType": "RANGE"},
],
"Projection": {
"NonKeyAttributes": ["Album", "Artist"],
"ProjectionType": "INCLUDE",
},
"ProvisionedThroughput": {
"ReadCapacityUnits": "5",
"WriteCapacityUnits": "5",
},
},
],
"LocalSecondaryIndexes": [
{
"IndexName": "myLSI",
"KeySchema": [
{"AttributeName": "Album", "KeyType": "HASH"},
{"AttributeName": "Sales", "KeyType": "RANGE"},
],
"Projection": {
"NonKeyAttributes": ["Artist", "NumberOfSongs"],
"ProjectionType": "INCLUDE",
},
}
],
"StreamSpecification": {"StreamViewType": "KEYS_ONLY"},
},
2019-10-31 15:44:26 +00:00
}
},
}
dynamodb_template_json = json.dumps(dynamodb_template)
2019-10-31 15:44:26 +00:00
cfn_conn = boto3.client("cloudformation", "us-east-1")
cfn_conn.create_stack(
2019-10-31 15:44:26 +00:00
StackName="dynamodb_stack", TemplateBody=dynamodb_template_json
)
dynamodb_client = boto3.client("dynamodb", region_name="us-east-1")
table_desc = dynamodb_client.describe_table(TableName="myTableName")["Table"]
table_desc["StreamSpecification"].should.equal(
{"StreamEnabled": True, "StreamViewType": "KEYS_ONLY",}
)
2019-10-31 15:44:26 +00:00
dynamodb_conn = boto3.resource("dynamodb", region_name="us-east-1")
table = dynamodb_conn.Table("myTableName")
table.name.should.equal("myTableName")
2019-10-31 15:44:26 +00:00
table.put_item(
Item={"Album": "myAlbum", "Artist": "myArtist", "Sales": 10, "NumberOfSongs": 5}
)
response = table.get_item(Key={"Album": "myAlbum", "Artist": "myArtist"})
2019-10-31 15:44:26 +00:00
response["Item"]["Album"].should.equal("myAlbum")
response["Item"]["Sales"].should.equal(Decimal("10"))
response["Item"]["NumberOfSongs"].should.equal(Decimal("5"))
response["Item"]["Album"].should.equal("myAlbum")
@mock_cloudformation
@mock_logs
@mock_s3
def test_create_log_group_using_fntransform():
s3_resource = boto3.resource("s3")
s3_resource.create_bucket(
Bucket="owi-common-cf",
CreateBucketConfiguration={"LocationConstraint": "us-west-2"},
)
s3_resource.Object("owi-common-cf", "snippets/test.json").put(
Body=json.dumps({"lgname": {"name": "some-log-group"}})
)
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"EnvironmentMapping": {
"Fn::Transform": {
"Name": "AWS::Include",
"Parameters": {"Location": "s3://owi-common-cf/snippets/test.json"},
}
}
},
"Resources": {
"LogGroup": {
"Properties": {
"LogGroupName": {
"Fn::FindInMap": ["EnvironmentMapping", "lgname", "name"]
},
"RetentionInDays": 90,
},
"Type": "AWS::Logs::LogGroup",
}
},
}
2021-10-18 19:44:29 +00:00
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(
StackName="test_stack", TemplateBody=json.dumps(template),
)
logs_conn = boto3.client("logs", region_name="us-west-2")
log_group = logs_conn.describe_log_groups()["logGroups"][0]
log_group["logGroupName"].should.equal("some-log-group")
@mock_cloudformation
@mock_logs
def test_create_cloudwatch_logs_resource_policy():
policy_document = json.dumps(
{
"Statement": [
{
"Action": ["logs:CreateLogStream", "logs:PutLogEvents",],
"Effect": "Allow",
"Principal": {"Service": "es.amazonaws.com"},
"Resource": "*",
}
]
}
)
template1 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"LogGroupPolicy1": {
"Type": "AWS::Logs::ResourcePolicy",
"Properties": {
"PolicyDocument": policy_document,
"PolicyName": "TestPolicyA",
},
}
},
}
template2 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"LogGroupPolicy1": {
"Type": "AWS::Logs::ResourcePolicy",
"Properties": {
"PolicyDocument": policy_document,
"PolicyName": "TestPolicyB",
},
},
"LogGroupPolicy2": {
"Type": "AWS::Logs::ResourcePolicy",
"Properties": {
"PolicyDocument": policy_document,
"PolicyName": "TestPolicyC",
},
},
},
}
cf_conn = boto3.client("cloudformation", "us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=json.dumps(template1))
logs_conn = boto3.client("logs", region_name="us-east-1")
policies = logs_conn.describe_resource_policies()["resourcePolicies"]
policies.should.have.length_of(1)
policies.should.be.containing_item_with_attributes(
policyName="TestPolicyA", policyDocument=policy_document
)
cf_conn.update_stack(StackName="test_stack", TemplateBody=json.dumps(template2))
policies = logs_conn.describe_resource_policies()["resourcePolicies"]
policies.should.have.length_of(2)
policies.should.be.containing_item_with_attributes(
policyName="TestPolicyB", policyDocument=policy_document
)
policies.should.be.containing_item_with_attributes(
policyName="TestPolicyC", policyDocument=policy_document
)
cf_conn.update_stack(StackName="test_stack", TemplateBody=json.dumps(template1))
policies = logs_conn.describe_resource_policies()["resourcePolicies"]
policies.should.have.length_of(1)
policies.should.be.containing_item_with_attributes(
policyName="TestPolicyA", policyDocument=policy_document
)
@mock_cloudformation
@mock_logs
def test_delete_stack_containing_cloudwatch_logs_resource_policy():
template1 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"LogGroupPolicy1": {
"Type": "AWS::Logs::ResourcePolicy",
"Properties": {
"PolicyDocument": '{"Statement":[{"Action":"logs:*","Effect":"Allow","Principal":"*","Resource":"*"}]}',
"PolicyName": "TestPolicyA",
},
}
},
}
cf_conn = boto3.client("cloudformation", "us-east-1")
cf_conn.create_stack(StackName="test_stack", TemplateBody=json.dumps(template1))
logs_conn = boto3.client("logs", region_name="us-east-1")
policies = logs_conn.describe_resource_policies()["resourcePolicies"]
policies.should.have.length_of(1)
cf_conn.delete_stack(StackName="test_stack")
policies = logs_conn.describe_resource_policies()["resourcePolicies"]
policies.should.have.length_of(0)
2020-03-22 21:03:42 +00:00
@mock_cloudformation
@mock_events
def test_stack_events_create_rule_integration():
2020-03-22 21:03:42 +00:00
events_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Event": {
2020-03-22 21:03:42 +00:00
"Type": "AWS::Events::Rule",
"Properties": {
"Name": "quick-fox",
"State": "ENABLED",
"ScheduleExpression": "rate(5 minutes)",
},
}
},
}
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(
2020-04-02 01:48:40 +00:00
StackName="test_stack", TemplateBody=json.dumps(events_template)
2020-03-22 21:03:42 +00:00
)
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"].should.have.length_of(1)
rules["Rules"][0]["Name"].should.equal("quick-fox")
rules["Rules"][0]["State"].should.equal("ENABLED")
rules["Rules"][0]["ScheduleExpression"].should.equal("rate(5 minutes)")
@mock_cloudformation
@mock_events
def test_stack_events_delete_rule_integration():
events_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Event": {
"Type": "AWS::Events::Rule",
"Properties": {
"Name": "quick-fox",
"State": "ENABLED",
"ScheduleExpression": "rate(5 minutes)",
},
}
},
}
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(
2020-04-02 01:48:40 +00:00
StackName="test_stack", TemplateBody=json.dumps(events_template)
)
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"].should.have.length_of(1)
cf_conn.delete_stack(StackName="test_stack")
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"].should.have.length_of(0)
@mock_cloudformation
@mock_events
def test_stack_events_create_rule_without_name_integration():
events_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Event": {
"Type": "AWS::Events::Rule",
"Properties": {
"State": "ENABLED",
"ScheduleExpression": "rate(5 minutes)",
},
}
},
}
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(
2020-04-02 01:48:40 +00:00
StackName="test_stack", TemplateBody=json.dumps(events_template)
)
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"][0]["Name"].should.contain("test_stack-Event-")
@mock_cloudformation
@mock_events
@mock_logs
def test_stack_events_create_rule_as_target():
events_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"SecurityGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": {"Fn::GetAtt": ["Event", "Arn"]},
"RetentionInDays": 3,
2020-04-02 01:48:40 +00:00
},
},
"Event": {
"Type": "AWS::Events::Rule",
"Properties": {
"State": "ENABLED",
"ScheduleExpression": "rate(5 minutes)",
},
2020-04-02 01:48:40 +00:00
},
},
}
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(
2020-04-02 01:48:40 +00:00
StackName="test_stack", TemplateBody=json.dumps(events_template)
)
rules = boto3.client("events", "us-west-2").list_rules()
log_groups = boto3.client("logs", "us-west-2").describe_log_groups()
rules["Rules"][0]["Name"].should.contain("test_stack-Event-")
log_groups["logGroups"][0]["logGroupName"].should.equal(rules["Rules"][0]["Arn"])
log_groups["logGroups"][0]["retentionInDays"].should.equal(3)
@mock_cloudformation
@mock_events
def test_stack_events_update_rule_integration():
events_template = Template(
"""{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Event": {
"Type": "AWS::Events::Rule",
"Properties": {
"Name": "$Name",
"State": "$State",
"ScheduleExpression": "rate(5 minutes)",
},
}
},
} """
)
cf_conn = boto3.client("cloudformation", "us-west-2")
original_template = events_template.substitute(Name="Foo", State="ENABLED")
cf_conn.create_stack(StackName="test_stack", TemplateBody=original_template)
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"].should.have.length_of(1)
rules["Rules"][0]["Name"].should.equal("Foo")
rules["Rules"][0]["State"].should.equal("ENABLED")
update_template = events_template.substitute(Name="Bar", State="DISABLED")
cf_conn.update_stack(StackName="test_stack", TemplateBody=update_template)
rules = boto3.client("events", "us-west-2").list_rules()
rules["Rules"].should.have.length_of(1)
rules["Rules"][0]["Name"].should.equal("Bar")
rules["Rules"][0]["State"].should.equal("DISABLED")
@mock_cloudformation
@mock_autoscaling
def test_autoscaling_propagate_tags():
autoscaling_group_with_tags = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"AutoScalingGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AutoScalingGroupName": "test-scaling-group",
"DesiredCapacity": 1,
"MinSize": 1,
"MaxSize": 50,
"LaunchConfigurationName": "test-launch-config",
"AvailabilityZones": ["us-east-1a"],
"Tags": [
{
"Key": "test-key-propagate",
"Value": "test",
"PropagateAtLaunch": True,
},
{
"Key": "test-key-no-propagate",
"Value": "test",
"PropagateAtLaunch": False,
},
],
},
"DependsOn": "LaunchConfig",
},
"LaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"LaunchConfigurationName": "test-launch-config",
"ImageId": EXAMPLE_AMI_ID,
"InstanceType": "t2.medium",
},
},
},
}
boto3.client("cloudformation", "us-east-1").create_stack(
StackName="propagate_tags_test",
TemplateBody=json.dumps(autoscaling_group_with_tags),
)
autoscaling = boto3.client("autoscaling", "us-east-1")
autoscaling_group_tags = autoscaling.describe_auto_scaling_groups()[
"AutoScalingGroups"
][0]["Tags"]
propagation_dict = {
tag["Key"]: tag["PropagateAtLaunch"] for tag in autoscaling_group_tags
}
assert propagation_dict["test-key-propagate"]
assert not propagation_dict["test-key-no-propagate"]
@mock_cloudformation
@mock_events
def test_stack_eventbus_create_from_cfn_integration():
eventbus_template = """{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"EventBus": {
"Type": "AWS::Events::EventBus",
"Properties": {
"Name": "MyCustomEventBus"
},
}
},
}"""
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(StackName="test_stack", TemplateBody=eventbus_template)
event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="MyCustom"
)
event_buses["EventBuses"].should.have.length_of(1)
event_buses["EventBuses"][0]["Name"].should.equal("MyCustomEventBus")
@mock_cloudformation
@mock_events
def test_stack_events_delete_eventbus_integration():
eventbus_template = """{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"EventBus": {
"Type": "AWS::Events::EventBus",
"Properties": {
"Name": "MyCustomEventBus"
},
}
},
}"""
cf_conn = boto3.client("cloudformation", "us-west-2")
cf_conn.create_stack(StackName="test_stack", TemplateBody=eventbus_template)
event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="MyCustom"
)
event_buses["EventBuses"].should.have.length_of(1)
cf_conn.delete_stack(StackName="test_stack")
event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="MyCustom"
)
event_buses["EventBuses"].should.have.length_of(0)
@mock_cloudformation
@mock_events
def test_stack_events_delete_from_cfn_integration():
eventbus_template = Template(
"""{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"$resource_name": {
"Type": "AWS::Events::EventBus",
"Properties": {
"Name": "$name"
},
}
},
}"""
)
cf_conn = boto3.client("cloudformation", "us-west-2")
original_template = eventbus_template.substitute(
{"resource_name": "original", "name": "MyCustomEventBus"}
)
cf_conn.create_stack(StackName="test_stack", TemplateBody=original_template)
original_event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="MyCustom"
)
original_event_buses["EventBuses"].should.have.length_of(1)
original_eventbus = original_event_buses["EventBuses"][0]
updated_template = eventbus_template.substitute(
{"resource_name": "updated", "name": "AnotherEventBus"}
)
cf_conn.update_stack(StackName="test_stack", TemplateBody=updated_template)
update_event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="AnotherEventBus"
)
update_event_buses["EventBuses"].should.have.length_of(1)
update_event_buses["EventBuses"][0]["Arn"].shouldnt.equal(original_eventbus["Arn"])
@mock_cloudformation
@mock_events
def test_stack_events_update_from_cfn_integration():
eventbus_template = Template(
"""{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"EventBus": {
"Type": "AWS::Events::EventBus",
"Properties": {
"Name": "$name"
},
}
},
}"""
)
cf_conn = boto3.client("cloudformation", "us-west-2")
original_template = eventbus_template.substitute({"name": "MyCustomEventBus"})
cf_conn.create_stack(StackName="test_stack", TemplateBody=original_template)
original_event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="MyCustom"
)
original_event_buses["EventBuses"].should.have.length_of(1)
original_eventbus = original_event_buses["EventBuses"][0]
updated_template = eventbus_template.substitute({"name": "NewEventBus"})
cf_conn.update_stack(StackName="test_stack", TemplateBody=updated_template)
update_event_buses = boto3.client("events", "us-west-2").list_event_buses(
NamePrefix="NewEventBus"
)
update_event_buses["EventBuses"].should.have.length_of(1)
update_event_buses["EventBuses"][0]["Name"].should.equal("NewEventBus")
update_event_buses["EventBuses"][0]["Arn"].shouldnt.equal(original_eventbus["Arn"])
@mock_cloudformation
@mock_events
def test_stack_events_get_attribute_integration():
eventbus_template = """{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"EventBus": {
"Type": "AWS::Events::EventBus",
"Properties": {
"Name": "MyEventBus"
},
}
},
"Outputs": {
"bus_arn": {"Value": {"Fn::GetAtt": ["EventBus", "Arn"]}},
"bus_name": {"Value": {"Fn::GetAtt": ["EventBus", "Name"]}},
}
}"""
cf = boto3.client("cloudformation", "us-west-2")
events = boto3.client("events", "us-west-2")
cf.create_stack(StackName="test_stack", TemplateBody=eventbus_template)
stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0]
outputs = stack["Outputs"]
output_arn = list(filter(lambda item: item["OutputKey"] == "bus_arn", outputs))[0]
output_name = list(filter(lambda item: item["OutputKey"] == "bus_name", outputs))[0]
event_bus = events.list_event_buses(NamePrefix="MyEventBus")["EventBuses"][0]
output_arn["OutputValue"].should.equal(event_bus["Arn"])
output_name["OutputValue"].should.equal(event_bus["Name"])
@mock_cloudformation
@mock_dynamodb2
def test_dynamodb_table_creation():
CFN_TEMPLATE = {
"Outputs": {"MyTableName": {"Value": {"Ref": "MyTable"}},},
"Resources": {
"MyTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"KeySchema": [{"AttributeName": "id", "KeyType": "HASH"}],
"AttributeDefinitions": [
{"AttributeName": "id", "AttributeType": "S"}
],
"BillingMode": "PAY_PER_REQUEST",
},
},
},
}
stack_name = "foobar"
cfn = boto3.client("cloudformation", "us-west-2")
cfn.create_stack(StackName=stack_name, TemplateBody=json.dumps(CFN_TEMPLATE))
# Wait until moto creates the stack
waiter = cfn.get_waiter("stack_create_complete")
waiter.wait(StackName=stack_name)
# Verify the TableName is part of the outputs
stack = cfn.describe_stacks(StackName=stack_name)["Stacks"][0]
outputs = stack["Outputs"]
outputs.should.have.length_of(1)
outputs[0]["OutputKey"].should.equal("MyTableName")
outputs[0]["OutputValue"].should.contain("foobar")
# Assert the table is created
ddb = boto3.client("dynamodb", "us-west-2")
table_names = ddb.list_tables()["TableNames"]
table_names.should.equal([outputs[0]["OutputValue"]])