Add redshift support for cloudformation.

This commit is contained in:
Steve Pulec 2015-08-08 13:29:37 -04:00
commit 73f03d1ccf
4 changed files with 313 additions and 16 deletions

View file

@ -0,0 +1,187 @@
from __future__ import unicode_literals
template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters" : {
"DatabaseName" : {
"Description" : "The name of the first database to be created when the cluster is created",
"Type" : "String",
"Default" : "dev",
"AllowedPattern" : "([a-z]|[0-9])+"
},
"ClusterType" : {
"Description" : "The type of cluster",
"Type" : "String",
"Default" : "single-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"
},
"NodeType" : {
"Description" : "The type of node to be provisioned",
"Type" : "String",
"Default" : "dw1.xlarge",
"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])*"
},
"MasterUserPassword" : {
"Description" : "The password that is associated with the master user account for the cluster that is being created.",
"Type" : "String",
"NoEcho" : "true"
},
"InboundTraffic" : {
"Description" : "Allow inbound traffic to the cluster from this CIDR range.",
"Type" : "String",
"MinLength": "9",
"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."
},
"PortNumber" : {
"Description" : "The port number on which the cluster accepts incoming connections.",
"Type" : "Number",
"Default" : "5439"
}
},
"Conditions" : {
"IsMultiNodeCluster" : {
"Fn::Equals" : [{ "Ref" : "ClusterType" }, "multi-node" ]
}
},
"Resources" : {
"RedshiftCluster" : {
"Type" : "AWS::Redshift::Cluster",
"DependsOn" : "AttachGateway",
"Properties" : {
"ClusterType" : { "Ref" : "ClusterType" },
"NumberOfNodes" : { "Fn::If" : [ "IsMultiNodeCluster", { "Ref" : "NumberOfNodes" }, { "Ref" : "AWS::NoValue" }]},
"NodeType" : { "Ref" : "NodeType" },
"DBName" : { "Ref" : "DatabaseName" },
"MasterUsername" : { "Ref" : "MasterUsername" },
"MasterUserPassword" : { "Ref" : "MasterUserPassword" },
"ClusterParameterGroupName" : { "Ref" : "RedshiftClusterParameterGroup" },
"VpcSecurityGroupIds" : [ { "Ref" : "SecurityGroup" } ],
"ClusterSubnetGroupName" : { "Ref" : "RedshiftClusterSubnetGroup" },
"PubliclyAccessible" : "true",
"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"
}]
}
},
"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"
}
},
"PublicSubnet" : {
"Type" : "AWS::EC2::Subnet",
"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"
},
"AttachGateway" : {
"Type" : "AWS::EC2::VPCGatewayAttachment",
"Properties" : {
"VpcId" : { "Ref" : "VPC" },
"InternetGatewayId" : { "Ref" : "myInternetGateway" }
}
},
"PublicRouteTable" : {
"Type" : "AWS::EC2::RouteTable",
"Properties" : {
"VpcId" : {
"Ref" : "VPC"
}
}
},
"PublicRoute" : {
"Type" : "AWS::EC2::Route",
"DependsOn" : "AttachGateway",
"Properties" : {
"RouteTableId" : {
"Ref" : "PublicRouteTable"
},
"DestinationCidrBlock" : "0.0.0.0/0",
"GatewayId" : {
"Ref" : "myInternetGateway"
}
}
},
"PublicSubnetRouteTableAssociation" : {
"Type" : "AWS::EC2::SubnetRouteTableAssociation",
"Properties" : {
"SubnetId" : {
"Ref" : "PublicSubnet"
},
"RouteTableId" : {
"Ref" : "PublicRouteTable"
}
}
}
},
"Outputs" : {
"ClusterEndpoint" : {
"Description" : "Cluster endpoint",
"Value" : { "Fn::Join" : [ ":", [ { "Fn::GetAtt" : [ "RedshiftCluster", "Endpoint.Address" ] }, { "Fn::GetAtt" : [ "RedshiftCluster", "Endpoint.Port" ] } ] ] }
},
"ClusterName" : {
"Description" : "Name of cluster",
"Value" : { "Ref" : "RedshiftCluster" }
},
"ParameterGroupName" : {
"Description" : "Name of parameter group",
"Value" : { "Ref" : "RedshiftClusterParameterGroup" }
},
"RedshiftClusterSubnetGroupName" : {
"Description" : "Name of cluster subnet group",
"Value" : { "Ref" : "RedshiftClusterSubnetGroup" }
},
"RedshiftClusterSecurityGroupName" : {
"Description" : "Name of cluster security group",
"Value" : { "Ref" : "SecurityGroup" }
}
}
}

View file

@ -8,6 +8,7 @@ import boto.ec2.autoscale
import boto.ec2.elb
from boto.exception import BotoServerError
import boto.iam
import boto.redshift
import boto.sns
import boto.sqs
import boto.vpc
@ -20,6 +21,7 @@ from moto import (
mock_elb,
mock_iam,
mock_rds,
mock_redshift,
mock_route53,
mock_sns,
mock_sqs,
@ -29,6 +31,7 @@ from .fixtures import (
ec2_classic_eip,
fn_join,
rds_mysql_with_read_replica,
redshift,
route53_ec2_instance_with_public_ip,
route53_health_check,
route53_roundrobin,
@ -288,20 +291,50 @@ def test_stack_elb_integration_with_attached_ec2_instances():
load_balancer.instances[0].id.should.equal(ec2_instance.id)
list(load_balancer.availability_zones).should.equal(['us-east1'])
load_balancer_name = load_balancer.name
stack = conn.describe_stacks()[0]
stack_resources = stack.describe_resources()
stack_resources.should.have.length_of(2)
for resource in stack_resources:
if resource.resource_type == 'AWS::ElasticLoadBalancing::LoadBalancer':
load_balancer = resource
else:
ec2_instance = resource
load_balancer.logical_resource_id.should.equal("MyELB")
load_balancer.physical_resource_id.should.equal(load_balancer_name)
ec2_instance.physical_resource_id.should.equal(instance_id)
@mock_ec2()
@mock_redshift()
@mock_cloudformation()
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),
]
)
redshift_conn = boto.redshift.connect_to_region("us-west-2")
cluster_res = redshift_conn.describe_clusters()
clusters = cluster_res['DescribeClustersResponse']['DescribeClustersResult']['Clusters']
clusters.should.have.length_of(1)
cluster = clusters[0]
cluster['DBName'].should.equal("mydb")
cluster['NumberOfNodes'].should.equal(2)
cluster['NodeType'].should.equal("dw1.xlarge")
cluster['MasterUsername'].should.equal("myuser")
cluster['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")
@mock_ec2()