wombat-cli 0.3.4 → 0.4.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +15 -1
  5. data/Gemfile +3 -0
  6. data/README.md +2 -2
  7. data/Rakefile +25 -0
  8. data/bin/wombat +1 -1
  9. data/generator_files/cookbooks/automate/metadata.rb +1 -1
  10. data/generator_files/cookbooks/automate/recipes/update-users.rb +1 -1
  11. data/generator_files/cookbooks/chef_server/recipes/default.rb +24 -11
  12. data/generator_files/cookbooks/workstation/.kitchen.ec2.yml +2 -1
  13. data/generator_files/cookbooks/workstation/metadata.rb +1 -1
  14. data/generator_files/cookbooks/workstation/recipes/default.rb +1 -2
  15. data/generator_files/cookbooks/workstation/templates/default/ise_profile.ps1.erb +2 -2
  16. data/generator_files/cookbooks/workstation/test/integration/default/workstation_spec.rb +4 -4
  17. data/generator_files/packer/automate.json +129 -107
  18. data/generator_files/packer/build-node.json +134 -112
  19. data/generator_files/packer/chef-server.json +130 -108
  20. data/generator_files/packer/compliance.json +126 -104
  21. data/generator_files/packer/infranodes-windows.json +136 -97
  22. data/generator_files/packer/infranodes.json +127 -106
  23. data/generator_files/packer/workstation.json +134 -95
  24. data/generator_files/templates/arm.json.erb +576 -0
  25. data/generator_files/wombat.yml +6 -2
  26. data/lib/wombat/aws.rb +67 -0
  27. data/lib/wombat/build.rb +273 -184
  28. data/lib/wombat/cli.rb +182 -147
  29. data/lib/wombat/common.rb +228 -220
  30. data/lib/wombat/crypto.rb +65 -0
  31. data/lib/wombat/delete.rb +48 -18
  32. data/lib/wombat/deploy.rb +147 -34
  33. data/lib/wombat/init.rb +21 -19
  34. data/lib/wombat/latest.rb +27 -0
  35. data/lib/wombat/output.rb +31 -30
  36. data/lib/wombat/update.rb +13 -10
  37. data/lib/wombat/version.rb +1 -1
  38. data/spec/functional/common_spec.rb +26 -0
  39. data/spec/spec_helper.rb +103 -0
  40. data/spec/unit/common_spec.rb +116 -0
  41. data/wombat-cli.gemspec +2 -1
  42. metadata +36 -11
  43. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{delivery.crt → automate.crt} +0 -0
  44. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{delivery.key → automate.key} +0 -0
  45. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{chef-server.crt → chef.crt} +0 -0
  46. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{chef-server.key → chef.key} +0 -0
data/lib/wombat/common.rb CHANGED
@@ -1,266 +1,274 @@
1
1
  require 'yaml'
2
2
  require 'json'
3
3
  require 'erb'
4
- require 'openssl'
5
- require 'net/ssh'
6
4
  require 'benchmark'
7
5
  require 'fileutils'
8
6
 
9
- module Common
7
+ module Wombat
8
+ module Common
10
9
 
11
- def banner(msg)
12
- puts "==> #{msg}"
13
- end
14
-
15
- def info(msg)
16
- puts " #{msg}"
17
- end
10
+ def banner(msg)
11
+ puts "==> #{msg}"
12
+ end
18
13
 
19
- def warn(msg)
20
- puts ">>> #{msg}"
21
- end
14
+ def info(msg)
15
+ puts " #{msg}"
16
+ end
22
17
 
23
- def duration(total)
24
- total = 0 if total.nil?
25
- minutes = (total / 60).to_i
26
- seconds = (total - (minutes * 60))
27
- format("%dm%.2fs", minutes, seconds)
28
- end
18
+ def warn(msg)
19
+ puts ">>> #{msg}"
20
+ end
29
21
 
30
- def wombat
31
- if !File.exists?('wombat.yml')
32
- warn('No wombat.yml found, copying example')
33
- gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
34
- FileUtils.cp_r "#{gen_dir}/wombat.yml", Dir.pwd
22
+ def duration(total)
23
+ total = 0 if total.nil?
24
+ minutes = (total / 60).to_i
25
+ seconds = (total - (minutes * 60))
26
+ format("%dm%.2fs", minutes, seconds)
35
27
  end
36
- YAML.load(File.read('wombat.yml'))
37
- end
38
28
 
39
- def lock
40
- if !File.exists?('wombat.lock')
41
- warn('No wombat.lock found')
42
- return 1
43
- else
44
- JSON.parse(File.read('wombat.lock'))
29
+ def wombat
30
+ @wombat_yml ||= ENV['WOMBAT_YML'] unless ENV['WOMBAT_YML'].nil?
31
+ @wombat_yml ||= 'wombat.yml'
32
+ if !File.exist?(@wombat_yml)
33
+ warn('No wombat.yml found, copying example')
34
+ gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
35
+ FileUtils.cp_r "#{gen_dir}/wombat.yml", Dir.pwd
36
+ end
37
+ YAML.load(File.read(@wombat_yml))
45
38
  end
46
- end
47
39
 
48
- def bootstrap_aws
49
- @workstation_passwd = wombat['workstations']['password']
50
- rendered = ERB.new(File.read("#{conf['template_dir']}/bootstrap-aws.erb"), nil, '-').result(binding)
51
- Dir.mkdir("#{conf['packer_dir']}/scripts", 0755) unless File.exist?("#{conf['packer_dir']}/scripts")
52
- File.open("#{conf['packer_dir']}/scripts/bootstrap-aws.txt", 'w') { |file| file.puts rendered }
53
- banner("Generated: #{conf['packer_dir']}/scripts/bootstrap-aws.txt")
54
- end
40
+ def lock
41
+ if !File.exist?('wombat.lock')
42
+ warn('No wombat.lock found')
43
+ return 1
44
+ else
45
+ JSON.parse(File.read('wombat.lock'))
46
+ end
47
+ end
55
48
 
56
- def gen_x509_cert(hostname)
57
- rsa_key = OpenSSL::PKey::RSA.new(2048)
58
- public_key = rsa_key.public_key
59
-
60
- subject = "/C=AU/ST=New South Wales/L=Sydney/O=#{wombat['org']}/OU=wombats/CN=#{wombat['domain_prefix']}#{hostname}.#{wombat['domain']}"
61
-
62
- cert = OpenSSL::X509::Certificate.new
63
- cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
64
- cert.not_before = Time.now
65
- cert.not_after = Time.now + 365 * 24 * 60 * 60
66
- cert.public_key = public_key
67
- cert.serial = 0x0
68
- cert.version = 2
69
-
70
- ef = OpenSSL::X509::ExtensionFactory.new
71
- ef.subject_certificate = cert
72
- ef.issuer_certificate = cert
73
- cert.extensions = [
74
- ef.create_extension('basicConstraints', 'CA:TRUE', true),
75
- ef.create_extension('subjectKeyIdentifier', 'hash'),
76
- # ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
77
- ]
78
- cert.add_extension ef.create_extension('authorityKeyIdentifier',
79
- 'keyid:always,issuer:always')
80
-
81
- cert.sign(rsa_key, OpenSSL::Digest::SHA256.new)
82
-
83
- Dir.mkdir(conf['key_dir'], 0755) unless File.exist?(conf['key_dir'])
84
-
85
- if File.exist?("#{conf['key_dir']}/#{hostname}.crt") && File.exist?("#{conf['key_dir']}/#{hostname}.key")
86
- puts "An x509 certificate already exists for #{hostname}"
87
- else
88
- File.open("#{conf['key_dir']}/#{hostname}.crt", 'w') { |file| file.puts cert.to_pem }
89
- File.open("#{conf['key_dir']}/#{hostname}.key", 'w') { |file| file.puts rsa_key.to_pem }
90
- puts "Certificate created for #{wombat['domain_prefix']}#{hostname}.#{wombat['domain']}"
49
+ def bootstrap_aws
50
+ @workstation_passwd = wombat['workstations']['password']
51
+ rendered = ERB.new(File.read("#{conf['template_dir']}/bootstrap-aws.erb"), nil, '-').result(binding)
52
+ Dir.mkdir("#{conf['packer_dir']}/scripts", 0755) unless File.exist?("#{conf['packer_dir']}/scripts")
53
+ File.open("#{conf['packer_dir']}/scripts/bootstrap-aws.txt", 'w') { |file| file.puts rendered }
54
+ banner("Generated: #{conf['packer_dir']}/scripts/bootstrap-aws.txt")
91
55
  end
92
- end
93
56
 
94
- def gen_ssh_key
95
- rsa_key = OpenSSL::PKey::RSA.new 2048
57
+ def parse_log(log, cloud)
58
+ regex = case cloud
59
+ when 'gcp'
60
+ 'A disk image was created:'
61
+ when 'azure'
62
+ 'OSDiskUri:'
63
+ else
64
+ "#{wombat['aws']['region']}:"
65
+ end
66
+
67
+ File.read(log).split("\n").grep(/#{regex}/) {|x| x.split[1]}.last
68
+ end
96
69
 
97
- type = rsa_key.ssh_type
98
- data = [rsa_key.to_blob].pack('m0')
70
+ def infranodes
71
+ unless wombat['infranodes'].nil?
72
+ wombat['infranodes'].sort
73
+ else
74
+ puts 'No infranodes listed in wombat.yml'
75
+ {}
76
+ end
77
+ end
99
78
 
100
- openssh_format = "#{type} #{data}"
79
+ def build_nodes
80
+ build_nodes = {}
81
+ 1.upto(wombat['build-nodes']['count'].to_i) do |i|
82
+ build_nodes["build-node-#{i}"] = i
83
+ end
84
+ build_nodes
85
+ end
101
86
 
102
- Dir.mkdir(conf['key_dir'], 0755) unless File.exist?(conf['key_dir'])
87
+ def workstations
88
+ workstations = {}
89
+ 1.upto(wombat['workstations']['count'].to_i) do |i|
90
+ workstations["workstation-#{i}"] = i
91
+ end
92
+ workstations
93
+ end
103
94
 
104
- if File.exist?("#{conf['key_dir']}/public.pub") && File.exist?("#{conf['key_dir']}/private.pem")
105
- puts 'An SSH keypair already exists'
106
- else
107
- File.open("#{conf['key_dir']}/public.pub", 'w') { |file| file.puts openssh_format }
108
- File.open("#{conf['key_dir']}/private.pem", 'w') { |file| file.puts rsa_key.to_pem }
109
- puts 'SSH Keypair created'
95
+ def create_infranodes_json
96
+ infranodes_file_path = File.join(conf['files_dir'], 'infranodes-info.json')
97
+ if File.exists?(infranodes_file_path) && is_valid_json?(infranodes_file_path)
98
+ current_state = JSON(File.read(infranodes_file_path))
99
+ else
100
+ current_state = nil
101
+ end
102
+ return if current_state == infranodes # yay idempotence
103
+ File.open(infranodes_file_path, 'w') do |f|
104
+ f.puts JSON.pretty_generate(infranodes)
105
+ end
110
106
  end
111
- end
112
107
 
113
- def parse_log(log, cloud)
114
- regex = cloud == 'gcp' ? "A disk image was created:" : "#{wombat['aws']['region']}:"
115
- File.read(log).split("\n").grep(/#{regex}/) {|x| x.split[1]}.last
116
- end
108
+ def linux
109
+ wombat['linux'].nil? ? 'ubuntu' : wombat['linux']
110
+ end
117
111
 
118
- def infranodes
119
- unless wombat['infranodes'].nil?
120
- wombat['infranodes'].sort
121
- else
122
- puts 'No infranodes listed in wombat.yml'
112
+ def conf
113
+ conf = wombat['conf']
114
+ conf ||= {}
115
+ conf['files_dir'] ||= 'files'
116
+ conf['key_dir'] ||= 'keys'
117
+ conf['cookbook_dir'] ||= 'cookbooks'
118
+ conf['packer_dir'] ||= 'packer'
119
+ conf['log_dir'] ||= 'logs'
120
+ conf['stack_dir'] ||= 'stacks'
121
+ conf['template_dir'] ||= 'templates'
122
+ conf['timeout'] ||= 7200
123
+ conf['audio'] ||= false
124
+ conf
123
125
  end
124
- end
125
126
 
126
- def build_nodes
127
- build_nodes = {}
128
- 1.upto(wombat['build-nodes']['count'].to_i) do |i|
129
- build_nodes["build-node-#{i}"] = i
127
+ def is_mac?
128
+ (/darwin/ =~ RUBY_PLATFORM) != nil
130
129
  end
131
- build_nodes
132
- end
133
130
 
134
- def workstations
135
- workstations = {}
136
- 1.upto(wombat['workstations']['count'].to_i) do |i|
137
- workstations["workstation-#{i}"] = i
131
+ def audio?
132
+ is_mac? && conf['audio']
138
133
  end
139
- workstations
140
- end
141
134
 
142
- def create_infranodes_json
143
- if File.exists?("#{conf['packer_dir']}/file/infranodes-info.json")
144
- current_state = JSON(File.read('files/infranodes-info.json'))
145
- else
146
- current_state = nil
135
+ def logs
136
+ path = "#{conf['log_dir']}/#{cloud}*.log"
137
+ Dir.glob(path).reject { |l| !l.match(wombat['linux']) }
147
138
  end
148
- return if current_state == infranodes # yay idempotence
149
- File.open("#{conf['packer_dir']}/files/infranodes-info.json", 'w') do |f|
150
- f.puts JSON.pretty_generate(infranodes)
139
+
140
+ def calculate_templates
141
+ globs = "*.json"
142
+ Dir.chdir(conf['packer_dir']) do
143
+ Array(globs).
144
+ map { |glob| result = Dir.glob("#{glob}"); result.empty? ? glob : result }.
145
+ flatten.
146
+ sort.
147
+ delete_if { |file| file =~ /\.variables\./ }.
148
+ map { |template| template.sub(/\.json$/, '') }
149
+ end
151
150
  end
152
- end
153
151
 
154
- def linux
155
- wombat['linux'].nil? ? 'ubuntu' : wombat['linux']
156
- end
152
+ def update_lock(cloud)
153
+ copy = {}
154
+ copy = wombat
157
155
 
158
- def conf
159
- conf = wombat['conf']
160
- conf ||= {}
161
- conf['key_dir'] ||= 'keys'
162
- conf['cookbook_dir'] ||= 'cookbooks'
163
- conf['packer_dir'] ||= 'packer'
164
- conf['log_dir'] ||= 'logs'
165
- conf['stack_dir'] ||= 'stacks'
166
- conf['template_dir'] ||= 'templates'
167
- conf['timeout'] ||= 7200
168
- conf['audio'] ||= false
169
- conf
170
- end
156
+ # Check that the copy contains a key for the named cloud
157
+ unless copy.key?(cloud)
158
+ throw "The Cloud '#{cloud}' is not specified in Wombat"
159
+ end
171
160
 
172
- def is_mac?
173
- (/darwin/ =~ RUBY_PLATFORM) != nil
174
- end
161
+ # Determine the region/location/zone for the specific cloud
162
+ case cloud
163
+ when 'aws'
164
+ region = copy['aws']['region']
165
+ when 'azure'
166
+ region = copy['azure']['location']
167
+ when 'gce'
168
+ region = copy['gce']['zone']
169
+ end
175
170
 
176
- def audio?
177
- is_mac? && conf['audio']
178
- end
171
+ linux = copy['linux']
172
+ copy['amis'] = { region => {} }
173
+
174
+ if logs.length == 0
175
+ warn('No logs found - skipping lock update')
176
+ else
177
+ logs.each do |log|
178
+ case log
179
+ when /build-node/
180
+ copy['amis'][region]['build-node'] ||= {}
181
+ num = log.split('-')[3]
182
+ copy['amis'][region]['build-node'].store(num, parse_log(log, cloud))
183
+ when /workstation/
184
+ copy['amis'][region]['workstation'] ||= {}
185
+ num = log.split('-')[2]
186
+ copy['amis'][region]['workstation'].store(num, parse_log(log, cloud))
187
+ when /infranodes/
188
+ copy['amis'][region]['infranodes'] ||= {}
189
+ name = log.split('-')[2]
190
+ copy['amis'][region]['infranodes'].store(name, parse_log(log, cloud))
191
+ else
192
+ instance = log.match("#{cloud}-(.*)-#{linux}\.log")[1]
193
+ copy['amis'][region].store(instance, parse_log(log, cloud))
194
+ end
195
+ end
196
+ copy['last_updated'] = Time.now.gmtime.strftime('%Y%m%d%H%M%S')
197
+ banner('Updating wombat.lock')
198
+ File.open('wombat.lock', 'w') do |f|
199
+ f.write(JSON.pretty_generate(copy))
200
+ end
201
+ end
202
+ end
179
203
 
180
- def logs
181
- Dir.glob("#{conf['log_dir']}/#{cloud}*.log").reject { |l| !l.match(wombat['linux']) }
182
- end
204
+ def update_template(cloud)
205
+ if lock == 1
206
+ warn('No lock - skipping template creation')
207
+ else
208
+
209
+ # Determine the region/location/zone for the specific cloud
210
+ case cloud
211
+ when 'aws'
212
+ region = lock['aws']['region']
213
+ template_file = "cfn.json.erb"
214
+ @chef_server_ami = lock['amis'][region]['chef-server']
215
+ @automate_ami = lock['amis'][region]['automate']
216
+ @compliance_ami = lock['amis'][region]['compliance']
217
+ @availability_zone = lock['aws']['az']
218
+ @iam_roles = lock['aws']['iam_roles']
219
+ when 'azure'
220
+ region = lock['azure']['location']
221
+ @storage_account = lock['azure']['storage_account']
222
+ template_file = "arm.json.erb"
223
+ @chef_server_uri = lock['amis'][region]['chef-server']
224
+ @automate_uri = lock['amis'][region]['automate']
225
+ @compliance_uri = lock['amis'][region]['compliance']
226
+
227
+ # set an admin password for the machines that are created
228
+ @password = "aAan0orxevCma4gG"
229
+ when 'gce'
230
+ region = lock['gce']['zone']
231
+ end
183
232
 
184
- def calculate_templates
185
- globs = "*.json"
186
- Dir.chdir(conf['packer_dir']) do
187
- Array(globs).
188
- map { |glob| result = Dir.glob("#{glob}"); result.empty? ? glob : result }.
189
- flatten.
190
- sort.
191
- delete_if { |file| file =~ /\.variables\./ }.
192
- map { |template| template.sub(/\.json$/, '') }
193
- end
194
- end
233
+ if lock['amis'][region].key?('build-node')
234
+ @build_nodes = lock['build-nodes']['count'].to_i
235
+ @build_node_ami = {}
236
+ 1.upto(@build_nodes) do |i|
237
+ @build_node_ami[i] = lock['amis'][region]['build-node'][i.to_s]
238
+ end
239
+ end
195
240
 
196
- def update_lock(cloud)
197
- copy = {}
198
- copy = wombat
199
- region = copy[cloud]['region']
200
- linux = copy['linux']
201
- copy['amis'] = { region => {} }
202
-
203
- if logs.length == 0
204
- warn('No logs found - skipping lock update')
205
- else
206
- logs.each do |log|
207
- case log
208
- when /build-node/
209
- copy['amis'][region]['build-node'] ||= {}
210
- num = log.split('-')[3]
211
- copy['amis'][region]['build-node'].store(num, parse_log(log, cloud))
212
- when /workstation/
213
- copy['amis'][region]['workstation'] ||= {}
214
- num = log.split('-')[2]
215
- copy['amis'][region]['workstation'].store(num, parse_log(log, cloud))
216
- when /infranodes/
217
- copy['amis'][region]['infranodes'] ||= {}
218
- name = log.split('-')[2]
219
- copy['amis'][region]['infranodes'].store(name, parse_log(log, cloud))
220
- else
221
- instance = log.match("#{cloud}-(.*)-#{linux}\.log")[1]
222
- copy['amis'][region].store(instance, parse_log(log, cloud))
241
+ @infra = {}
242
+ infranodes.each do |name, _rl|
243
+ @infra[name] = lock['amis'][region]['infranodes'][name]
223
244
  end
224
- end
225
- copy['last_updated'] = Time.now.gmtime.strftime('%Y%m%d%H%M%S')
226
- banner('Updating wombat.lock')
227
- File.open('wombat.lock', 'w') do |f|
228
- f.write(JSON.pretty_generate(copy))
245
+
246
+ if lock['amis'][region].key?('workstation')
247
+ @workstations = lock['workstations']['count'].to_i
248
+ @workstation_ami = {}
249
+ 1.upto(@workstations) do |i|
250
+ @workstation_ami[i] = lock['amis'][region]['workstation'][i.to_s]
251
+ end
252
+ end
253
+
254
+ @demo = lock['name']
255
+ @version = lock['version']
256
+ @ttl = lock['ttl']
257
+
258
+ rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding)
259
+ Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir'])
260
+ File.open("#{conf['stack_dir']}/#{@demo}.json", 'w') { |file| file.puts rendered_cfn }
261
+ banner("Generated: #{conf['stack_dir']}/#{@demo}.json")
229
262
  end
230
263
  end
231
- end
232
264
 
233
- def update_template(cloud)
234
- if lock == 1
235
- warn('No lock - skipping template creation')
236
- else
237
- region = lock['aws']['region']
238
- @chef_server_ami = lock['amis'][region]['chef-server']
239
- @automate_ami = lock['amis'][region]['automate']
240
- @compliance_ami = lock['amis'][region]['compliance']
241
- @build_nodes = lock['build-nodes']['count'].to_i
242
- @build_node_ami = {}
243
- 1.upto(@build_nodes) do |i|
244
- @build_node_ami[i] = lock['amis'][region]['build-node'][i.to_s]
245
- end
246
- @infra = {}
247
- infranodes.each do |name, _rl|
248
- @infra[name] = lock['amis'][region]['infranodes'][name]
249
- end
250
- @workstations = lock['workstations']['count'].to_i
251
- @workstation_ami = {}
252
- 1.upto(@workstations) do |i|
253
- @workstation_ami[i] = lock['amis'][region]['workstation'][i.to_s]
265
+ def is_valid_json?(file)
266
+ begin
267
+ JSON.parse(file)
268
+ true
269
+ rescue JSON::ParserError => e
270
+ false
254
271
  end
255
- @availability_zone = lock['aws']['az']
256
- @iam_roles = lock['aws']['iam_roles']
257
- @demo = lock['name']
258
- @version = lock['version']
259
- @ttl = lock['ttl']
260
- rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/cfn.json.erb"), nil, '-').result(binding)
261
- Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir'])
262
- File.open("#{conf['stack_dir']}/#{@demo}.json", 'w') { |file| file.puts rendered_cfn }
263
- banner("Generated: #{conf['stack_dir']}/#{@demo}.json")
264
272
  end
265
273
  end
266
- end
274
+ end
@@ -0,0 +1,65 @@
1
+ require 'openssl'
2
+ require 'net/ssh'
3
+
4
+ module Wombat
5
+ module Crypto
6
+ include Wombat::Common
7
+
8
+ def gen_x509_cert(hostname)
9
+ rsa_key = OpenSSL::PKey::RSA.new(2048)
10
+ public_key = rsa_key.public_key
11
+
12
+ subject = "/C=AU/ST=New South Wales/L=Sydney/O=#{wombat['org']}/OU=wombats/CN=#{wombat['domain_prefix']}#{hostname}.#{wombat['domain']}"
13
+
14
+ cert = OpenSSL::X509::Certificate.new
15
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
16
+ cert.not_before = Time.now
17
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
18
+ cert.public_key = public_key
19
+ cert.serial = 0x0
20
+ cert.version = 2
21
+
22
+ ef = OpenSSL::X509::ExtensionFactory.new
23
+ ef.subject_certificate = cert
24
+ ef.issuer_certificate = cert
25
+ cert.extensions = [
26
+ ef.create_extension('basicConstraints', 'CA:TRUE', true),
27
+ ef.create_extension('subjectKeyIdentifier', 'hash'),
28
+ # ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
29
+ ]
30
+ cert.add_extension ef.create_extension('authorityKeyIdentifier',
31
+ 'keyid:always,issuer:always')
32
+
33
+ cert.sign(rsa_key, OpenSSL::Digest::SHA256.new)
34
+
35
+ Dir.mkdir(conf['key_dir'], 0755) unless File.exist?(conf['key_dir'])
36
+
37
+ if File.exist?("#{conf['key_dir']}/#{hostname}.crt") && File.exist?("#{conf['key_dir']}/#{hostname}.key")
38
+ puts "An x509 certificate already exists for #{hostname}"
39
+ else
40
+ File.open("#{conf['key_dir']}/#{hostname}.crt", 'w') { |file| file.puts cert.to_pem }
41
+ File.open("#{conf['key_dir']}/#{hostname}.key", 'w') { |file| file.puts rsa_key.to_pem }
42
+ puts "Certificate created for #{wombat['domain_prefix']}#{hostname}.#{wombat['domain']}"
43
+ end
44
+ end
45
+
46
+ def gen_ssh_key
47
+ rsa_key = OpenSSL::PKey::RSA.new 2048
48
+
49
+ type = rsa_key.ssh_type
50
+ data = [rsa_key.to_blob].pack('m0')
51
+
52
+ openssh_format = "#{type} #{data}"
53
+
54
+ Dir.mkdir(conf['key_dir'], 0755) unless File.exist?(conf['key_dir'])
55
+
56
+ if File.exist?("#{conf['key_dir']}/public.pub") && File.exist?("#{conf['key_dir']}/private.pem")
57
+ puts 'An SSH keypair already exists'
58
+ else
59
+ File.open("#{conf['key_dir']}/public.pub", 'w') { |file| file.puts openssh_format }
60
+ File.open("#{conf['key_dir']}/private.pem", 'w') { |file| file.puts rsa_key.to_pem }
61
+ puts 'SSH Keypair created'
62
+ end
63
+ end
64
+ end
65
+ end
data/lib/wombat/delete.rb CHANGED
@@ -1,28 +1,58 @@
1
1
  require 'wombat/common'
2
2
  require 'aws-sdk'
3
+ require 'ms_rest_azure'
4
+ require 'azure_mgmt_resources'
3
5
 
4
- class DeleteRunner
5
- include Common
6
+ module Wombat
7
+ class DeleteRunner
8
+ include Wombat::Common
6
9
 
7
- attr_reader :stack, :cloud
10
+ attr_reader :stack, :cloud
8
11
 
9
- def initialize(opts)
10
- @stack = opts.stack
11
- @cloud = opts.cloud.nil? ? "aws" : opts.cloud
12
- end
12
+ def initialize(opts)
13
+ @stack = opts.stack
14
+ @cloud = opts.cloud.nil? ? "aws" : opts.cloud
15
+ end
13
16
 
14
- def start
15
- cfn_delete_stack(stack)
16
- end
17
+ def start
18
+ cfn_delete_stack(stack)
19
+ end
20
+
21
+ private
22
+
23
+ def cfn_delete_stack(stack)
24
+
25
+ # Delete the stack from the correct platform
26
+ case @cloud
27
+ when "aws"
28
+ cfn = Aws::CloudFormation::Client.new(region: lock['aws']['region'])
29
+
30
+ resp = cfn.delete_stack({
31
+ stack_name: stack,
32
+ })
33
+ banner("Deleted #{stack}")
34
+
35
+ when "azure"
36
+
37
+ # Create the connection to Azure using the information in the environment variables
38
+ subscription_id = ENV['AZURE_SUBSCRIPTION_ID']
39
+ tenant_id = ENV['AZURE_TENANT_ID']
40
+ client_id = ENV['AZURE_CLIENT_ID']
41
+ client_secret = ENV['AZURE_CLIENT_SECRET']
42
+
43
+ token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
44
+ azure_conn = MsRest::TokenCredentials.new(token_provider)
45
+
46
+ # Create a resource client so that the resource group can be deleted
47
+ resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn)
48
+ resource_management_client.subscription_id = subscription_id
17
49
 
18
- private
50
+ banner(format("Deleting resource group: %s", stack))
19
51
 
20
- def cfn_delete_stack(stack)
21
- cfn = Aws::CloudFormation::Client.new(region: lock['aws']['region'])
52
+ resource_management_client.resource_groups.begin_delete(stack)
22
53
 
23
- resp = cfn.delete_stack({
24
- stack_name: stack,
25
- })
26
- banner("Deleted #{stack}")
54
+ info "Destroy operation accepted and will continue in the background."
55
+ end
56
+ end
27
57
  end
28
- end
58
+ end