enscalator 0.4.0.pre.alpha.pre.16

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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +9 -0
  4. data/.rubocop_todo.yml +59 -0
  5. data/.travis.yml +22 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +148 -0
  10. data/Rakefile +43 -0
  11. data/bin/console +11 -0
  12. data/bin/setup +7 -0
  13. data/enscalator.gemspec +57 -0
  14. data/exe/enscalator +13 -0
  15. data/lib/enscalator/core/cf_parameters.rb +146 -0
  16. data/lib/enscalator/core/cf_resources.rb +225 -0
  17. data/lib/enscalator/core/instance_type.rb +205 -0
  18. data/lib/enscalator/core/network_config.rb +21 -0
  19. data/lib/enscalator/core.rb +10 -0
  20. data/lib/enscalator/enapp.rb +248 -0
  21. data/lib/enscalator/helpers/dns.rb +62 -0
  22. data/lib/enscalator/helpers/stack.rb +107 -0
  23. data/lib/enscalator/helpers/sub_process.rb +72 -0
  24. data/lib/enscalator/helpers/wrappers.rb +55 -0
  25. data/lib/enscalator/helpers.rb +127 -0
  26. data/lib/enscalator/plugins/amazon_linux.rb +93 -0
  27. data/lib/enscalator/plugins/auto_scale.rb +80 -0
  28. data/lib/enscalator/plugins/core_os.rb +88 -0
  29. data/lib/enscalator/plugins/couchbase.rb +98 -0
  30. data/lib/enscalator/plugins/debian.rb +71 -0
  31. data/lib/enscalator/plugins/elastic_beanstalk.rb +74 -0
  32. data/lib/enscalator/plugins/elasticache.rb +168 -0
  33. data/lib/enscalator/plugins/elasticsearch_amazon.rb +75 -0
  34. data/lib/enscalator/plugins/elasticsearch_bitnami.rb +198 -0
  35. data/lib/enscalator/plugins/elasticsearch_opsworks.rb +225 -0
  36. data/lib/enscalator/plugins/elb.rb +139 -0
  37. data/lib/enscalator/plugins/nat_gateway.rb +71 -0
  38. data/lib/enscalator/plugins/rds.rb +141 -0
  39. data/lib/enscalator/plugins/redis.rb +38 -0
  40. data/lib/enscalator/plugins/rethink_db.rb +21 -0
  41. data/lib/enscalator/plugins/route53.rb +143 -0
  42. data/lib/enscalator/plugins/ubuntu.rb +85 -0
  43. data/lib/enscalator/plugins/user-data/elasticsearch +367 -0
  44. data/lib/enscalator/plugins/vpc_peering_connection.rb +48 -0
  45. data/lib/enscalator/plugins.rb +30 -0
  46. data/lib/enscalator/rich_template_dsl.rb +209 -0
  47. data/lib/enscalator/templates/vpc_peering.rb +112 -0
  48. data/lib/enscalator/templates.rb +20 -0
  49. data/lib/enscalator/version.rb +5 -0
  50. data/lib/enscalator/vpc.rb +11 -0
  51. data/lib/enscalator/vpc_with_nat_gateway.rb +311 -0
  52. data/lib/enscalator/vpc_with_nat_instance.rb +402 -0
  53. data/lib/enscalator.rb +103 -0
  54. metadata +427 -0
@@ -0,0 +1,402 @@
1
+ module Enscalator
2
+ module Templates
3
+ # Amazon AWS Virtual Private Cloud template with NAT instance
4
+ class VPCWithNATInstance < Enscalator::RichTemplateDSL
5
+ # Subnet size (256 addresses)
6
+ SUBNET_CIDR_BLOCK_SIZE = 24
7
+
8
+ # Template method
9
+ def tpl
10
+ description = <<-EOS.gsub(/^\s+\|/, '')
11
+ |AWS CloudFormation template for the VPC environment.
12
+ |For each availability zone stack creates: the public subnet, internet gateway, NAT EC2 instance
13
+ |internet access from private subnets, routing configuration for corresponding subnets
14
+ |and security groups.
15
+ EOS
16
+ value Description: description
17
+
18
+ nat_key_name = gen_ssh_key_name('vpc-nat', region, stack_name)
19
+ pre_run { create_ssh_key nat_key_name, region, force_create: false }
20
+
21
+ parameter_ec2_instance_type 'NAT', type: 'c4.large'
22
+
23
+ mapping 'AWSNATAMI',
24
+ 'us-east-1': { AMI: 'ami-68115b02' },
25
+ 'us-west-1': { AMI: 'ami-ef1a718f' },
26
+ 'us-west-2': { AMI: 'ami-77a4b816' },
27
+ 'eu-west-1': { AMI: 'ami-c0993ab3' },
28
+ 'eu-central-1': { AMI: 'ami-0b322e67' },
29
+ 'ap-northeast-1': { AMI: 'ami-f885ae96' },
30
+ 'ap-southeast-1': { AMI: 'ami-e2fc3f81' },
31
+ 'ap-southeast-2': { AMI: 'ami-e3217a80' },
32
+ 'sa-east-1': { AMI: 'ami-8631b5ea' }
33
+
34
+ mapping 'AWSRegionNetConfig', Core::NetworkConfig.mapping_vpc_net
35
+
36
+ resource 'VPC',
37
+ Type: 'AWS::EC2::VPC',
38
+ Properties: {
39
+ CidrBlock: find_in_map('AWSRegionNetConfig', ref('AWS::Region'), 'VPC'),
40
+ EnableDnsSupport: 'true',
41
+ EnableDnsHostnames: 'true',
42
+ Tags: [
43
+ {
44
+ Key: 'Name',
45
+ Value: aws_stack_name
46
+ },
47
+ {
48
+ Key: 'Application',
49
+ Value: aws_stack_name
50
+ },
51
+ {
52
+ Key: 'Network',
53
+ Value: 'Public'
54
+ }
55
+ ]
56
+ }
57
+
58
+ resource 'InternetGateway',
59
+ Type: 'AWS::EC2::InternetGateway',
60
+ Properties: {
61
+ Tags: [
62
+ {
63
+ Key: 'Name',
64
+ Value: 'Public Gateway'
65
+ },
66
+ {
67
+ Key: 'Application',
68
+ Value: aws_stack_name
69
+ },
70
+ {
71
+ Key: 'Network',
72
+ Value: 'Public'
73
+ }
74
+ ]
75
+ }
76
+
77
+ resource 'GatewayToInternet',
78
+ DependsOn: %w( VPC InternetGateway ),
79
+ Type: 'AWS::EC2::VPCGatewayAttachment',
80
+ Properties: {
81
+ VpcId: ref('VPC'),
82
+ InternetGatewayId: ref('InternetGateway')
83
+ }
84
+
85
+ resource 'PublicRouteTable',
86
+ DependsOn: ['VPC'],
87
+ Type: 'AWS::EC2::RouteTable',
88
+ Properties: {
89
+ VpcId: ref('VPC'),
90
+ Tags: [
91
+ {
92
+ Key: 'Name',
93
+ Value: 'Public'
94
+ },
95
+ {
96
+ Key: 'Application',
97
+ Value: aws_stack_name
98
+ },
99
+ {
100
+ Key: 'Network',
101
+ Value: 'Public'
102
+ }
103
+ ]
104
+ }
105
+
106
+ resource 'PublicRoute',
107
+ DependsOn: %w( PublicRouteTable InternetGateway ),
108
+ Type: 'AWS::EC2::Route',
109
+ Properties: {
110
+ RouteTableId: ref('PublicRouteTable'),
111
+ DestinationCidrBlock: '0.0.0.0/0',
112
+ GatewayId: ref('InternetGateway')
113
+ }
114
+
115
+ resource 'PublicNetworkAcl',
116
+ DependsOn: ['VPC'],
117
+ Type: 'AWS::EC2::NetworkAcl',
118
+ Properties: {
119
+ VpcId: ref('VPC'),
120
+ Tags: [
121
+ {
122
+ Key: 'Name',
123
+ Value: 'Public'
124
+ },
125
+ {
126
+ Key: 'Application',
127
+ Value: aws_stack_name
128
+ },
129
+ {
130
+ Key: 'Network',
131
+ Value: 'Public'
132
+ }
133
+ ]
134
+ }
135
+
136
+ resource 'InboundHTTPPublicNetworkAclEntry',
137
+ DependsOn: ['PublicNetworkAcl'],
138
+ Type: 'AWS::EC2::NetworkAclEntry',
139
+ Properties: {
140
+ NetworkAclId: ref('PublicNetworkAcl'),
141
+ RuleNumber: '100',
142
+ Protocol: '-1',
143
+ RuleAction: 'allow',
144
+ Egress: 'false',
145
+ CidrBlock: '0.0.0.0/0',
146
+ PortRange: { From: '0', To: '65535' }
147
+ }
148
+
149
+ resource 'OutboundHTTPPublicNetworkAclEntry',
150
+ DependsOn: ['PublicNetworkAcl'],
151
+ Type: 'AWS::EC2::NetworkAclEntry',
152
+ Properties: {
153
+ NetworkAclId: ref('PublicNetworkAcl'),
154
+ RuleNumber: '100',
155
+ Protocol: '-1',
156
+ RuleAction: 'allow',
157
+ Egress: 'true',
158
+ CidrBlock: '0.0.0.0/0',
159
+ PortRange: { From: '0', To: '65535' }
160
+ }
161
+
162
+ resource 'PrivateNetworkAcl',
163
+ DependsOn: ['VPC'],
164
+ Type: 'AWS::EC2::NetworkAcl',
165
+ Properties: {
166
+ VpcId: ref('VPC'),
167
+ Tags: [
168
+ {
169
+ Key: 'Name',
170
+ Value: 'Private'
171
+ },
172
+ {
173
+ Key: 'Application',
174
+ Value: aws_stack_name
175
+ },
176
+ {
177
+ Key: 'Network',
178
+ Value: 'Private'
179
+ }
180
+ ]
181
+ }
182
+
183
+ resource 'InboundPrivateNetworkAclEntry',
184
+ DependsOn: ['PrivateNetworkAcl'],
185
+ Type: 'AWS::EC2::NetworkAclEntry',
186
+ Properties: {
187
+ NetworkAclId: ref('PrivateNetworkAcl'),
188
+ RuleNumber: '100',
189
+ Protocol: '6',
190
+ RuleAction: 'allow',
191
+ Egress: 'false',
192
+ CidrBlock: '0.0.0.0/0',
193
+ PortRange: { From: '0', To: '65535' }
194
+ }
195
+
196
+ resource 'OutBoundPrivateNetworkAclEntry',
197
+ DependsOn: ['PrivateNetworkAcl'],
198
+ Type: 'AWS::EC2::NetworkAclEntry',
199
+ Properties: {
200
+ NetworkAclId: ref('PrivateNetworkAcl'),
201
+ RuleNumber: '100',
202
+ Protocol: '6',
203
+ RuleAction: 'allow',
204
+ Egress: 'true',
205
+ CidrBlock: '0.0.0.0/0',
206
+ PortRange: { From: '0', To: '65535' }
207
+ }
208
+
209
+ resource 'NATSecurityGroup',
210
+ DependsOn: ['PrivateSecurityGroup'],
211
+ Type: 'AWS::EC2::SecurityGroup',
212
+ Properties: {
213
+ GroupDescription: 'Enable internal access to the NAT device',
214
+ VpcId: ref('VPC'),
215
+ SecurityGroupIngress: [
216
+ {
217
+ IpProtocol: 'tcp',
218
+ FromPort: '22',
219
+ ToPort: '22',
220
+ SourceSecurityGroupId: ref('PrivateSecurityGroup')
221
+ },
222
+ {
223
+ IpProtocol: 'tcp',
224
+ FromPort: '80',
225
+ ToPort: '80',
226
+ SourceSecurityGroupId: ref('PrivateSecurityGroup')
227
+ },
228
+ {
229
+ IpProtocol: 'tcp',
230
+ FromPort: '443',
231
+ ToPort: '443',
232
+ SourceSecurityGroupId: ref('PrivateSecurityGroup')
233
+ },
234
+ {
235
+ IpProtocol: 'tcp',
236
+ FromPort: '465',
237
+ ToPort: '465',
238
+ SourceSecurityGroupId: ref('PrivateSecurityGroup')
239
+ },
240
+ {
241
+ IpProtocol: 'icmp',
242
+ FromPort: '-1',
243
+ ToPort: '-1',
244
+ CidrIp: '10.0.0.0/8'
245
+ }
246
+ ],
247
+ SecurityGroupEgress: [
248
+ {
249
+ IpProtocol: '-1',
250
+ FromPort: '0',
251
+ ToPort: '65535',
252
+ CidrIp: '0.0.0.0/0'
253
+ }
254
+ ],
255
+ Tags: [
256
+ {
257
+ Key: 'Name',
258
+ Value: 'NAT'
259
+ }
260
+ ]
261
+ }
262
+
263
+ resource 'PrivateSecurityGroup',
264
+ DependsOn: ['VPC'],
265
+ Type: 'AWS::EC2::SecurityGroup',
266
+ Properties: {
267
+ GroupDescription: 'Allow the Application instances to access the NAT device',
268
+ VpcId: ref('VPC'),
269
+ SecurityGroupEgress: [
270
+ {
271
+ IpProtocol: 'tcp',
272
+ FromPort: '0',
273
+ ToPort: '65535',
274
+ CidrIp: '10.0.0.0/8'
275
+ }
276
+ ],
277
+ SecurityGroupIngress: [
278
+ {
279
+ IpProtocol: 'tcp',
280
+ FromPort: '0',
281
+ ToPort: '65535',
282
+ CidrIp: '10.0.0.0/8'
283
+ }
284
+ ],
285
+ Tags: [
286
+ {
287
+ Key: 'Name',
288
+ Value: 'Private'
289
+ }
290
+ ]
291
+ }
292
+
293
+ current_cidr_block = Core::NetworkConfig.mapping_vpc_net[region.to_sym][:VPC]
294
+ public_cidr_blocks =
295
+ IPAddress(current_cidr_block).subnet(SUBNET_CIDR_BLOCK_SIZE).map(&:to_string).first(availability_zones.size)
296
+
297
+ availability_zones.zip(public_cidr_blocks).each do |pair, cidr_block|
298
+ suffix = pair.first
299
+ public_subnet_name = "PublicSubnet#{suffix.upcase}"
300
+ resource public_subnet_name,
301
+ DependsOn: ['VPC'],
302
+ Type: 'AWS::EC2::Subnet',
303
+ Properties: {
304
+ VpcId: ref('VPC'),
305
+ AvailabilityZone: join('', ref('AWS::Region'), suffix.to_s),
306
+ CidrBlock: cidr_block,
307
+ Tags: [
308
+ {
309
+ Key: 'Name',
310
+ Value: "Public #{suffix.upcase}"
311
+ },
312
+ {
313
+ Key: 'Application',
314
+ Value: aws_stack_name
315
+ },
316
+ {
317
+ Key: 'Network',
318
+ Value: 'Public'
319
+ }
320
+ ]
321
+ }
322
+
323
+ resource "PublicSubnetRouteTableAssociation#{suffix.upcase}",
324
+ DependsOn: [public_subnet_name, 'PublicRouteTable'],
325
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
326
+ Properties: {
327
+ SubnetId: ref(public_subnet_name),
328
+ RouteTableId: ref('PublicRouteTable')
329
+ }
330
+
331
+ nat_device_name = "NATDevice#{suffix.upcase}"
332
+ resource nat_device_name,
333
+ DependsOn: [public_subnet_name, 'NATSecurityGroup'],
334
+ Type: 'AWS::EC2::Instance',
335
+ Properties: {
336
+ InstanceType: ref('NATInstanceType'),
337
+ KeyName: nat_key_name,
338
+ SourceDestCheck: 'false',
339
+ ImageId: find_in_map('AWSNATAMI', ref('AWS::Region'), 'AMI'),
340
+ NetworkInterfaces: [
341
+ {
342
+ AssociatePublicIpAddress: 'true',
343
+ DeviceIndex: '0',
344
+ SubnetId: ref(public_subnet_name),
345
+ GroupSet: [ref('NATSecurityGroup')]
346
+ }
347
+ ],
348
+ Tags: [
349
+ {
350
+ Key: 'Name',
351
+ Value: nat_device_name
352
+ }
353
+ ]
354
+ }
355
+
356
+ private_route_table_name = "PrivateRouteTable#{suffix.upcase}"
357
+ resource private_route_table_name,
358
+ DependsOn: ['VPC'],
359
+ Type: 'AWS::EC2::RouteTable',
360
+ Properties: {
361
+ VpcId: ref('VPC'),
362
+ Tags: [
363
+ {
364
+ Key: 'Name',
365
+ Value: "Private #{suffix.upcase}"
366
+ },
367
+ {
368
+ Key: 'Application',
369
+ Value: aws_stack_name
370
+ },
371
+ {
372
+ Key: 'Network',
373
+ Value: 'Private'
374
+ }
375
+ ]
376
+ }
377
+
378
+ resource "PrivateRoute#{suffix.upcase}",
379
+ DependsOn: [private_route_table_name, nat_device_name],
380
+ Type: 'AWS::EC2::Route',
381
+ Properties: {
382
+ RouteTableId: ref(private_route_table_name),
383
+ DestinationCidrBlock: '0.0.0.0/0',
384
+ InstanceId: ref(nat_device_name)
385
+ }
386
+
387
+ output public_subnet_name,
388
+ Description: "Created Subnet #{suffix.upcase}",
389
+ Value: ref(public_subnet_name)
390
+ end
391
+
392
+ output 'VpcId',
393
+ Description: 'Created VPC',
394
+ Value: ref('VPC')
395
+
396
+ output 'PrivateSecurityGroup',
397
+ Description: 'SecurityGroup to add private resources',
398
+ Value: ref('PrivateSecurityGroup')
399
+ end # def tpl
400
+ end # class VPC
401
+ end # module Templates
402
+ end # module Enscalator
data/lib/enscalator.rb ADDED
@@ -0,0 +1,103 @@
1
+ require 'trollop'
2
+ require 'ipaddr'
3
+ require 'ipaddress'
4
+ require 'digest'
5
+ require 'aws-sdk'
6
+ require 'active_support'
7
+ require 'active_support/core_ext'
8
+ require 'active_support/inflector'
9
+ require 'active_support/inflector/inflections'
10
+ require 'enscalator/version'
11
+ require 'enscalator/helpers'
12
+ require 'enscalator/core'
13
+ require 'enscalator/plugins'
14
+ require 'enscalator/rich_template_dsl'
15
+ require 'enscalator/vpc_with_nat_gateway'
16
+ require 'enscalator/vpc_with_nat_instance'
17
+ require 'enscalator/vpc'
18
+ require 'enscalator/enapp'
19
+ require 'enscalator/templates'
20
+
21
+ # Namespace for Enscalator related code
22
+ module Enscalator
23
+ # Main method to actually run Enscalator
24
+ # @param [Array] argv list of command-line arguments
25
+ def self.run!(argv)
26
+ argv_dup = argv.dup
27
+ display_name = name.downcase
28
+ parser = Trollop::Parser.new do
29
+ banner "Usage: #{display_name} [arguments]"
30
+
31
+ opt :list_templates, 'List all available templates', default: false, short: 'l'
32
+ opt :template, 'Template name', type: String, short: 't'
33
+ opt :template_file, 'Template filename', type: String, short: 'f'
34
+ opt :region, 'AWS Region', type: String, default: 'us-east-1', short: 'r'
35
+ opt :parameters, "Parameters 'Key1=Value1;Key2=Value2'", type: String, short: 'p'
36
+ opt :stack_name, 'Stack name', type: String, short: 's'
37
+ opt :private_hosted_zone, "Private hosted zone (e.x. 'default-vpc.internal')", type: String, short: 'z'
38
+ opt :public_hosted_zone, 'Public hosted zone', type: String, short: 'g'
39
+ opt :create_stack, 'Create the stack', default: false, short: 'c'
40
+ opt :update_stack, 'Update already deployed stack', default: false, short: 'u'
41
+ opt :pre_run, 'Use pre-run hooks', default: true, short: 'e'
42
+ opt :post_run, 'Use post-run hooks', default: true, short: 'o'
43
+ opt :expand, 'Print generated JSON template', default: false, short: 'x'
44
+ opt :capabilities, 'AWS capabilities', default: 'CAPABILITY_IAM', short: 'a'
45
+ opt :vpc_stack_name, 'VPC stack name', default: 'default-vpc', short: 'n'
46
+ opt :availability_zone, 'Deploy to specific availability zone', default: 'all', short: 'd'
47
+ opt :profile, 'Use a specific profile from your credential file', type: String, default: nil
48
+
49
+ conflicts :list_templates, :create_stack, :update_stack, :expand
50
+ end
51
+
52
+ opts = Trollop.with_standard_exception_handling(parser) do
53
+ fail Trollop::HelpNeeded if argv.empty?
54
+ parser.parse argv
55
+ end
56
+
57
+ if opts[:availability_zone_given]
58
+ valid_values = ('a'..'e').to_a << 'all'
59
+ unless valid_values.include? opts[:availability_zone]
60
+ STDERR.puts %(Availability zone can be only one off "#{valid_values.join(',')}")
61
+ exit
62
+ end
63
+ end
64
+
65
+ # fallback to create_stack action when no action was given
66
+ if !opts[:create_stack] && !opts[:update_stack]
67
+ opts[:create_stack] = true
68
+ end
69
+
70
+ # load template from given file and update template list
71
+ if opts[:template_file]
72
+ unless File.exist?(opts[:template_file])
73
+ abort(format('Unable to find file "%s"', opts[:template_file]))
74
+ end
75
+ load(opts[:template_file])
76
+ unless Enscalator::Templates.all_valid?
77
+ STDERR.puts 'Some templates missing required tpl method:'
78
+ Enscalator::Templates.all.select { |a| !a.instance_methods.include?(:tpl) }.each do |tpl|
79
+ STDERR.puts tpl.name.demodulize
80
+ end
81
+ exit
82
+ end
83
+ end
84
+
85
+ templates = Enscalator::Templates.constants.map(&:to_s)
86
+
87
+ if opts[:list_templates]
88
+ STDERR.puts 'Available templates:'
89
+ STDERR.puts templates.sort
90
+ exit
91
+ end
92
+
93
+ if opts[:template] && templates.include?(opts[:template])
94
+ # for stack_name use template name as a base and convert it from camelcase to underscore case
95
+ opts[:stack_name] ||= opts[:template].underscore.gsub(/[_]/, '-')
96
+ Object.const_get("Enscalator::Templates::#{opts[:template]}").new(opts.merge(ARGV: argv_dup)).exec!
97
+ elsif opts[:template_given] && !opts[:template].empty?
98
+ STDERR.puts %(Template "#{opts[:template]}" doesn't exist)
99
+ else
100
+ STDERR.puts 'Template name cannot be an empty string'
101
+ end
102
+ end
103
+ end