chemtrail 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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