chemtrail 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da5e94625a540c598754777705713135aed7b298
4
- data.tar.gz: 570c93e465273a2a6a23eaa19cde303ebcddfb20
3
+ metadata.gz: 9cea4ac78802e46bcfa27fd2d3ce436f104dbd89
4
+ data.tar.gz: 35c9eeaf91b64f21d96299b1833a968176ce7c92
5
5
  SHA512:
6
- metadata.gz: e16c9ae30693142fa40c50d5d17e7d3dee6880390f02743bffb3af23943bf4097e7b902a66f6243315742d9211b8c91c3d8758bb768d2e8223a9eee6512809a7
7
- data.tar.gz: 2046dabd55f6dae8f7c8589f6bc884e9b1e17e4ad4c4bd8928505fe45cda76062c986d9487e33bdf628d86631518057da12d5ad46165e67ef182c02f38780cb7
6
+ metadata.gz: a1554bfd2d565f01113e4c2461320d74994dfd1a65afdcc2b6a41edb5545d78b04ea1fdb649cc84a568296341cb5f6e6d9521d15da6ab55472e9bbcc31aa91a0
7
+ data.tar.gz: bd66df7c9d4448ce81e6e12d7d8771072a2374bb9a74bcd84c52aa61c8c897c84e3f813deac044025c926ccb181e8ef8e2302441f001d5a9414d4fc7ede1af99
data/README.md CHANGED
@@ -33,7 +33,9 @@ Or install it yourself as:
33
33
  Usage
34
34
  -----
35
35
 
36
- See the `examples/` directory for examples of subclassing and testing templates.
36
+ See the `examples/` directory for an example of test-driving the
37
+ [OpsWorks VPC](http://docs.aws.amazon.com/opsworks/latest/userguide/workingstacks-vpc.html)
38
+ CloudFormation template.
37
39
 
38
40
  Listing all available templates in `lib/templates`:
39
41
 
@@ -47,6 +49,10 @@ Building a template:
47
49
 
48
50
  $ chemtrail build crazy:cat:pants
49
51
 
52
+ Validating a template with Amazon (note that you will need to set the environment variables `AWS_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_KEY`):
53
+
54
+ $ chemtrail validate tangy:socks
55
+
50
56
 
51
57
  Contributing
52
58
  ------------
@@ -1,4 +1,5 @@
1
1
  LoadBalancerSecurityGroup:
2
+ GroupDescription: Enable HTTP access on port 80
2
3
  SecurityGroupIngress:
3
4
  - IpProtocol: tcp
4
5
  FromPort: "80"
@@ -1,4 +1,5 @@
1
1
  OpsWorksSecurityGroup:
2
+ GroupDescription: Allow the OpsWorks instances to access the NAT device
2
3
  SecurityGroupIngress:
3
4
  - IpProtocol: tcp
4
5
  FromPort: "80"
@@ -1,6 +1,9 @@
1
+ PrivateRoute:
2
+ DestinationCidrBlock: 0.0.0.0/0
3
+
1
4
  PrivateSubnet:
2
5
  Tags:
3
- - Key: Network
6
+ - Key: Name
4
7
  Value: Private
5
8
 
6
9
  PrivateRouteTable:
@@ -57,4 +57,3 @@ VPC:
57
57
  Tags:
58
58
  - Key: Network
59
59
  Value: Public
60
-
@@ -32,7 +32,7 @@ module OpsworksVpc
32
32
 
33
33
  def ip
34
34
  @ip ||= Chemtrail::Resource.new("NATIPAddress", "AWS::EC2::EIP", nat_device_config["NATIPAddress"]).tap do |config|
35
- config.properties["VpcId"] = vpc
35
+ config.properties["InstanceId"] = device
36
36
  end
37
37
  end
38
38
 
@@ -25,14 +25,14 @@ module OpsworksVpc
25
25
  @subnet ||= Chemtrail::Resource.new("PrivateSubnet", "AWS::EC2::Subnet", resources_config["PrivateSubnet"]).tap do |config|
26
26
  config.properties["VpcId"] = vpc
27
27
  config.properties["CidrBlock"] = subnet_config.find("Private", "CIDR")
28
- config.properties["Tags"] << stack_name.as_tag("Application")
28
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
29
29
  end
30
30
  end
31
31
 
32
32
  def route_table
33
33
  @route_table ||= Chemtrail::Resource.new("PrivateRouteTable", "AWS::EC2::RouteTable", resources_config["PrivateRouteTable"]).tap do |config|
34
34
  config.properties["VpcId"] = vpc
35
- config.properties["Tags"] << stack_name.as_tag("Application")
35
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
36
36
  end
37
37
  end
38
38
 
@@ -53,7 +53,7 @@ module OpsworksVpc
53
53
  def network_acl
54
54
  @network_acl ||= Chemtrail::Resource.new("PrivateNetworkAcl", "AWS::EC2::NetworkAcl", resources_config["PrivateNetworkAcl"]).tap do |config|
55
55
  config.properties["VpcId"] = vpc
56
- config.properties["Tags"] << stack_name.as_tag("Application")
56
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
57
57
  end
58
58
  end
59
59
 
@@ -24,13 +24,13 @@ module OpsworksVpc
24
24
  @subnet ||= Chemtrail::Resource.new("PublicSubnet", "AWS::EC2::Subnet", resources_config["PublicSubnet"]).tap do |config|
25
25
  config.properties["VpcId"] = vpc
26
26
  config.properties["CidrBlock"] = subnet_config.find("Public", "CIDR")
27
- config.properties["Tags"] << stack_name.as_tag("Application")
27
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
28
28
  end
29
29
  end
30
30
 
31
31
  def internet_gateway
32
32
  @internet_gateway ||= Chemtrail::Resource.new("InternetGateway", "AWS::EC2::InternetGateway", resources_config["InternetGateway"]).tap do |config|
33
- config.properties["Tags"] << stack_name.as_tag("Application")
33
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
34
34
  end
35
35
  end
36
36
 
@@ -44,14 +44,14 @@ module OpsworksVpc
44
44
  def route_table
45
45
  @route_table ||= Chemtrail::Resource.new("PublicRouteTable", "AWS::EC2::RouteTable", resources_config["PublicRouteTable"]).tap do |config|
46
46
  config.properties["VpcId"] = vpc
47
- config.properties["Tags"] << stack_name.as_tag("Application")
47
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
48
48
  end
49
49
  end
50
50
 
51
51
  def route
52
52
  @route ||= Chemtrail::Resource.new("PublicRoute", "AWS::EC2::Route", resources_config["PublicRoute"]).tap do |config|
53
53
  config.properties["RouteTableId"] = route_table
54
- config.properties["GatewayId"] = gateway_to_internet
54
+ config.properties["GatewayId"] = internet_gateway
55
55
  end
56
56
  end
57
57
 
@@ -22,7 +22,7 @@ module OpsworksVpc
22
22
  def network_acl
23
23
  @network_acl ||= Chemtrail::Resource.new("PublicNetworkAcl", "AWS::EC2::NetworkAcl", resources_config["PublicNetworkAcl"]).tap do |config|
24
24
  config.properties["VpcId"] = vpc
25
- config.properties["Tags"] << stack_name.as_tag("Application")
25
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
26
26
  end
27
27
  end
28
28
 
@@ -34,10 +34,10 @@ module OpsworksVpc
34
34
 
35
35
  def outputs
36
36
  [
37
- Chemtrail::Output.new("VPC", vpc),
38
- Chemtrail::Output.new("PrivateSubnets", private_network.subnet),
39
- Chemtrail::Output.new("PublicSubnets", public_network.subnet),
40
- Chemtrail::Output.new("LoadBalancer", load_balancer.elb),
37
+ Chemtrail::Output.new("VPC", vpc, "VPC"),
38
+ Chemtrail::Output.new("PrivateSubnets", private_network.subnet, "Private Subnet"),
39
+ Chemtrail::Output.new("PublicSubnets", public_network.subnet, "Public Subnet"),
40
+ Chemtrail::Output.new("LoadBalancer", load_balancer.elb, "Load Balancer"),
41
41
  ]
42
42
  end
43
43
 
@@ -56,7 +56,7 @@ module OpsworksVpc
56
56
  def vpc
57
57
  @vpc ||= Chemtrail::Resource.new("VPC", "AWS::EC2::VPC", stack_config["VPC"]).tap do |config|
58
58
  config.properties["CidrBlock"] = subnet_config.find("VPC", "CIDR")
59
- config.properties["Tags"] << stack_name.as_tag("Application")
59
+ config.properties["Tags"].unshift(stack_name.as_tag("Application"))
60
60
  end
61
61
  end
62
62
 
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe "parity with cloudformation" do
4
+ let(:template_path) { File.expand_path("../../support/OpsWorksinVPC.template", __FILE__) }
5
+ let(:template_contents) { File.read(template_path) }
6
+ let(:template) { JSON.parse(template_contents) }
7
+ let(:generated) { OpsworksVpc::Stack.new.to_hash }
8
+
9
+ it "maintains parity with cloudformation" do
10
+ template.should == generated
11
+ end
12
+ end
@@ -39,7 +39,7 @@ describe OpsworksVpc::NatDevice do
39
39
  end
40
40
 
41
41
  describe "NATIPAddress" do
42
- its(:ip) { should have_property("VpcId").with_reference("VPC") }
42
+ its(:ip) { should have_property("InstanceId").with_reference("NATDevice") }
43
43
  end
44
44
  end
45
45
  end
@@ -27,6 +27,11 @@ describe OpsworksVpc::PublicNetwork do
27
27
  its(:gateway_to_internet) { should have_property("InternetGatewayId").with_reference("InternetGateway") }
28
28
  end
29
29
 
30
+ describe "PublicRoute" do
31
+ its(:route) { should have_property("RouteTableId").with_reference("PublicRouteTable") }
32
+ its(:route) { should have_property("GatewayId").with_reference("InternetGateway") }
33
+ end
34
+
30
35
  describe "PublicRouteTable" do
31
36
  its(:route_table) { should have_property("VpcId").with_reference("VPC") }
32
37
  its(:route_table) { should have_tag("Application").with_reference("AWS::StackName") }
@@ -2,6 +2,7 @@ $:<< File.expand_path("../../../lib", __FILE__)
2
2
 
3
3
  require "chemtrail"
4
4
  require "chemtrail/rspec"
5
+ require "json"
5
6
 
6
7
  Dir.glob(File.expand_path("../../lib/templates/**/*_template.rb", __FILE__)).each { |t| require t }
7
8
 
@@ -0,0 +1,394 @@
1
+ {
2
+ "AWSTemplateFormatVersion" : "2010-09-09",
3
+
4
+ "Description" : "Sample template showing how to create a VPC environment for AWS OpsWorks. The stack contains 2 subnets: the first subnet is public and contains the load balancer, a NAT device for internet access from the private subnet. The second subnet is private. You will be billed for the AWS resources used if you create a stack from this template.",
5
+
6
+ "Parameters" : {
7
+
8
+ "NATInstanceType" : {
9
+ "Description" : "NAT Device EC2 instance type",
10
+ "Type" : "String",
11
+ "Default" : "m1.small",
12
+ "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
13
+ "ConstraintDescription" : "must be a valid EC2 instance type."
14
+ }
15
+ },
16
+
17
+ "Mappings" : {
18
+ "AWSNATAMI" : {
19
+ "us-east-1" : { "AMI" : "ami-c6699baf" },
20
+ "us-west-2" : { "AMI" : "ami-52ff7262" },
21
+ "us-west-1" : { "AMI" : "ami-3bcc9e7e" },
22
+ "eu-west-1" : { "AMI" : "ami-0b5b6c7f" },
23
+ "ap-southeast-1" : { "AMI" : "ami-02eb9350" },
24
+ "ap-southeast-2" : { "AMI" : "ami-ab990e91" },
25
+ "ap-northeast-1" : { "AMI" : "ami-14d86d15" },
26
+ "sa-east-1" : { "AMI" : "ami-0439e619" }
27
+ },
28
+
29
+ "AWSInstanceType2Arch" : {
30
+ "t1.micro" : { "Arch" : "64" },
31
+ "m1.small" : { "Arch" : "64" },
32
+ "m1.medium" : { "Arch" : "64" },
33
+ "m1.large" : { "Arch" : "64" },
34
+ "m1.xlarge" : { "Arch" : "64" },
35
+ "m2.xlarge" : { "Arch" : "64" },
36
+ "m2.2xlarge" : { "Arch" : "64" },
37
+ "m2.4xlarge" : { "Arch" : "64" },
38
+ "c1.medium" : { "Arch" : "64" },
39
+ "c1.xlarge" : { "Arch" : "64" },
40
+ "cc1.4xlarge" : { "Arch" : "64Cluster" },
41
+ "cc2.8xlarge" : { "Arch" : "64Cluster" },
42
+ "cg1.4xlarge" : { "Arch" : "64GPU" }
43
+ },
44
+
45
+ "SubnetConfig" : {
46
+ "VPC" : { "CIDR" : "10.0.0.0/16" },
47
+ "Public" : { "CIDR" : "10.0.0.0/24" },
48
+ "Private" : { "CIDR" : "10.0.1.0/24" }
49
+ }
50
+ },
51
+
52
+ "Resources" : {
53
+
54
+ "VPC" : {
55
+ "Type" : "AWS::EC2::VPC",
56
+ "Properties" : {
57
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "VPC", "CIDR" ]},
58
+ "Tags" : [
59
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
60
+ { "Key" : "Network", "Value" : "Public" }
61
+ ]
62
+ }
63
+ },
64
+
65
+ "PublicSubnet" : {
66
+ "Type" : "AWS::EC2::Subnet",
67
+ "Properties" : {
68
+ "VpcId" : { "Ref" : "VPC" },
69
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "Public", "CIDR" ]},
70
+ "Tags" : [
71
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
72
+ { "Key" : "Network", "Value" : "Public" }
73
+ ]
74
+ }
75
+ },
76
+
77
+ "InternetGateway" : {
78
+ "Type" : "AWS::EC2::InternetGateway",
79
+ "Properties" : {
80
+ "Tags" : [
81
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
82
+ { "Key" : "Network", "Value" : "Public" }
83
+ ]
84
+ }
85
+ },
86
+
87
+ "GatewayToInternet" : {
88
+ "Type" : "AWS::EC2::VPCGatewayAttachment",
89
+ "Properties" : {
90
+ "VpcId" : { "Ref" : "VPC" },
91
+ "InternetGatewayId" : { "Ref" : "InternetGateway" }
92
+ }
93
+ },
94
+
95
+ "PublicRouteTable" : {
96
+ "Type" : "AWS::EC2::RouteTable",
97
+ "Properties" : {
98
+ "VpcId" : { "Ref" : "VPC" },
99
+ "Tags" : [
100
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
101
+ { "Key" : "Network", "Value" : "Public" }
102
+ ]
103
+ }
104
+ },
105
+
106
+ "PublicRoute" : {
107
+ "Type" : "AWS::EC2::Route",
108
+ "Properties" : {
109
+ "RouteTableId" : { "Ref" : "PublicRouteTable" },
110
+ "DestinationCidrBlock" : "0.0.0.0/0",
111
+ "GatewayId" : { "Ref" : "InternetGateway" }
112
+ }
113
+ },
114
+
115
+ "PublicSubnetRouteTableAssociation" : {
116
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
117
+ "Properties" : {
118
+ "SubnetId" : { "Ref" : "PublicSubnet" },
119
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
120
+ }
121
+ },
122
+
123
+ "PublicNetworkAcl" : {
124
+ "Type" : "AWS::EC2::NetworkAcl",
125
+ "Properties" : {
126
+ "VpcId" : { "Ref" : "VPC" },
127
+ "Tags" : [
128
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
129
+ { "Key" : "Network", "Value" : "Public" }
130
+ ]
131
+ }
132
+ },
133
+
134
+ "InboundHTTPPublicNetworkAclEntry" : {
135
+ "Type" : "AWS::EC2::NetworkAclEntry",
136
+ "Properties" : {
137
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
138
+ "RuleNumber" : "100",
139
+ "Protocol" : "6",
140
+ "RuleAction" : "allow",
141
+ "Egress" : "false",
142
+ "CidrBlock" : "0.0.0.0/0",
143
+ "PortRange" : { "From" : "80", "To" : "80" }
144
+ }
145
+ },
146
+
147
+ "InboundHTTPSPublicNetworkAclEntry" : {
148
+ "Type" : "AWS::EC2::NetworkAclEntry",
149
+ "Properties" : {
150
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
151
+ "RuleNumber" : "101",
152
+ "Protocol" : "6",
153
+ "RuleAction" : "allow",
154
+ "Egress" : "false",
155
+ "CidrBlock" : "0.0.0.0/0",
156
+ "PortRange" : { "From" : "443", "To" : "443" }
157
+ }
158
+ },
159
+
160
+ "InboundSSHPublicNetworkAclEntry" : {
161
+ "Type" : "AWS::EC2::NetworkAclEntry",
162
+ "Properties" : {
163
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
164
+ "RuleNumber" : "102",
165
+ "Protocol" : "6",
166
+ "RuleAction" : "allow",
167
+ "Egress" : "false",
168
+ "CidrBlock" : "0.0.0.0/0",
169
+ "PortRange" : { "From" : "22", "To" : "22" }
170
+ }
171
+ },
172
+
173
+ "InboundEmphemeralPublicNetworkAclEntry" : {
174
+ "Type" : "AWS::EC2::NetworkAclEntry",
175
+ "Properties" : {
176
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
177
+ "RuleNumber" : "103",
178
+ "Protocol" : "6",
179
+ "RuleAction" : "allow",
180
+ "Egress" : "false",
181
+ "CidrBlock" : "0.0.0.0/0",
182
+ "PortRange" : { "From" : "1024", "To" : "65535" }
183
+ }
184
+ },
185
+
186
+ "OutboundPublicNetworkAclEntry" : {
187
+ "Type" : "AWS::EC2::NetworkAclEntry",
188
+ "Properties" : {
189
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
190
+ "RuleNumber" : "100",
191
+ "Protocol" : "6",
192
+ "RuleAction" : "allow",
193
+ "Egress" : "true",
194
+ "CidrBlock" : "0.0.0.0/0",
195
+ "PortRange" : { "From" : "0", "To" : "65535" }
196
+ }
197
+ },
198
+
199
+ "PublicSubnetNetworkAclAssociation" : {
200
+ "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
201
+ "Properties" : {
202
+ "SubnetId" : { "Ref" : "PublicSubnet" },
203
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" }
204
+ }
205
+ },
206
+
207
+ "PrivateSubnet" : {
208
+ "Type" : "AWS::EC2::Subnet",
209
+ "Properties" : {
210
+ "VpcId" : { "Ref" : "VPC" },
211
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "Private", "CIDR" ]},
212
+ "Tags" : [
213
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
214
+ { "Key" : "Name", "Value" : "Private" }
215
+ ]
216
+ }
217
+ },
218
+
219
+ "PrivateRouteTable" : {
220
+ "Type" : "AWS::EC2::RouteTable",
221
+ "Properties" : {
222
+ "VpcId" : { "Ref" : "VPC" },
223
+ "Tags" : [
224
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
225
+ { "Key" : "Network", "Value" : "Private" }
226
+ ]
227
+ }
228
+ },
229
+
230
+ "PrivateSubnetRouteTableAssociation" : {
231
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
232
+ "Properties" : {
233
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
234
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" }
235
+ }
236
+ },
237
+
238
+ "PrivateRoute" : {
239
+ "Type" : "AWS::EC2::Route",
240
+ "Properties" : {
241
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" },
242
+ "DestinationCidrBlock" : "0.0.0.0/0",
243
+ "InstanceId" : { "Ref" : "NATDevice" }
244
+ }
245
+ },
246
+
247
+ "PrivateNetworkAcl" : {
248
+ "Type" : "AWS::EC2::NetworkAcl",
249
+ "Properties" : {
250
+ "VpcId" : { "Ref" : "VPC" },
251
+ "Tags" : [
252
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
253
+ { "Key" : "Network", "Value" : "Private" }
254
+ ]
255
+ }
256
+ },
257
+
258
+ "InboundPrivateNetworkAclEntry" : {
259
+ "Type" : "AWS::EC2::NetworkAclEntry",
260
+ "Properties" : {
261
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" },
262
+ "RuleNumber" : "100",
263
+ "Protocol" : "6",
264
+ "RuleAction" : "allow",
265
+ "Egress" : "false",
266
+ "CidrBlock" : "0.0.0.0/0",
267
+ "PortRange" : { "From" : "0", "To" : "65535" }
268
+ }
269
+ },
270
+
271
+ "OutBoundPrivateNetworkAclEntry" : {
272
+ "Type" : "AWS::EC2::NetworkAclEntry",
273
+ "Properties" : {
274
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" },
275
+ "RuleNumber" : "100",
276
+ "Protocol" : "6",
277
+ "RuleAction" : "allow",
278
+ "Egress" : "true",
279
+ "CidrBlock" : "0.0.0.0/0",
280
+ "PortRange" : { "From" : "0", "To" : "65535" }
281
+ }
282
+ },
283
+
284
+ "PrivateSubnetNetworkAclAssociation" : {
285
+ "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
286
+ "Properties" : {
287
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
288
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" }
289
+ }
290
+ },
291
+
292
+ "NATIPAddress" : {
293
+ "Type" : "AWS::EC2::EIP",
294
+ "Properties" : {
295
+ "Domain" : "vpc",
296
+ "InstanceId" : { "Ref" : "NATDevice" }
297
+ }
298
+ },
299
+
300
+ "NATDevice" : {
301
+ "Type" : "AWS::EC2::Instance",
302
+ "Properties" : {
303
+ "InstanceType" : { "Ref" : "NATInstanceType" },
304
+ "SubnetId" : { "Ref" : "PublicSubnet" },
305
+ "SourceDestCheck" : "false",
306
+ "ImageId" : { "Fn::FindInMap" : [ "AWSNATAMI", { "Ref" : "AWS::Region" }, "AMI" ]},
307
+ "SecurityGroupIds" : [{ "Ref" : "NATSecurityGroup" }]
308
+ }
309
+ },
310
+
311
+ "NATSecurityGroup" : {
312
+ "Type" : "AWS::EC2::SecurityGroup",
313
+ "Properties" : {
314
+ "GroupDescription" : "Enable internal access to the NAT device",
315
+ "VpcId" : { "Ref" : "VPC" },
316
+ "SecurityGroupIngress" : [
317
+ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "SourceSecurityGroupId" : { "Ref" : "OpsWorksSecurityGroup" }} ,
318
+ { "IpProtocol" : "tcp", "FromPort" : "9418", "ToPort" : "9418", "SourceSecurityGroupId" : { "Ref" : "OpsWorksSecurityGroup" }} ,
319
+ { "IpProtocol" : "tcp", "FromPort" : "443", "ToPort" : "443", "SourceSecurityGroupId" : { "Ref" : "OpsWorksSecurityGroup" } } ],
320
+ "SecurityGroupEgress" : [
321
+ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0" } ,
322
+ { "IpProtocol" : "tcp", "FromPort" : "9418", "ToPort" : "9418", "CidrIp" : "0.0.0.0/0" } ,
323
+ { "IpProtocol" : "tcp", "FromPort" : "443", "ToPort" : "443", "CidrIp" : "0.0.0.0/0" } ]
324
+ }
325
+ },
326
+
327
+
328
+ "OpsWorksSecurityGroup" : {
329
+ "Type" : "AWS::EC2::SecurityGroup",
330
+ "Properties" : {
331
+ "GroupDescription" : "Allow the OpsWorks instances to access the NAT device",
332
+ "VpcId" : { "Ref" : "VPC" },
333
+ "SecurityGroupIngress" : [
334
+ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "SourceSecurityGroupId" : { "Ref" : "LoadBalancerSecurityGroup" }} ]
335
+ }
336
+ },
337
+
338
+ "ElasticLoadBalancer" : {
339
+ "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
340
+ "Properties" : {
341
+ "SecurityGroups" : [ { "Ref" : "LoadBalancerSecurityGroup" } ],
342
+ "Subnets" : [ { "Ref" : "PublicSubnet" } ],
343
+ "Listeners" : [ { "LoadBalancerPort" : "80", "InstancePort" : "80", "Protocol" : "HTTP" } ],
344
+ "HealthCheck" : {
345
+ "Target" : "HTTP:80/",
346
+ "HealthyThreshold" : "3",
347
+ "UnhealthyThreshold" : "5",
348
+ "Interval" : "90",
349
+ "Timeout" : "60"
350
+ }
351
+ }
352
+ },
353
+
354
+ "LoadBalancerSecurityGroup" : {
355
+ "Type" : "AWS::EC2::SecurityGroup",
356
+ "Properties" : {
357
+ "GroupDescription" : "Enable HTTP access on port 80",
358
+ "VpcId" : { "Ref" : "VPC" },
359
+ "SecurityGroupIngress" : [ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0" } ],
360
+ "SecurityGroupEgress" : [ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"} ]
361
+ }
362
+ }
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+ },
371
+
372
+ "Outputs" : {
373
+
374
+ "VPC" : {
375
+ "Description" : "VPC",
376
+ "Value" : {"Ref" : "VPC"}
377
+ },
378
+
379
+ "PublicSubnets" : {
380
+ "Description" : "Public Subnet",
381
+ "Value" : {"Ref" : "PublicSubnet" }
382
+ },
383
+
384
+ "PrivateSubnets" : {
385
+ "Description" : "Private Subnet",
386
+ "Value" : {"Ref" : "PrivateSubnet" }
387
+ },
388
+
389
+ "LoadBalancer" : {
390
+ "Description" : "Load Balancer",
391
+ "Value" : {"Ref" : "ElasticLoadBalancer" }
392
+ }
393
+ }
394
+ }
data/lib/chemtrail/cli.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require "thor"
2
2
  require "json"
3
3
  require "chemtrail"
4
+ require "aws-sdk-core"
4
5
 
5
6
  class Chemtrail::Cli < Thor
7
+ attr_writer :cloud_formation
8
+
6
9
  default_task :list
7
10
 
8
11
  desc "list", "Lists all available templates"
@@ -15,14 +18,33 @@ class Chemtrail::Cli < Thor
15
18
  desc "build TEMPLATE", "Builds the selected template"
16
19
  method_option :path, :default => File.expand_path("lib/templates")
17
20
  def build(template_name)
18
- require_templates_from(options[:path])
19
- template_class = fetch_template_class_named(template_name)
20
- template_json = JSON.pretty_generate(template_class.new.to_hash)
21
+ template_json = extract_template_json(template_name, options[:path])
21
22
  Kernel.puts(template_json)
22
23
  end
23
24
 
25
+ desc "validate TEMPLATE", "Validates the selected template with AWS"
26
+ method_option :path, :default => File.expand_path("lib/templates")
27
+ def validate(template_name)
28
+ template_json = extract_template_json(template_name, options[:path])
29
+ cloud_formation.validate_template(template_body: template_json)
30
+ Kernel.puts("Template #{template_name} is valid")
31
+ rescue Aws::CloudFormation::Errors::ValidationError => e
32
+ raise Thor::Error.new(e)
33
+ end
34
+
24
35
  protected
25
36
 
37
+ def extract_template_json(template_name, template_path)
38
+ require_templates_from(template_path)
39
+ template_class = fetch_template_class_named(template_name)
40
+ template_instance = template_class.new
41
+ JSON.pretty_generate(template_instance.to_hash)
42
+ end
43
+
44
+ def cloud_formation
45
+ @cloud_formation ||= Aws::CloudFormation.new
46
+ end
47
+
26
48
  def require_templates_from(path)
27
49
  Dir.glob(File.expand_path("**/*_template.rb", path)).each { |t| require t }
28
50
  end
@@ -1,3 +1,3 @@
1
1
  module Chemtrail
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -1,8 +1,13 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Chemtrail::Cli do
4
+ let(:taco_json) { JSON.pretty_generate(Tacos.new.to_hash) }
5
+ let(:fake_cloud_formation) { double(:cloud_formation) }
6
+
4
7
  subject(:cli) { Chemtrail::Cli.new }
5
8
 
9
+ before { cli.cloud_formation = fake_cloud_formation }
10
+
6
11
  describe "#list" do
7
12
  it "lists available templates" do
8
13
  got_tacos = false
@@ -13,7 +18,7 @@ describe Chemtrail::Cli do
13
18
 
14
19
  describe "#build" do
15
20
  it "builds the specified template" do
16
- Kernel.should_receive(:puts).with(JSON.pretty_generate(Tacos.new.to_hash))
21
+ Kernel.should_receive(:puts).with(taco_json)
17
22
  cli.build("tacos")
18
23
  end
19
24
 
@@ -23,4 +28,31 @@ describe Chemtrail::Cli do
23
28
  end
24
29
  end
25
30
  end
31
+
32
+ describe "#validate" do
33
+ before { Kernel.stub(:puts) }
34
+
35
+ it "validates verifies the specified template" do
36
+ fake_cloud_formation.should_receive(:validate_template).with(template_body: taco_json)
37
+ cli.validate("tacos")
38
+ end
39
+
40
+ context "when the template does not exist" do
41
+ it "blows up" do
42
+ expect { cli.build("an toenails") }.to raise_error(Thor::Error)
43
+ end
44
+ end
45
+
46
+ context "when the template validation blows up" do
47
+ before do
48
+ fake_cloud_formation.stub(:validate_template) do
49
+ raise Aws::CloudFormation::Errors::ValidationError.new("ugh swans")
50
+ end
51
+ end
52
+
53
+ it "blows up" do
54
+ expect { cli.build("sustainable hookahs") }.to raise_error(Thor::Error)
55
+ end
56
+ end
57
+ end
26
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chemtrail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doc Ritezel
@@ -115,6 +115,7 @@ files:
115
115
  - examples/lib/templates/opsworks_vpc/public_network.rb
116
116
  - examples/lib/templates/opsworks_vpc/public_network_acl.rb
117
117
  - examples/lib/templates/opsworks_vpc_template.rb
118
+ - examples/spec/integration/parity_spec.rb
118
119
  - examples/spec/lib/templates/opsworks_vpc/load_balancer_spec.rb
119
120
  - examples/spec/lib/templates/opsworks_vpc/nat_device_spec.rb
120
121
  - examples/spec/lib/templates/opsworks_vpc/opsworks_spec.rb
@@ -123,6 +124,7 @@ files:
123
124
  - examples/spec/lib/templates/opsworks_vpc/public_network_spec.rb
124
125
  - examples/spec/lib/templates/opsworks_vpc_template_spec.rb
125
126
  - examples/spec/spec_helper.rb
127
+ - examples/spec/support/OpsWorksinVPC.template
126
128
  - lib/chemtrail.rb
127
129
  - lib/chemtrail/class_name_inflector.rb
128
130
  - lib/chemtrail/cli.rb