Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Stephan Huber 2019-12-23 08:38:53 +01:00
commit 0527e88d46
541 changed files with 75504 additions and 51429 deletions

View file

@ -1,9 +1,3 @@
from __future__ import unicode_literals
template = {
"Resources": {
"EC2EIP": {
"Type": "AWS::EC2::EIP"
}
}
}
template = {"Resources": {"EC2EIP": {"Type": "AWS::EC2::EIP"}}}

View file

@ -1,23 +1,11 @@
from __future__ import unicode_literals
template = {
"Resources": {
"EC2EIP": {
"Type": "AWS::EC2::EIP"
}
},
"Resources": {"EC2EIP": {"Type": "AWS::EC2::EIP"}},
"Outputs": {
"EIP": {
"Description": "EIP for joining",
"Value": {
"Fn::Join": [
":",
[
"test eip",
{"Ref": "EC2EIP"}
]
]
}
"Value": {"Fn::Join": [":", ["test eip", {"Ref": "EC2EIP"}]]},
}
}
},
}

View file

@ -2,38 +2,45 @@ from __future__ import unicode_literals
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Sample Template to create a KMS Key. The Fn::GetAtt is used to retrieve the ARN",
"Resources" : {
"myKey" : {
"Type" : "AWS::KMS::Key",
"Properties" : {
"Resources": {
"myKey": {
"Type": "AWS::KMS::Key",
"Properties": {
"Description": "Sample KmsKey",
"EnableKeyRotation": False,
"Enabled": True,
"KeyPolicy" : {
"KeyPolicy": {
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": { "Fn::Join" : ["" , ["arn:aws:iam::", {"Ref" : "AWS::AccountId"} ,":root" ]] }
},
"Action": "kms:*",
"Resource": "*"
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:aws:iam::",
{"Ref": "AWS::AccountId"},
":root",
],
]
}
},
"Action": "kms:*",
"Resource": "*",
}
]
}
}
],
},
},
}
},
"Outputs" : {
"KeyArn" : {
"Outputs": {
"KeyArn": {
"Description": "Generated Key Arn",
"Value" : { "Fn::GetAtt" : [ "myKey", "Arn" ] }
"Value": {"Fn::GetAtt": ["myKey", "Arn"]},
}
}
}
},
}

View file

@ -2,9 +2,7 @@ from __future__ import unicode_literals
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Sample Template RDS_MySQL_With_Read_Replica: Sample template showing how to create a highly-available, RDS DBInstance with a read replica. **WARNING** This template creates an Amazon Relational Database Service database instance and Amazon CloudWatch alarms. You will be billed for the AWS resources used if you create a stack from this template.",
"Parameters": {
"DBName": {
"Default": "MyDatabase",
@ -13,13 +11,9 @@ template = {
"MinLength": "1",
"MaxLength": "64",
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters."
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.",
},
"DBInstanceIdentifier": {
"Type": "String"
},
"DBInstanceIdentifier": {"Type": "String"},
"DBUser": {
"NoEcho": "true",
"Description": "The database admin account username",
@ -27,9 +21,8 @@ template = {
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters."
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.",
},
"DBPassword": {
"NoEcho": "true",
"Description": "The database admin account password",
@ -37,112 +30,121 @@ template = {
"MinLength": "1",
"MaxLength": "41",
"AllowedPattern": "[a-zA-Z0-9]+",
"ConstraintDescription": "must contain only alphanumeric characters."
"ConstraintDescription": "must contain only alphanumeric characters.",
},
"DBAllocatedStorage": {
"Default": "5",
"Description": "The size of the database (Gb)",
"Type": "Number",
"MinValue": "5",
"MaxValue": "1024",
"ConstraintDescription": "must be between 5 and 1024Gb."
"ConstraintDescription": "must be between 5 and 1024Gb.",
},
"DBInstanceClass": {
"Description": "The database instance type",
"Type": "String",
"Default": "db.m1.small",
"AllowedValues": ["db.t1.micro", "db.m1.small", "db.m1.medium", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.m3.medium", "db.m3.large", "db.m3.xlarge", "db.m3.2xlarge", "db.r3.large", "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge", "db.r3.8xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.cr1.8xlarge"],
"ConstraintDescription": "must select a valid database instance type."
"AllowedValues": [
"db.t1.micro",
"db.m1.small",
"db.m1.medium",
"db.m1.large",
"db.m1.xlarge",
"db.m2.xlarge",
"db.m2.2xlarge",
"db.m2.4xlarge",
"db.m3.medium",
"db.m3.large",
"db.m3.xlarge",
"db.m3.2xlarge",
"db.r3.large",
"db.r3.xlarge",
"db.r3.2xlarge",
"db.r3.4xlarge",
"db.r3.8xlarge",
"db.m2.xlarge",
"db.m2.2xlarge",
"db.m2.4xlarge",
"db.cr1.8xlarge",
],
"ConstraintDescription": "must select a valid database instance type.",
},
"EC2SecurityGroup": {
"Description": "The EC2 security group that contains instances that need access to the database",
"Default": "default",
"Type": "String",
"AllowedPattern": "[a-zA-Z0-9\\-]+",
"ConstraintDescription": "must be a valid security group name."
"ConstraintDescription": "must be a valid security group name.",
},
"MultiAZ": {
"Description": "Multi-AZ master database",
"Type": "String",
"Default": "false",
"AllowedValues": ["true", "false"],
"ConstraintDescription": "must be true or false."
}
"ConstraintDescription": "must be true or false.",
},
},
"Conditions": {
"Is-EC2-VPC": {"Fn::Or": [{"Fn::Equals": [{"Ref": "AWS::Region"}, "eu-central-1"]},
{"Fn::Equals": [{"Ref": "AWS::Region"}, "cn-north-1"]}]},
"Is-EC2-Classic": {"Fn::Not": [{"Condition": "Is-EC2-VPC"}]}
"Is-EC2-VPC": {
"Fn::Or": [
{"Fn::Equals": [{"Ref": "AWS::Region"}, "eu-central-1"]},
{"Fn::Equals": [{"Ref": "AWS::Region"}, "cn-north-1"]},
]
},
"Is-EC2-Classic": {"Fn::Not": [{"Condition": "Is-EC2-VPC"}]},
},
"Resources": {
"DBParameterGroup": {
"Type": "AWS::RDS::DBParameterGroup",
"Properties": {
"Description": "DB Parameter Goup",
"Family": "MySQL5.1",
"Parameters": {
"BACKLOG_QUEUE_LIMIT": "2048"
}
}
"Parameters": {"BACKLOG_QUEUE_LIMIT": "2048"},
},
},
"DBEC2SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Condition": "Is-EC2-VPC",
"Properties": {
"GroupDescription": "Open database for access",
"SecurityGroupIngress": [{
"IpProtocol": "tcp",
"FromPort": "3306",
"ToPort": "3306",
"SourceSecurityGroupName": {"Ref": "EC2SecurityGroup"}
}]
}
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "3306",
"ToPort": "3306",
"SourceSecurityGroupName": {"Ref": "EC2SecurityGroup"},
}
],
},
},
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Condition": "Is-EC2-Classic",
"Properties": {
"DBSecurityGroupIngress": [{
"EC2SecurityGroupName": {"Ref": "EC2SecurityGroup"}
}],
"GroupDescription": "database access"
}
"DBSecurityGroupIngress": [
{"EC2SecurityGroupName": {"Ref": "EC2SecurityGroup"}}
],
"GroupDescription": "database access",
},
},
"my_vpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
}
},
"my_vpc": {"Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}},
"EC2Subnet": {
"Type": "AWS::EC2::Subnet",
"Condition": "Is-EC2-VPC",
"Properties": {
"AvailabilityZone": "eu-central-1a",
"CidrBlock": "10.0.1.0/24",
"VpcId": {"Ref": "my_vpc"}
}
"VpcId": {"Ref": "my_vpc"},
},
},
"DBSubnet": {
"Type": "AWS::RDS::DBSubnetGroup",
"Condition": "Is-EC2-VPC",
"Properties": {
"DBSubnetGroupDescription": "my db subnet group",
"SubnetIds": [{"Ref": "EC2Subnet"}],
}
},
},
"MasterDB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
@ -151,54 +153,79 @@ template = {
"AllocatedStorage": {"Ref": "DBAllocatedStorage"},
"DBInstanceClass": {"Ref": "DBInstanceClass"},
"Engine": "MySQL",
"DBSubnetGroupName": {"Fn::If": ["Is-EC2-VPC", {"Ref": "DBSubnet"}, {"Ref": "AWS::NoValue"}]},
"DBSubnetGroupName": {
"Fn::If": [
"Is-EC2-VPC",
{"Ref": "DBSubnet"},
{"Ref": "AWS::NoValue"},
]
},
"MasterUsername": {"Ref": "DBUser"},
"MasterUserPassword": {"Ref": "DBPassword"},
"MultiAZ": {"Ref": "MultiAZ"},
"Tags": [{"Key": "Name", "Value": "Master Database"}],
"VPCSecurityGroups": {"Fn::If": ["Is-EC2-VPC", [{"Fn::GetAtt": ["DBEC2SecurityGroup", "GroupId"]}], {"Ref": "AWS::NoValue"}]},
"DBSecurityGroups": {"Fn::If": ["Is-EC2-Classic", [{"Ref": "DBSecurityGroup"}], {"Ref": "AWS::NoValue"}]}
"VPCSecurityGroups": {
"Fn::If": [
"Is-EC2-VPC",
[{"Fn::GetAtt": ["DBEC2SecurityGroup", "GroupId"]}],
{"Ref": "AWS::NoValue"},
]
},
"DBSecurityGroups": {
"Fn::If": [
"Is-EC2-Classic",
[{"Ref": "DBSecurityGroup"}],
{"Ref": "AWS::NoValue"},
]
},
},
"DeletionPolicy": "Snapshot"
"DeletionPolicy": "Snapshot",
},
"ReplicaDB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"SourceDBInstanceIdentifier": {"Ref": "MasterDB"},
"DBInstanceClass": {"Ref": "DBInstanceClass"},
"Tags": [{"Key": "Name", "Value": "Read Replica Database"}]
}
}
"Tags": [{"Key": "Name", "Value": "Read Replica Database"}],
},
},
},
"Outputs": {
"EC2Platform": {
"Description": "Platform in which this stack is deployed",
"Value": {"Fn::If": ["Is-EC2-VPC", "EC2-VPC", "EC2-Classic"]}
"Value": {"Fn::If": ["Is-EC2-VPC", "EC2-VPC", "EC2-Classic"]},
},
"MasterJDBCConnectionString": {
"Description": "JDBC connection string for the master database",
"Value": {"Fn::Join": ["", ["jdbc:mysql://",
{"Fn::GetAtt": [
"MasterDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": [
"MasterDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"}]]}
"Value": {
"Fn::Join": [
"",
[
"jdbc:mysql://",
{"Fn::GetAtt": ["MasterDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": ["MasterDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"},
],
]
},
},
"ReplicaJDBCConnectionString": {
"Description": "JDBC connection string for the replica database",
"Value": {"Fn::Join": ["", ["jdbc:mysql://",
{"Fn::GetAtt": [
"ReplicaDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": [
"ReplicaDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"}]]}
}
}
"Value": {
"Fn::Join": [
"",
[
"jdbc:mysql://",
{"Fn::GetAtt": ["ReplicaDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": ["ReplicaDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"},
],
]
},
},
},
}

View file

@ -2,9 +2,7 @@ from __future__ import unicode_literals
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Sample Template RDS_MySQL_With_Read_Replica: Sample template showing how to create a highly-available, RDS DBInstance with a read replica. **WARNING** This template creates an Amazon Relational Database Service database instance and Amazon CloudWatch alarms. You will be billed for the AWS resources used if you create a stack from this template.",
"Parameters": {
"DBName": {
"Default": "MyDatabase",
@ -13,13 +11,9 @@ template = {
"MinLength": "1",
"MaxLength": "64",
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters."
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.",
},
"DBInstanceIdentifier": {
"Type": "String"
},
"DBInstanceIdentifier": {"Type": "String"},
"DBUser": {
"NoEcho": "true",
"Description": "The database admin account username",
@ -27,9 +21,8 @@ template = {
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters."
"ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.",
},
"DBPassword": {
"NoEcho": "true",
"Description": "The database admin account password",
@ -37,101 +30,113 @@ template = {
"MinLength": "1",
"MaxLength": "41",
"AllowedPattern": "[a-zA-Z0-9]+",
"ConstraintDescription": "must contain only alphanumeric characters."
"ConstraintDescription": "must contain only alphanumeric characters.",
},
"DBAllocatedStorage": {
"Default": "5",
"Description": "The size of the database (Gb)",
"Type": "Number",
"MinValue": "5",
"MaxValue": "1024",
"ConstraintDescription": "must be between 5 and 1024Gb."
"ConstraintDescription": "must be between 5 and 1024Gb.",
},
"DBInstanceClass": {
"Description": "The database instance type",
"Type": "String",
"Default": "db.m1.small",
"AllowedValues": ["db.t1.micro", "db.m1.small", "db.m1.medium", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.m3.medium", "db.m3.large", "db.m3.xlarge", "db.m3.2xlarge", "db.r3.large", "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge", "db.r3.8xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.cr1.8xlarge"],
"ConstraintDescription": "must select a valid database instance type."
"AllowedValues": [
"db.t1.micro",
"db.m1.small",
"db.m1.medium",
"db.m1.large",
"db.m1.xlarge",
"db.m2.xlarge",
"db.m2.2xlarge",
"db.m2.4xlarge",
"db.m3.medium",
"db.m3.large",
"db.m3.xlarge",
"db.m3.2xlarge",
"db.r3.large",
"db.r3.xlarge",
"db.r3.2xlarge",
"db.r3.4xlarge",
"db.r3.8xlarge",
"db.m2.xlarge",
"db.m2.2xlarge",
"db.m2.4xlarge",
"db.cr1.8xlarge",
],
"ConstraintDescription": "must select a valid database instance type.",
},
"EC2SecurityGroup": {
"Description": "The EC2 security group that contains instances that need access to the database",
"Default": "default",
"Type": "String",
"AllowedPattern": "[a-zA-Z0-9\\-]+",
"ConstraintDescription": "must be a valid security group name."
"ConstraintDescription": "must be a valid security group name.",
},
"MultiAZ": {
"Description": "Multi-AZ master database",
"Type": "String",
"Default": "false",
"AllowedValues": ["true", "false"],
"ConstraintDescription": "must be true or false."
}
"ConstraintDescription": "must be true or false.",
},
},
"Conditions": {
"Is-EC2-VPC": {"Fn::Or": [{"Fn::Equals": [{"Ref": "AWS::Region"}, "eu-central-1"]},
{"Fn::Equals": [{"Ref": "AWS::Region"}, "cn-north-1"]}]},
"Is-EC2-Classic": {"Fn::Not": [{"Condition": "Is-EC2-VPC"}]}
"Is-EC2-VPC": {
"Fn::Or": [
{"Fn::Equals": [{"Ref": "AWS::Region"}, "eu-central-1"]},
{"Fn::Equals": [{"Ref": "AWS::Region"}, "cn-north-1"]},
]
},
"Is-EC2-Classic": {"Fn::Not": [{"Condition": "Is-EC2-VPC"}]},
},
"Resources": {
"DBEC2SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Condition": "Is-EC2-VPC",
"Properties": {
"GroupDescription": "Open database for access",
"SecurityGroupIngress": [{
"IpProtocol": "tcp",
"FromPort": "3306",
"ToPort": "3306",
"SourceSecurityGroupName": {"Ref": "EC2SecurityGroup"}
}]
}
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "3306",
"ToPort": "3306",
"SourceSecurityGroupName": {"Ref": "EC2SecurityGroup"},
}
],
},
},
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Condition": "Is-EC2-Classic",
"Properties": {
"DBSecurityGroupIngress": [{
"EC2SecurityGroupName": {"Ref": "EC2SecurityGroup"}
}],
"GroupDescription": "database access"
}
"DBSecurityGroupIngress": [
{"EC2SecurityGroupName": {"Ref": "EC2SecurityGroup"}}
],
"GroupDescription": "database access",
},
},
"my_vpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
}
},
"my_vpc": {"Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}},
"EC2Subnet": {
"Type": "AWS::EC2::Subnet",
"Condition": "Is-EC2-VPC",
"Properties": {
"AvailabilityZone": "eu-central-1a",
"CidrBlock": "10.0.1.0/24",
"VpcId": {"Ref": "my_vpc"}
}
"VpcId": {"Ref": "my_vpc"},
},
},
"DBSubnet": {
"Type": "AWS::RDS::DBSubnetGroup",
"Condition": "Is-EC2-VPC",
"Properties": {
"DBSubnetGroupDescription": "my db subnet group",
"SubnetIds": [{"Ref": "EC2Subnet"}],
}
},
},
"MasterDB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
@ -140,54 +145,79 @@ template = {
"AllocatedStorage": {"Ref": "DBAllocatedStorage"},
"DBInstanceClass": {"Ref": "DBInstanceClass"},
"Engine": "MySQL",
"DBSubnetGroupName": {"Fn::If": ["Is-EC2-VPC", {"Ref": "DBSubnet"}, {"Ref": "AWS::NoValue"}]},
"DBSubnetGroupName": {
"Fn::If": [
"Is-EC2-VPC",
{"Ref": "DBSubnet"},
{"Ref": "AWS::NoValue"},
]
},
"MasterUsername": {"Ref": "DBUser"},
"MasterUserPassword": {"Ref": "DBPassword"},
"MultiAZ": {"Ref": "MultiAZ"},
"Tags": [{"Key": "Name", "Value": "Master Database"}],
"VPCSecurityGroups": {"Fn::If": ["Is-EC2-VPC", [{"Fn::GetAtt": ["DBEC2SecurityGroup", "GroupId"]}], {"Ref": "AWS::NoValue"}]},
"DBSecurityGroups": {"Fn::If": ["Is-EC2-Classic", [{"Ref": "DBSecurityGroup"}], {"Ref": "AWS::NoValue"}]}
"VPCSecurityGroups": {
"Fn::If": [
"Is-EC2-VPC",
[{"Fn::GetAtt": ["DBEC2SecurityGroup", "GroupId"]}],
{"Ref": "AWS::NoValue"},
]
},
"DBSecurityGroups": {
"Fn::If": [
"Is-EC2-Classic",
[{"Ref": "DBSecurityGroup"}],
{"Ref": "AWS::NoValue"},
]
},
},
"DeletionPolicy": "Snapshot"
"DeletionPolicy": "Snapshot",
},
"ReplicaDB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"SourceDBInstanceIdentifier": {"Ref": "MasterDB"},
"DBInstanceClass": {"Ref": "DBInstanceClass"},
"Tags": [{"Key": "Name", "Value": "Read Replica Database"}]
}
}
"Tags": [{"Key": "Name", "Value": "Read Replica Database"}],
},
},
},
"Outputs": {
"EC2Platform": {
"Description": "Platform in which this stack is deployed",
"Value": {"Fn::If": ["Is-EC2-VPC", "EC2-VPC", "EC2-Classic"]}
"Value": {"Fn::If": ["Is-EC2-VPC", "EC2-VPC", "EC2-Classic"]},
},
"MasterJDBCConnectionString": {
"Description": "JDBC connection string for the master database",
"Value": {"Fn::Join": ["", ["jdbc:mysql://",
{"Fn::GetAtt": [
"MasterDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": [
"MasterDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"}]]}
"Value": {
"Fn::Join": [
"",
[
"jdbc:mysql://",
{"Fn::GetAtt": ["MasterDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": ["MasterDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"},
],
]
},
},
"ReplicaJDBCConnectionString": {
"Description": "JDBC connection string for the replica database",
"Value": {"Fn::Join": ["", ["jdbc:mysql://",
{"Fn::GetAtt": [
"ReplicaDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": [
"ReplicaDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"}]]}
}
}
"Value": {
"Fn::Join": [
"",
[
"jdbc:mysql://",
{"Fn::GetAtt": ["ReplicaDB", "Endpoint.Address"]},
":",
{"Fn::GetAtt": ["ReplicaDB", "Endpoint.Port"]},
"/",
{"Ref": "DBName"},
],
]
},
},
},
}

View file

@ -7,35 +7,35 @@ template = {
"Description": "The name of the first database to be created when the cluster is created",
"Type": "String",
"Default": "dev",
"AllowedPattern": "([a-z]|[0-9])+"
"AllowedPattern": "([a-z]|[0-9])+",
},
"ClusterType": {
"Description": "The type of cluster",
"Type": "String",
"Default": "single-node",
"AllowedValues": ["single-node", "multi-node"]
"AllowedValues": ["single-node", "multi-node"],
},
"NumberOfNodes": {
"Description": "The number of compute nodes in the cluster. For multi-node clusters, the NumberOfNodes parameter must be greater than 1",
"Type": "Number",
"Default": "1"
"Default": "1",
},
"NodeType": {
"Description": "The type of node to be provisioned",
"Type": "String",
"Default": "dw1.xlarge",
"AllowedValues": ["dw1.xlarge", "dw1.8xlarge", "dw2.large", "dw2.8xlarge"]
"AllowedValues": ["dw1.xlarge", "dw1.8xlarge", "dw2.large", "dw2.8xlarge"],
},
"MasterUsername": {
"Description": "The user name that is associated with the master user account for the cluster that is being created",
"Type": "String",
"Default": "defaultuser",
"AllowedPattern": "([a-z])([a-z]|[0-9])*"
"AllowedPattern": "([a-z])([a-z]|[0-9])*",
},
"MasterUserPassword": {
"MasterUserPassword": {
"Description": "The password that is associated with the master user account for the cluster that is being created.",
"Type": "String",
"NoEcho": "true"
"NoEcho": "true",
},
"InboundTraffic": {
"Description": "Allow inbound traffic to the cluster from this CIDR range.",
@ -44,18 +44,16 @@ template = {
"MaxLength": "18",
"Default": "0.0.0.0/0",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "must be a valid CIDR range of the form x.x.x.x/x."
"ConstraintDescription": "must be a valid CIDR range of the form x.x.x.x/x.",
},
"PortNumber": {
"Description": "The port number on which the cluster accepts incoming connections.",
"Type": "Number",
"Default": "5439"
}
"Default": "5439",
},
},
"Conditions": {
"IsMultiNodeCluster": {
"Fn::Equals": [{"Ref": "ClusterType"}, "multi-node"]
}
"IsMultiNodeCluster": {"Fn::Equals": [{"Ref": "ClusterType"}, "multi-node"]}
},
"Resources": {
"RedshiftCluster": {
@ -63,7 +61,13 @@ template = {
"DependsOn": "AttachGateway",
"Properties": {
"ClusterType": {"Ref": "ClusterType"},
"NumberOfNodes": {"Fn::If": ["IsMultiNodeCluster", {"Ref": "NumberOfNodes"}, {"Ref": "AWS::NoValue"}]},
"NumberOfNodes": {
"Fn::If": [
"IsMultiNodeCluster",
{"Ref": "NumberOfNodes"},
{"Ref": "AWS::NoValue"},
]
},
"NodeType": {"Ref": "NodeType"},
"DBName": {"Ref": "DatabaseName"},
"MasterUsername": {"Ref": "MasterUsername"},
@ -72,116 +76,106 @@ template = {
"VpcSecurityGroupIds": [{"Ref": "SecurityGroup"}],
"ClusterSubnetGroupName": {"Ref": "RedshiftClusterSubnetGroup"},
"PubliclyAccessible": "true",
"Port": {"Ref": "PortNumber"}
}
"Port": {"Ref": "PortNumber"},
},
},
"RedshiftClusterParameterGroup": {
"Type": "AWS::Redshift::ClusterParameterGroup",
"Properties": {
"Description": "Cluster parameter group",
"ParameterGroupFamily": "redshift-1.0",
"Parameters": [{
"ParameterName": "enable_user_activity_logging",
"ParameterValue": "true"
}]
}
"Parameters": [
{
"ParameterName": "enable_user_activity_logging",
"ParameterValue": "true",
}
],
},
},
"RedshiftClusterSubnetGroup": {
"Type": "AWS::Redshift::ClusterSubnetGroup",
"Properties": {
"Description": "Cluster subnet group",
"SubnetIds": [{"Ref": "PublicSubnet"}]
}
},
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16"
}
"SubnetIds": [{"Ref": "PublicSubnet"}],
},
},
"VPC": {"Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}},
"PublicSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "10.0.0.0/24",
"VpcId": {"Ref": "VPC"}
}
"Properties": {"CidrBlock": "10.0.0.0/24", "VpcId": {"Ref": "VPC"}},
},
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Security group",
"SecurityGroupIngress": [{
"CidrIp": {"Ref": "InboundTraffic"},
"FromPort": {"Ref": "PortNumber"},
"ToPort": {"Ref": "PortNumber"},
"IpProtocol": "tcp"
}],
"VpcId": {"Ref": "VPC"}
}
},
"myInternetGateway": {
"Type": "AWS::EC2::InternetGateway"
"SecurityGroupIngress": [
{
"CidrIp": {"Ref": "InboundTraffic"},
"FromPort": {"Ref": "PortNumber"},
"ToPort": {"Ref": "PortNumber"},
"IpProtocol": "tcp",
}
],
"VpcId": {"Ref": "VPC"},
},
},
"myInternetGateway": {"Type": "AWS::EC2::InternetGateway"},
"AttachGateway": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": {"Ref": "VPC"},
"InternetGatewayId": {"Ref": "myInternetGateway"}
}
"InternetGatewayId": {"Ref": "myInternetGateway"},
},
},
"PublicRouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "VPC"
}
}
"Properties": {"VpcId": {"Ref": "VPC"}},
},
"PublicRoute": {
"Type": "AWS::EC2::Route",
"DependsOn": "AttachGateway",
"Properties": {
"RouteTableId": {
"Ref": "PublicRouteTable"
},
"RouteTableId": {"Ref": "PublicRouteTable"},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "myInternetGateway"
}
}
"GatewayId": {"Ref": "myInternetGateway"},
},
},
"PublicSubnetRouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"SubnetId": {
"Ref": "PublicSubnet"
},
"RouteTableId": {
"Ref": "PublicRouteTable"
}
}
}
"SubnetId": {"Ref": "PublicSubnet"},
"RouteTableId": {"Ref": "PublicRouteTable"},
},
},
},
"Outputs": {
"ClusterEndpoint": {
"Description": "Cluster endpoint",
"Value": {"Fn::Join": [":", [{"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Address"]}, {"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Port"]}]]}
"Value": {
"Fn::Join": [
":",
[
{"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Address"]},
{"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Port"]},
],
]
},
},
"ClusterName": {
"Description": "Name of cluster",
"Value": {"Ref": "RedshiftCluster"}
"Value": {"Ref": "RedshiftCluster"},
},
"ParameterGroupName": {
"Description": "Name of parameter group",
"Value": {"Ref": "RedshiftClusterParameterGroup"}
"Value": {"Ref": "RedshiftClusterParameterGroup"},
},
"RedshiftClusterSubnetGroupName": {
"Description": "Name of cluster subnet group",
"Value": {"Ref": "RedshiftClusterSubnetGroup"}
"Value": {"Ref": "RedshiftClusterSubnetGroup"},
},
"RedshiftClusterSecurityGroupName": {
"Description": "Name of cluster security group",
"Value": {"Ref": "SecurityGroup"}
}
}
"Value": {"Ref": "SecurityGroup"},
},
},
}

View file

@ -1,47 +1,38 @@
from __future__ import unicode_literals
template = {
"Parameters": {
"R53ZoneName": {
"Type": "String",
"Default": "my_zone"
}
},
"Parameters": {"R53ZoneName": {"Type": "String", "Default": "my_zone"}},
"Resources": {
"Ec2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-1234abcd",
"PrivateIpAddress": "10.0.0.25",
}
"Properties": {"ImageId": "ami-1234abcd", "PrivateIpAddress": "10.0.0.25"},
},
"HostedZone": {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"Name": {"Ref": "R53ZoneName"}
}
"Properties": {"Name": {"Ref": "R53ZoneName"}},
},
"myDNSRecord": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
"HostedZoneId": {"Ref": "HostedZone"},
"Comment": "DNS name for my instance.",
"Name": {
"Fn::Join": ["", [
{"Ref": "Ec2Instance"}, ".",
{"Ref": "AWS::Region"}, ".",
{"Ref": "R53ZoneName"}, "."
]]
"Fn::Join": [
"",
[
{"Ref": "Ec2Instance"},
".",
{"Ref": "AWS::Region"},
".",
{"Ref": "R53ZoneName"},
".",
],
]
},
"Type": "A",
"TTL": "900",
"ResourceRecords": [
{"Fn::GetAtt": ["Ec2Instance", "PrivateIp"]}
]
}
}
"ResourceRecords": [{"Fn::GetAtt": ["Ec2Instance", "PrivateIp"]}],
},
},
},
}

View file

@ -4,11 +4,8 @@ template = {
"Resources": {
"HostedZone": {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"Name": "my_zone"
}
"Properties": {"Name": "my_zone"},
},
"my_health_check": {
"Type": "AWS::Route53::HealthCheck",
"Properties": {
@ -20,9 +17,8 @@ template = {
"ResourcePath": "/",
"Type": "HTTP",
}
}
},
},
"myDNSRecord": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
@ -33,7 +29,7 @@ template = {
"TTL": "900",
"ResourceRecords": ["my.example.com"],
"HealthCheckId": {"Ref": "my_health_check"},
}
}
},
},
},
}
}

View file

@ -2,53 +2,71 @@ from __future__ import unicode_literals
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Sample Template Route53_RoundRobin: Sample template showing how to use weighted round robin (WRR) DNS entried via Amazon Route 53. This contrived sample uses weighted CNAME records to illustrate that the weighting influences the return records. It assumes that you already have a Hosted Zone registered with Amazon Route 53. **WARNING** This template creates one or more AWS resources. You will be billed for the AWS resources used if you create a stack from this template.",
"Parameters": {
"R53ZoneName": {
"Type": "String",
"Default": "my_zone"
}
},
"Parameters": {"R53ZoneName": {"Type": "String", "Default": "my_zone"}},
"Resources": {
"MyZone": {
"Type": "AWS::Route53::HostedZone",
"Properties": {
"Name": {"Ref": "R53ZoneName"}
}
"Properties": {"Name": {"Ref": "R53ZoneName"}},
},
"MyDNSRecord": {
"Type": "AWS::Route53::RecordSetGroup",
"Properties": {
"HostedZoneId": {"Ref": "MyZone"},
"Comment": "Contrived example to redirect to aws.amazon.com 75% of the time and www.amazon.com 25% of the time.",
"RecordSets": [{
"SetIdentifier": {"Fn::Join": [" ", [{"Ref": "AWS::StackName"}, "AWS"]]},
"Name": {"Fn::Join": ["", [{"Ref": "AWS::StackName"}, ".", {"Ref": "AWS::Region"}, ".", {"Ref": "R53ZoneName"}, "."]]},
"Type": "CNAME",
"TTL": "900",
"ResourceRecords": ["aws.amazon.com"],
"Weight": "3"
}, {
"SetIdentifier": {"Fn::Join": [" ", [{"Ref": "AWS::StackName"}, "Amazon"]]},
"Name": {"Fn::Join": ["", [{"Ref": "AWS::StackName"}, ".", {"Ref": "AWS::Region"}, ".", {"Ref": "R53ZoneName"}, "."]]},
"Type": "CNAME",
"TTL": "900",
"ResourceRecords": ["www.amazon.com"],
"Weight": "1"
}]
}
}
"RecordSets": [
{
"SetIdentifier": {
"Fn::Join": [" ", [{"Ref": "AWS::StackName"}, "AWS"]]
},
"Name": {
"Fn::Join": [
"",
[
{"Ref": "AWS::StackName"},
".",
{"Ref": "AWS::Region"},
".",
{"Ref": "R53ZoneName"},
".",
],
]
},
"Type": "CNAME",
"TTL": "900",
"ResourceRecords": ["aws.amazon.com"],
"Weight": "3",
},
{
"SetIdentifier": {
"Fn::Join": [" ", [{"Ref": "AWS::StackName"}, "Amazon"]]
},
"Name": {
"Fn::Join": [
"",
[
{"Ref": "AWS::StackName"},
".",
{"Ref": "AWS::Region"},
".",
{"Ref": "R53ZoneName"},
".",
],
]
},
"Type": "CNAME",
"TTL": "900",
"ResourceRecords": ["www.amazon.com"],
"Weight": "1",
},
],
},
},
},
"Outputs": {
"DomainName": {
"Description": "Fully qualified domain name",
"Value": {"Ref": "MyDNSRecord"}
"Value": {"Ref": "MyDNSRecord"},
}
}
},
}

View file

@ -10,7 +10,7 @@ template = {
"MinLength": "9",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"MaxLength": "18",
"Type": "String"
"Type": "String",
},
"KeyName": {
"Type": "String",
@ -18,7 +18,7 @@ template = {
"MinLength": "1",
"AllowedPattern": "[\\x20-\\x7E]*",
"MaxLength": "255",
"ConstraintDescription": "can contain only ASCII characters."
"ConstraintDescription": "can contain only ASCII characters.",
},
"InstanceType": {
"Default": "m1.small",
@ -40,8 +40,8 @@ template = {
"c1.xlarge",
"cc1.4xlarge",
"cc2.8xlarge",
"cg1.4xlarge"
]
"cg1.4xlarge",
],
},
"VolumeSize": {
"Description": "WebServer EC2 instance type",
@ -49,8 +49,8 @@ template = {
"Type": "Number",
"MaxValue": "1024",
"MinValue": "5",
"ConstraintDescription": "must be between 5 and 1024 Gb."
}
"ConstraintDescription": "must be between 5 and 1024 Gb.",
},
},
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
@ -59,17 +59,9 @@ template = {
"Value": {
"Fn::Join": [
"",
[
"http://",
{
"Fn::GetAtt": [
"WebServer",
"PublicDnsName"
]
}
]
["http://", {"Fn::GetAtt": ["WebServer", "PublicDnsName"]}],
]
}
},
}
},
"Resources": {
@ -81,19 +73,17 @@ template = {
"ToPort": "80",
"IpProtocol": "tcp",
"CidrIp": "0.0.0.0/0",
"FromPort": "80"
"FromPort": "80",
},
{
"ToPort": "22",
"IpProtocol": "tcp",
"CidrIp": {
"Ref": "SSHLocation"
},
"FromPort": "22"
}
"CidrIp": {"Ref": "SSHLocation"},
"FromPort": "22",
},
],
"GroupDescription": "Enable SSH access and HTTP access on the inbound port"
}
"GroupDescription": "Enable SSH access and HTTP access on the inbound port",
},
},
"WebServer": {
"Type": "AWS::EC2::Instance",
@ -108,23 +98,17 @@ template = {
"# Helper function\n",
"function error_exit\n",
"{\n",
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
{
"Ref": "WaitHandle"
},
' /opt/aws/bin/cfn-signal -e 1 -r "$1" \'',
{"Ref": "WaitHandle"},
"'\n",
" exit 1\n",
"}\n",
"# Install Rails packages\n",
"/opt/aws/bin/cfn-init -s ",
{
"Ref": "AWS::StackId"
},
{"Ref": "AWS::StackId"},
" -r WebServer ",
" --region ",
{
"Ref": "AWS::Region"
},
{"Ref": "AWS::Region"},
" || error_exit 'Failed to run cfn-init'\n",
"# Wait for the EBS volume to show up\n",
"while [ ! -e /dev/sdh ]; do echo Waiting for EBS volume to attach; sleep 5; done\n",
@ -137,56 +121,38 @@ template = {
"git init\n",
"gollum --port 80 --host 0.0.0.0 &\n",
"# If all is well so signal success\n",
"/opt/aws/bin/cfn-signal -e $? -r \"Rails application setup complete\" '",
{
"Ref": "WaitHandle"
},
"'\n"
]
'/opt/aws/bin/cfn-signal -e $? -r "Rails application setup complete" \'',
{"Ref": "WaitHandle"},
"'\n",
],
]
}
},
"KeyName": {
"Ref": "KeyName"
},
"SecurityGroups": [
{
"Ref": "WebServerSecurityGroup"
}
],
"InstanceType": {
"Ref": "InstanceType"
},
"KeyName": {"Ref": "KeyName"},
"SecurityGroups": [{"Ref": "WebServerSecurityGroup"}],
"InstanceType": {"Ref": "InstanceType"},
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
{"Ref": "AWS::Region"},
{
"Fn::FindInMap": [
"AWSInstanceType2Arch",
{
"Ref": "InstanceType"
},
"Arch"
{"Ref": "InstanceType"},
"Arch",
]
}
},
]
}
},
},
"Metadata": {
"AWS::CloudFormation::Init": {
"config": {
"packages": {
"rubygems": {
"nokogiri": [
"1.5.10"
],
"nokogiri": ["1.5.10"],
"rdiscount": [],
"gollum": [
"1.1.1"
]
"gollum": ["1.1.1"],
},
"yum": {
"libxslt-devel": [],
@ -196,150 +162,99 @@ template = {
"ruby-devel": [],
"ruby-rdoc": [],
"make": [],
"libxml2-devel": []
}
"libxml2-devel": [],
},
}
}
}
}
},
},
"DataVolume": {
"Type": "AWS::EC2::Volume",
"Properties": {
"Tags": [
{
"Value": "Gollum Data Volume",
"Key": "Usage"
}
],
"AvailabilityZone": {
"Fn::GetAtt": [
"WebServer",
"AvailabilityZone"
]
},
"Tags": [{"Value": "Gollum Data Volume", "Key": "Usage"}],
"AvailabilityZone": {"Fn::GetAtt": ["WebServer", "AvailabilityZone"]},
"Size": "100",
}
},
},
"MountPoint": {
"Type": "AWS::EC2::VolumeAttachment",
"Properties": {
"InstanceId": {
"Ref": "WebServer"
},
"InstanceId": {"Ref": "WebServer"},
"Device": "/dev/sdh",
"VolumeId": {
"Ref": "DataVolume"
}
}
"VolumeId": {"Ref": "DataVolume"},
},
},
"WaitCondition": {
"DependsOn": "MountPoint",
"Type": "AWS::CloudFormation::WaitCondition",
"Properties": {
"Handle": {
"Ref": "WaitHandle"
},
"Timeout": "300"
},
"Properties": {"Handle": {"Ref": "WaitHandle"}, "Timeout": "300"},
"Metadata": {
"Comment1": "Note that the WaitCondition is dependent on the volume mount point allowing the volume to be created and attached to the EC2 instance",
"Comment2": "The instance bootstrap script waits for the volume to be attached to the instance prior to installing Gollum and signalling completion"
}
"Comment2": "The instance bootstrap script waits for the volume to be attached to the instance prior to installing Gollum and signalling completion",
},
},
"WaitHandle": {
"Type": "AWS::CloudFormation::WaitConditionHandle"
}
"WaitHandle": {"Type": "AWS::CloudFormation::WaitConditionHandle"},
},
"Mappings": {
"AWSInstanceType2Arch": {
"m3.2xlarge": {
"Arch": "64"
},
"m2.2xlarge": {
"Arch": "64"
},
"m1.small": {
"Arch": "64"
},
"c1.medium": {
"Arch": "64"
},
"cg1.4xlarge": {
"Arch": "64HVM"
},
"m2.xlarge": {
"Arch": "64"
},
"t1.micro": {
"Arch": "64"
},
"cc1.4xlarge": {
"Arch": "64HVM"
},
"m1.medium": {
"Arch": "64"
},
"cc2.8xlarge": {
"Arch": "64HVM"
},
"m1.large": {
"Arch": "64"
},
"m1.xlarge": {
"Arch": "64"
},
"m2.4xlarge": {
"Arch": "64"
},
"c1.xlarge": {
"Arch": "64"
},
"m3.xlarge": {
"Arch": "64"
}
"m3.2xlarge": {"Arch": "64"},
"m2.2xlarge": {"Arch": "64"},
"m1.small": {"Arch": "64"},
"c1.medium": {"Arch": "64"},
"cg1.4xlarge": {"Arch": "64HVM"},
"m2.xlarge": {"Arch": "64"},
"t1.micro": {"Arch": "64"},
"cc1.4xlarge": {"Arch": "64HVM"},
"m1.medium": {"Arch": "64"},
"cc2.8xlarge": {"Arch": "64HVM"},
"m1.large": {"Arch": "64"},
"m1.xlarge": {"Arch": "64"},
"m2.4xlarge": {"Arch": "64"},
"c1.xlarge": {"Arch": "64"},
"m3.xlarge": {"Arch": "64"},
},
"AWSRegionArch2AMI": {
"ap-southeast-1": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-b4b0cae6",
"64": "ami-beb0caec"
"64": "ami-beb0caec",
},
"ap-southeast-2": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-b3990e89",
"64": "ami-bd990e87"
"64": "ami-bd990e87",
},
"us-west-2": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-38fe7308",
"64": "ami-30fe7300"
"64": "ami-30fe7300",
},
"us-east-1": {
"64HVM": "ami-0da96764",
"32": "ami-31814f58",
"64": "ami-1b814f72"
"64": "ami-1b814f72",
},
"ap-northeast-1": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-0644f007",
"64": "ami-0a44f00b"
"64": "ami-0a44f00b",
},
"us-west-1": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-11d68a54",
"64": "ami-1bd68a5e"
"64": "ami-1bd68a5e",
},
"eu-west-1": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-973b06e3",
"64": "ami-953b06e1"
"64": "ami-953b06e1",
},
"sa-east-1": {
"64HVM": "NOT_YET_SUPPORTED",
"32": "ami-3e3be423",
"64": "ami-3c3be421"
}
}
}
"64": "ami-3c3be421",
},
},
},
}

View file

@ -1,12 +1,5 @@
from __future__ import unicode_literals
template = {
"Resources": {
"VPCEIP": {
"Type": "AWS::EC2::EIP",
"Properties": {
"Domain": "vpc"
}
}
}
"Resources": {"VPCEIP": {"Type": "AWS::EC2::EIP", "Properties": {"Domain": "vpc"}}}
}

View file

@ -6,33 +6,26 @@ template = {
"Resources": {
"ENI": {
"Type": "AWS::EC2::NetworkInterface",
"Properties": {
"SubnetId": {"Ref": "Subnet"}
}
"Properties": {"SubnetId": {"Ref": "Subnet"}},
},
"Subnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"AvailabilityZone": "us-east-1a",
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.0.0/24"
}
"CidrBlock": "10.0.0.0/24",
},
},
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16"
}
}
"VPC": {"Type": "AWS::EC2::VPC", "Properties": {"CidrBlock": "10.0.0.0/16"}},
},
"Outputs": {
"NinjaENI": {
"Description": "Elastic IP mapping to Auto-Scaling Group",
"Value": {"Ref": "ENI"}
"Value": {"Ref": "ENI"},
},
"ENIIpAddress": {
"Description": "ENI's Private IP address",
"Value": {"Fn::GetAtt": ["ENI", "PrimaryPrivateIpAddress"]}
}
}
"Value": {"Fn::GetAtt": ["ENI", "PrimaryPrivateIpAddress"]},
},
},
}

View file

@ -10,7 +10,7 @@ template = {
"MinLength": "9",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"MaxLength": "18",
"Type": "String"
"Type": "String",
},
"KeyName": {
"Type": "String",
@ -18,7 +18,7 @@ template = {
"MinLength": "1",
"AllowedPattern": "[\\x20-\\x7E]*",
"MaxLength": "255",
"ConstraintDescription": "can contain only ASCII characters."
"ConstraintDescription": "can contain only ASCII characters.",
},
"InstanceType": {
"Default": "m1.small",
@ -40,9 +40,9 @@ template = {
"c1.xlarge",
"cc1.4xlarge",
"cc2.8xlarge",
"cg1.4xlarge"
]
}
"cg1.4xlarge",
],
},
},
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
@ -51,116 +51,61 @@ template = {
"Value": {
"Fn::Join": [
"",
[
"http://",
{
"Fn::GetAtt": [
"WebServerInstance",
"PublicIp"
]
}
]
["http://", {"Fn::GetAtt": ["WebServerInstance", "PublicIp"]}],
]
}
},
}
},
"Resources": {
"Subnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "VPC"
},
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.0.0/24",
"Tags": [
{
"Value": {
"Ref": "AWS::StackId"
},
"Key": "Application"
}
]
}
},
"WebServerWaitHandle": {
"Type": "AWS::CloudFormation::WaitConditionHandle"
"Tags": [{"Value": {"Ref": "AWS::StackId"}, "Key": "Application"}],
},
},
"WebServerWaitHandle": {"Type": "AWS::CloudFormation::WaitConditionHandle"},
"Route": {
"Type": "AWS::EC2::Route",
"Properties": {
"GatewayId": {
"Ref": "InternetGateway"
},
"GatewayId": {"Ref": "InternetGateway"},
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": {
"Ref": "RouteTable"
}
"RouteTableId": {"Ref": "RouteTable"},
},
"DependsOn": "AttachGateway"
"DependsOn": "AttachGateway",
},
"SubnetRouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"SubnetId": {
"Ref": "Subnet"
},
"RouteTableId": {
"Ref": "RouteTable"
}
}
"SubnetId": {"Ref": "Subnet"},
"RouteTableId": {"Ref": "RouteTable"},
},
},
"InternetGateway": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {
"Tags": [
{
"Value": {
"Ref": "AWS::StackId"
},
"Key": "Application"
}
]
}
"Tags": [{"Value": {"Ref": "AWS::StackId"}, "Key": "Application"}]
},
},
"RouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "VPC"
},
"Tags": [
{
"Value": {
"Ref": "AWS::StackId"
},
"Key": "Application"
}
]
}
"VpcId": {"Ref": "VPC"},
"Tags": [{"Value": {"Ref": "AWS::StackId"}, "Key": "Application"}],
},
},
"WebServerWaitCondition": {
"Type": "AWS::CloudFormation::WaitCondition",
"Properties": {
"Handle": {
"Ref": "WebServerWaitHandle"
},
"Timeout": "300"
},
"DependsOn": "WebServerInstance"
"Properties": {"Handle": {"Ref": "WebServerWaitHandle"}, "Timeout": "300"},
"DependsOn": "WebServerInstance",
},
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"Tags": [
{
"Value": {
"Ref": "AWS::StackId"
},
"Key": "Application"
}
]
}
"Tags": [{"Value": {"Ref": "AWS::StackId"}, "Key": "Application"}],
},
},
"InstanceSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
@ -169,23 +114,19 @@ template = {
{
"ToPort": "22",
"IpProtocol": "tcp",
"CidrIp": {
"Ref": "SSHLocation"
},
"FromPort": "22"
"CidrIp": {"Ref": "SSHLocation"},
"FromPort": "22",
},
{
"ToPort": "80",
"IpProtocol": "tcp",
"CidrIp": "0.0.0.0/0",
"FromPort": "80"
}
"FromPort": "80",
},
],
"VpcId": {
"Ref": "VPC"
},
"GroupDescription": "Enable SSH access via port 22"
}
"VpcId": {"Ref": "VPC"},
"GroupDescription": "Enable SSH access via port 22",
},
},
"WebServerInstance": {
"Type": "AWS::EC2::Instance",
@ -200,71 +141,39 @@ template = {
"# Helper function\n",
"function error_exit\n",
"{\n",
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
{
"Ref": "WebServerWaitHandle"
},
' /opt/aws/bin/cfn-signal -e 1 -r "$1" \'',
{"Ref": "WebServerWaitHandle"},
"'\n",
" exit 1\n",
"}\n",
"# Install the simple web page\n",
"/opt/aws/bin/cfn-init -s ",
{
"Ref": "AWS::StackId"
},
{"Ref": "AWS::StackId"},
" -r WebServerInstance ",
" --region ",
{
"Ref": "AWS::Region"
},
{"Ref": "AWS::Region"},
" || error_exit 'Failed to run cfn-init'\n",
"# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
"/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",
"# All done so signal success\n",
"/opt/aws/bin/cfn-signal -e 0 -r \"WebServer setup complete\" '",
{
"Ref": "WebServerWaitHandle"
},
"'\n"
]
'/opt/aws/bin/cfn-signal -e 0 -r "WebServer setup complete" \'',
{"Ref": "WebServerWaitHandle"},
"'\n",
],
]
}
},
"Tags": [
{
"Value": {
"Ref": "AWS::StackId"
},
"Key": "Application"
},
{
"Value": "Bar",
"Key": "Foo"
}
{"Value": {"Ref": "AWS::StackId"}, "Key": "Application"},
{"Value": "Bar", "Key": "Foo"},
],
"SecurityGroupIds": [
{
"Ref": "InstanceSecurityGroup"
}
],
"KeyName": {
"Ref": "KeyName"
},
"SubnetId": {
"Ref": "Subnet"
},
"SecurityGroupIds": [{"Ref": "InstanceSecurityGroup"}],
"KeyName": {"Ref": "KeyName"},
"SubnetId": {"Ref": "Subnet"},
"ImageId": {
"Fn::FindInMap": [
"RegionMap",
{
"Ref": "AWS::Region"
},
"AMI"
]
"Fn::FindInMap": ["RegionMap", {"Ref": "AWS::Region"}, "AMI"]
},
"InstanceType": {
"Ref": "InstanceType"
}
"InstanceType": {"Ref": "InstanceType"},
},
"Metadata": {
"Comment": "Install a simple PHP application",
@ -278,21 +187,17 @@ template = {
[
"[main]\n",
"stack=",
{
"Ref": "AWS::StackId"
},
{"Ref": "AWS::StackId"},
"\n",
"region=",
{
"Ref": "AWS::Region"
},
"\n"
]
{"Ref": "AWS::Region"},
"\n",
],
]
},
"owner": "root",
"group": "root",
"mode": "000400"
"mode": "000400",
},
"/etc/cfn/hooks.d/cfn-auto-reloader.conf": {
"content": {
@ -303,17 +208,13 @@ template = {
"triggers=post.update\n",
"path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
"action=/opt/aws/bin/cfn-init -s ",
{
"Ref": "AWS::StackId"
},
{"Ref": "AWS::StackId"},
" -r WebServerInstance ",
" --region ",
{
"Ref": "AWS::Region"
},
{"Ref": "AWS::Region"},
"\n",
"runas=root\n"
]
"runas=root\n",
],
]
}
},
@ -324,85 +225,52 @@ template = {
[
"<?php\n",
"echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
"?>\n"
]
"?>\n",
],
]
},
"owner": "apache",
"group": "apache",
"mode": "000644"
}
"mode": "000644",
},
},
"services": {
"sysvinit": {
"httpd": {
"ensureRunning": "true",
"enabled": "true"
},
"httpd": {"ensureRunning": "true", "enabled": "true"},
"sendmail": {
"ensureRunning": "false",
"enabled": "false"
}
"enabled": "false",
},
}
},
"packages": {
"yum": {
"httpd": [],
"php": []
}
}
"packages": {"yum": {"httpd": [], "php": []}},
}
}
}
},
},
},
"IPAddress": {
"Type": "AWS::EC2::EIP",
"Properties": {
"InstanceId": {
"Ref": "WebServerInstance"
},
"Domain": "vpc"
},
"DependsOn": "AttachGateway"
"Properties": {"InstanceId": {"Ref": "WebServerInstance"}, "Domain": "vpc"},
"DependsOn": "AttachGateway",
},
"AttachGateway": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": {
"Ref": "VPC"
},
"InternetGatewayId": {
"Ref": "InternetGateway"
}
}
}
"VpcId": {"Ref": "VPC"},
"InternetGatewayId": {"Ref": "InternetGateway"},
},
},
},
"Mappings": {
"RegionMap": {
"ap-southeast-1": {
"AMI": "ami-74dda626"
},
"ap-southeast-2": {
"AMI": "ami-b3990e89"
},
"us-west-2": {
"AMI": "ami-16fd7026"
},
"us-east-1": {
"AMI": "ami-7f418316"
},
"ap-northeast-1": {
"AMI": "ami-dcfa4edd"
},
"us-west-1": {
"AMI": "ami-951945d0"
},
"eu-west-1": {
"AMI": "ami-24506250"
},
"sa-east-1": {
"AMI": "ami-3e3be423"
}
"ap-southeast-1": {"AMI": "ami-74dda626"},
"ap-southeast-2": {"AMI": "ami-b3990e89"},
"us-west-2": {"AMI": "ami-16fd7026"},
"us-east-1": {"AMI": "ami-7f418316"},
"ap-northeast-1": {"AMI": "ami-dcfa4edd"},
"us-west-1": {"AMI": "ami-951945d0"},
"eu-west-1": {"AMI": "ami-24506250"},
"sa-east-1": {"AMI": "ami-3e3be423"},
}
}
},
}

View file

@ -4,16 +4,24 @@ import os
import json
import boto
import boto.iam
import boto.s3
import boto.s3.key
import boto.cloudformation
from boto.exception import BotoServerError
import sure # noqa
# Ensure 'assert_raises' context manager support for Python 2.6
import tests.backport_assert_raises # noqa
from nose.tools import assert_raises
from moto.core import ACCOUNT_ID
from moto import mock_cloudformation_deprecated, mock_s3_deprecated, mock_route53_deprecated
from moto import (
mock_cloudformation_deprecated,
mock_s3_deprecated,
mock_route53_deprecated,
mock_iam_deprecated,
)
from moto.cloudformation import cloudformation_backends
dummy_template = {
@ -33,12 +41,7 @@ dummy_template3 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 3",
"Resources": {
"VPC": {
"Properties": {
"CidrBlock": "192.168.0.0/16",
},
"Type": "AWS::EC2::VPC"
}
"VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"}
},
}
@ -50,24 +53,22 @@ dummy_template_json3 = json.dumps(dummy_template3)
@mock_cloudformation_deprecated
def test_create_stack():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
stack = conn.describe_stacks()[0]
stack.stack_name.should.equal('test_stack')
stack.get_template().should.equal({
'GetTemplateResponse': {
'GetTemplateResult': {
'TemplateBody': dummy_template_json,
'ResponseMetadata': {
'RequestId': '2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE'
stack.stack_name.should.equal("test_stack")
stack.get_template().should.equal(
{
"GetTemplateResponse": {
"GetTemplateResult": {
"TemplateBody": dummy_template_json,
"ResponseMetadata": {
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
},
}
}
}
})
)
@mock_cloudformation_deprecated
@ -77,44 +78,34 @@ def test_create_stack_hosted_zone_by_id():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 1",
"Parameters": {
},
"Parameters": {},
"Resources": {
"Bar": {
"Type" : "AWS::Route53::HostedZone",
"Properties" : {
"Name" : "foo.bar.baz",
}
},
"Type": "AWS::Route53::HostedZone",
"Properties": {"Name": "foo.bar.baz"},
}
},
}
dummy_template2 = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 2",
"Parameters": {
"ZoneId": { "Type": "String" }
},
"Parameters": {"ZoneId": {"Type": "String"}},
"Resources": {
"Foo": {
"Properties": {
"HostedZoneId": {"Ref": "ZoneId"},
"RecordSets": []
},
"Type": "AWS::Route53::RecordSetGroup"
"Properties": {"HostedZoneId": {"Ref": "ZoneId"}, "RecordSets": []},
"Type": "AWS::Route53::RecordSetGroup",
}
},
}
conn.create_stack(
"test_stack",
template_body=json.dumps(dummy_template),
parameters={}.items()
"test_stack", template_body=json.dumps(dummy_template), parameters={}.items()
)
r53_conn = boto.connect_route53()
zone_id = r53_conn.get_zones()[0].id
conn.create_stack(
"test_stack",
template_body=json.dumps(dummy_template2),
parameters={"ZoneId": zone_id}.items()
parameters={"ZoneId": zone_id}.items(),
)
stack = conn.describe_stacks()[0]
@ -139,62 +130,57 @@ def test_create_stack_with_notification_arn():
conn.create_stack(
"test_stack_with_notifications",
template_body=dummy_template_json,
notification_arns='arn:aws:sns:us-east-1:123456789012:fake-queue'
notification_arns="arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID),
)
stack = conn.describe_stacks()[0]
[n.value for n in stack.notification_arns].should.contain(
'arn:aws:sns:us-east-1:123456789012:fake-queue')
"arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)
)
@mock_cloudformation_deprecated
@mock_s3_deprecated
def test_create_stack_from_s3_url():
s3_conn = boto.s3.connect_to_region('us-west-1')
s3_conn = boto.s3.connect_to_region("us-west-1")
bucket = s3_conn.create_bucket("foobar")
key = boto.s3.key.Key(bucket)
key.key = "template-key"
key.set_contents_from_string(dummy_template_json)
key_url = key.generate_url(expires_in=0, query_auth=False)
conn = boto.cloudformation.connect_to_region('us-west-1')
conn.create_stack('new-stack', template_url=key_url)
conn = boto.cloudformation.connect_to_region("us-west-1")
conn.create_stack("new-stack", template_url=key_url)
stack = conn.describe_stacks()[0]
stack.stack_name.should.equal('new-stack')
stack.stack_name.should.equal("new-stack")
stack.get_template().should.equal(
{
'GetTemplateResponse': {
'GetTemplateResult': {
'TemplateBody': dummy_template_json,
'ResponseMetadata': {
'RequestId': '2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE'
}
"GetTemplateResponse": {
"GetTemplateResult": {
"TemplateBody": dummy_template_json,
"ResponseMetadata": {
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
},
}
}
})
}
)
@mock_cloudformation_deprecated
def test_describe_stack_by_name():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
stack = conn.describe_stacks("test_stack")[0]
stack.stack_name.should.equal('test_stack')
stack.stack_name.should.equal("test_stack")
@mock_cloudformation_deprecated
def test_describe_stack_by_stack_id():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
stack = conn.describe_stacks("test_stack")[0]
stack_by_id = conn.describe_stacks(stack.stack_id)[0]
@ -205,10 +191,7 @@ def test_describe_stack_by_stack_id():
@mock_cloudformation_deprecated
def test_describe_deleted_stack():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
stack = conn.describe_stacks("test_stack")[0]
stack_id = stack.stack_id
@ -222,36 +205,28 @@ def test_describe_deleted_stack():
@mock_cloudformation_deprecated
def test_get_template_by_name():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
template = conn.get_template("test_stack")
template.should.equal({
'GetTemplateResponse': {
'GetTemplateResult': {
'TemplateBody': dummy_template_json,
'ResponseMetadata': {
'RequestId': '2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE'
template.should.equal(
{
"GetTemplateResponse": {
"GetTemplateResult": {
"TemplateBody": dummy_template_json,
"ResponseMetadata": {
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
},
}
}
}
})
)
@mock_cloudformation_deprecated
def test_list_stacks():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack(
"test_stack2",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
conn.create_stack("test_stack2", template_body=dummy_template_json)
stacks = conn.list_stacks()
stacks.should.have.length_of(2)
@ -261,10 +236,7 @@ def test_list_stacks():
@mock_cloudformation_deprecated
def test_delete_stack_by_name():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
conn.describe_stacks().should.have.length_of(1)
conn.delete_stack("test_stack")
@ -274,10 +246,7 @@ def test_delete_stack_by_name():
@mock_cloudformation_deprecated
def test_delete_stack_by_id():
conn = boto.connect_cloudformation()
stack_id = conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
stack_id = conn.create_stack("test_stack", template_body=dummy_template_json)
conn.describe_stacks().should.have.length_of(1)
conn.delete_stack(stack_id)
@ -291,10 +260,7 @@ def test_delete_stack_by_id():
@mock_cloudformation_deprecated
def test_delete_stack_with_resource_missing_delete_attr():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json3,
)
conn.create_stack("test_stack", template_body=dummy_template_json3)
conn.describe_stacks().should.have.length_of(1)
conn.delete_stack("test_stack")
@ -318,19 +284,22 @@ def test_cloudformation_params():
"APPNAME": {
"Default": "app-name",
"Description": "The name of the app",
"Type": "String"
"Type": "String",
}
}
},
}
dummy_template_json = json.dumps(dummy_template)
cfn = boto.connect_cloudformation()
cfn.create_stack('test_stack1', template_body=dummy_template_json, parameters=[
('APPNAME', 'testing123')])
stack = cfn.describe_stacks('test_stack1')[0]
cfn.create_stack(
"test_stack1",
template_body=dummy_template_json,
parameters=[("APPNAME", "testing123")],
)
stack = cfn.describe_stacks("test_stack1")[0]
stack.parameters.should.have.length_of(1)
param = stack.parameters[0]
param.key.should.equal('APPNAME')
param.value.should.equal('testing123')
param.key.should.equal("APPNAME")
param.value.should.equal("testing123")
@mock_cloudformation_deprecated
@ -339,52 +308,34 @@ def test_cloudformation_params_conditions_and_resources_are_distinct():
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 1",
"Conditions": {
"FooEnabled": {
"Fn::Equals": [
{
"Ref": "FooEnabled"
},
"true"
]
},
"FooEnabled": {"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]},
"FooDisabled": {
"Fn::Not": [
{
"Fn::Equals": [
{
"Ref": "FooEnabled"
},
"true"
]
}
]
}
"Fn::Not": [{"Fn::Equals": [{"Ref": "FooEnabled"}, "true"]}]
},
},
"Parameters": {
"FooEnabled": {
"Type": "String",
"AllowedValues": [
"true",
"false"
]
}
"FooEnabled": {"Type": "String", "AllowedValues": ["true", "false"]}
},
"Resources": {
"Bar": {
"Properties": {
"CidrBlock": "192.168.0.0/16",
},
"Properties": {"CidrBlock": "192.168.0.0/16"},
"Condition": "FooDisabled",
"Type": "AWS::EC2::VPC"
"Type": "AWS::EC2::VPC",
}
}
},
}
dummy_template_json = json.dumps(dummy_template)
cfn = boto.connect_cloudformation()
cfn.create_stack('test_stack1', template_body=dummy_template_json, parameters=[('FooEnabled', 'true')])
stack = cfn.describe_stacks('test_stack1')[0]
cfn.create_stack(
"test_stack1",
template_body=dummy_template_json,
parameters=[("FooEnabled", "true")],
)
stack = cfn.describe_stacks("test_stack1")[0]
resources = stack.list_resources()
assert not [resource for resource in resources if resource.logical_resource_id == 'Bar']
assert not [
resource for resource in resources if resource.logical_resource_id == "Bar"
]
@mock_cloudformation_deprecated
@ -403,48 +354,46 @@ def test_stack_tags():
@mock_cloudformation_deprecated
def test_update_stack():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
conn.update_stack("test_stack", dummy_template_json2)
stack = conn.describe_stacks()[0]
stack.stack_status.should.equal("UPDATE_COMPLETE")
stack.get_template().should.equal({
'GetTemplateResponse': {
'GetTemplateResult': {
'TemplateBody': dummy_template_json2,
'ResponseMetadata': {
'RequestId': '2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE'
stack.get_template().should.equal(
{
"GetTemplateResponse": {
"GetTemplateResult": {
"TemplateBody": dummy_template_json2,
"ResponseMetadata": {
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
},
}
}
}
})
)
@mock_cloudformation_deprecated
def test_update_stack_with_previous_template():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
)
conn.create_stack("test_stack", template_body=dummy_template_json)
conn.update_stack("test_stack", use_previous_template=True)
stack = conn.describe_stacks()[0]
stack.stack_status.should.equal("UPDATE_COMPLETE")
stack.get_template().should.equal({
'GetTemplateResponse': {
'GetTemplateResult': {
'TemplateBody': dummy_template_json,
'ResponseMetadata': {
'RequestId': '2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE'
stack.get_template().should.equal(
{
"GetTemplateResponse": {
"GetTemplateResult": {
"TemplateBody": dummy_template_json,
"ResponseMetadata": {
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
},
}
}
}
})
)
@mock_cloudformation_deprecated
@ -454,29 +403,23 @@ def test_update_stack_with_parameters():
"Description": "Stack",
"Resources": {
"VPC": {
"Properties": {
"CidrBlock": {"Ref": "Bar"}
},
"Type": "AWS::EC2::VPC"
"Properties": {"CidrBlock": {"Ref": "Bar"}},
"Type": "AWS::EC2::VPC",
}
},
"Parameters": {
"Bar": {
"Type": "String"
}
}
"Parameters": {"Bar": {"Type": "String"}},
}
dummy_template_json = json.dumps(dummy_template)
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
parameters=[("Bar", "192.168.0.0/16")]
parameters=[("Bar", "192.168.0.0/16")],
)
conn.update_stack(
"test_stack",
template_body=dummy_template_json,
parameters=[("Bar", "192.168.0.1/16")]
parameters=[("Bar", "192.168.0.1/16")],
)
stack = conn.describe_stacks()[0]
@ -487,14 +430,10 @@ def test_update_stack_with_parameters():
def test_update_stack_replace_tags():
conn = boto.connect_cloudformation()
conn.create_stack(
"test_stack",
template_body=dummy_template_json,
tags={"foo": "bar"},
"test_stack", template_body=dummy_template_json, tags={"foo": "bar"}
)
conn.update_stack(
"test_stack",
template_body=dummy_template_json,
tags={"foo": "baz"},
"test_stack", template_body=dummy_template_json, tags={"foo": "baz"}
)
stack = conn.describe_stacks()[0]
@ -506,28 +445,26 @@ def test_update_stack_replace_tags():
@mock_cloudformation_deprecated
def test_update_stack_when_rolled_back():
conn = boto.connect_cloudformation()
stack_id = conn.create_stack(
"test_stack", template_body=dummy_template_json)
stack_id = conn.create_stack("test_stack", template_body=dummy_template_json)
cloudformation_backends[conn.region.name].stacks[
stack_id].status = 'ROLLBACK_COMPLETE'
stack_id
].status = "ROLLBACK_COMPLETE"
with assert_raises(BotoServerError) as err:
conn.update_stack("test_stack", dummy_template_json)
ex = err.exception
ex.body.should.match(
r'is in ROLLBACK_COMPLETE state and can not be updated')
ex.error_code.should.equal('ValidationError')
ex.reason.should.equal('Bad Request')
ex.body.should.match(r"is in ROLLBACK_COMPLETE state and can not be updated")
ex.error_code.should.equal("ValidationError")
ex.reason.should.equal("Bad Request")
ex.status.should.equal(400)
@mock_cloudformation_deprecated
def test_describe_stack_events_shows_create_update_and_delete():
conn = boto.connect_cloudformation()
stack_id = conn.create_stack(
"test_stack", template_body=dummy_template_json)
stack_id = conn.create_stack("test_stack", template_body=dummy_template_json)
conn.update_stack(stack_id, template_body=dummy_template_json2)
conn.delete_stack(stack_id)
@ -538,14 +475,16 @@ def test_describe_stack_events_shows_create_update_and_delete():
# testing ordering of stack events without assuming resource events will not exist
# the AWS API returns events in reverse chronological order
stack_events_to_look_for = iter([
("DELETE_COMPLETE", None),
("DELETE_IN_PROGRESS", "User Initiated"),
("UPDATE_COMPLETE", None),
("UPDATE_IN_PROGRESS", "User Initiated"),
("CREATE_COMPLETE", None),
("CREATE_IN_PROGRESS", "User Initiated"),
])
stack_events_to_look_for = iter(
[
("DELETE_COMPLETE", None),
("DELETE_IN_PROGRESS", "User Initiated"),
("UPDATE_COMPLETE", None),
("UPDATE_IN_PROGRESS", "User Initiated"),
("CREATE_COMPLETE", None),
("CREATE_IN_PROGRESS", "User Initiated"),
]
)
try:
for event in events:
event.stack_id.should.equal(stack_id)
@ -556,12 +495,10 @@ def test_describe_stack_events_shows_create_update_and_delete():
event.logical_resource_id.should.equal("test_stack")
event.physical_resource_id.should.equal(stack_id)
status_to_look_for, reason_to_look_for = next(
stack_events_to_look_for)
status_to_look_for, reason_to_look_for = next(stack_events_to_look_for)
event.resource_status.should.equal(status_to_look_for)
if reason_to_look_for is not None:
event.resource_status_reason.should.equal(
reason_to_look_for)
event.resource_status_reason.should.equal(reason_to_look_for)
except StopIteration:
assert False, "Too many stack events"
@ -574,74 +511,60 @@ def test_create_stack_lambda_and_dynamodb():
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack Lambda Test 1",
"Parameters": {
},
"Parameters": {},
"Resources": {
"func1": {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code": {
"S3Bucket": "bucket_123",
"S3Key": "key_123"
},
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"S3Bucket": "bucket_123", "S3Key": "key_123"},
"FunctionName": "func1",
"Handler": "handler.handler",
"Role": "role1",
"Role": get_role_name(),
"Runtime": "python2.7",
"Description": "descr",
"MemorySize": 12345,
}
},
},
"func1version": {
"Type": "AWS::Lambda::Version",
"Properties": {
"FunctionName": {
"Ref": "func1"
}
}
"Properties": {"FunctionName": {"Ref": "func1"}},
},
"tab1": {
"Type" : "AWS::DynamoDB::Table",
"Properties" : {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "tab1",
"KeySchema": [{
"AttributeName": "attr1",
"KeyType": "HASH"
}],
"AttributeDefinitions": [{
"AttributeName": "attr1",
"AttributeType": "string"
}],
"KeySchema": [{"AttributeName": "attr1", "KeyType": "HASH"}],
"AttributeDefinitions": [
{"AttributeName": "attr1", "AttributeType": "string"}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 10
}
}
"WriteCapacityUnits": 10,
},
},
},
"func1mapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"Properties": {
"FunctionName": {
"Ref": "func1"
},
"FunctionName": {"Ref": "func1"},
"EventSourceArn": "arn:aws:dynamodb:region:XXXXXX:table/tab1/stream/2000T00:00:00.000",
"StartingPosition": "0",
"BatchSize": 100,
"Enabled": True
}
}
"Enabled": True,
},
},
},
}
validate_s3_before = os.environ.get('VALIDATE_LAMBDA_S3', '')
validate_s3_before = os.environ.get("VALIDATE_LAMBDA_S3", "")
try:
os.environ['VALIDATE_LAMBDA_S3'] = 'false'
os.environ["VALIDATE_LAMBDA_S3"] = "false"
conn.create_stack(
"test_stack_lambda_1",
template_body=json.dumps(dummy_template),
parameters={}.items()
parameters={}.items(),
)
finally:
os.environ['VALIDATE_LAMBDA_S3'] = validate_s3_before
os.environ["VALIDATE_LAMBDA_S3"] = validate_s3_before
stack = conn.describe_stacks()[0]
resources = stack.list_resources()
@ -657,20 +580,26 @@ def test_create_stack_kinesis():
"Parameters": {},
"Resources": {
"stream1": {
"Type" : "AWS::Kinesis::Stream",
"Properties" : {
"Name": "stream1",
"ShardCount": 2
}
"Type": "AWS::Kinesis::Stream",
"Properties": {"Name": "stream1", "ShardCount": 2},
}
}
},
}
conn.create_stack(
"test_stack_kinesis_1",
template_body=json.dumps(dummy_template),
parameters={}.items()
parameters={}.items(),
)
stack = conn.describe_stacks()[0]
resources = stack.list_resources()
assert len(resources) == 1
def get_role_name():
with mock_iam_deprecated():
iam = boto.connect_iam()
role = iam.create_role("my-role")["create_role_response"]["create_role_result"][
"role"
]["arn"]
return role

View file

@ -1,87 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
# Standard library modules
import unittest
# Third-party modules
import boto3
from botocore.exceptions import ClientError
# Package modules
from moto import mock_cloudformation
AWS_REGION = 'us-west-1'
SG_STACK_NAME = 'simple-sg-stack'
SG_TEMPLATE = """
AWSTemplateFormatVersion: 2010-09-09
Description: Simple test CF template for moto_cloudformation
Resources:
SimpleSecurityGroup:
Type: AWS::EC2::SecurityGroup
Description: "A simple security group"
Properties:
GroupName: simple-security-group
GroupDescription: "A simple security group"
SecurityGroupEgress:
-
Description: "Egress to remote HTTPS servers"
CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 443
ToPort: 443
Outputs:
SimpleSecurityGroupName:
Value: !GetAtt SimpleSecurityGroup.GroupId
Export:
Name: "SimpleSecurityGroup"
"""
EC2_STACK_NAME = 'simple-ec2-stack'
EC2_TEMPLATE = """
---
# The latest template format version is "2010-09-09" and as of 2018-04-09
# is currently the only valid value.
AWSTemplateFormatVersion: 2010-09-09
Description: Simple test CF template for moto_cloudformation
Resources:
SimpleInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-03cf127a
InstanceType: t2.micro
SecurityGroups: !Split [',', !ImportValue SimpleSecurityGroup]
"""
class TestSimpleInstance(unittest.TestCase):
def test_simple_instance(self):
"""Test that we can create a simple CloudFormation stack that imports values from an existing CloudFormation stack"""
with mock_cloudformation():
client = boto3.client('cloudformation', region_name=AWS_REGION)
client.create_stack(StackName=SG_STACK_NAME, TemplateBody=SG_TEMPLATE)
response = client.create_stack(StackName=EC2_STACK_NAME, TemplateBody=EC2_TEMPLATE)
self.assertIn('StackId', response)
response = client.describe_stacks(StackName=response['StackId'])
self.assertIn('Stacks', response)
stack_info = response['Stacks']
self.assertEqual(1, len(stack_info))
self.assertIn('StackName', stack_info[0])
self.assertEqual(EC2_STACK_NAME, stack_info[0]['StackName'])
def test_simple_instance_missing_export(self):
"""Test that we get an exception if a CloudFormation stack tries to imports a non-existent export value"""
with mock_cloudformation():
client = boto3.client('cloudformation', region_name=AWS_REGION)
with self.assertRaises(ClientError) as e:
client.create_stack(StackName=EC2_STACK_NAME, TemplateBody=EC2_TEMPLATE)
self.assertIn('Error', e.exception.response)
self.assertIn('Code', e.exception.response['Error'])
self.assertEqual('ExportNotFound', e.exception.response['Error']['Code'])
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
# Standard library modules
import unittest
# Third-party modules
import boto3
from botocore.exceptions import ClientError
# Package modules
from moto import mock_cloudformation
AWS_REGION = "us-west-1"
SG_STACK_NAME = "simple-sg-stack"
SG_TEMPLATE = """
AWSTemplateFormatVersion: 2010-09-09
Description: Simple test CF template for moto_cloudformation
Resources:
SimpleSecurityGroup:
Type: AWS::EC2::SecurityGroup
Description: "A simple security group"
Properties:
GroupName: simple-security-group
GroupDescription: "A simple security group"
SecurityGroupEgress:
-
Description: "Egress to remote HTTPS servers"
CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 443
ToPort: 443
Outputs:
SimpleSecurityGroupName:
Value: !GetAtt SimpleSecurityGroup.GroupId
Export:
Name: "SimpleSecurityGroup"
"""
EC2_STACK_NAME = "simple-ec2-stack"
EC2_TEMPLATE = """
---
# The latest template format version is "2010-09-09" and as of 2018-04-09
# is currently the only valid value.
AWSTemplateFormatVersion: 2010-09-09
Description: Simple test CF template for moto_cloudformation
Resources:
SimpleInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-03cf127a
InstanceType: t2.micro
SecurityGroups: !Split [',', !ImportValue SimpleSecurityGroup]
"""
class TestSimpleInstance(unittest.TestCase):
def test_simple_instance(self):
"""Test that we can create a simple CloudFormation stack that imports values from an existing CloudFormation stack"""
with mock_cloudformation():
client = boto3.client("cloudformation", region_name=AWS_REGION)
client.create_stack(StackName=SG_STACK_NAME, TemplateBody=SG_TEMPLATE)
response = client.create_stack(
StackName=EC2_STACK_NAME, TemplateBody=EC2_TEMPLATE
)
self.assertIn("StackId", response)
response = client.describe_stacks(StackName=response["StackId"])
self.assertIn("Stacks", response)
stack_info = response["Stacks"]
self.assertEqual(1, len(stack_info))
self.assertIn("StackName", stack_info[0])
self.assertEqual(EC2_STACK_NAME, stack_info[0]["StackName"])
def test_simple_instance_missing_export(self):
"""Test that we get an exception if a CloudFormation stack tries to imports a non-existent export value"""
with mock_cloudformation():
client = boto3.client("cloudformation", region_name=AWS_REGION)
with self.assertRaises(ClientError) as e:
client.create_stack(StackName=EC2_STACK_NAME, TemplateBody=EC2_TEMPLATE)
self.assertIn("Error", e.exception.response)
self.assertIn("Code", e.exception.response["Error"])
self.assertEqual("ExportNotFound", e.exception.response["Error"]["Code"])

View file

@ -1,33 +1,36 @@
from __future__ import unicode_literals
import json
from six.moves.urllib.parse import urlencode
import re
import sure # noqa
import moto.server as server
'''
Test the different server responses
'''
def test_cloudformation_server_get():
backend = server.create_backend_app("cloudformation")
stack_name = 'test stack'
test_client = backend.test_client()
template_body = {
"Resources": {},
}
create_stack_resp = test_client.action_data("CreateStack", StackName=stack_name,
TemplateBody=json.dumps(template_body))
create_stack_resp.should.match(
r"<CreateStackResponse>.*<CreateStackResult>.*<StackId>.*</StackId>.*</CreateStackResult>.*</CreateStackResponse>", re.DOTALL)
stack_id_from_create_response = re.search(
"<StackId>(.*)</StackId>", create_stack_resp).groups()[0]
list_stacks_resp = test_client.action_data("ListStacks")
stack_id_from_list_response = re.search(
"<StackId>(.*)</StackId>", list_stacks_resp).groups()[0]
stack_id_from_create_response.should.equal(stack_id_from_list_response)
from __future__ import unicode_literals
import json
from six.moves.urllib.parse import urlencode
import re
import sure # noqa
import moto.server as server
"""
Test the different server responses
"""
def test_cloudformation_server_get():
backend = server.create_backend_app("cloudformation")
stack_name = "test stack"
test_client = backend.test_client()
template_body = {"Resources": {}}
create_stack_resp = test_client.action_data(
"CreateStack", StackName=stack_name, TemplateBody=json.dumps(template_body)
)
create_stack_resp.should.match(
r"<CreateStackResponse>.*<CreateStackResult>.*<StackId>.*</StackId>.*</CreateStackResult>.*</CreateStackResponse>",
re.DOTALL,
)
stack_id_from_create_response = re.search(
"<StackId>(.*)</StackId>", create_stack_resp
).groups()[0]
list_stacks_resp = test_client.action_data("ListStacks")
stack_id_from_list_response = re.search(
"<StackId>(.*)</StackId>", list_stacks_resp
).groups()[0]
stack_id_from_create_response.should.equal(stack_id_from_list_response)

View file

@ -7,91 +7,57 @@ import sure # noqa
from moto.cloudformation.exceptions import ValidationError
from moto.cloudformation.models import FakeStack
from moto.cloudformation.parsing import resource_class_from_type, parse_condition, Export
from moto.cloudformation.parsing import (
resource_class_from_type,
parse_condition,
Export,
)
from moto.sqs.models import Queue
from moto.s3.models import FakeBucket
from moto.cloudformation.utils import yaml_tag_constructor
from boto.cloudformation.stack import Output
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
"Resources": {
"Queue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "my-queue",
"VisibilityTimeout": 60,
}
},
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain"
"Properties": {"QueueName": "my-queue", "VisibilityTimeout": 60},
},
"S3Bucket": {"Type": "AWS::S3::Bucket", "DeletionPolicy": "Retain"},
},
}
name_type_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Create a multi-az, load balanced, Auto Scaled sample web site. The Auto Scaling trigger is based on the CPU utilization of the web servers. The AMI is chosen based on the region in which the stack is run. This example creates a web service running across all availability zones in a region. The instances are load balanced with a simple health check. The web site is available on port 80, however, the instances can be configured to listen on any port (8888 by default). **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
"Resources": {
"Queue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"VisibilityTimeout": 60,
}
},
"Queue": {"Type": "AWS::SQS::Queue", "Properties": {"VisibilityTimeout": 60}}
},
}
output_dict = {
"Outputs": {
"Output1": {
"Value": {"Ref": "Queue"},
"Description": "This is a description."
}
"Output1": {"Value": {"Ref": "Queue"}, "Description": "This is a description."}
}
}
bad_output = {
"Outputs": {
"Output1": {
"Value": {"Fn::GetAtt": ["Queue", "InvalidAttribute"]}
}
}
"Outputs": {"Output1": {"Value": {"Fn::GetAtt": ["Queue", "InvalidAttribute"]}}}
}
get_attribute_output = {
"Outputs": {
"Output1": {
"Value": {"Fn::GetAtt": ["Queue", "QueueName"]}
}
}
"Outputs": {"Output1": {"Value": {"Fn::GetAtt": ["Queue", "QueueName"]}}}
}
get_availability_zones_output = {
"Outputs": {
"Output1": {
"Value": {"Fn::GetAZs": ""}
}
}
}
get_availability_zones_output = {"Outputs": {"Output1": {"Value": {"Fn::GetAZs": ""}}}}
parameters = {
"Parameters": {
"Param": {
"Type": "String",
},
"NoEchoParam": {
"Type": "String",
"NoEcho": True
}
"Param": {"Type": "String"},
"NoEchoParam": {"Type": "String", "NoEcho": True},
}
}
@ -101,11 +67,11 @@ split_select_template = {
"Queue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {"Fn::Select": [ "1", {"Fn::Split": [ "-", "123-myqueue" ] } ] },
"QueueName": {"Fn::Select": ["1", {"Fn::Split": ["-", "123-myqueue"]}]},
"VisibilityTimeout": 60,
}
},
}
}
},
}
sub_template = {
@ -114,18 +80,18 @@ sub_template = {
"Queue1": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue-${!Literal}'},
"QueueName": {"Fn::Sub": "${AWS::StackName}-queue-${!Literal}"},
"VisibilityTimeout": 60,
}
},
},
"Queue2": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {"Fn::Sub": '${Queue1.QueueName}'},
"QueueName": {"Fn::Sub": "${Queue1.QueueName}"},
"VisibilityTimeout": 60,
}
},
},
}
},
}
export_value_template = {
@ -134,17 +100,12 @@ export_value_template = {
"Queue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {"Fn::Sub": '${AWS::StackName}-queue'},
"QueueName": {"Fn::Sub": "${AWS::StackName}-queue"},
"VisibilityTimeout": 60,
}
},
}
},
"Outputs": {
"Output1": {
"Value": "value",
"Export": {"Name": 'queue-us-west-1'}
}
}
"Outputs": {"Output1": {"Value": "value", "Export": {"Name": "queue-us-west-1"}}},
}
import_value_template = {
@ -153,33 +114,30 @@ import_value_template = {
"Queue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {"Fn::ImportValue": 'queue-us-west-1'},
"QueueName": {"Fn::ImportValue": "queue-us-west-1"},
"VisibilityTimeout": 60,
}
},
}
}
},
}
outputs_template = dict(list(dummy_template.items()) +
list(output_dict.items()))
bad_outputs_template = dict(
list(dummy_template.items()) + list(bad_output.items()))
outputs_template = dict(list(dummy_template.items()) + list(output_dict.items()))
bad_outputs_template = dict(list(dummy_template.items()) + list(bad_output.items()))
get_attribute_outputs_template = dict(
list(dummy_template.items()) + list(get_attribute_output.items()))
list(dummy_template.items()) + list(get_attribute_output.items())
)
get_availability_zones_template = dict(
list(dummy_template.items()) + list(get_availability_zones_output.items()))
list(dummy_template.items()) + list(get_availability_zones_output.items())
)
parameters_template = dict(
list(dummy_template.items()) + list(parameters.items()))
parameters_template = dict(list(dummy_template.items()) + list(parameters.items()))
dummy_template_json = json.dumps(dummy_template)
name_type_template_json = json.dumps(name_type_template)
output_type_template_json = json.dumps(outputs_template)
bad_output_template_json = json.dumps(bad_outputs_template)
get_attribute_outputs_template_json = json.dumps(
get_attribute_outputs_template)
get_availability_zones_template_json = json.dumps(
get_availability_zones_template)
get_attribute_outputs_template_json = json.dumps(get_attribute_outputs_template)
get_availability_zones_template_json = json.dumps(get_availability_zones_template)
parameters_template_json = json.dumps(parameters_template)
split_select_template_json = json.dumps(split_select_template)
sub_template_json = json.dumps(sub_template)
@ -193,15 +151,16 @@ def test_parse_stack_resources():
name="test_stack",
template=dummy_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.resource_map.should.have.length_of(2)
queue = stack.resource_map['Queue']
queue = stack.resource_map["Queue"]
queue.should.be.a(Queue)
queue.name.should.equal("my-queue")
bucket = stack.resource_map['S3Bucket']
bucket = stack.resource_map["S3Bucket"]
bucket.should.be.a(FakeBucket)
bucket.physical_resource_id.should.equal(bucket.name)
@ -209,8 +168,7 @@ def test_parse_stack_resources():
@patch("moto.cloudformation.parsing.logger")
def test_missing_resource_logs(logger):
resource_class_from_type("foobar")
logger.warning.assert_called_with(
'No Moto CloudFormation support for %s', 'foobar')
logger.warning.assert_called_with("No Moto CloudFormation support for %s", "foobar")
def test_parse_stack_with_name_type_resource():
@ -219,10 +177,11 @@ def test_parse_stack_with_name_type_resource():
name="test_stack",
template=name_type_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.resource_map.should.have.length_of(1)
list(stack.resource_map.keys())[0].should.equal('Queue')
list(stack.resource_map.keys())[0].should.equal("Queue")
queue = list(stack.resource_map.values())[0]
queue.should.be.a(Queue)
@ -233,10 +192,11 @@ def test_parse_stack_with_yaml_template():
name="test_stack",
template=yaml.dump(name_type_template),
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.resource_map.should.have.length_of(1)
list(stack.resource_map.keys())[0].should.equal('Queue')
list(stack.resource_map.keys())[0].should.equal("Queue")
queue = list(stack.resource_map.values())[0]
queue.should.be.a(Queue)
@ -247,10 +207,11 @@ def test_parse_stack_with_outputs():
name="test_stack",
template=output_type_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.output_map.should.have.length_of(1)
list(stack.output_map.keys())[0].should.equal('Output1')
list(stack.output_map.keys())[0].should.equal("Output1")
output = list(stack.output_map.values())[0]
output.should.be.a(Output)
output.description.should.equal("This is a description.")
@ -262,14 +223,16 @@ def test_parse_stack_with_get_attribute_outputs():
name="test_stack",
template=get_attribute_outputs_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.output_map.should.have.length_of(1)
list(stack.output_map.keys())[0].should.equal('Output1')
list(stack.output_map.keys())[0].should.equal("Output1")
output = list(stack.output_map.values())[0]
output.should.be.a(Output)
output.value.should.equal("my-queue")
def test_parse_stack_with_get_attribute_kms():
from .fixtures.kms_key import template
@ -279,31 +242,35 @@ def test_parse_stack_with_get_attribute_kms():
name="test_stack",
template=template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.output_map.should.have.length_of(1)
list(stack.output_map.keys())[0].should.equal('KeyArn')
list(stack.output_map.keys())[0].should.equal("KeyArn")
output = list(stack.output_map.values())[0]
output.should.be.a(Output)
def test_parse_stack_with_get_availability_zones():
stack = FakeStack(
stack_id="test_id",
name="test_stack",
template=get_availability_zones_template_json,
parameters={},
region_name='us-east-1')
region_name="us-east-1",
)
stack.output_map.should.have.length_of(1)
list(stack.output_map.keys())[0].should.equal('Output1')
list(stack.output_map.keys())[0].should.equal("Output1")
output = list(stack.output_map.values())[0]
output.should.be.a(Output)
output.value.should.equal([ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d" ])
output.value.should.equal(["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"])
def test_parse_stack_with_bad_get_attribute_outputs():
FakeStack.when.called_with(
"test_id", "test_stack", bad_output_template_json, {}, "us-west-1").should.throw(ValidationError)
"test_id", "test_stack", bad_output_template_json, {}, "us-west-1"
).should.throw(ValidationError)
def test_parse_stack_with_parameters():
@ -312,7 +279,8 @@ def test_parse_stack_with_parameters():
name="test_stack",
template=parameters_template_json,
parameters={"Param": "visible value", "NoEchoParam": "hidden value"},
region_name='us-west-1')
region_name="us-west-1",
)
stack.resource_map.no_echo_parameter_keys.should.have("NoEchoParam")
stack.resource_map.no_echo_parameter_keys.should_not.have("Param")
@ -334,21 +302,13 @@ def test_parse_equals_condition():
def test_parse_not_condition():
parse_condition(
condition={
"Fn::Not": [{
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
}]
},
condition={"Fn::Not": [{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]}]},
resources_map={"EnvType": "prod"},
condition_map={},
).should.equal(False)
parse_condition(
condition={
"Fn::Not": [{
"Fn::Equals": [{"Ref": "EnvType"}, "prod"]
}]
},
condition={"Fn::Not": [{"Fn::Equals": [{"Ref": "EnvType"}, "prod"]}]},
resources_map={"EnvType": "staging"},
condition_map={},
).should.equal(True)
@ -416,10 +376,11 @@ def test_parse_split_and_select():
name="test_stack",
template=split_select_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
stack.resource_map.should.have.length_of(1)
queue = stack.resource_map['Queue']
queue = stack.resource_map["Queue"]
queue.name.should.equal("myqueue")
@ -429,10 +390,11 @@ def test_sub():
name="test_stack",
template=sub_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
queue1 = stack.resource_map['Queue1']
queue2 = stack.resource_map['Queue2']
queue1 = stack.resource_map["Queue1"]
queue2 = stack.resource_map["Queue2"]
queue2.name.should.equal(queue1.name)
@ -442,20 +404,21 @@ def test_import():
name="test_stack",
template=export_value_template_json,
parameters={},
region_name='us-west-1')
region_name="us-west-1",
)
import_stack = FakeStack(
stack_id="test_id",
name="test_stack",
template=import_value_template_json,
parameters={},
region_name='us-west-1',
cross_stack_resources={export_stack.exports[0].value: export_stack.exports[0]})
region_name="us-west-1",
cross_stack_resources={export_stack.exports[0].value: export_stack.exports[0]},
)
queue = import_stack.resource_map['Queue']
queue = import_stack.resource_map["Queue"]
queue.name.should.equal("value")
def test_short_form_func_in_yaml_teamplate():
template = """---
KeyB64: !Base64 valueToEncode
@ -476,24 +439,24 @@ def test_short_form_func_in_yaml_teamplate():
KeySplit: !Split [A, B]
KeySub: !Sub A
"""
yaml.add_multi_constructor('', yaml_tag_constructor, Loader=yaml.Loader)
yaml.add_multi_constructor("", yaml_tag_constructor, Loader=yaml.Loader)
template_dict = yaml.load(template, Loader=yaml.Loader)
key_and_expects = [
['KeyRef', {'Ref': 'foo'}],
['KeyB64', {'Fn::Base64': 'valueToEncode'}],
['KeyAnd', {'Fn::And': ['A', 'B']}],
['KeyEquals', {'Fn::Equals': ['A', 'B']}],
['KeyIf', {'Fn::If': ['A', 'B', 'C']}],
['KeyNot', {'Fn::Not': ['A']}],
['KeyOr', {'Fn::Or': ['A', 'B']}],
['KeyFindInMap', {'Fn::FindInMap': ['A', 'B', 'C']}],
['KeyGetAtt', {'Fn::GetAtt': ['A', 'B']}],
['KeyGetAZs', {'Fn::GetAZs': 'A'}],
['KeyImportValue', {'Fn::ImportValue': 'A'}],
['KeyJoin', {'Fn::Join': [ ":", [ 'A', 'B', 'C' ] ]}],
['KeySelect', {'Fn::Select': ['A', 'B']}],
['KeySplit', {'Fn::Split': ['A', 'B']}],
['KeySub', {'Fn::Sub': 'A'}],
["KeyRef", {"Ref": "foo"}],
["KeyB64", {"Fn::Base64": "valueToEncode"}],
["KeyAnd", {"Fn::And": ["A", "B"]}],
["KeyEquals", {"Fn::Equals": ["A", "B"]}],
["KeyIf", {"Fn::If": ["A", "B", "C"]}],
["KeyNot", {"Fn::Not": ["A"]}],
["KeyOr", {"Fn::Or": ["A", "B"]}],
["KeyFindInMap", {"Fn::FindInMap": ["A", "B", "C"]}],
["KeyGetAtt", {"Fn::GetAtt": ["A", "B"]}],
["KeyGetAZs", {"Fn::GetAZs": "A"}],
["KeyImportValue", {"Fn::ImportValue": "A"}],
["KeyJoin", {"Fn::Join": [":", ["A", "B", "C"]]}],
["KeySelect", {"Fn::Select": ["A", "B"]}],
["KeySplit", {"Fn::Split": ["A", "B"]}],
["KeySub", {"Fn::Sub": "A"}],
]
for k, v in key_and_expects:
template_dict.should.have.key(k).which.should.be.equal(v)

View file

@ -9,7 +9,11 @@ import botocore
from moto.cloudformation.exceptions import ValidationError
from moto.cloudformation.models import FakeStack
from moto.cloudformation.parsing import resource_class_from_type, parse_condition, Export
from moto.cloudformation.parsing import (
resource_class_from_type,
parse_condition,
Export,
)
from moto.sqs.models import Queue
from moto.s3.models import FakeBucket
from moto.cloudformation.utils import yaml_tag_constructor
@ -27,25 +31,16 @@ json_template = {
"KeyName": "dummy",
"InstanceType": "t2.micro",
"Tags": [
{
"Key": "Description",
"Value": "Test tag"
},
{
"Key": "Name",
"Value": "Name tag for tests"
}
]
}
{"Key": "Description", "Value": "Test tag"},
{"Key": "Name", "Value": "Name tag for tests"},
],
},
}
}
},
}
# One resource is required
json_bad_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Stack 1"
}
json_bad_template = {"AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack 1"}
dummy_template_json = json.dumps(json_template)
dummy_bad_template_json = json.dumps(json_bad_template)
@ -53,25 +48,25 @@ dummy_bad_template_json = json.dumps(json_bad_template)
@mock_cloudformation
def test_boto3_json_validate_successful():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
response = cf_conn.validate_template(
TemplateBody=dummy_template_json,
)
assert response['Description'] == "Stack 1"
assert response['Parameters'] == []
assert response['ResponseMetadata']['HTTPStatusCode'] == 200
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
response = cf_conn.validate_template(TemplateBody=dummy_template_json)
assert response["Description"] == "Stack 1"
assert response["Parameters"] == []
assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
@mock_cloudformation
def test_boto3_json_invalid_missing_resource():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
try:
cf_conn.validate_template(
TemplateBody=dummy_bad_template_json,
)
cf_conn.validate_template(TemplateBody=dummy_bad_template_json)
assert False
except botocore.exceptions.ClientError as e:
assert str(e) == 'An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack' \
' with id Missing top level item Resources to file module does not exist'
assert (
str(e)
== "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack"
" with id Missing top level item Resources to file module does not exist"
)
assert True
@ -91,25 +86,26 @@ yaml_bad_template = """
Description: Simple CloudFormation Test Template
"""
@mock_cloudformation
def test_boto3_yaml_validate_successful():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
response = cf_conn.validate_template(
TemplateBody=yaml_template,
)
assert response['Description'] == "Simple CloudFormation Test Template"
assert response['Parameters'] == []
assert response['ResponseMetadata']['HTTPStatusCode'] == 200
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
response = cf_conn.validate_template(TemplateBody=yaml_template)
assert response["Description"] == "Simple CloudFormation Test Template"
assert response["Parameters"] == []
assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
@mock_cloudformation
def test_boto3_yaml_invalid_missing_resource():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
try:
cf_conn.validate_template(
TemplateBody=yaml_bad_template,
)
cf_conn.validate_template(TemplateBody=yaml_bad_template)
assert False
except botocore.exceptions.ClientError as e:
assert str(e) == 'An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack' \
' with id Missing top level item Resources to file module does not exist'
assert (
str(e)
== "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack"
" with id Missing top level item Resources to file module does not exist"
)
assert True