chef-provisioning-vagrant 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require 'bundler'
2
- require 'bundler/gem_tasks'
3
-
4
- task :spec do
5
- require File.expand_path('spec/run')
6
- end
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
3
+
4
+ task :spec do
5
+ require File.expand_path('spec/run')
6
+ end
@@ -1,58 +1,58 @@
1
- require 'chef/provider/lwrp_base'
2
- require 'chef/mixin/shell_out'
3
-
4
- class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
5
-
6
- use_inline_resources
7
-
8
- include Chef::Mixin::ShellOut
9
-
10
- def whyrun_supported?
11
- true
12
- end
13
-
14
- action :create do
15
- if !box_exists?(new_resource)
16
- if new_resource.url
17
- converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}'" do
18
- shell_out("vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}").error!
19
- end
20
- else
21
- raise "Box #{new_resource.name} does not exist"
22
- end
23
- end
24
- end
25
-
26
- action :delete do
27
- if box_exists?(new_resource.name)
28
- converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}'" do
29
- shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}").error!
30
- end
31
- end
32
- end
33
-
34
- # Since all box names must be unique for a particular vagrant provider, this hash now
35
- # keys off the provider name, as opposed to the box name. The version is not currently
36
- # used, but is collected as metadata for future consumption
37
- def list_boxes
38
- @list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
39
- line =~ /^(\S+)\s+\((.+),(.+)\)\s*$/
40
- if result.has_key?($2)
41
- result[$2][$1] = $3
42
- else
43
- result[$2] = { $1 => $3 }
44
- end
45
- result
46
- end
47
- end
48
-
49
- # In some rather strained logic, we hook into the vagrant provider, then
50
- # the box name to make sure we have the correct box already installed.
51
- def box_exists?(new_resource)
52
- boxes = list_boxes
53
- boxes[new_resource.vagrant_provider].has_key?(new_resource.name)
54
- end
55
-
56
- def load_current_resource
57
- end
58
- end
1
+ require 'chef/provider/lwrp_base'
2
+ require 'chef/mixin/shell_out'
3
+
4
+ class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
5
+
6
+ use_inline_resources
7
+
8
+ include Chef::Mixin::ShellOut
9
+
10
+ def whyrun_supported?
11
+ true
12
+ end
13
+
14
+ action :create do
15
+ if !box_exists?(new_resource)
16
+ if new_resource.url
17
+ converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}'" do
18
+ shell_out("vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}").error!
19
+ end
20
+ else
21
+ raise "Box #{new_resource.name} does not exist"
22
+ end
23
+ end
24
+ end
25
+
26
+ action :delete do
27
+ if box_exists?(new_resource.name)
28
+ converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}'" do
29
+ shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}").error!
30
+ end
31
+ end
32
+ end
33
+
34
+ # Since all box names must be unique for a particular vagrant provider, this hash now
35
+ # keys off the provider name, as opposed to the box name. The version is not currently
36
+ # used, but is collected as metadata for future consumption
37
+ def list_boxes
38
+ @list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
39
+ line =~ /^(\S+)\s+\((.+),(.+)\)\s*$/
40
+ if result.has_key?($2)
41
+ result[$2][$1] = $3
42
+ else
43
+ result[$2] = { $1 => $3 }
44
+ end
45
+ result
46
+ end
47
+ end
48
+
49
+ # In some rather strained logic, we hook into the vagrant provider, then
50
+ # the box name to make sure we have the correct box already installed.
51
+ def box_exists?(new_resource)
52
+ boxes = list_boxes
53
+ boxes[new_resource.vagrant_provider].has_key?(new_resource.name)
54
+ end
55
+
56
+ def load_current_resource
57
+ end
58
+ end
@@ -1,40 +1,40 @@
1
- require 'chef/provider/lwrp_base'
2
- require 'cheffish'
3
-
4
- class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
5
-
6
- use_inline_resources
7
-
8
- def whyrun_supported?
9
- true
10
- end
11
-
12
- action :create do
13
- the_base_path = new_resource.path
14
- Cheffish.inline_resource(self, :create) do
15
- directory the_base_path
16
- file ::File.join(the_base_path, 'Vagrantfile') do
17
- content <<EOM
18
- Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
19
- eval(IO.read(vm_file), nil, vm_file)
20
- end
21
- EOM
22
- end
23
- end
24
- end
25
-
26
- action :delete do
27
- the_base_path = new_resource.path
28
- Cheffish.inline_resource(self, :delete) do
29
- file ::File.join(the_base_path, 'Vagrantfile') do
30
- action :delete
31
- end
32
- directory the_base_path do
33
- action :delete
34
- end
35
- end
36
- end
37
-
38
- def load_current_resource
39
- end
40
- end
1
+ require 'chef/provider/lwrp_base'
2
+ require 'cheffish'
3
+
4
+ class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
5
+
6
+ use_inline_resources
7
+
8
+ def whyrun_supported?
9
+ true
10
+ end
11
+
12
+ action :create do
13
+ the_base_path = new_resource.path
14
+ Cheffish.inline_resource(self, :create) do
15
+ directory the_base_path
16
+ file ::File.join(the_base_path, 'Vagrantfile') do
17
+ content <<EOM
18
+ Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
19
+ eval(IO.read(vm_file), nil, vm_file)
20
+ end
21
+ EOM
22
+ end
23
+ end
24
+ end
25
+
26
+ action :delete do
27
+ the_base_path = new_resource.path
28
+ Cheffish.inline_resource(self, :delete) do
29
+ file ::File.join(the_base_path, 'Vagrantfile') do
30
+ action :delete
31
+ end
32
+ directory the_base_path do
33
+ action :delete
34
+ end
35
+ end
36
+ end
37
+
38
+ def load_current_resource
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
- require 'chef/provisioning/vagrant_driver/driver'
2
-
3
- Chef::Provisioning.register_driver_class("vagrant", Chef::Provisioning::VagrantDriver::Driver)
1
+ require 'chef/provisioning/vagrant_driver/driver'
2
+
3
+ Chef::Provisioning.register_driver_class("vagrant", Chef::Provisioning::VagrantDriver::Driver)
@@ -1,36 +1,36 @@
1
- require 'chef/provisioning'
2
- require 'chef/resource/vagrant_cluster'
3
- require 'chef/provider/vagrant_cluster'
4
- require 'chef/resource/vagrant_box'
5
- require 'chef/provider/vagrant_box'
6
- require 'chef/provisioning/vagrant_driver/driver'
7
-
8
- class Chef
9
- module Provisioning
10
- module VagrantDriver
11
- def self.with_vagrant_box(run_context, box_name, vagrant_options = {}, &block)
12
- if box_name.is_a?(Chef::Resource::VagrantBox)
13
- new_options = { :vagrant_options => { 'vm.box' => box_name.name } }
14
- new_options[:vagrant_options]['vm.box_url'] = box_name.url if box_name.url
15
- new_options[:vagrant_provider] = box_name.vagrant_provider
16
- else
17
- new_options = { :vagrant_options => { 'vm.box' => box_name } }
18
- end
19
-
20
- run_context.chef_provisioning.add_machine_options(new_options, &block)
21
- end
22
- end
23
- end
24
-
25
- module DSL
26
- module Recipe
27
- def with_vagrant_cluster(cluster_path, &block)
28
- with_driver("vagrant:#{cluster_path}", &block)
29
- end
30
-
31
- def with_vagrant_box(box_name, vagrant_options = {}, &block)
32
- Chef::Provisioning::VagrantDriver.with_vagrant_box(run_context, box_name, vagrant_options, &block)
33
- end
34
- end
35
- end
36
- end
1
+ require 'chef/provisioning'
2
+ require 'chef/resource/vagrant_cluster'
3
+ require 'chef/provider/vagrant_cluster'
4
+ require 'chef/resource/vagrant_box'
5
+ require 'chef/provider/vagrant_box'
6
+ require 'chef/provisioning/vagrant_driver/driver'
7
+
8
+ class Chef
9
+ module Provisioning
10
+ module VagrantDriver
11
+ def self.with_vagrant_box(run_context, box_name, vagrant_options = {}, &block)
12
+ if box_name.is_a?(Chef::Resource::VagrantBox)
13
+ new_options = { :vagrant_options => { 'vm.box' => box_name.name } }
14
+ new_options[:vagrant_options]['vm.box_url'] = box_name.url if box_name.url
15
+ new_options[:vagrant_provider] = box_name.vagrant_provider
16
+ else
17
+ new_options = { :vagrant_options => { 'vm.box' => box_name } }
18
+ end
19
+
20
+ run_context.chef_provisioning.add_machine_options(new_options, &block)
21
+ end
22
+ end
23
+ end
24
+
25
+ module DSL
26
+ module Recipe
27
+ def with_vagrant_cluster(cluster_path, &block)
28
+ with_driver("vagrant:#{cluster_path}", &block)
29
+ end
30
+
31
+ def with_vagrant_box(box_name, vagrant_options = {}, &block)
32
+ Chef::Provisioning::VagrantDriver.with_vagrant_box(run_context, box_name, vagrant_options, &block)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,470 +1,470 @@
1
- require 'chef/mixin/shell_out'
2
- require 'chef/provisioning/driver'
3
- require 'chef/provisioning/machine/windows_machine'
4
- require 'chef/provisioning/machine/unix_machine'
5
- require 'chef/provisioning/convergence_strategy/install_msi'
6
- require 'chef/provisioning/convergence_strategy/install_cached'
7
- require 'chef/provisioning/transport/winrm'
8
- require 'chef/provisioning/transport/ssh'
9
- require 'chef/provisioning/vagrant_driver/version'
10
- require 'chef/resource/vagrant_cluster'
11
- require 'chef/provider/vagrant_cluster'
12
-
13
- class Chef
14
- module Provisioning
15
- module VagrantDriver
16
- # Provisions machines in vagrant.
17
- class Driver < Chef::Provisioning::Driver
18
-
19
- include Chef::Mixin::ShellOut
20
-
21
- # Create a new vagrant driver.
22
- #
23
- # ## Parameters
24
- # cluster_path - path to the directory containing the vagrant files, which
25
- # should have been created with the vagrant_cluster resource.
26
- def initialize(driver_url, config)
27
- super
28
- scheme, cluster_path = driver_url.split(':', 2)
29
- @cluster_path = cluster_path
30
- end
31
-
32
- attr_reader :cluster_path
33
-
34
- def self.from_url(driver_url, config)
35
- Driver.new(driver_url, config)
36
- end
37
-
38
- def self.canonicalize_url(driver_url, config)
39
- scheme, cluster_path = driver_url.split(':', 2)
40
- cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'vms'))
41
- "vagrant:#{cluster_path}"
42
- end
43
-
44
- # Acquire a machine, generally by provisioning it. Returns a Machine
45
- # object pointing at the machine, allowing useful actions like setup,
46
- # converge, execute, file and directory.
47
- def allocate_machine(action_handler, machine_spec, machine_options)
48
- ensure_vagrant_cluster(action_handler)
49
- vm_name = machine_spec.name
50
- vm_file_path = File.join(cluster_path, "#{machine_spec.name}.vm")
51
- vm_file_updated = create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
52
- if vm_file_updated || !machine_spec.location
53
- old_location = machine_spec.location
54
- machine_spec.location = {
55
- 'driver_url' => driver_url,
56
- 'driver_version' => Chef::Provisioning::VagrantDriver::VERSION,
57
- 'vm_name' => vm_name,
58
- 'vm_file_path' => vm_file_path,
59
- 'allocated_at' => Time.now.utc.to_s,
60
- 'host_node' => action_handler.host_node
61
- }
62
- machine_spec.location['needs_reload'] = true if vm_file_updated
63
- if machine_options[:vagrant_options]
64
- %w(vm.guest winrm.host winrm.port winrm.username winrm.password).each do |key|
65
- machine_spec.location[key] = machine_options[:vagrant_options][key] if machine_options[:vagrant_options][key]
66
- end
67
- end
68
- machine_spec.location['chef_client_timeout'] = machine_options[:chef_client_timeout] if machine_options[:chef_client_timeout]
69
- end
70
- end
71
-
72
- def ready_machine(action_handler, machine_spec, machine_options)
73
- start_machine(action_handler, machine_spec, machine_options)
74
- machine_for(machine_spec, machine_options)
75
- end
76
-
77
- # Connect to machine without acquiring it
78
- def connect_to_machine(machine_spec, machine_options)
79
- machine_for(machine_spec, machine_options)
80
- end
81
-
82
- def destroy_machine(action_handler, machine_spec, machine_options)
83
- if machine_spec.location
84
- vm_name = machine_spec.location['vm_name']
85
- current_status = vagrant_status(vm_name)
86
- if current_status != 'not created'
87
- action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
88
- result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
89
- if result.exitstatus != 0
90
- raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
91
- end
92
- end
93
- end
94
-
95
- convergence_strategy_for(machine_spec, machine_options).
96
- cleanup_convergence(action_handler, machine_spec)
97
-
98
- vm_file_path = machine_spec.location['vm_file_path']
99
- Chef::Provisioning.inline_resource(action_handler) do
100
- file vm_file_path do
101
- action :delete
102
- end
103
- end
104
- end
105
- end
106
-
107
- def stop_machine(action_handler, machine_spec, machine_options)
108
- if machine_spec.location
109
- vm_name = machine_spec.location['vm_name']
110
- current_status = vagrant_status(vm_name)
111
- if current_status == 'running'
112
- action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
113
- result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
114
- if result.exitstatus != 0
115
- raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
116
- end
117
- end
118
- end
119
- end
120
- end
121
-
122
- def ready_machines(action_handler, specs_and_options, parallelizer)
123
- start_machines(action_handler, specs_and_options)
124
- machines = []
125
- specs_and_options.each_pair do |spec, options|
126
- machine = machine_for(spec, options)
127
- machines << machine
128
- yield machine if block_given?
129
- end
130
- machines
131
- end
132
-
133
- def destroy_machines(action_handler, specs_and_options, parallelizer)
134
- all_names = []
135
- all_status = []
136
- all_outputs = {}
137
- specs_and_options.each_key do |spec|
138
- if spec.location
139
- vm_name = spec.location['vm_name']
140
- current_status = vagrant_status(vm_name)
141
- if current_status != 'not created'
142
- all_names.push(vm_name)
143
- all_status.push(current_status)
144
- end
145
- end
146
- end
147
- if all_names.length > 0
148
- names = all_names.join(" ")
149
- statuses = all_status.join(", ")
150
- action_handler.perform_action "run vagrant destroy -f #{names} (status was '#{statuses}')" do
151
- result = shell_out("vagrant destroy -f #{names}", :cwd => cluster_path)
152
- if result.exitstatus != 0
153
- raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
154
- end
155
- end
156
- end
157
- specs_and_options.each_pair do |spec, options|
158
- convergence_strategy_for(spec, options).cleanup_convergence(action_handler, spec)
159
-
160
- vm_file_path = spec.location['vm_file_path']
161
- Chef::Provisioning.inline_resource(action_handler) do
162
- file vm_file_path do
163
- action :delete
164
- end
165
- end
166
- yield spec if block_given?
167
- end
168
- end
169
-
170
- def stop_machines(action_handler, specs_and_options, parallelizer)
171
- all_names = []
172
- specs_and_options.each_key do |spec|
173
- if spec.location
174
- vm_name = spec.location['vm_name']
175
- current_status = vagrant_status(vm_name)
176
- if current_status == 'running'
177
- all_names.push(vm_name)
178
- end
179
- end
180
- end
181
- if all_names.length > 0
182
- names = all_names.join(" ")
183
- action_handler.perform_action "run vagrant halt #{names} (status was 'running')" do
184
- result = shell_out("vagrant halt #{names}", :cwd => cluster_path)
185
- if result.exitstatus != 0
186
- raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
187
- end
188
- end
189
- end
190
- end
191
-
192
- # Used by vagrant_cluster and machine to get the string used to configure vagrant
193
- def self.vagrant_config_string(vagrant_config, variable, line_prefix)
194
- hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
195
- result = ''
196
- vagrant_config.each_pair do |key, value|
197
- result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
198
- end
199
- result
200
- end
201
-
202
- def driver_url
203
- "vagrant:#{cluster_path}"
204
- end
205
-
206
- protected
207
-
208
- def ensure_vagrant_cluster(action_handler)
209
- _cluster_path = cluster_path
210
- Chef::Provisioning.inline_resource(action_handler) do
211
- vagrant_cluster _cluster_path
212
- end
213
- end
214
-
215
- def create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
216
- # Determine contents of vm file
217
- vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
218
- vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
219
- merged_vagrant_options = { 'vm.hostname' => vm_name }
220
- if machine_options[:vagrant_options]
221
- merged_vagrant_options = Cheffish::MergedConfig.new(machine_options[:vagrant_options], merged_vagrant_options)
222
- end
223
- merged_vagrant_options.each_pair do |key, value|
224
- if key == 'vm.network'
225
- vm_file_content << " config.#{key}(#{value})\n"
226
- else
227
- vm_file_content << " config.#{key} = #{value.inspect}\n"
228
- end
229
- end
230
- vm_file_content << machine_options[:vagrant_config] if machine_options[:vagrant_config]
231
- vm_file_content << " end\nend\n"
232
-
233
- # Set up vagrant file
234
- Chef::Provisioning.inline_resource(action_handler) do
235
- file vm_file_path do
236
- content vm_file_content
237
- action :create
238
- end
239
- end
240
- end
241
-
242
- def start_machine(action_handler, machine_spec, machine_options)
243
- vm_name = machine_spec.location['vm_name']
244
- vm_provider = machine_options.has_key?(:vagrant_provider) ? machine_options[:vagrant_provider] : 'virtualbox'
245
- up_timeout = machine_options[:up_timeout] || 10*60
246
- current_status = vagrant_status(vm_name)
247
- vm_file_updated = machine_spec.location['needs_reload']
248
- machine_spec.location['needs_reload'] = false
249
- if current_status != 'running'
250
- # Run vagrant up if vm is not running
251
- action_handler.perform_action "run vagrant up #{vm_name} --provider #{vm_provider} (status was '#{current_status}')" do
252
- result = shell_out("vagrant up #{vm_name} --provider #{vm_provider}", :cwd => cluster_path,
253
- :timeout => up_timeout)
254
- if result.exitstatus != 0
255
- raise "vagrant up #{vm_name} --provider #{vm_provider} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
256
- end
257
- parse_vagrant_up(result.stdout, machine_spec)
258
- end
259
- elsif vm_file_updated
260
- # Run vagrant reload if vm is running and vm file changed
261
- action_handler.perform_action "run vagrant reload #{vm_name}" do
262
- result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path,
263
- :timeout => up_timeout)
264
- if result.exitstatus != 0
265
- raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
266
- end
267
- parse_vagrant_up(result.stdout, machine_spec)
268
- end
269
- end
270
- end
271
-
272
- def start_machines(action_handler, specs_and_options)
273
- up_names = []
274
- up_status = []
275
- up_specs = {}
276
- update_names = []
277
- update_specs = {}
278
- timeouts = []
279
- specs_and_options.each_pair do |spec, options|
280
- vm_name = spec.location['vm_name']
281
-
282
- vm_file_updated = spec.location['needs_reload']
283
- spec.location['needs_reload'] = false
284
-
285
- current_status = vagrant_status(vm_name)
286
- if current_status != 'running'
287
- up_names.push(vm_name)
288
- up_status.push(current_status)
289
- up_specs[vm_name] = spec
290
- elsif vm_file_updated
291
- update_names.push(vm_name)
292
- update_specs[vm_name] = spec
293
- end
294
- timeouts.push(options[:up_timeout])
295
- end
296
- # Use the highest timeout, if any exist
297
- up_timeout = timeouts.compact.max
298
- up_timeout ||= 10*60
299
- if up_names.length > 0
300
- # Run vagrant up if vm is not running
301
- names = up_names.join(" ")
302
- statuses = up_status.join(", ")
303
- action_handler.perform_action "run vagrant up --parallel #{names} (status was '#{statuses}')" do
304
- result = shell_out("vagrant up --parallel #{names}", :cwd => cluster_path,
305
- :timeout => up_timeout)
306
- if result.exitstatus != 0
307
- raise "vagrant up #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
308
- end
309
- parse_multi_vagrant_up(result.stdout, up_specs)
310
- end
311
- end
312
- if update_names.length > 0
313
- names = update_names.join(" ")
314
- # Run vagrant reload if vm is running and vm file changed
315
- action_handler.perform_action "run vagrant reload #{names}" do
316
- result = shell_out("vagrant reload #{names}", :cwd => cluster_path,
317
- :timeout => up_timeout)
318
- if result.exitstatus != 0
319
- raise "vagrant reload #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
320
- end
321
- parse_multi_vagrant_up(result.stdout, update_specs)
322
- end
323
- end
324
- end
325
-
326
- def parse_vagrant_up(output, machine_spec)
327
- # Grab forwarded port info
328
- machine_spec.location['forwarded_ports'] = {}
329
- in_forwarding_ports = false
330
- output.lines.each do |line|
331
- if in_forwarding_ports
332
- if line =~ /-- (\d+) => (\d+)/
333
- machine_spec.location['forwarded_ports'][$1] = $2
334
- else
335
- in_forwarding_ports = false
336
- end
337
- elsif line =~ /Forwarding ports...$/
338
- in_forwarding_ports = true
339
- end
340
- end
341
- end
342
-
343
- def parse_multi_vagrant_up(output, all_machine_specs)
344
- # Grab forwarded port info
345
- in_forwarding_ports = {}
346
- all_machine_specs.each_pair do |key, spec|
347
- spec.location['forwarded_ports'] = {}
348
- in_forwarding_ports[key] = false
349
- end
350
- output.lines.each do |line|
351
- /^\[(.*?)\]/.match(line)
352
- node_name = $1
353
- if in_forwarding_ports[node_name]
354
- if line =~ /-- (\d+) => (\d+)/
355
- spec = all_machine_specs[node_name]
356
- spec.location['forwarded_ports'][$1] = $2
357
- else
358
- in_forwarding_ports[node_name] = false
359
- end
360
- elsif line =~ /Forwarding ports...$/
361
- in_forwarding_ports[node_name] = true
362
- end
363
- end
364
- end
365
-
366
- def machine_for(machine_spec, machine_options)
367
- if machine_spec.location['vm.guest'].to_s == 'windows'
368
- Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec),
369
- convergence_strategy_for(machine_spec, machine_options))
370
- else
371
- Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec),
372
- convergence_strategy_for(machine_spec, machine_options))
373
- end
374
- end
375
-
376
- def convergence_strategy_for(machine_spec, machine_options)
377
- if machine_spec.location['vm.guest'].to_s == 'windows'
378
- Chef::Provisioning::ConvergenceStrategy::InstallMsi.
379
- new(machine_options[:convergence_options], config)
380
- else
381
- Chef::Provisioning::ConvergenceStrategy::InstallCached.
382
- new(machine_options[:convergence_options], config)
383
- end
384
- end
385
-
386
- def transport_for(machine_spec)
387
- if machine_spec.location['vm.guest'].to_s == 'windows'
388
- create_winrm_transport(machine_spec)
389
- else
390
- create_ssh_transport(machine_spec)
391
- end
392
- end
393
-
394
- def vagrant_status(name)
395
- status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
396
- if status_output =~ /^#{name}\s+(.+)\s+\((.+)\)/
397
- $1
398
- else
399
- 'not created'
400
- end
401
- end
402
-
403
- def create_winrm_transport(machine_spec)
404
- forwarded_ports = machine_spec.location['forwarded_ports']
405
-
406
- # TODO IPv6 loopback? What do we do for that?
407
- hostname = machine_spec.location['winrm.host'] || '127.0.0.1'
408
- port = machine_spec.location['winrm.port'] || 5985
409
- port = forwarded_ports[port] if forwarded_ports[port]
410
- endpoint = "http://#{hostname}:#{port}/wsman"
411
- type = :plaintext
412
- options = {
413
- :user => machine_spec.location['winrm.username'] || 'vagrant',
414
- :pass => machine_spec.location['winrm.password'] || 'vagrant',
415
- :disable_sspi => true
416
- }
417
-
418
- Chef::Provisioning::Transport::WinRM.new(endpoint, type, options)
419
- end
420
-
421
- def create_ssh_transport(machine_spec)
422
- vagrant_ssh_config = vagrant_ssh_config_for(machine_spec)
423
- hostname = vagrant_ssh_config['HostName']
424
- username = vagrant_ssh_config['User']
425
- ssh_options = {
426
- :port => vagrant_ssh_config['Port'],
427
- :auth_methods => ['publickey'],
428
- :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
429
- :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
430
- :keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
431
- :keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
432
- }
433
- ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
434
- options = {
435
- :prefix => 'sudo '
436
- }
437
- Chef::Provisioning::Transport::SSH.new(hostname, username, ssh_options, options, config)
438
- end
439
-
440
- def vagrant_ssh_config_for(machine_spec)
441
- vagrant_ssh_config = {}
442
- result = shell_out("vagrant ssh-config #{machine_spec.location['vm_name']}",
443
- :cwd => cluster_path)
444
- result.stdout.lines.inject({}) do |result, line|
445
- line =~ /^\s*(\S+)\s+(.+?)(\r\n|\r|\n|\z)/
446
- vagrant_ssh_config[$1] = $2
447
- end
448
- vagrant_ssh_config
449
- end
450
-
451
- def yes_or_no(str)
452
- case str
453
- when 'yes'
454
- true
455
- else
456
- false
457
- end
458
- end
459
-
460
- def strip_quotes(str)
461
- if str[0] == '"' && str[-1] == '"' && str.size >= 2
462
- str[1..-2]
463
- else
464
- str
465
- end
466
- end
467
- end
468
- end
469
- end
470
- end
1
+ require 'chef/mixin/shell_out'
2
+ require 'chef/provisioning/driver'
3
+ require 'chef/provisioning/machine/windows_machine'
4
+ require 'chef/provisioning/machine/unix_machine'
5
+ require 'chef/provisioning/convergence_strategy/install_msi'
6
+ require 'chef/provisioning/convergence_strategy/install_cached'
7
+ require 'chef/provisioning/transport/winrm'
8
+ require 'chef/provisioning/transport/ssh'
9
+ require 'chef/provisioning/vagrant_driver/version'
10
+ require 'chef/resource/vagrant_cluster'
11
+ require 'chef/provider/vagrant_cluster'
12
+
13
+ class Chef
14
+ module Provisioning
15
+ module VagrantDriver
16
+ # Provisions machines in vagrant.
17
+ class Driver < Chef::Provisioning::Driver
18
+
19
+ include Chef::Mixin::ShellOut
20
+
21
+ # Create a new vagrant driver.
22
+ #
23
+ # ## Parameters
24
+ # cluster_path - path to the directory containing the vagrant files, which
25
+ # should have been created with the vagrant_cluster resource.
26
+ def initialize(driver_url, config)
27
+ super
28
+ scheme, cluster_path = driver_url.split(':', 2)
29
+ @cluster_path = cluster_path
30
+ end
31
+
32
+ attr_reader :cluster_path
33
+
34
+ def self.from_url(driver_url, config)
35
+ Driver.new(driver_url, config)
36
+ end
37
+
38
+ def self.canonicalize_url(driver_url, config)
39
+ scheme, cluster_path = driver_url.split(':', 2)
40
+ cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'vms'))
41
+ "vagrant:#{cluster_path}"
42
+ end
43
+
44
+ # Acquire a machine, generally by provisioning it. Returns a Machine
45
+ # object pointing at the machine, allowing useful actions like setup,
46
+ # converge, execute, file and directory.
47
+ def allocate_machine(action_handler, machine_spec, machine_options)
48
+ ensure_vagrant_cluster(action_handler)
49
+ vm_name = machine_spec.name
50
+ vm_file_path = File.join(cluster_path, "#{machine_spec.name}.vm")
51
+ vm_file_updated = create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
52
+ if vm_file_updated || !machine_spec.location
53
+ old_location = machine_spec.location
54
+ machine_spec.location = {
55
+ 'driver_url' => driver_url,
56
+ 'driver_version' => Chef::Provisioning::VagrantDriver::VERSION,
57
+ 'vm_name' => vm_name,
58
+ 'vm_file_path' => vm_file_path,
59
+ 'allocated_at' => Time.now.utc.to_s,
60
+ 'host_node' => action_handler.host_node
61
+ }
62
+ machine_spec.location['needs_reload'] = true if vm_file_updated
63
+ if machine_options[:vagrant_options]
64
+ %w(vm.guest winrm.host winrm.port winrm.username winrm.password).each do |key|
65
+ machine_spec.location[key] = machine_options[:vagrant_options][key] if machine_options[:vagrant_options][key]
66
+ end
67
+ end
68
+ machine_spec.location['chef_client_timeout'] = machine_options[:chef_client_timeout] if machine_options[:chef_client_timeout]
69
+ end
70
+ end
71
+
72
+ def ready_machine(action_handler, machine_spec, machine_options)
73
+ start_machine(action_handler, machine_spec, machine_options)
74
+ machine_for(machine_spec, machine_options)
75
+ end
76
+
77
+ # Connect to machine without acquiring it
78
+ def connect_to_machine(machine_spec, machine_options)
79
+ machine_for(machine_spec, machine_options)
80
+ end
81
+
82
+ def destroy_machine(action_handler, machine_spec, machine_options)
83
+ if machine_spec.location
84
+ vm_name = machine_spec.location['vm_name']
85
+ current_status = vagrant_status(vm_name)
86
+ if current_status != 'not created'
87
+ action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
88
+ result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
89
+ if result.exitstatus != 0
90
+ raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
91
+ end
92
+ end
93
+ end
94
+
95
+ convergence_strategy_for(machine_spec, machine_options).
96
+ cleanup_convergence(action_handler, machine_spec)
97
+
98
+ vm_file_path = machine_spec.location['vm_file_path']
99
+ Chef::Provisioning.inline_resource(action_handler) do
100
+ file vm_file_path do
101
+ action :delete
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def stop_machine(action_handler, machine_spec, machine_options)
108
+ if machine_spec.location
109
+ vm_name = machine_spec.location['vm_name']
110
+ current_status = vagrant_status(vm_name)
111
+ if current_status == 'running'
112
+ action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
113
+ result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
114
+ if result.exitstatus != 0
115
+ raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def ready_machines(action_handler, specs_and_options, parallelizer)
123
+ start_machines(action_handler, specs_and_options)
124
+ machines = []
125
+ specs_and_options.each_pair do |spec, options|
126
+ machine = machine_for(spec, options)
127
+ machines << machine
128
+ yield machine if block_given?
129
+ end
130
+ machines
131
+ end
132
+
133
+ def destroy_machines(action_handler, specs_and_options, parallelizer)
134
+ all_names = []
135
+ all_status = []
136
+ all_outputs = {}
137
+ specs_and_options.each_key do |spec|
138
+ if spec.location
139
+ vm_name = spec.location['vm_name']
140
+ current_status = vagrant_status(vm_name)
141
+ if current_status != 'not created'
142
+ all_names.push(vm_name)
143
+ all_status.push(current_status)
144
+ end
145
+ end
146
+ end
147
+ if all_names.length > 0
148
+ names = all_names.join(" ")
149
+ statuses = all_status.join(", ")
150
+ action_handler.perform_action "run vagrant destroy -f #{names} (status was '#{statuses}')" do
151
+ result = shell_out("vagrant destroy -f #{names}", :cwd => cluster_path)
152
+ if result.exitstatus != 0
153
+ raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
154
+ end
155
+ end
156
+ end
157
+ specs_and_options.each_pair do |spec, options|
158
+ convergence_strategy_for(spec, options).cleanup_convergence(action_handler, spec)
159
+
160
+ vm_file_path = spec.location['vm_file_path']
161
+ Chef::Provisioning.inline_resource(action_handler) do
162
+ file vm_file_path do
163
+ action :delete
164
+ end
165
+ end
166
+ yield spec if block_given?
167
+ end
168
+ end
169
+
170
+ def stop_machines(action_handler, specs_and_options, parallelizer)
171
+ all_names = []
172
+ specs_and_options.each_key do |spec|
173
+ if spec.location
174
+ vm_name = spec.location['vm_name']
175
+ current_status = vagrant_status(vm_name)
176
+ if current_status == 'running'
177
+ all_names.push(vm_name)
178
+ end
179
+ end
180
+ end
181
+ if all_names.length > 0
182
+ names = all_names.join(" ")
183
+ action_handler.perform_action "run vagrant halt #{names} (status was 'running')" do
184
+ result = shell_out("vagrant halt #{names}", :cwd => cluster_path)
185
+ if result.exitstatus != 0
186
+ raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ # Used by vagrant_cluster and machine to get the string used to configure vagrant
193
+ def self.vagrant_config_string(vagrant_config, variable, line_prefix)
194
+ hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
195
+ result = ''
196
+ vagrant_config.each_pair do |key, value|
197
+ result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
198
+ end
199
+ result
200
+ end
201
+
202
+ def driver_url
203
+ "vagrant:#{cluster_path}"
204
+ end
205
+
206
+ protected
207
+
208
+ def ensure_vagrant_cluster(action_handler)
209
+ _cluster_path = cluster_path
210
+ Chef::Provisioning.inline_resource(action_handler) do
211
+ vagrant_cluster _cluster_path
212
+ end
213
+ end
214
+
215
+ def create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
216
+ # Determine contents of vm file
217
+ vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
218
+ vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
219
+ merged_vagrant_options = { 'vm.hostname' => vm_name }
220
+ if machine_options[:vagrant_options]
221
+ merged_vagrant_options = Cheffish::MergedConfig.new(machine_options[:vagrant_options], merged_vagrant_options)
222
+ end
223
+ merged_vagrant_options.each_pair do |key, value|
224
+ if key == 'vm.network'
225
+ vm_file_content << " config.#{key}(#{value})\n"
226
+ else
227
+ vm_file_content << " config.#{key} = #{value.inspect}\n"
228
+ end
229
+ end
230
+ vm_file_content << machine_options[:vagrant_config] if machine_options[:vagrant_config]
231
+ vm_file_content << " end\nend\n"
232
+
233
+ # Set up vagrant file
234
+ Chef::Provisioning.inline_resource(action_handler) do
235
+ file vm_file_path do
236
+ content vm_file_content
237
+ action :create
238
+ end
239
+ end
240
+ end
241
+
242
+ def start_machine(action_handler, machine_spec, machine_options)
243
+ vm_name = machine_spec.location['vm_name']
244
+ vm_provider = machine_options.has_key?(:vagrant_provider) ? machine_options[:vagrant_provider] : 'virtualbox'
245
+ up_timeout = machine_options[:up_timeout] || 10*60
246
+ current_status = vagrant_status(vm_name)
247
+ vm_file_updated = machine_spec.location['needs_reload']
248
+ machine_spec.location['needs_reload'] = false
249
+ if current_status != 'running'
250
+ # Run vagrant up if vm is not running
251
+ action_handler.perform_action "run vagrant up #{vm_name} --provider #{vm_provider} (status was '#{current_status}')" do
252
+ result = shell_out("vagrant up #{vm_name} --provider #{vm_provider}", :cwd => cluster_path,
253
+ :timeout => up_timeout)
254
+ if result.exitstatus != 0
255
+ raise "vagrant up #{vm_name} --provider #{vm_provider} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
256
+ end
257
+ parse_vagrant_up(result.stdout, machine_spec)
258
+ end
259
+ elsif vm_file_updated
260
+ # Run vagrant reload if vm is running and vm file changed
261
+ action_handler.perform_action "run vagrant reload #{vm_name}" do
262
+ result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path,
263
+ :timeout => up_timeout)
264
+ if result.exitstatus != 0
265
+ raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
266
+ end
267
+ parse_vagrant_up(result.stdout, machine_spec)
268
+ end
269
+ end
270
+ end
271
+
272
+ def start_machines(action_handler, specs_and_options)
273
+ up_names = []
274
+ up_status = []
275
+ up_specs = {}
276
+ update_names = []
277
+ update_specs = {}
278
+ timeouts = []
279
+ specs_and_options.each_pair do |spec, options|
280
+ vm_name = spec.location['vm_name']
281
+
282
+ vm_file_updated = spec.location['needs_reload']
283
+ spec.location['needs_reload'] = false
284
+
285
+ current_status = vagrant_status(vm_name)
286
+ if current_status != 'running'
287
+ up_names.push(vm_name)
288
+ up_status.push(current_status)
289
+ up_specs[vm_name] = spec
290
+ elsif vm_file_updated
291
+ update_names.push(vm_name)
292
+ update_specs[vm_name] = spec
293
+ end
294
+ timeouts.push(options[:up_timeout])
295
+ end
296
+ # Use the highest timeout, if any exist
297
+ up_timeout = timeouts.compact.max
298
+ up_timeout ||= 10*60
299
+ if up_names.length > 0
300
+ # Run vagrant up if vm is not running
301
+ names = up_names.join(" ")
302
+ statuses = up_status.join(", ")
303
+ action_handler.perform_action "run vagrant up --parallel #{names} (status was '#{statuses}')" do
304
+ result = shell_out("vagrant up --parallel #{names}", :cwd => cluster_path,
305
+ :timeout => up_timeout)
306
+ if result.exitstatus != 0
307
+ raise "vagrant up #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
308
+ end
309
+ parse_multi_vagrant_up(result.stdout, up_specs)
310
+ end
311
+ end
312
+ if update_names.length > 0
313
+ names = update_names.join(" ")
314
+ # Run vagrant reload if vm is running and vm file changed
315
+ action_handler.perform_action "run vagrant reload #{names}" do
316
+ result = shell_out("vagrant reload #{names}", :cwd => cluster_path,
317
+ :timeout => up_timeout)
318
+ if result.exitstatus != 0
319
+ raise "vagrant reload #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
320
+ end
321
+ parse_multi_vagrant_up(result.stdout, update_specs)
322
+ end
323
+ end
324
+ end
325
+
326
+ def parse_vagrant_up(output, machine_spec)
327
+ # Grab forwarded port info
328
+ machine_spec.location['forwarded_ports'] = {}
329
+ in_forwarding_ports = false
330
+ output.lines.each do |line|
331
+ if in_forwarding_ports
332
+ if line =~ /-- (\d+) => (\d+)/
333
+ machine_spec.location['forwarded_ports'][$1] = $2
334
+ else
335
+ in_forwarding_ports = false
336
+ end
337
+ elsif line =~ /Forwarding ports...$/
338
+ in_forwarding_ports = true
339
+ end
340
+ end
341
+ end
342
+
343
+ def parse_multi_vagrant_up(output, all_machine_specs)
344
+ # Grab forwarded port info
345
+ in_forwarding_ports = {}
346
+ all_machine_specs.each_pair do |key, spec|
347
+ spec.location['forwarded_ports'] = {}
348
+ in_forwarding_ports[key] = false
349
+ end
350
+ output.lines.each do |line|
351
+ /^\[(.*?)\]/.match(line)
352
+ node_name = $1
353
+ if in_forwarding_ports[node_name]
354
+ if line =~ /-- (\d+) => (\d+)/
355
+ spec = all_machine_specs[node_name]
356
+ spec.location['forwarded_ports'][$1] = $2
357
+ else
358
+ in_forwarding_ports[node_name] = false
359
+ end
360
+ elsif line =~ /Forwarding ports...$/
361
+ in_forwarding_ports[node_name] = true
362
+ end
363
+ end
364
+ end
365
+
366
+ def machine_for(machine_spec, machine_options)
367
+ if machine_spec.location['vm.guest'].to_s == 'windows'
368
+ Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec),
369
+ convergence_strategy_for(machine_spec, machine_options))
370
+ else
371
+ Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec),
372
+ convergence_strategy_for(machine_spec, machine_options))
373
+ end
374
+ end
375
+
376
+ def convergence_strategy_for(machine_spec, machine_options)
377
+ if machine_spec.location['vm.guest'].to_s == 'windows'
378
+ Chef::Provisioning::ConvergenceStrategy::InstallMsi.
379
+ new(machine_options[:convergence_options], config)
380
+ else
381
+ Chef::Provisioning::ConvergenceStrategy::InstallCached.
382
+ new(machine_options[:convergence_options], config)
383
+ end
384
+ end
385
+
386
+ def transport_for(machine_spec)
387
+ if machine_spec.location['vm.guest'].to_s == 'windows'
388
+ create_winrm_transport(machine_spec)
389
+ else
390
+ create_ssh_transport(machine_spec)
391
+ end
392
+ end
393
+
394
+ def vagrant_status(name)
395
+ status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
396
+ if status_output =~ /^#{name}\s+(.+)\s+\((.+)\)/
397
+ $1
398
+ else
399
+ 'not created'
400
+ end
401
+ end
402
+
403
+ def create_winrm_transport(machine_spec)
404
+ forwarded_ports = machine_spec.location['forwarded_ports']
405
+
406
+ # TODO IPv6 loopback? What do we do for that?
407
+ hostname = machine_spec.location['winrm.host'] || '127.0.0.1'
408
+ port = machine_spec.location['winrm.port'] || 5985
409
+ port = forwarded_ports[port] if forwarded_ports[port]
410
+ endpoint = "http://#{hostname}:#{port}/wsman"
411
+ type = :plaintext
412
+ options = {
413
+ :user => machine_spec.location['winrm.username'] || 'vagrant',
414
+ :pass => machine_spec.location['winrm.password'] || 'vagrant',
415
+ :disable_sspi => true
416
+ }
417
+
418
+ Chef::Provisioning::Transport::WinRM.new(endpoint, type, options)
419
+ end
420
+
421
+ def create_ssh_transport(machine_spec)
422
+ vagrant_ssh_config = vagrant_ssh_config_for(machine_spec)
423
+ hostname = vagrant_ssh_config['HostName']
424
+ username = vagrant_ssh_config['User']
425
+ ssh_options = {
426
+ :port => vagrant_ssh_config['Port'],
427
+ :auth_methods => ['publickey'],
428
+ :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
429
+ :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
430
+ :keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
431
+ :keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
432
+ }
433
+ ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
434
+ options = {
435
+ :prefix => 'sudo '
436
+ }
437
+ Chef::Provisioning::Transport::SSH.new(hostname, username, ssh_options, options, config)
438
+ end
439
+
440
+ def vagrant_ssh_config_for(machine_spec)
441
+ vagrant_ssh_config = {}
442
+ result = shell_out("vagrant ssh-config #{machine_spec.location['vm_name']}",
443
+ :cwd => cluster_path)
444
+ result.stdout.lines.inject({}) do |result, line|
445
+ line =~ /^\s*(\S+)\s+(.+?)(\r\n|\r|\n|\z)/
446
+ vagrant_ssh_config[$1] = $2
447
+ end
448
+ vagrant_ssh_config
449
+ end
450
+
451
+ def yes_or_no(str)
452
+ case str
453
+ when 'yes'
454
+ true
455
+ else
456
+ false
457
+ end
458
+ end
459
+
460
+ def strip_quotes(str)
461
+ if str[0] == '"' && str[-1] == '"' && str.size >= 2
462
+ str[1..-2]
463
+ else
464
+ str
465
+ end
466
+ end
467
+ end
468
+ end
469
+ end
470
+ end