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
@@ -0,0 +1,26 @@
1
+ require 'aws-sdk'
2
+ require_relative 'vominator'
3
+ require_relative 'constants'
4
+
5
+ module Vominator
6
+ class SecurityGroups
7
+
8
+ def self.get_security_groups(environment, product, filter=false)
9
+ if PUKE_CONFIG[environment]['products'].include? product
10
+ config_file = File.expand_path("#{VOMINATOR_CONFIG['configuration_path']}/products/#{product}/security_groups.yaml")
11
+ if filter
12
+ security_groups = Array.new
13
+ YAML.load(File.read(config_file)).each do |security_group|
14
+ if filter.include? security_group.keys[0]
15
+ security_groups.push security_group
16
+ end
17
+ end
18
+ else
19
+ security_groups = YAML.load(File.read(config_file))
20
+ end
21
+ return security_groups if security_groups.kind_of?(Array)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ require 'aws-sdk'
2
+ require_relative 'constants'
3
+
4
+ module Vominator
5
+ class SSM
6
+
7
+ def self.get_documents(client,max_results=25)
8
+ resp = client.list_documents(:max_results => max_results)
9
+ documents = resp[:document_identifiers]
10
+ while resp[:next_token]
11
+ resp = client.list_documents(:next_token => resp[:next_token])
12
+ documents += resp[:document_identifiers]
13
+ end
14
+
15
+ return documents.map {|doc| doc.name }
16
+ end
17
+
18
+ def self.describe_document(client,name)
19
+ return client.describe_document(:name => name).document
20
+ end
21
+
22
+ def self.get_document(client,name)
23
+ return client.get_document(:name => name)
24
+ end
25
+
26
+ def self.put_document(client,name,data)
27
+ client.create_document(:name => name, :content => data)
28
+ sleep 2 until Vominator::SSM.describe_document(client,name).status == 'active'
29
+
30
+ if Vominator::SSM.describe_document(client,name).status == 'active'
31
+ return true
32
+ else
33
+ return false
34
+ end
35
+ end
36
+
37
+ def self.associated?(client,name,instance_id)
38
+ begin
39
+ client.describe_association(:name => name, :instance_id => instance_id)
40
+ return true
41
+ rescue Aws::SSM::Errors::AssociationDoesNotExist
42
+ return false
43
+ end
44
+ end
45
+
46
+ def self.create_association(client,name,instance_id)
47
+ client.create_association(:name => name, :instance_id => instance_id)
48
+ sleep 2 until Vominator::SSM.associated?(client,name,instance_id)
49
+
50
+ if Vominator::SSM.associated?(client,name,instance_id)
51
+ return true
52
+ else
53
+ return false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ module Vominator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,74 @@
1
+ require 'yaml'
2
+ require 'colored'
3
+ require 'highline/import'
4
+ require_relative 'version'
5
+
6
+ module Vominator
7
+ def self.get_config(file='~/.vominator.yaml')
8
+ config_file = ENV['VOMINATOR_CONFIG'] || File.expand_path(file)
9
+ if File.exist?(config_file)
10
+ vominator_config = YAML.load(File.read(config_file))
11
+ return vominator_config if vominator_config.kind_of?(Hash)
12
+ else
13
+ LOGGER.fatal("Unable to load vominator configuration file from #{config_file}")
14
+ return false
15
+ end
16
+ end
17
+
18
+ def self.get_puke_config(puke_dir)
19
+ if File.exist?(puke_dir)
20
+ config_file = "#{puke_dir}/config.yaml"
21
+ puke_config = YAML.load(File.read(config_file))
22
+ else
23
+ raise("Unable to open puke configuration at #{puke_dir}")
24
+ end
25
+ return puke_config if puke_config.kind_of?(Hash)
26
+ end
27
+
28
+ def self.get_key_pair(vominator_config)
29
+ return vominator_config['key_pair_name']
30
+ end
31
+
32
+ def self.get_puke_variables(environment)
33
+ data = PUKE_CONFIG[environment]
34
+ return data
35
+ end
36
+
37
+ def self.yesno?(prompt: 'Continue?', default: true)
38
+ a = ''
39
+ s = default ? '[Y/n]' : '[y/N]'
40
+ d = default ? 'y' : 'n'
41
+ until %w[y n].include? a
42
+ a = ask("#{prompt} #{s} ") { |q| q.limit = 1; q.case = :downcase }
43
+ a = d if a.length == 0
44
+ end
45
+ a == 'y'
46
+ end
47
+
48
+ class Logger
49
+ def self.info(message)
50
+ puts message
51
+ end
52
+
53
+ def self.test(message)
54
+ puts message.cyan
55
+ end
56
+
57
+ def self.error(message)
58
+ puts message.red
59
+ end
60
+
61
+ def self.fatal(message)
62
+ puts message.red
63
+ exit(1)
64
+ end
65
+
66
+ def self.success(message)
67
+ puts message.green
68
+ end
69
+
70
+ def self.warning(message)
71
+ puts message.yellow
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,82 @@
1
+ require 'aws-sdk'
2
+ require_relative 'constants'
3
+
4
+ module Vominator
5
+ class VPC
6
+ def self.get_vpc(client, vpc_id)
7
+ return client.describe_vpcs(filters: [{name: 'vpc-id', values: [vpc_id]}]).vpcs.first
8
+ end
9
+ def self.get_vpc_by_cidr(client, cidr_block)
10
+ return client.describe_vpcs(filters: [{name: 'cidr', values: [cidr_block]}]).vpcs.first
11
+ end
12
+
13
+ def self.create_vpc(client, cidr_block, tenancy='default')
14
+ resp = client.create_vpc(:cidr_block => cidr_block, :instance_tenancy => tenancy)
15
+ sleep 2 until Vominator::VPC.get_vpc(client,resp.vpc.vpc_id).state == 'available'
16
+ return resp.vpc
17
+ end
18
+
19
+ def self.get_internet_gateway(client, gateway_id)
20
+ return client.describe_internet_gateways(filters: [{name: 'internet-gateway-id', values: [gateway_id]}]).internet_gateways.first
21
+ end
22
+
23
+ def self.create_internet_gateway(client)
24
+ resp = client.create_internet_gateway
25
+ return resp.internet_gateway
26
+ end
27
+
28
+ def self.attach_internet_gateway(client, gateway_id, vpc_id)
29
+ resp = client.attach_internet_gateway(internet_gateway_id: gateway_id, vpc_id: vpc_id)
30
+ sleep 2 until Vominator::VPC.get_internet_gateway(client, gateway_id).attachments.first.state == 'available'
31
+ return true
32
+ end
33
+
34
+ def self.get_nat_gateway(client, gateway_id)
35
+ return client.describe_nat_gateways(filter: [{name: 'nat-gateway-id', values: [gateway_id]}]).nat_gateways.first
36
+ end
37
+
38
+ def self.create_nat_gateway(client, subnet_id, allocation_id)
39
+ resp = client.create_nat_gateway(subnet_id: subnet_id, allocation_id: allocation_id).nat_gateway
40
+ sleep 2 until Vominator::VPC.get_nat_gateway(client, resp.nat_gateway_id).state == 'available'
41
+ return resp
42
+ end
43
+
44
+ def self.get_route_tables(client, vpc_id)
45
+ return client.describe_route_tables(filters: [{name: 'vpc-id', values: [vpc_id]}]).route_tables
46
+ end
47
+
48
+ def self.get_route_table(client, route_table_id)
49
+ return client.describe_route_tables(filters: [{name: 'route-table-id', values: [route_table_id]}]).route_tables.first
50
+ end
51
+
52
+ def self.create_internet_gateway_route(client, route_table_id, destination_cidr_block, gateway_id)
53
+ return client.create_route(route_table_id: route_table_id, destination_cidr_block: destination_cidr_block, gateway_id: gateway_id)
54
+ end
55
+
56
+ def self.create_nat_gateway_route(client, route_table_id, destination_cidr_block, nat_gateway_id)
57
+ return client.create_route(route_table_id: route_table_id, destination_cidr_block: destination_cidr_block, nat_gateway_id: nat_gateway_id)
58
+ end
59
+
60
+ def self.create_route_table(client, vpc_id)
61
+ resp = client.create_route_table(vpc_id: vpc_id).route_table
62
+ sleep 2 until Vominator::VPC.get_route_table(client, resp.route_table_id)
63
+ return resp
64
+ end
65
+
66
+ def self.get_subnet(client, subnet_id)
67
+ return client.describe_subnets(filters: [{name: 'subnet-id', values: [subnet_id]}]).subnets.first
68
+ end
69
+
70
+ def self.create_subnet(client, vpc_id, cidr_block, availability_zone)
71
+ resp = client.create_subnet(vpc_id: vpc_id, cidr_block: cidr_block, availability_zone: availability_zone).subnet
72
+ sleep 2 until Vominator::VPC.get_subnet(client, resp.subnet_id).state == 'available'
73
+ return resp
74
+ end
75
+
76
+ def self.associate_route_table(client, subnet_id, route_table_id)
77
+ return client.associate_route_table(subnet_id: subnet_id, route_table_id: route_table_id)
78
+ end
79
+
80
+
81
+ end
82
+ end
data/lib/vpc.rb ADDED
@@ -0,0 +1,8 @@
1
+ vominator_module = ARGV[1] || nil
2
+
3
+ case vominator_module
4
+ when 'create'
5
+ require_relative '../lib/vpc/create'
6
+ else
7
+ puts 'Module not found. Currently supported modules are: create'
8
+ end
data/lib/vpc/create.rb ADDED
@@ -0,0 +1,188 @@
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/vpc'
7
+ require_relative '../vominator/ec2'
8
+ require_relative '../vominator/route53'
9
+
10
+ options = {}
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = 'Usage: vominate vpc create [options]'.yellow
14
+
15
+ opts.on('-eENVIRONMENT', '--environment ENVIRONMENT', 'REQUIRED: The environment which you want to create a VPC for. IE foo') do |value|
16
+ options[:environment] = value
17
+ end
18
+
19
+ opts.on('--region Region', 'REQUIRED: The AWS Region that you want to create the VPC in. IE us-east-1') do |value|
20
+ options[:region] = value
21
+ end
22
+
23
+ opts.on('--availability-zones AVAILABILITY ZONES', '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') do |value|
24
+ options[:availability_zones] = value
25
+ end
26
+
27
+ opts.on('--parent-domain PARENT DOMAIN', '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') do |value|
28
+ options[:parent_domain] = value
29
+ end
30
+
31
+ opts.on('--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') do |value|
32
+ options[:cidr_block] = value
33
+ end
34
+
35
+ #opts.on('-t', '--test', 'OPTIONAL: Test run. Show what would be changed without making any actual changes') do
36
+ # options[:test] = true
37
+ #end
38
+
39
+ #opts.on('-l', '--list', 'OPTIONAL: List out Vominator aware VPCs') do
40
+ # options[:list] = true
41
+ #end
42
+
43
+ opts.on('-d', '--debug', 'OPTIONAL: debug output') do
44
+ options[:debug] = true
45
+ end
46
+
47
+ opts.on_tail(:NONE, '-h', '--help', 'OPTIONAL: Display this screen') do
48
+ puts opts
49
+ exit
50
+ end
51
+
52
+ begin
53
+ opts.parse!
54
+ ## Validate Data Inputs
55
+ throw Exception unless ((options.include? :environment) && (options.include? :region) && (options.include? :parent_domain) && (options.include? :cidr_block)) || options[:list]
56
+ rescue
57
+ puts opts
58
+ exit
59
+ end
60
+ end
61
+
62
+ TEST = options[:test]
63
+ def test?(message)
64
+ LOGGER.test(message) if TEST
65
+ TEST
66
+ end
67
+
68
+ unless test?('Vominator is running in test mode. It will NOT make any changes.')
69
+ LOGGER.warning('WARNING: Vominator will make changes to your environment. Please run test mode first if you are unsure.')
70
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
71
+ exit(1)
72
+ end
73
+ end
74
+
75
+ puke_config = Vominator.get_puke_variables(options[:environment])
76
+
77
+ if puke_config
78
+ LOGGER.fatal("An environment with the name of #{options[:environment]} is already defined. Please choose a different name")
79
+ else
80
+ puke_config = Hash.new
81
+ puke_config['region_name'] = options[:region]
82
+ end
83
+
84
+ ec2_client = Aws::EC2::Client.new(region: puke_config['region_name'])
85
+ r53_client = Aws::Route53::Client.new(region: puke_config['region_name'])
86
+
87
+
88
+ fqdn = "#{options[:environment]}.#{options[:parent_domain]}"
89
+
90
+ # Couple of sanity checks before we get started....
91
+ if Vominator::VPC.get_vpc_by_cidr(ec2_client, options[:cidr_block])
92
+ LOGGER.warning("A VPC already exists with a netblock of #{options[:cidr_block]}. Generally you want these to be unique")
93
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
94
+ exit(1)
95
+ end
96
+ end
97
+
98
+ if Vominator::Route53.get_zone_by_domain(r53_client, fqdn)
99
+ LOGGER.fatal("A Zonefile already exists for #{fqdn}. Please choose a different environment name")
100
+ end
101
+
102
+ if options[:availability_zones]
103
+ availability_zones = options[:availability_zones].split(',')
104
+ else
105
+ availability_zones = Vominator::AWS.get_availability_zones(ec2_client)
106
+ end
107
+
108
+ parent_zone = Vominator::Route53.get_zone_by_domain(r53_client, options[:parent_domain])
109
+
110
+ unless parent_zone
111
+ LOGGER.warning("We could not find the parent zone of #{options[:parent_domain]}. You can proceed and we will provide the settings so you can manually create the entry.")
112
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
113
+ exit(1)
114
+ end
115
+ end
116
+
117
+
118
+ LOGGER.info("Starting the creation process. This may take a couple of minutes")
119
+
120
+ environment_zone = Vominator::Route53.create_zone(r53_client, fqdn)
121
+
122
+ if parent_zone
123
+ Vominator::Route53.create_nameserver_records(r53_client,parent_zone.id, fqdn, environment_zone.delegation_set.name_servers)
124
+ end
125
+
126
+ vpc = Vominator::VPC.create_vpc(ec2_client,options[:cidr_block])
127
+
128
+ gateway = Vominator::VPC.create_internet_gateway(ec2_client)
129
+
130
+ Vominator::VPC.attach_internet_gateway(ec2_client, gateway.internet_gateway_id, vpc.vpc_id)
131
+
132
+ public_route_table = Vominator::VPC.get_route_tables(ec2_client, vpc.vpc_id).first
133
+ Vominator::EC2.tag_resource(ec2_client, public_route_table.route_table_id,[{key: 'Name', value: "dmz-#{options[:environment]}-#{options[:region]}"}])
134
+
135
+ Vominator::VPC.create_internet_gateway_route(ec2_client, public_route_table.route_table_id, '0.0.0.0/0', gateway.internet_gateway_id)
136
+
137
+ third_octet = 1
138
+
139
+ route_tables = Hash.new
140
+ route_tables['public'] = public_route_table.route_table_id
141
+
142
+ availability_zones.each do |zone|
143
+
144
+ private_route_table = Vominator::VPC.create_route_table(ec2_client, vpc.vpc_id)
145
+ route_tables[zone] = private_route_table.route_table_id
146
+
147
+ Vominator::EC2.tag_resource(ec2_client, private_route_table.route_table_id,[{key: 'Name', value: "nat-#{options[:environment]}-#{zone}"}])
148
+
149
+ public_subnet_cidr_block = "#{options[:cidr_block].split('.')[0]}.#{options[:cidr_block].split('.')[1]}.#{third_octet}.0/24"
150
+ public_subnet = Vominator::VPC.create_subnet(ec2_client, vpc.vpc_id, public_subnet_cidr_block, zone)
151
+
152
+ Vominator::VPC.associate_route_table(ec2_client, public_subnet.subnet_id, public_route_table.route_table_id)
153
+
154
+ public_ip = Vominator::EC2.allocate_public_ip(ec2_client)
155
+ nat_gateway = Vominator::VPC.create_nat_gateway(ec2_client, public_subnet.subnet_id, public_ip.allocation_id)
156
+
157
+ Vominator::VPC.create_nat_gateway_route(ec2_client, private_route_table.route_table_id, '0.0.0.0/0', nat_gateway.nat_gateway_id)
158
+
159
+ third_octet += 1
160
+ end
161
+
162
+ unless parent_zone
163
+ LOGGER.warning("Parent zone not was not found in Route53. Use the below information to create the proper entries in your parent zone.")
164
+ LOGGER.warning("FQDN: #{fqdn}")
165
+ LOGGER.warning("Record Type: NS")
166
+ LOGGER.warning("Nameservers: #{environment_zone.delegation_set.name_servers}")
167
+ end
168
+
169
+ config = {
170
+ options[:environment] => {
171
+ 'vpc_id' => vpc.vpc_id,
172
+ 'route_tables' => route_tables,
173
+ 'region_name' => options[:region],
174
+ 'zone' => environment_zone.hosted_zone.id.split('/')[2],
175
+ 'octet' => options[:cidr_block].split('.')[1],
176
+ 'domain' => fqdn,
177
+ 'linux_cloud_config_template' => 'EDIT_ME',
178
+ 'linux_paravirtual_base_image' => 'EDIT_ME',
179
+ 'linux_hvm_base_image' => 'EDIT_ME',
180
+ 'windows_cloud_config_template' => 'EDIT_ME',
181
+ 'windows_paravirtual_base_image' => 'EDIT_ME',
182
+ 'windows_hvm_base_image' => 'EDIT_ME',
183
+ 'chef_host' => 'EDIT_ME',
184
+ 'products' => []
185
+ }}
186
+
187
+ LOGGER.success("Append the below to your config.yaml file in your puke directory")
188
+ LOGGER.info(config.to_yaml)