vominator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +5 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +116 -0
  7. data/Rakefile +12 -0
  8. data/bin/vominate +12 -0
  9. data/circle.yml +5 -0
  10. data/lib/ec2.rb +12 -0
  11. data/lib/ec2/instances.rb +362 -0
  12. data/lib/ec2/security_groups.rb +314 -0
  13. data/lib/ec2/ssm.rb +81 -0
  14. data/lib/vominator/aws.rb +15 -0
  15. data/lib/vominator/constants.rb +53 -0
  16. data/lib/vominator/ec2.rb +308 -0
  17. data/lib/vominator/instances.rb +34 -0
  18. data/lib/vominator/route53.rb +125 -0
  19. data/lib/vominator/security_groups.rb +26 -0
  20. data/lib/vominator/ssm.rb +57 -0
  21. data/lib/vominator/version.rb +3 -0
  22. data/lib/vominator/vominator.rb +74 -0
  23. data/lib/vominator/vpc.rb +82 -0
  24. data/lib/vpc.rb +8 -0
  25. data/lib/vpc/create.rb +188 -0
  26. data/spec/lib/instances_spec.rb +2 -0
  27. data/spec/lib/vominator/aws_spec.rb +6 -0
  28. data/spec/lib/vominator/ec2_spec.rb +783 -0
  29. data/spec/lib/vominator/instances_spec.rb +96 -0
  30. data/spec/lib/vominator/route53_spec.rb +64 -0
  31. data/spec/lib/vominator/ssm_spec.rb +95 -0
  32. data/spec/lib/vominator/vominator_spec.rb +209 -0
  33. data/spec/spec_helper.rb +103 -0
  34. data/spec/support/matchers/exit_with_code.rb +24 -0
  35. data/test/puke/cloud-configs/.gitkeep +0 -0
  36. data/test/puke/cloud-configs/cloud-config-example.erb +63 -0
  37. data/test/puke/config.yaml +16 -0
  38. data/test/puke/products/sample-api/instances.yaml +37 -0
  39. data/test/puke/products/sample-api/security_groups.yaml +19 -0
  40. data/test/vominator.yaml +7 -0
  41. data/vominator.gemspec +34 -0
  42. metadata +259 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 66a177e22bafb6bb313e6bb9f3438054424e194a
4
+ data.tar.gz: dfc070049d6062072a57dd3d76ba086f3204b802
5
+ SHA512:
6
+ metadata.gz: 66a135ad654b22b8877e3eb6dbaf3a0998b10f55dfb59225591e61e3426153242b8ce0d4c3d5dd78fdcefaa1b8e70e6b8a8f6bd433107577cc22fd62652c4bc6
7
+ data.tar.gz: f6ed4717fcf783aa9d4215d3880874adcaf87a0bfa6f8937e2e6f0cad992faafc3a8ce2761fb5ad6d4546bba2a54b8b8607fe63ae2ebe3fe23357c73a38b3eb1
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ *.swp
24
+ .idea
25
+ rspec.xml
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --format documentation
3
+ --format RspecJunitFormatter
4
+ --out rspec.xml
5
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vominator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Chris Kelly
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Vominator
2
+
3
+
4
+ **NOTE** This is still in development, and for now depends on VPCs and such being setup by Vominator from the start. Documentation is still a WIP but the below should be enough to get someone going.
5
+
6
+ A CLI utility for managing AWS resources from yaml templates. This allow you to define resources within a dev VPC, and replicate that to a QA/Staging/Prod VPC without any additional work.
7
+
8
+ This CLI utility expects that your VPCs are setup in a specific way. You should use this tool to create a new VPC before launching resources into it.
9
+
10
+ This CLI utility will require you to define resources via YAML files. This should be kept in a repo of yours. You can see https://github.com/digitaljanitors/sample-puke for a reference.
11
+
12
+
13
+ ## Installation
14
+
15
+ See Usage for details about puke
16
+
17
+ 1. `$ gem install vominator`
18
+ 2. Create ~/.vominator.yaml
19
+ ```
20
+ ---
21
+ access_key_id: AWS_SECRET_KEY
22
+ secret_access_key: AWS_SECRET_ACCESS_KEY
23
+ configuration_path: Location to puke
24
+ key_pair_name: infrastructure@example.com
25
+ ```
26
+ ## Usage
27
+
28
+ Everything with Vominator revolves around the concept of defining products. These products are a logical grouping of resources that describe how your product is deployed and accessed. These products are then associated with an environment so that you can quickly replicate resources between VPCs.
29
+
30
+ You will want to create a directory somewhere on your file system that contains your "puke". This is the code that describes how your environment should be built. You can see an example repo here: https://github.com/digitaljanitors/sample-puke
31
+
32
+ ```
33
+ ├── config.yaml
34
+ └── products
35
+ └── sample-api
36
+ ├── instances.yaml
37
+ └── security_groups.yaml
38
+ ```
39
+
40
+ In short, under products you create a directory for each new product. Most likely you will have a base product that gets associated to every VPC. You would then create an instances.yaml and security_groups.yaml file that describes everything you want as your base. This would generally be a jumpbox and/or VPN server, and possibly groups such as outbound-connections. config.yaml will be generated for you by vominator using the vpc creation command.
41
+
42
+ **This repo should be checked in to your own revision control system.**
43
+
44
+ ### Creating your VPCs
45
+
46
+ Vominator should be used to bootstrap and build your VPCs that will be managed.
47
+
48
+ This will do the following...
49
+ * Create a VPC using the specified /16 network block within the specified region.
50
+ * Create a route53 zone equal to ${environment}.${parent-domain}. If we cannot find the parent domains zone file in route53 you will be prompted with the approriate details to configure your parent zone file.
51
+ * Create an IGW device
52
+
53
+ For each specified or auto detected availability zone for the account this will do several things...
54
+ * Create a public /24 subnet starting at 10.x.1.0/24
55
+ * Create a private /24 subnet started at 10.x.11.0/24
56
+ * Create a NAT gateway device for the AZ and configure a routing table for that zone.
57
+
58
+ Vominator will then output a block of YAML that can be put into your puke specific config.yaml.
59
+
60
+ ```
61
+ $ vominate vpc create -h
62
+ Usage: vominate vpc create [options]
63
+ -e, --environment ENVIRONMENT REQUIRED: The environment which you want to create a VPC for. IE foo
64
+ --region Region REQUIRED: The AWS Region that you want to create the VPC in. IE us-east-1
65
+ --availability-zones AVAILABILITY ZONES
66
+ OPTIONAL: A comma delimited list of specific availability zones that you want to prepare. If you don't specify then we will use all that are available. IE us-east-1c,us-east-1d,us-east-1e
67
+ --parent-domain PARENT DOMAIN
68
+ REQUIRED: The parent domain name that will be used to create a seperate subdomain zone file for the new environment. IE, if you provide foo.org and your environment as bar, this will yield a new Route 53 zone file called bar.foo.org
69
+ --cidr-block CIDR Block REQUIRED: The network block for the new environment. This must be a /16 and the second octet should be unique for this environment. IE. 10.123.0.0/16
70
+ -d, --debug OPTIONAL: debug output
71
+ -h, --help OPTIONAL: Display this screen
72
+ ```
73
+
74
+ ### Managing your security groups
75
+ Security groups get defined in your security_groups.yaml file for each product. You can reference the sample puke to get an idea of whats possible.
76
+
77
+ ```
78
+ $ vominate ec2 security_groups -h
79
+ Usage: vominate ec2 security_groups [options]
80
+ -p, --product PRODUCT REQUIRED: The product which you want to manage security groups for
81
+ -e, --environment ENVIRONMENT REQUIRED: The environment which you want to manage security groups for
82
+ --security-groups GROUPS OPTIONAL: Comma Delimited list of security groups
83
+ --delete Enable Deletions. This should be used with care
84
+ -t, --test OPTIONAL: Test run. Show what would be changed without making any actual changes
85
+ -l, --list OPTIONAL: List out products and environments
86
+ --verbose OPTIONAL: Show all security group rules in tables
87
+ -d, --debug OPTIONAL: debug output
88
+ -h, --help OPTIONAL: Display this screen
89
+
90
+ ```
91
+ ### Managing your instances
92
+ Instances are managed in your instances.yaml file for each product. You can reference the sample puke to get an idea of whats possible.
93
+ ```
94
+ $ vominate instances -h
95
+ Usage: vominate instance [options]
96
+ -p, --product PRODUCT REQUIRED: The product which you want to manage instances for
97
+ -e, --environment ENVIRONMENT REQUIRED: The environment which you want to manage instances for
98
+ -s, --servers SERVERS OPTIONAL: Comma Delimited list of servers that you want to manage instances for
99
+ -t, --test OPTIONAL: Test run. Show what would be changed without making any actual changes
100
+ --fix-security-groups OPTIONAL: Fix an instances security groups
101
+ --disable-term-protection OPTIONAL: This will disable termination protection on the targeted instances
102
+ --terminate OPTIONAL: This will terminate the specified instances. Must be combined with -s
103
+ --rebuild OPTIONAL: This will terminate and relaunch the specified instances. Must be combined with -s
104
+ -l, --list OPTIONAL: List out products and environments
105
+ -d, --debug OPTIONAL: debug output
106
+ -h, --help OPTIONAL: Display this screen
107
+ ```
108
+
109
+
110
+ ## Contributing
111
+
112
+ 1. Fork it ( https://github.com/[my-github-username]/vominator/fork )
113
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
114
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
115
+ 4. Push to the branch (`git push origin my-new-feature`)
116
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+ rescue LoadError
10
+ # no rspec available
11
+ end
12
+
data/bin/vominate ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ aws_module = ARGV[0] || nil
4
+
5
+ case aws_module
6
+ when 'ec2'
7
+ require_relative '../lib/ec2'
8
+ when 'vpc'
9
+ require_relative '../lib/vpc'
10
+ else
11
+ puts 'Module not found. Currently supported modules are: ec2 vpc'
12
+ end
data/circle.yml ADDED
@@ -0,0 +1,5 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.1.2
4
+ environment:
5
+ VOMINATOR_CONFIG: test/vominator.yaml
data/lib/ec2.rb ADDED
@@ -0,0 +1,12 @@
1
+ vominator_module = ARGV[1] || nil
2
+
3
+ case vominator_module
4
+ when 'instances'
5
+ require_relative '../lib/ec2/instances'
6
+ when 'ssm'
7
+ require_relative '../lib/ec2/ssm'
8
+ when 'security_groups'
9
+ require_relative '../lib/ec2/security_groups'
10
+ else
11
+ puts 'Module not found. Currently supported modules are: instances, ssm, security_groups'
12
+ end
@@ -0,0 +1,362 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'colored'
4
+ require_relative '../vominator/constants'
5
+ require_relative '../vominator/aws'
6
+ require_relative '../vominator/ec2'
7
+ require_relative '../vominator/instances'
8
+ require_relative '../vominator/route53'
9
+ require_relative '../vominator/ssm'
10
+
11
+ options = {}
12
+
13
+ OptionParser.new do |opts|
14
+ opts.banner = 'Usage: vominate instance [options]'.yellow
15
+
16
+ opts.on('-pPRODUCT', '--product PRODUCT', 'REQUIRED: The product which you want to manage instances for') do |value|
17
+ options[:product] = value
18
+ end
19
+
20
+ opts.on('-eENVIRONMENT', '--environment ENVIRONMENT', 'REQUIRED: The environment which you want to manage instances for') do |value|
21
+ options[:environment] = value
22
+ end
23
+
24
+ opts.on('-sSERVERS', '--servers SERVERS', 'OPTIONAL: Comma Delimited list of servers that you want to manage instances for') do |value|
25
+ options[:servers] = value.split(',')
26
+ end
27
+
28
+ opts.on('-t', '--test', 'OPTIONAL: Test run. Show what would be changed without making any actual changes') do
29
+ options[:test] = true
30
+ end
31
+
32
+ opts.on('--fix-security-groups', 'OPTIONAL: Fix an instances security groups') do
33
+ options[:fix_security_groups] = true
34
+ end
35
+
36
+ opts.on('--disable-term-protection', 'OPTIONAL: This will disable termination protection on the targeted instances') do
37
+ options[:disable_term_protection] = true
38
+ end
39
+
40
+ opts.on('--terminate', 'OPTIONAL: This will terminate the specified instances.') do
41
+ options[:terminate] = true
42
+ end
43
+
44
+ opts.on('--rebuild', 'OPTIONAL: This will terminate and relaunch the specified instances.') do
45
+ options[:rebuild] = true
46
+ end
47
+
48
+ opts.on('-l', '--list', 'OPTIONAL: List out products and environments') do
49
+ options[:list] = true
50
+ end
51
+
52
+ opts.on('-d', '--debug', 'OPTIONAL: debug output') do
53
+ options[:debug] = true
54
+ end
55
+
56
+ opts.on_tail(:NONE, '-h', '--help', 'OPTIONAL: Display this screen') do
57
+ puts opts
58
+ exit
59
+ end
60
+
61
+ begin
62
+ opts.parse!
63
+ throw Exception unless ((options.include? :environment) && (options.include? :product)) || options[:list]
64
+ if options[:terminate] || options[:rebuild]
65
+ throw Exception unless (options[:disable_term_protection])
66
+ end
67
+
68
+ rescue
69
+ puts opts
70
+ exit
71
+ end
72
+ end
73
+
74
+ TEST = options[:test]
75
+ def test?(message)
76
+ LOGGER.test(message) if TEST
77
+ TEST
78
+ end
79
+
80
+
81
+ if options[:list]
82
+ data = {}
83
+ PUKE_CONFIG.keys.each do |environment|
84
+ LOGGER.info "--#{environment}"
85
+ products = PUKE_CONFIG[environment]['products'] || Array.new
86
+ products.each do |product|
87
+ LOGGER.info " --#{product}"
88
+ end
89
+ end
90
+ exit(1)
91
+ end
92
+
93
+ puke_config = Vominator.get_puke_variables(options[:environment])
94
+
95
+ #TODO: Validate Environment and Product
96
+ LOGGER.info("Working on #{options[:product]} in #{options[:environment]}.")
97
+
98
+
99
+ unless test?('Vominator is running in test mode. It will NOT make any changes.')
100
+ LOGGER.warning('WARNING: Vominator will make changes to your environment. Please run test mode first if you are unsure.')
101
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
102
+ exit(1)
103
+ end
104
+ end
105
+
106
+ if options[:servers]
107
+ instances = Vominator::Instances.get_instances(options[:environment], options[:product], options[:servers])
108
+ else
109
+ instances = Vominator::Instances.get_instances(options[:environment], options[:product], false)
110
+ end
111
+
112
+ unless instances
113
+ LOGGER.fatal('Unable to load instances. Make sure the product is correctly defined for the environment you have selected.')
114
+ end
115
+
116
+ #Get ec2 connection, which is then passed to specific functions. Maybe a better way to do this?
117
+ ec2 = Aws::EC2::Resource.new(region: puke_config['region_name'])
118
+ ec2_client = Aws::EC2::Client.new(region: puke_config['region_name'])
119
+
120
+ #Get some basic metadata about our existing instances in the account. Maybe look for ways to filter this for faster API response.
121
+ ec2_instances = Vominator::EC2.get_instances(ec2)
122
+
123
+ #Get route53 connection which is then passed to specific functions. Maybe a better way to do this?
124
+ r53 = Aws::Route53::Client.new(region: puke_config['region_name'])
125
+
126
+ #Get existing DNS entries for the zone.
127
+ route53_records = Vominator::Route53.get_records(r53, puke_config['zone'])
128
+
129
+ #Get SSM connection
130
+ ssm = Aws::SSM::Client.new(region: puke_config['region_name'])
131
+
132
+ #Get existing Subnets for the VPC
133
+ existing_subnets = Vominator::EC2.get_subnets(ec2, puke_config['vpc_id'])
134
+
135
+ #Get existing Security Groups for the VPC
136
+ vpc_security_groups = Vominator::EC2.get_security_group_name_ids_hash(ec2, puke_config['vpc_id'])
137
+
138
+ instances.each do |instance|
139
+ hostname = instance.keys[0]
140
+ fqdn = "#{hostname}.#{puke_config['domain']}"
141
+ instance_type = instance['type'][options[:environment]]
142
+ instance_ip = instance['ip'].sub('OCTET',puke_config['octet'])
143
+ instance_security_groups = instance['security_groups'].map { |sg| sg}.uniq.sort
144
+ ebs_optimized = instance['ebs_optimized'].nil? ? false : instance['ebs_optimized']
145
+ source_dest_check = instance['source_dest_check'].nil? ? true : instance['source_dest_check']
146
+ instance_ebs_volumes = instance['ebs'].nil? ? [] : instance['ebs']
147
+ key_name = Vominator.get_key_pair(VOMINATOR_CONFIG)
148
+ ssm_documents = instance['ssm_documents'].nil? ? [] : instance['ssm_documents']
149
+
150
+ LOGGER.info("Working on #{fqdn}")
151
+
152
+ if instance['environment'] && !instance['environment'].include?(options[:environment])
153
+ LOGGER.info("#{fqdn} is not marked for deployment in #{options[:environment]}")
154
+ next
155
+ end
156
+
157
+ if instance_type.nil?
158
+ LOGGER.error("No instance size definition for #{fqdn}")
159
+ next
160
+ end
161
+
162
+ if instance['ami']
163
+ ami = instance['ami']
164
+ else
165
+ ami = Vominator::EC2.get_ami(puke_config,instance_type,instance['family'])
166
+ end
167
+
168
+ #Check to see if the subnet exists for the instance. If not we should create it.
169
+ subnet = "#{instance_ip.rpartition('.')[0]}.0/24"
170
+ unless existing_subnets[subnet]
171
+ unless test?("Would create a subnet for #{subnet} in #{instance['az']} and associate with the appropriate routing table")
172
+ existing_subnets[subnet] = Vominator::EC2.create_subnet(ec2, subnet, instance['az'], puke_config['vpc_id'], puke_config['route_tables'][instance['az']])
173
+ LOGGER.success("Created #{subnet} in #{instance['az']} for #{fqdn}")
174
+ end
175
+ end
176
+
177
+ #If the instance exists, perform verification and other tasks on that instance
178
+ if ec2_instances[instance_ip]
179
+
180
+ ec2_instance = Vominator::EC2.get_instance(ec2, ec2_instances[instance_ip][:instance_id])
181
+ ec2_instance_security_groups = ec2_instances[instance_ip][:security_groups].uniq.sort
182
+ ec2_instance_ebs_volumes = Vominator::EC2.get_instance_ebs_volumes(ec2, ec2_instances[instance_ip][:instance_id])
183
+
184
+ if options[:disable_term_protection]
185
+ unless test?("Would disable instance termination protection for #{fqdn}")
186
+ Vominator::EC2.set_termination_protection(ec2_client, ec2_instance.id, false)
187
+ LOGGER.success("Disabled instance termination protection for #{fqdn}")
188
+ end
189
+ else
190
+ unless Vominator::EC2.get_termination_protection(ec2_client, ec2_instance.id)
191
+ unless test?("Would enable instance termination protection for #{fqdn}")
192
+ Vominator::EC2.set_termination_protection(ec2_client, ec2_instance.id, true)
193
+ LOGGER.success("Enabled instance termination protection for #{fqdn}")
194
+ end
195
+ end
196
+ end
197
+
198
+ if options[:terminate] #check if the instance even exists?
199
+ unless test?("Would terminate #{fqdn}")
200
+ if Vominator::EC2.terminate_instance(ec2,ec2_instances[instance_ip][:instance_id])
201
+ LOGGER.success("Succesfully terminated #{fqdn}")
202
+ LOGGER.info('Performing Cleanup Tasks...')
203
+ if Vominator::Route53.delete_record(r53,puke_config['zone'],fqdn, instance_ip)
204
+ LOGGER.success("Removed DNS entry for #{fqdn}")
205
+ end
206
+ else
207
+ LOGGER.fatal("Failed to terminate #{fqdn}")
208
+ end
209
+ #TODO: Should include deleting chef client and node.
210
+ end
211
+ next
212
+ end
213
+
214
+ if options[:rebuild]
215
+ unless test?("Would rebuild #{fqdn}")
216
+ if Vominator::EC2.terminate_instance(ec2,ec2_instances[instance_ip][:instance_id])
217
+ LOGGER.success("Succesfully terminated #{fqdn}")
218
+ LOGGER.info("Triggering rebuild of #{fqdn}")
219
+ ec2_instances.delete(instance_ip)
220
+ else
221
+ LOGGER.fatal("Failed to terminate #{fqdn}")
222
+ end
223
+ #TODO: Should include deleting chef client and node.
224
+ end
225
+ options[:rebuild] = false
226
+ options[:disable_term_protection] = false
227
+ redo
228
+ end
229
+
230
+ if ec2_instance.instance_type != instance_type
231
+ unless test?("Would resize #{fqdn} from an #{ec2_instance.instance_type} to an #{instance_type}")
232
+ LOGGER.info("Resizing #{fqdn} from an #{ec2_instance.instance_type} to an #{instance_type}")
233
+ if Vominator::EC2.set_instance_type(ec2, ec2_instance.id, instance_type, fqdn) == instance_type
234
+ LOGGER.success("Succesfully resized #{fqdn} to #{instance_type}")
235
+ else
236
+ LOGGER.fatal("Failed to resize #{fqdn} to #{instance_type}")
237
+ end
238
+ end
239
+ end
240
+
241
+ if instance['eip'] && ec2_instance.public_ip_address.nil?
242
+ unless test?("Would create and associate a public IP for #{fqdn}")
243
+ LOGGER.info("Associating a public IP for #{fqdn}")
244
+ eip = Vominator::EC2.assign_public_ip(ec2_client, ec2_instance.id)
245
+ if eip
246
+ LOGGER.success("Successfully associated #{eip} to #{fqdn}")
247
+ else
248
+ LOGGER.fatal("An error occured associating a public IP for #{fqdn}")
249
+ end
250
+ end
251
+ elsif !instance['eip'] && ec2_instance.public_ip_address
252
+ unless test?("Would remove the elastic IP for #{fqdn}")
253
+ LOGGER.info("Removing public IP from #{fqdn}")
254
+ if Vominator::EC2.remove_public_ip(ec2_client, ec2_instance.id)
255
+ LOGGER.success("Successfully removed the public IP for #{fqdn}")
256
+ else
257
+ LOGGER.fatal("Failed to remove the public IP for #{fqdn}")
258
+ end
259
+ end
260
+ end
261
+
262
+ unless ec2_instance.source_dest_check == source_dest_check
263
+ unless test?("Would set the source_dest_check to #{source_dest_check}")
264
+ if Vominator::EC2.set_source_dest_check(ec2, ec2_instance.id, source_dest_check) == source_dest_check
265
+ LOGGER.success("Succesfully set source destination check to #{source_dest_check} for #{fqdn}")
266
+ else
267
+ LOGGER.fatal("Failed to set source destination check to #{source_dest_check} for #{fqdn}")
268
+ end
269
+ end
270
+ end
271
+
272
+ unless ec2_instance.ebs_optimized == ebs_optimized
273
+ unless test?("Would set EBS optimization to #{ebs_optimized}")
274
+ if Vominator::EC2.set_ebs_optimized(ec2, ec2_instance.id, ebs_optimized, fqdn) == ebs_optimized
275
+ LOGGER.success("Succesfully set EBS optimization to #{ebs_optimized} for #{fqdn}")
276
+ else
277
+ LOGGER.fatal("Failed to set EBS optimization to #{ebs_optimized} for #{fqdn}")
278
+ end
279
+ end
280
+ end
281
+
282
+ unless ec2_instance_security_groups == instance_security_groups
283
+ LOGGER.info("Security group mismatch detected for #{fqdn}")
284
+ sg_missing = instance_security_groups - ec2_instance_security_groups
285
+ sg_undefined = ec2_instance_security_groups - instance_security_groups
286
+
287
+ if sg_missing.count > 0
288
+ unless test?("Would add #{sg_missing.join(', ')} to #{fqdn}")
289
+ LOGGER.info("#{fqdn} is missing the following security groups: #{sg_missing.join(', ')}")
290
+ updated_groups = instance_security_groups - Vominator::EC2.set_security_groups(ec2, ec2_instance.id, instance_security_groups, vpc_security_groups)
291
+ if updated_groups.count > 0
292
+ LOGGER.fatal "Failed to set #{updated_groups.join(', ')} for #{fqdn}"
293
+ else
294
+ LOGGER.success "Succesfully set security groups for #{fqdn}"
295
+ end
296
+ end
297
+ end
298
+
299
+ if sg_undefined.count > 0
300
+ unless test?("Would remove #{sg_undefined.join(', ')} from #{fqdn}")
301
+ LOGGER.warning("#{fqdn} has the following extra security groups: #{sg_undefined.join(', ')}. You will be prompted to remove these.")
302
+ if Vominator.yesno?(prompt: 'Is it safe to remove these groups?', default: false)
303
+ if Vominator::EC2.set_security_groups(ec2, ec2_instance.id, instance_security_groups, vpc_security_groups, false) == instance_security_groups
304
+ LOGGER.success("Succesfully updated the security groups for #{fqdn}")
305
+ else
306
+ LOGGER.fatal("Failed to remove security groups from #{fqdn}")
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end
312
+
313
+ instance_ebs_volumes.each do |device,options|
314
+ unless ec2_instance_ebs_volumes.include? device
315
+ unless test?("Would create and mount a #{options['type']} EBS volume on #{device} for #{fqdn}")
316
+ if Vominator::EC2.add_ebs_volume(ec2, ec2_instance.id, options['type'], options['size'], device, options['iops'], options['encrypted'])
317
+ LOGGER.success("Succesfully created and mounted #{device} to #{fqdn}")
318
+ else
319
+ LOGGER.fatal("Failed to create and mount #{device} to #{fqdn}")
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ unless route53_records.include?("#{fqdn}.")
326
+ unless test?("Would add a DNS record for #{fqdn}")
327
+ if Vominator::Route53.create_record(r53,puke_config['zone'],fqdn,instance_ip)
328
+ LOGGER.success("Succesfuly created a dns record for #{fqdn}")
329
+ else
330
+ LOGGER.fatal("Failed to create a DNS record for #{fqdn}")
331
+ end
332
+ end
333
+ end
334
+
335
+ ssm_documents.each do |doc_name|
336
+ unless Vominator::SSM.associated?(ssm,doc_name,ec2_instance.id)
337
+ unless test?("Would associate SSM Document #{doc_name} to #{fqdn}")
338
+ if Vominator::SSM.create_association(ssm,doc_name,ec2_instance.id)
339
+ LOGGER.success("Succesfully associated #{doc_name} to #{fqdn}")
340
+ else
341
+ LOGGER.fatal("Failed to associate #{doc_name} to #{fqdn}")
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ else #The instance does not exist, in which case we want to create it.
348
+ user_data = Vominator::Instances.generate_cloud_config(hostname, options[:environment], instance['family'], instance['roles'], instance['recipes'])
349
+ security_group_ids = instance_security_groups.map {|sg| vpc_security_groups[sg] }
350
+
351
+ unless test?("Would create #{fqdn}")
352
+ ec2_instance = Vominator::EC2.create_instance(ec2, hostname, options[:environment], ami, existing_subnets[subnet].id, instance_type, key_name, instance_ip, instance['az'], security_group_ids, user_data, ebs_optimized, instance['iam_profile'])
353
+ if ec2_instance
354
+ LOGGER.success("Succesfully created #{fqdn}")
355
+ ec2_instances[instance_ip] = {:instance_id => ec2_instance.id, :security_groups => ec2_instance.security_groups.map { |sg| sg.group_name}}
356
+ redo
357
+ else
358
+ LOGGER.fatal("Failed to create #{fqdn}")
359
+ end
360
+ end
361
+ end
362
+ end