vominator 0.0.1
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 +7 -0
- data/.gitignore +25 -0
- data/.rspec +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +116 -0
- data/Rakefile +12 -0
- data/bin/vominate +12 -0
- data/circle.yml +5 -0
- data/lib/ec2.rb +12 -0
- data/lib/ec2/instances.rb +362 -0
- data/lib/ec2/security_groups.rb +314 -0
- data/lib/ec2/ssm.rb +81 -0
- data/lib/vominator/aws.rb +15 -0
- data/lib/vominator/constants.rb +53 -0
- data/lib/vominator/ec2.rb +308 -0
- data/lib/vominator/instances.rb +34 -0
- data/lib/vominator/route53.rb +125 -0
- data/lib/vominator/security_groups.rb +26 -0
- data/lib/vominator/ssm.rb +57 -0
- data/lib/vominator/version.rb +3 -0
- data/lib/vominator/vominator.rb +74 -0
- data/lib/vominator/vpc.rb +82 -0
- data/lib/vpc.rb +8 -0
- data/lib/vpc/create.rb +188 -0
- data/spec/lib/instances_spec.rb +2 -0
- data/spec/lib/vominator/aws_spec.rb +6 -0
- data/spec/lib/vominator/ec2_spec.rb +783 -0
- data/spec/lib/vominator/instances_spec.rb +96 -0
- data/spec/lib/vominator/route53_spec.rb +64 -0
- data/spec/lib/vominator/ssm_spec.rb +95 -0
- data/spec/lib/vominator/vominator_spec.rb +209 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/support/matchers/exit_with_code.rb +24 -0
- data/test/puke/cloud-configs/.gitkeep +0 -0
- data/test/puke/cloud-configs/cloud-config-example.erb +63 -0
- data/test/puke/config.yaml +16 -0
- data/test/puke/products/sample-api/instances.yaml +37 -0
- data/test/puke/products/sample-api/security_groups.yaml +19 -0
- data/test/vominator.yaml +7 -0
- data/vominator.gemspec +34 -0
- 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
data/Gemfile
ADDED
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
data/bin/vominate
ADDED
data/circle.yml
ADDED
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
|