chef-metal-vagrant 0.3.1 → 0.4.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a07ff0eedf9e89eba8989cdc17243d58432e1ec6
4
- data.tar.gz: ff8ac29e8cd1f16e7205d3c0cea531be1834b4d6
3
+ metadata.gz: 869d2fb17c59772fa9c03e88440319b308bd277d
4
+ data.tar.gz: 1da119611bf939a3f1c633ab971fbd48699b7361
5
5
  SHA512:
6
- metadata.gz: e2a033f8c623251ed6e9009dc673f3371bd748ea871710c7921c647d065b56e50efe3a039e56bdc5bb5c9050174778157457ab2c4c21da5fc4f751fbdc43eaf8
7
- data.tar.gz: ec2df65a5bf1ebcbeb402b69950a290e6d54b6352744b9174da134cc6328fc50429f413a7f781b1b4620ed961a26475bfdbe907035e8951479dcffcdca9574a4
6
+ metadata.gz: 22757248dcbc506598c6430e8bba41d15745c98b56fcbcff5beed457b91fa203acf8d105d061c3f8481ddd04809e1ec457e9e629db24e1caed7257c2b35ffac5
7
+ data.tar.gz: 3236ad050f5bac33de4adc8550036902cae02b0678a1b01b7119ec99b8c20d33d5a2e1f8c5e68b2a30d973b573c9fbfd2047fb9e8a0a832a45ac793865738c51
data/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # chef-metal-vagrant
2
2
 
3
- This is the Vagrant provisioner for chef-metal.
3
+ This is the Vagrant driver for chef-metal.
@@ -1,10 +1,8 @@
1
1
  require 'chef/provider/lwrp_base'
2
- require 'chef_metal/provider_action_handler'
2
+ require 'cheffish'
3
3
 
4
4
  class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
5
5
 
6
- include ChefMetal::ProviderActionHandler
7
-
8
6
  use_inline_resources
9
7
 
10
8
  def whyrun_supported?
@@ -13,7 +11,7 @@ class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
13
11
 
14
12
  action :create do
15
13
  the_base_path = new_resource.path
16
- ChefMetal.inline_resource(self) do
14
+ Cheffish.inline_resource(self, :create) do
17
15
  directory the_base_path
18
16
  file ::File.join(the_base_path, 'Vagrantfile') do
19
17
  content <<EOM
@@ -27,7 +25,7 @@ EOM
27
25
 
28
26
  action :delete do
29
27
  the_base_path = new_resource.path
30
- ChefMetal.inline_resource(self) do
28
+ Cheffish.inline_resource(self, :delete) do
31
29
  file ::File.join(the_base_path, 'Vagrantfile') do
32
30
  action :delete
33
31
  end
@@ -9,7 +9,7 @@ class Chef::Resource::VagrantBox < Chef::Resource::LWRPBase
9
9
 
10
10
  attribute :name, :kind_of => String, :name_attribute => true
11
11
  attribute :url, :kind_of => String
12
- attribute :provisioner_options, :kind_of => Hash
12
+ attribute :driver_options, :kind_of => Hash
13
13
 
14
14
  def after_created
15
15
  super
@@ -11,6 +11,6 @@ class Chef::Resource::VagrantCluster < Chef::Resource::LWRPBase
11
11
 
12
12
  def after_created
13
13
  super
14
- run_context.chef_metal.with_provisioner ChefMetalVagrant::VagrantProvisioner.new(path)
14
+ run_context.chef_metal.with_driver "vagrant:#{path}"
15
15
  end
16
16
  end
@@ -0,0 +1,3 @@
1
+ require 'chef_metal_vagrant/vagrant_driver'
2
+
3
+ ChefMetal.register_driver_class("vagrant", ChefMetalVagrant::VagrantDriver)
@@ -0,0 +1,461 @@
1
+ require 'chef/mixin/shell_out'
2
+ require 'chef_metal/driver'
3
+ require 'chef_metal/machine/windows_machine'
4
+ require 'chef_metal/machine/unix_machine'
5
+ require 'chef_metal/convergence_strategy/install_msi'
6
+ require 'chef_metal/convergence_strategy/install_cached'
7
+ require 'chef_metal/transport/winrm'
8
+ require 'chef_metal/transport/ssh'
9
+ require 'chef_metal_vagrant/version'
10
+ require 'chef/resource/vagrant_cluster'
11
+ require 'chef/provider/vagrant_cluster'
12
+
13
+ module ChefMetalVagrant
14
+ # Provisions machines in vagrant.
15
+ class VagrantDriver < ChefMetal::Driver
16
+
17
+ include Chef::Mixin::ShellOut
18
+
19
+ # Create a new vagrant driver.
20
+ #
21
+ # ## Parameters
22
+ # cluster_path - path to the directory containing the vagrant files, which
23
+ # should have been created with the vagrant_cluster resource.
24
+ def initialize(driver_url, config)
25
+ super
26
+ scheme, cluster_path = driver_url.split(':', 2)
27
+ @cluster_path = cluster_path
28
+ end
29
+
30
+ attr_reader :cluster_path
31
+
32
+ def self.from_url(driver_url, config)
33
+ scheme, cluster_path = driver_url.split(':', 2)
34
+ cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'vms'))
35
+ VagrantDriver.new("vagrant:#{cluster_path}", config)
36
+ end
37
+
38
+ # Acquire a machine, generally by provisioning it. Returns a Machine
39
+ # object pointing at the machine, allowing useful actions like setup,
40
+ # converge, execute, file and directory.
41
+ def allocate_machine(action_handler, machine_spec, machine_options)
42
+ ensure_vagrant_cluster(action_handler)
43
+ vm_name = machine_spec.name
44
+ vm_file_path = File.join(cluster_path, "#{machine_spec.name}.vm")
45
+ vm_file_updated = create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
46
+ if vm_file_updated || !machine_spec.location
47
+ old_location = machine_spec.location
48
+ machine_spec.location = {
49
+ 'driver_url' => driver_url,
50
+ 'driver_version' => ChefMetalVagrant::VERSION,
51
+ 'vm_name' => vm_name,
52
+ 'vm_file_path' => vm_file_path,
53
+ 'allocated_at' => Time.now.utc.to_s,
54
+ 'host_node' => action_handler.host_node
55
+ }
56
+ machine_spec.location['needs_reload'] = true if vm_file_updated
57
+ if machine_options[:vagrant_options]
58
+ %w(vm.guest winrm.host winrm.port winrm.username winrm.password).each do |key|
59
+ machine_spec.location[key] = machine_options[:vagrant_options][key] if machine_options[:vagrant_options][key]
60
+ end
61
+ end
62
+ machine_spec.location['chef_client_timeout'] = machine_options[:chef_client_timeout] if machine_options[:chef_client_timeout]
63
+ end
64
+ end
65
+
66
+ def ready_machine(action_handler, machine_spec, machine_options)
67
+ start_machine(action_handler, machine_spec, machine_options)
68
+ machine_for(machine_spec, machine_options)
69
+ end
70
+
71
+ # Connect to machine without acquiring it
72
+ def connect_to_machine(machine_spec, machine_options)
73
+ machine_for(machine_spec, machine_options)
74
+ end
75
+
76
+ def destroy_machine(action_handler, machine_spec, machine_options)
77
+ if machine_spec.location
78
+ vm_name = machine_spec.location['vm_name']
79
+ current_status = vagrant_status(vm_name)
80
+ if current_status != 'not created'
81
+ action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
82
+ result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
83
+ if result.exitstatus != 0
84
+ raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
85
+ end
86
+ end
87
+ end
88
+
89
+ convergence_strategy_for(machine_spec, machine_options).
90
+ cleanup_convergence(action_handler, machine_spec)
91
+
92
+ vm_file_path = machine_spec.location['vm_file_path']
93
+ ChefMetal.inline_resource(action_handler) do
94
+ file vm_file_path do
95
+ action :delete
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ def stop_machine(action_handler, machine_spec, machine_options)
102
+ if machine_spec.location
103
+ vm_name = machine_spec.location['vm_name']
104
+ current_status = vagrant_status(vm_name)
105
+ if current_status == 'running'
106
+ action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
107
+ result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
108
+ if result.exitstatus != 0
109
+ raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def ready_machines(action_handler, specs_and_options, parallelizer)
117
+ start_machines(action_handler, specs_and_options)
118
+ machines = []
119
+ specs_and_options.each_pair do |spec, options|
120
+ machines.push(machine_for(spec, options))
121
+ end
122
+ machines
123
+ end
124
+
125
+ def destroy_machines(action_handler, specs_and_options, parallelizer)
126
+ all_names = []
127
+ all_status = []
128
+ all_outputs = {}
129
+ specs_and_options.each_key do |spec|
130
+ if spec.location
131
+ vm_name = spec.location['vm_name']
132
+ current_status = vagrant_status(vm_name)
133
+ if current_status != 'not created'
134
+ all_names.push(vm_name)
135
+ all_status.push(current_status)
136
+ end
137
+ end
138
+ end
139
+ if all_names.length > 0
140
+ names = all_names.join(" ")
141
+ statuses = all_status.join(", ")
142
+ action_handler.perform_action "run vagrant destroy -f #{names} (status was '#{statuses}')" do
143
+ result = shell_out("vagrant destroy -f #{names}", :cwd => cluster_path)
144
+ if result.exitstatus != 0
145
+ raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
146
+ end
147
+ end
148
+ end
149
+ specs_and_options.each_pair do |spec, options|
150
+ convergence_strategy_for(spec, options).
151
+ cleanup_convergence(action_handler, spec)
152
+
153
+ vm_file_path = spec.location['vm_file_path']
154
+ ChefMetal.inline_resource(action_handler) do
155
+ file vm_file_path do
156
+ action :delete
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def stop_machines(action_handler, specs_and_options, parallelizer)
163
+ all_names = []
164
+ specs_and_options.each_key do |spec|
165
+ if spec.location
166
+ vm_name = spec.location['vm_name']
167
+ current_status = vagrant_status(vm_name)
168
+ if current_status == 'running'
169
+ all_names.push(vm_name)
170
+ end
171
+ end
172
+ end
173
+ if all_names.length > 0
174
+ names = all_names.join(" ")
175
+ action_handler.perform_action "run vagrant halt #{names} (status was 'running')" do
176
+ result = shell_out("vagrant halt #{names}", :cwd => cluster_path)
177
+ if result.exitstatus != 0
178
+ raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ # Used by vagrant_cluster and machine to get the string used to configure vagrant
185
+ def self.vagrant_config_string(vagrant_config, variable, line_prefix)
186
+ hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
187
+
188
+ result = ''
189
+ vagrant_config.each_pair do |key, value|
190
+ result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
191
+ end
192
+ result
193
+ end
194
+
195
+ def driver_url
196
+ "vagrant:#{cluster_path}"
197
+ end
198
+
199
+ protected
200
+
201
+ def ensure_vagrant_cluster(action_handler)
202
+ _cluster_path = cluster_path
203
+ ChefMetal.inline_resource(action_handler) do
204
+ vagrant_cluster _cluster_path
205
+ end
206
+ end
207
+
208
+ def create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
209
+ # Determine contents of vm file
210
+ vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
211
+ vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
212
+ merged_vagrant_options = { 'vm.hostname' => vm_name }
213
+ if machine_options[:vagrant_options]
214
+ merged_vagrant_options = Cheffish::MergedConfig.new(machine_options[:vagrant_options], merged_vagrant_options)
215
+ end
216
+ merged_vagrant_options.each_pair do |key, value|
217
+ vm_file_content << " config.#{key} = #{value.inspect}\n"
218
+ end
219
+ vm_file_content << machine_options[:vagrant_config] if machine_options[:vagrant_config]
220
+ vm_file_content << " end\nend\n"
221
+
222
+ # Set up vagrant file
223
+ ChefMetal.inline_resource(action_handler) do
224
+ file vm_file_path do
225
+ content vm_file_content
226
+ action :create
227
+ end
228
+ end
229
+ end
230
+
231
+ def start_machine(action_handler, machine_spec, machine_options)
232
+ vm_name = machine_spec.location['vm_name']
233
+ up_timeout = machine_options[:up_timeout] || 10*60
234
+
235
+ current_status = vagrant_status(vm_name)
236
+ vm_file_updated = machine_spec.location['needs_reload']
237
+ machine_spec.location['needs_reload'] = false
238
+ if current_status != 'running'
239
+ # Run vagrant up if vm is not running
240
+ action_handler.perform_action "run vagrant up #{vm_name} (status was '#{current_status}')" do
241
+ result = shell_out("vagrant up #{vm_name}", :cwd => cluster_path,
242
+ :timeout => up_timeout)
243
+ if result.exitstatus != 0
244
+ raise "vagrant up #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
245
+ end
246
+ parse_vagrant_up(result.stdout, machine_spec)
247
+ end
248
+ elsif vm_file_updated
249
+ # Run vagrant reload if vm is running and vm file changed
250
+ action_handler.perform_action "run vagrant reload #{vm_name}" do
251
+ result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path,
252
+ :timeout => up_timeout)
253
+ if result.exitstatus != 0
254
+ raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
255
+ end
256
+ parse_vagrant_up(result.stdout, machine_spec)
257
+ end
258
+ end
259
+ end
260
+
261
+ def start_machines(action_handler, specs_and_options)
262
+ up_names = []
263
+ up_status = []
264
+ up_specs = {}
265
+ update_names = []
266
+ update_specs = {}
267
+ timeouts = []
268
+ specs_and_options.each_pair do |spec, options|
269
+ vm_name = spec.location['vm_name']
270
+
271
+ vm_file_updated = spec.location['needs_reload']
272
+ spec.location['needs_reload'] = false
273
+
274
+ current_status = vagrant_status(vm_name)
275
+ if current_status != 'running'
276
+ up_names.push(vm_name)
277
+ up_status.push(current_status)
278
+ up_specs[vm_name] = spec
279
+ elsif vm_file_updated
280
+ update_names.push(vm_name)
281
+ update_specs[vm_name] = spec
282
+ end
283
+ timeouts.push(options[:up_timeout])
284
+ end
285
+ # Use the highest timeout, if any exist
286
+ up_timeout = timeouts.compact.max
287
+ up_timeout ||= 10*60
288
+ if up_names.length > 0
289
+ # Run vagrant up if vm is not running
290
+ names = up_names.join(" ")
291
+ statuses = up_status.join(", ")
292
+ action_handler.perform_action "run vagrant up --parallel #{names} (status was '#{statuses}')" do
293
+ result = shell_out("vagrant up --parallel #{names}", :cwd => cluster_path,
294
+ :timeout => up_timeout)
295
+ if result.exitstatus != 0
296
+ raise "vagrant up #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
297
+ end
298
+ parse_multi_vagrant_up(result.stdout, up_specs)
299
+ end
300
+ end
301
+ if update_names.length > 0
302
+ names = update_names.join(" ")
303
+ # Run vagrant reload if vm is running and vm file changed
304
+ action_handler.perform_action "run vagrant reload #{names}" do
305
+ result = shell_out("vagrant reload #{names}", :cwd => cluster_path,
306
+ :timeout => up_timeout)
307
+ if result.exitstatus != 0
308
+ raise "vagrant reload #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
309
+ end
310
+ parse_multi_vagrant_up(result.stdout, update_specs)
311
+ end
312
+ end
313
+ end
314
+
315
+ def parse_vagrant_up(output, machine_spec)
316
+ # Grab forwarded port info
317
+ machine_spec.location['forwarded_ports'] = {}
318
+ in_forwarding_ports = false
319
+ output.lines.each do |line|
320
+ if in_forwarding_ports
321
+ if line =~ /-- (\d+) => (\d+)/
322
+ machine_spec.location['forwarded_ports'][$1] = $2
323
+ else
324
+ in_forwarding_ports = false
325
+ end
326
+ elsif line =~ /Forwarding ports...$/
327
+ in_forwarding_ports = true
328
+ end
329
+ end
330
+ end
331
+
332
+ def parse_multi_vagrant_up(output, all_machine_specs)
333
+ # Grab forwarded port info
334
+ in_forwarding_ports = {}
335
+ all_machine_specs.each_pair do |key, spec|
336
+ spec.location['forwarded_ports'] = {}
337
+ in_forwarding_ports[key] = false
338
+ end
339
+ output.lines.each do |line|
340
+ /^\[(.*?)\]/.match(line)
341
+ node_name = $1
342
+ if in_forwarding_ports[node_name]
343
+ if line =~ /-- (\d+) => (\d+)/
344
+ spec = all_machine_specs[node_name]
345
+ spec.location['forwarded_ports'][$1] = $2
346
+ else
347
+ in_forwarding_ports[node_name] = false
348
+ end
349
+ elsif line =~ /Forwarding ports...$/
350
+ in_forwarding_ports[node_name] = true
351
+ end
352
+ end
353
+ end
354
+
355
+ def machine_for(machine_spec, machine_options)
356
+ if machine_spec.location['vm.guest'].to_s == 'windows'
357
+ ChefMetal::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec),
358
+ convergence_strategy_for(machine_spec, machine_options))
359
+ else
360
+ ChefMetal::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec),
361
+ convergence_strategy_for(machine_spec, machine_options))
362
+ end
363
+ end
364
+
365
+ def convergence_strategy_for(machine_spec, machine_options)
366
+ if machine_spec.location['vm.guest'].to_s == 'windows'
367
+ @windows_convergence_strategy ||= begin
368
+ ChefMetal::ConvergenceStrategy::InstallMsi.
369
+ new(machine_options[:convergence_options], config)
370
+ end
371
+ else
372
+ @unix_convergence_strategy ||= begin
373
+ ChefMetal::ConvergenceStrategy::InstallCached.
374
+ new(machine_options[:convergence_options], config)
375
+ end
376
+ end
377
+ end
378
+
379
+ def transport_for(machine_spec)
380
+ if machine_spec.location['vm.guest'].to_s == 'windows'
381
+ create_winrm_transport(machine_spec)
382
+ else
383
+ create_ssh_transport(machine_spec)
384
+ end
385
+ end
386
+
387
+ def vagrant_status(name)
388
+ status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
389
+ if status_output =~ /^#{name}\s+([^\n]+)\s+\(([^\n]+)\)$/m
390
+ $1
391
+ else
392
+ 'not created'
393
+ end
394
+ end
395
+
396
+ def create_winrm_transport(machine_spec)
397
+ forwarded_ports = machine_spec.location['forwarded_ports']
398
+
399
+ # TODO IPv6 loopback? What do we do for that?
400
+ hostname = machine_spec.location['winrm.host'] || '127.0.0.1'
401
+ port = machine_spec.location['winrm.port'] || 5985
402
+ port = forwarded_ports[port] if forwarded_ports[port]
403
+ endpoint = "http://#{hostname}:#{port}/wsman"
404
+ type = :plaintext
405
+ options = {
406
+ :user => machine_spec.location['winrm.username'] || 'vagrant',
407
+ :pass => machine_spec.location['winrm.password'] || 'vagrant',
408
+ :disable_sspi => true
409
+ }
410
+
411
+ ChefMetal::Transport::WinRM.new(endpoint, type, options)
412
+ end
413
+
414
+ def create_ssh_transport(machine_spec)
415
+ vagrant_ssh_config = vagrant_ssh_config_for(machine_spec)
416
+ hostname = vagrant_ssh_config['HostName']
417
+ username = vagrant_ssh_config['User']
418
+ ssh_options = {
419
+ :port => vagrant_ssh_config['Port'],
420
+ :auth_methods => ['publickey'],
421
+ :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
422
+ :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
423
+ :keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
424
+ :keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
425
+ }
426
+ ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
427
+ options = {
428
+ :prefix => 'sudo '
429
+ }
430
+ ChefMetal::Transport::SSH.new(hostname, username, ssh_options, options, config)
431
+ end
432
+
433
+ def vagrant_ssh_config_for(machine_spec)
434
+ vagrant_ssh_config = {}
435
+ result = shell_out("vagrant ssh-config #{machine_spec.location['vm_name']}",
436
+ :cwd => cluster_path)
437
+ result.stdout.lines.inject({}) do |result, line|
438
+ line =~ /^\s*(\S+)\s+(.+)/
439
+ vagrant_ssh_config[$1] = $2
440
+ end
441
+ vagrant_ssh_config
442
+ end
443
+
444
+ def yes_or_no(str)
445
+ case str
446
+ when 'yes'
447
+ true
448
+ else
449
+ false
450
+ end
451
+ end
452
+
453
+ def strip_quotes(str)
454
+ if str[0] == '"' && str[-1] == '"' && str.size >= 2
455
+ str[1..-2]
456
+ else
457
+ str
458
+ end
459
+ end
460
+ end
461
+ end
@@ -1,3 +1,3 @@
1
1
  module ChefMetalVagrant
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.beta'
3
3
  end
@@ -3,29 +3,31 @@ require 'chef/resource/vagrant_cluster'
3
3
  require 'chef/provider/vagrant_cluster'
4
4
  require 'chef/resource/vagrant_box'
5
5
  require 'chef/provider/vagrant_box'
6
- require 'chef_metal_vagrant/vagrant_provisioner'
6
+ require 'chef_metal_vagrant/vagrant_driver'
7
7
 
8
8
  module ChefMetalVagrant
9
9
  def self.with_vagrant_box(run_context, box_name, vagrant_options = {}, &block)
10
10
  if box_name.is_a?(Chef::Resource::VagrantBox)
11
- new_options = { 'vagrant_options' => { 'vm.box' => box_name.name } }
12
- new_options['vagrant_options']['vm.box_url'] = box_name.url if box_name.url
11
+ new_options = { :vagrant_options => { 'vm.box' => box_name.name } }
12
+ new_options[:vagrant_options]['vm.box_url'] = box_name.url if box_name.url
13
13
  else
14
- new_options = { 'vagrant_options' => { 'vm.box' => box_name } }
14
+ new_options = { :vagrant_options => { 'vm.box' => box_name } }
15
15
  end
16
16
 
17
- run_context.chef_metal.add_provisioner_options(new_options, &block)
17
+ run_context.chef_metal.add_machine_options(new_options, &block)
18
18
  end
19
19
  end
20
20
 
21
21
  class Chef
22
- class Recipe
23
- def with_vagrant_cluster(cluster_path, &block)
24
- with_provisioner(ChefMetalVagrant::VagrantProvisioner.new(cluster_path), &block)
25
- end
22
+ module DSL
23
+ module Recipe
24
+ def with_vagrant_cluster(cluster_path, &block)
25
+ with_driver("vagrant:#{cluster_path}", &block)
26
+ end
26
27
 
27
- def with_vagrant_box(box_name, vagrant_options = {}, &block)
28
- ChefMetalVagrant.with_vagrant_box(run_context, box_name, vagrant_options, &block)
28
+ def with_vagrant_box(box_name, vagrant_options = {}, &block)
29
+ ChefMetalVagrant.with_vagrant_box(run_context, box_name, vagrant_options, &block)
30
+ end
29
31
  end
30
32
  end
31
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-metal-vagrant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-02 00:00:00.000000000 Z
11
+ date: 2014-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description: Provisioner for creating Vagrant instances in Chef Metal.
55
+ description: Driver for creating Vagrant instances in Chef Metal.
56
56
  email: jkeiser@getchef.com
57
57
  executables: []
58
58
  extensions: []
@@ -67,8 +67,8 @@ files:
67
67
  - lib/chef/provider/vagrant_cluster.rb
68
68
  - lib/chef/resource/vagrant_box.rb
69
69
  - lib/chef/resource/vagrant_cluster.rb
70
- - lib/chef_metal/provisioner_init/vagrant_cluster_init.rb
71
- - lib/chef_metal_vagrant/vagrant_provisioner.rb
70
+ - lib/chef_metal/driver_init/vagrant.rb
71
+ - lib/chef_metal_vagrant/vagrant_driver.rb
72
72
  - lib/chef_metal_vagrant/version.rb
73
73
  - lib/chef_metal_vagrant.rb
74
74
  homepage: https://github.com/opscode/chef-metal-fog
@@ -85,14 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
85
  version: '0'
86
86
  required_rubygems_version: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - '>='
88
+ - - '>'
89
89
  - !ruby/object:Gem::Version
90
- version: '0'
90
+ version: 1.3.1
91
91
  requirements: []
92
92
  rubyforge_project:
93
93
  rubygems_version: 2.0.3
94
94
  signing_key:
95
95
  specification_version: 4
96
- summary: Provisioner for creating Vagrant instances in Chef Metal.
96
+ summary: Driver for creating Vagrant instances in Chef Metal.
97
97
  test_files: []
98
98
  has_rdoc:
@@ -1,4 +0,0 @@
1
- require 'chef_metal_vagrant/vagrant_provisioner'
2
-
3
- ChefMetal.add_registered_provisioner_class("vagrant_cluster",
4
- ChefMetalVagrant::VagrantProvisioner)
@@ -1,361 +0,0 @@
1
- require 'chef/mixin/shell_out'
2
- require 'chef_metal/provisioner'
3
- require 'chef_metal/machine/windows_machine'
4
- require 'chef_metal/machine/unix_machine'
5
- require 'chef_metal/convergence_strategy/install_msi'
6
- require 'chef_metal/convergence_strategy/install_cached'
7
- require 'chef_metal/transport/winrm'
8
- require 'chef_metal/transport/ssh'
9
-
10
- module ChefMetalVagrant
11
- # Provisions machines in vagrant.
12
- class VagrantProvisioner < ChefMetal::Provisioner
13
-
14
- include Chef::Mixin::ShellOut
15
-
16
- # Create a new vagrant provisioner.
17
- #
18
- # ## Parameters
19
- # cluster_path - path to the directory containing the vagrant files, which
20
- # should have been created with the vagrant_cluster resource.
21
- def initialize(cluster_path)
22
- @cluster_path = cluster_path
23
- end
24
-
25
- attr_reader :cluster_path
26
-
27
- # Inflate a provisioner from node information; we don't want to force the
28
- # driver to figure out what the provisioner really needs, since it varies
29
- # from provisioner to provisioner.
30
- #
31
- # ## Parameters
32
- # node - node to inflate the provisioner for
33
- #
34
- # returns a VagrantProvisioner
35
- def self.inflate(node)
36
- node_url = node['normal']['provisioner_output']['provisioner_url']
37
- cluster_path = node_url.split(':', 2)[1].sub(/^\/\//, "")
38
- self.new(cluster_path)
39
- end
40
-
41
- # Acquire a machine, generally by provisioning it. Returns a Machine
42
- # object pointing at the machine, allowing useful actions like setup,
43
- # converge, execute, file and directory. The Machine object will have a
44
- # "node" property which must be saved to the server (if it is any
45
- # different from the original node object).
46
- #
47
- # ## Parameters
48
- # action_handler - the action_handler object that is calling this method; this
49
- # is generally a action_handler, but could be anything that can support the
50
- # ChefMetal::ActionHandler interface (i.e., in the case of the test
51
- # kitchen metal driver for acquiring and destroying VMs; see the base
52
- # class for what needs providing).
53
- # node - node object (deserialized json) representing this machine. If
54
- # the node has a provisioner_options hash in it, these will be used
55
- # instead of options provided by the provisioner. TODO compare and
56
- # fail if different?
57
- # node will have node['normal']['provisioner_options'] in it with any options.
58
- # It is a hash with this format (all keys are strings):
59
- #
60
- # -- provisioner_url: vagrant:<cluster_path>
61
- # -- vagrant_options: hash of properties of the "config"
62
- # object, i.e. "vm.box" => "ubuntu12" and "vm.box_url"
63
- # -- vagrant_config: string containing other vagrant config.
64
- # Should assume the variable "config" represents machine config.
65
- # Will be written verbatim into the vm's Vagrantfile.
66
- # -- transport_options: hash of options specifying the transport.
67
- # :type => :ssh
68
- # :type => :winrm
69
- # If not specified, ssh is used unless vm.guest is :windows. If that is
70
- # the case, the windows options are used and the port forward for 5985
71
- # is detected.
72
- # -- up_timeout: maximum time, in seconds, to wait for vagrant
73
- # to bring up the machine. Defaults to 10 minutes.
74
- # -- chef_client_timeout: maximum time, in seconds, to wait for chef-client
75
- # to complete. 0 or nil for no timeout. Defaults to 2 hours.
76
- #
77
- # node['normal']['provisioner_output'] will be populated with information
78
- # about the created machine. For vagrant, it is a hash with this
79
- # format:
80
- #
81
- # -- provisioner_url: vagrant_cluster://<current_node>/<cluster_path>
82
- # -- vm_name: name of vagrant vm created
83
- # -- vm_file_path: path to machine-specific vagrant config file
84
- # on disk
85
- # -- forwarded_ports: hash with key as guest_port => host_port
86
- #
87
- def acquire_machine(action_handler, node)
88
- # Set up the provisioner output
89
- provisioner_options = node['normal']['provisioner_options']
90
- vm_name = node['name']
91
- old_provisioner_output = node['normal']['provisioner_output']
92
- node['normal']['provisioner_output'] = provisioner_output = {
93
- 'provisioner_url' => provisioner_url(vm_name),
94
- 'vm_name' => vm_name,
95
- 'vm_file_path' => File.join(cluster_path, "#{vm_name}.vm")
96
- }
97
- # Preserve existing forwarded ports
98
- provisioner_output['forwarded_ports'] = old_provisioner_output['forwarded_ports'] if old_provisioner_output
99
-
100
- # TODO compare new options to existing and fail if we cannot change it
101
- # over (perhaps introduce a boolean that will force a delete and recreate
102
- # in such a case)
103
-
104
- #
105
- # This is where the work gets done:
106
- # Create the .vm file, start the vm, and return the Machine
107
- #
108
- vm_file_updated = create_vm_file(action_handler, vm_name, provisioner_output['vm_file_path'], provisioner_options)
109
- start_machine(action_handler, vm_name, vm_file_updated, provisioner_output, provisioner_options['up_timeout'])
110
- machine_for(node)
111
- end
112
-
113
- # Connect to machine without acquiring it
114
- def connect_to_machine(node)
115
- machine_for(node)
116
- end
117
-
118
- def delete_machine(action_handler, node)
119
- if node['normal'] && node['normal']['provisioner_output']
120
- provisioner_output = node['normal']['provisioner_output']
121
- else
122
- provisioner_output = {}
123
- end
124
- vm_name = provisioner_output['vm_name'] || node['name']
125
- current_status = vagrant_status(vm_name)
126
- if current_status != 'not created'
127
- action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
128
- result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
129
- if result.exitstatus != 0
130
- raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
131
- end
132
- end
133
- end
134
-
135
- convergence_strategy_for(node).cleanup_convergence(action_handler, node)
136
-
137
- vm_file_path = provisioner_output['vm_file_path'] || File.join(cluster_path, "#{vm_name}.vm")
138
- ChefMetal.inline_resource(action_handler) do
139
- file vm_file_path do
140
- action :delete
141
- end
142
- end
143
- end
144
-
145
- def stop_machine(action_handler, node)
146
- if node['normal'] && node['normal']['provisioner_output']
147
- provisioner_output = node['normal']['provisioner_output']
148
- else
149
- provisioner_output = {}
150
- end
151
- vm_name = provisioner_output['vm_name'] || node['name']
152
- current_status = vagrant_status(vm_name)
153
- if current_status == 'running'
154
- action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
155
- result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
156
- if result.exitstatus != 0
157
- raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
158
- end
159
- end
160
- end
161
- end
162
-
163
-
164
- # Used by vagrant_cluster and machine to get the string used to configure vagrant
165
- def self.vagrant_config_string(vagrant_config, variable, line_prefix)
166
- hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
167
-
168
- result = ''
169
- vagrant_config.each_pair do |key, value|
170
- result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
171
- end
172
- result
173
- end
174
-
175
- protected
176
-
177
- def provisioner_url(vm_name)
178
- "vagrant_cluster://#{vm_name}#{cluster_path}"
179
- end
180
-
181
- def create_vm_file(action_handler, vm_name, vm_file_path, provisioner_options)
182
- # Determine contents of vm file
183
- vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
184
- vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
185
- merged_vagrant_options = { 'vm.hostname' => vm_name }
186
- merged_vagrant_options.merge!(provisioner_options['vagrant_options']) if provisioner_options['vagrant_options']
187
- merged_vagrant_options.each_pair do |key, value|
188
- vm_file_content << " config.#{key} = #{value.inspect}\n"
189
- end
190
- vm_file_content << provisioner_options['vagrant_config'] if provisioner_options['vagrant_config']
191
- vm_file_content << " end\nend\n"
192
-
193
- # Set up vagrant file
194
- ChefMetal.inline_resource(action_handler) do
195
- file vm_file_path do
196
- content vm_file_content
197
- action :create
198
- end
199
- end
200
- end
201
-
202
- def start_machine(action_handler, vm_name, vm_file_updated, provisioner_output, up_timeout)
203
- # Check current status of vm
204
- current_status = vagrant_status(vm_name)
205
- up_timeout ||= 10*60
206
-
207
- if current_status != 'running'
208
- # Run vagrant up if vm is not running
209
- action_handler.perform_action "run vagrant up #{vm_name} (status was '#{current_status}')" do
210
- result = shell_out("vagrant up #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
211
- if result.exitstatus != 0
212
- raise "vagrant up #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
213
- end
214
- parse_vagrant_up(result.stdout, provisioner_output)
215
- end
216
- elsif vm_file_updated
217
- # Run vagrant reload if vm is running and vm file changed
218
- action_handler.perform_action "run vagrant reload #{vm_name}" do
219
- result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
220
- if result.exitstatus != 0
221
- raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
222
- end
223
- parse_vagrant_up(result.stdout, provisioner_output)
224
- end
225
- end
226
- end
227
-
228
- def parse_vagrant_up(output, provisioner_output)
229
- # Grab forwarded port info
230
- provisioner_output['forwarded_ports'] = {}
231
- in_forwarding_ports = false
232
- output.lines.each do |line|
233
- if in_forwarding_ports
234
- if line =~ /-- (\d+) => (\d+)/
235
- provisioner_output['forwarded_ports'][$1] = $2
236
- else
237
- in_forwarding_ports = false
238
- end
239
- elsif line =~ /Forwarding ports...$/
240
- in_forwarding_ports = true
241
- end
242
- end
243
- end
244
-
245
- def machine_for(node)
246
- if vagrant_option(node, 'vm.guest').to_s == 'windows'
247
- ChefMetal::Machine::WindowsMachine.new(node, transport_for(node), convergence_strategy_for(node))
248
- else
249
- ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
250
- end
251
- end
252
-
253
- def convergence_strategy_for(node)
254
- if vagrant_option(node, 'vm.guest').to_s == 'windows'
255
- @windows_convergence_strategy ||= begin
256
- options = {}
257
- provisioner_options = node['normal']['provisioner_options'] || {}
258
- options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
259
- ChefMetal::ConvergenceStrategy::InstallMsi.new(options)
260
- end
261
- else
262
- @unix_convergence_strategy ||= begin
263
- options = {}
264
- provisioner_options = node['normal']['provisioner_options'] || {}
265
- options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
266
- ChefMetal::ConvergenceStrategy::InstallCached.new(options)
267
- end
268
- end
269
- end
270
-
271
- def transport_for(node)
272
- if vagrant_option(node, 'vm.guest').to_s == 'windows'
273
- create_winrm_transport(node)
274
- else
275
- create_ssh_transport(node)
276
- end
277
- end
278
-
279
- def vagrant_option(node, option)
280
- if node['normal']['provisioner_options'] &&
281
- node['normal']['provisioner_options']['vagrant_options']
282
- node['normal']['provisioner_options']['vagrant_options'][option]
283
- else
284
- nil
285
- end
286
- end
287
-
288
- def vagrant_status(name)
289
- status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
290
- if status_output =~ /^#{name}\s+([^\n]+)\s+\(([^\n]+)\)$/m
291
- $1
292
- else
293
- 'not created'
294
- end
295
- end
296
-
297
- def create_winrm_transport(node)
298
- provisioner_output = node['default']['provisioner_output'] || {}
299
- forwarded_ports = provisioner_output['forwarded_ports'] || {}
300
-
301
- # TODO IPv6 loopback? What do we do for that?
302
- hostname = vagrant_option(node, 'winrm.host') || '127.0.0.1'
303
- port = vagrant_option(node, 'winrm.port') || forwarded_ports[5985] || 5985
304
- endpoint = "http://#{hostname}:#{port}/wsman"
305
- type = :plaintext
306
- options = {
307
- :user => vagrant_option(node, 'winrm.username') || 'vagrant',
308
- :pass => vagrant_option(node, 'winrm.password') || 'vagrant',
309
- :disable_sspi => true
310
- }
311
-
312
- ChefMetal::Transport::WinRM.new(endpoint, type, options)
313
- end
314
-
315
- def create_ssh_transport(node)
316
- vagrant_ssh_config = vagrant_ssh_config_for(node)
317
- hostname = vagrant_ssh_config['HostName']
318
- username = vagrant_ssh_config['User']
319
- ssh_options = {
320
- :port => vagrant_ssh_config['Port'],
321
- :auth_methods => ['publickey'],
322
- :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
323
- :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
324
- :keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
325
- :keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
326
- }
327
- ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
328
- options = {
329
- :prefix => 'sudo '
330
- }
331
- ChefMetal::Transport::SSH.new(hostname, username, ssh_options, options)
332
- end
333
-
334
- def vagrant_ssh_config_for(node)
335
- vagrant_ssh_config = {}
336
- result = shell_out("vagrant ssh-config #{node['normal']['provisioner_output']['vm_name']}", :cwd => cluster_path)
337
- result.stdout.lines.inject({}) do |result, line|
338
- line =~ /^\s*(\S+)\s+(.+)/
339
- vagrant_ssh_config[$1] = $2
340
- end
341
- vagrant_ssh_config
342
- end
343
-
344
- def yes_or_no(str)
345
- case str
346
- when 'yes'
347
- true
348
- else
349
- false
350
- end
351
- end
352
-
353
- def strip_quotes(str)
354
- if str[0] == '"' && str[-1] == '"' && str.size >= 2
355
- str[1..-2]
356
- else
357
- str
358
- end
359
- end
360
- end
361
- end