rspec-system 2.5.1 → 2.6.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.
@@ -5,13 +5,22 @@ require 'net/scp'
5
5
  require 'rspec-system/node_set/base'
6
6
 
7
7
  module RSpecSystem
8
- # A NodeSet implementation for Vagrant.
9
- class NodeSet::Vagrant < RSpecSystem::NodeSet::Base
8
+ # An abstract NodeSet implementation for Vagrant.
9
+ class NodeSet::VagrantBase < RSpecSystem::NodeSet::Base
10
10
  include RSpecSystem::Log
11
11
  include RSpecSystem::Util
12
12
 
13
- ENV_TYPE = 'vagrant'
14
- VALID_VM_OPTIONS = ['ip']
13
+ # @!group Abstract Methods
14
+
15
+ # The vagrant specific provider name
16
+ #
17
+ # @return [String] name of the provider as used by `vagrant --provider`
18
+ # @abstract override to return the name of the vagrant provider
19
+ def vagrant_provider_name
20
+ raise RuntimeError, "Unimplemented method #vagrant_provider_name"
21
+ end
22
+
23
+ # @!group Common Methods
15
24
 
16
25
  # Creates a new instance of RSpecSystem::NodeSet::Vagrant
17
26
  #
@@ -21,30 +30,49 @@ module RSpecSystem
21
30
  # @param options [Hash] options Hash
22
31
  def initialize(setname, config, custom_prefabs_path, options)
23
32
  super
24
- @vagrant_path = File.expand_path(File.join(RSpec.configuration.system_tmp, 'vagrant_projects', setname))
33
+ @vagrant_path = File.expand_path(File.join(RSpec.configuration.rs_tmp, 'vagrant_projects', setname))
25
34
 
26
- RSpec.configuration.rspec_storage[:nodes] ||= {}
35
+ RSpec.configuration.rs_storage[:nodes] ||= {}
27
36
  end
28
37
 
29
- # Setup the NodeSet by starting all nodes.
38
+ # Launch the nodes
30
39
  #
31
40
  # @return [void]
32
- def setup
41
+ def launch
33
42
  create_vagrantfile()
34
43
 
35
44
  teardown()
36
45
 
37
- output << bold(color("localhost$", :green)) << " vagrant up\n"
38
- vagrant("up")
46
+ nodes.each do |k,v|
47
+ RSpec.configuration.rs_storage[:nodes][k] ||= {}
48
+ output << bold(color("localhost$", :green)) << " vagrant up #{k}\n"
49
+ vagrant("up #{k} --provider=#{vagrant_provider_name}")
50
+ end
51
+
52
+ nil
53
+ end
39
54
 
40
- # Establish ssh connectivity
55
+ # Connect to the nodes
56
+ #
57
+ # @return [void]
58
+ def connect
41
59
  nodes.each do |k,v|
42
- output << bold(color("localhost$", :green)) << " ssh #{k}\n"
43
- chan = Net::SSH.start(k, 'vagrant', :config => ssh_config)
60
+ RSpec.configuration.rs_storage[:nodes][k] ||= {}
61
+
62
+ chan = ssh_connect(:host => k, :user => 'vagrant', :net_ssh_options => {
63
+ :config => ssh_config
64
+ })
44
65
 
45
- RSpec.configuration.rspec_storage[:nodes][k] = {
46
- :ssh => chan,
47
- }
66
+ # Copy the authorized keys from vagrant user to root then reconnect
67
+ cmd = 'mkdir /root/.ssh ; cp /home/vagrant/.ssh/authorized_keys /root/.ssh'
68
+
69
+ output << bold(color("#{k}$ ", :green)) << cmd << "\n"
70
+ ssh_exec!(chan, "cd /tmp && sudo sh -c #{shellescape(cmd)}")
71
+
72
+ chan = ssh_connect(:host => k, :user => 'root', :net_ssh_options => {
73
+ :config => ssh_config
74
+ })
75
+ RSpec.configuration.rs_storage[:nodes][k][:ssh] = chan
48
76
  end
49
77
 
50
78
  nil
@@ -55,7 +83,7 @@ module RSpecSystem
55
83
  # @return [void]
56
84
  def teardown
57
85
  nodes.each do |k,v|
58
- storage = RSpec.configuration.rspec_storage[:nodes][k]
86
+ storage = RSpec.configuration.rs_storage[:nodes][k]
59
87
 
60
88
  next if storage.nil?
61
89
 
@@ -67,49 +95,8 @@ module RSpecSystem
67
95
  output << bold(color("localhost$", :green)) << " vagrant destroy --force\n"
68
96
  vagrant("destroy --force")
69
97
  end
70
- nil
71
- end
72
98
 
73
- # Run a command on a host in the NodeSet.
74
- #
75
- # @param opts [Hash] options
76
- # @return [Hash] a hash containing :exit_code, :stdout and :stderr
77
- def run(opts)
78
- dest = opts[:n].name
79
- cmd = opts[:c]
80
-
81
- ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh]
82
- ssh_exec!(ssh, "cd /tmp && sudo sh -c #{shellescape(cmd)}")
83
- end
84
-
85
- # Transfer files to a host in the NodeSet.
86
- #
87
- # @param opts [Hash] options
88
- # @return [Boolean] returns true if command succeeded, false otherwise
89
- # @todo This is damn ugly, because we ssh in as vagrant, we copy to a temp
90
- # path then move it later. Its slow and brittle and we need a better
91
- # solution. Its also very Linux-centrix in its use of temp dirs.
92
- def rcp(opts)
93
- dest = opts[:d].name
94
- source = opts[:sp]
95
- dest_path = opts[:dp]
96
-
97
- # Grab a remote path for temp transfer
98
- tmpdest = tmppath
99
-
100
- # Do the copy and print out results for debugging
101
- cmd = "scp -r '#{source}' #{dest}:#{tmpdest}"
102
- output << bold(color("localhost$", :green)) << " #{cmd}\n"
103
- ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh]
104
- ssh.scp.upload! source.to_s, tmpdest.to_s, :recursive => true
105
-
106
- # Now we move the file into their final destination
107
- result = shell(:n => opts[:d], :c => "mv #{tmpdest} #{dest_path}")
108
- if result[:exit_code] == 0
109
- return true
110
- else
111
- return false
112
- end
99
+ nil
113
100
  end
114
101
 
115
102
  # Create the Vagrantfile for the NodeSet.
@@ -121,7 +108,7 @@ module RSpecSystem
121
108
  File.open(File.expand_path(File.join(@vagrant_path, "Vagrantfile")), 'w') do |f|
122
109
  f.write('Vagrant.configure("2") do |c|' + "\n")
123
110
  nodes.each do |k,v|
124
- ps = v.provider_specifics['vagrant']
111
+ ps = v.provider_specifics[provider_type]
125
112
  default_options = { 'mac' => randmac }
126
113
  options = default_options.merge(v.options || {})
127
114
 
@@ -130,8 +117,8 @@ module RSpecSystem
130
117
  node_config << " v.vm.box = '#{ps['box']}'\n"
131
118
  node_config << " v.vm.box_url = '#{ps['box_url']}'\n" unless ps['box_url'].nil?
132
119
  node_config << customize_vm(k,options)
133
- node_config << " v.vm.provider 'virtualbox' do |vbox|\n"
134
- node_config << customize_virtualbox(k,options)
120
+ node_config << " v.vm.provider '#{vagrant_provider_name}' do |prov, override|\n"
121
+ node_config << customize_provider(k,options)
135
122
  node_config << " end\n"
136
123
  node_config << " end\n"
137
124
 
@@ -142,26 +129,15 @@ module RSpecSystem
142
129
  nil
143
130
  end
144
131
 
145
- # Adds virtualbox customization to the Vagrantfile
146
- #
132
+ # Add provider specific customization to the Vagrantfile
133
+ #
147
134
  # @api private
148
135
  # @param name [String] name of the node
149
136
  # @param options [Hash] customization options
150
- # @return [String] a series of vbox.customize lines
151
- def customize_virtualbox(name,options)
152
- custom_config = ""
153
- options.each_pair do |key,value|
154
- next if VALID_VM_OPTIONS.include?(key)
155
- case key
156
- when 'cpus','memory'
157
- custom_config << " vbox.customize ['modifyvm', :id, '--#{key}','#{value}']\n"
158
- when 'mac'
159
- custom_config << " vbox.customize ['modifyvm', :id, '--macaddress1','#{value}']\n"
160
- else
161
- log.warn("Skipped invalid custom option for node #{name}: #{key}=#{value}")
162
- end
163
- end
164
- custom_config
137
+ # @return [String] a series of prov.customize lines
138
+ # @abstract Overridet ot provide your own customizations
139
+ def customize_provider(name,options)
140
+ ''
165
141
  end
166
142
 
167
143
  # Adds VM customization to the Vagrantfile
@@ -207,9 +183,6 @@ module RSpecSystem
207
183
  #
208
184
  # @api private
209
185
  # @param args [String] args to vagrant
210
- # @todo This seems a little too specific these days, might want to
211
- # generalize. It doesn't use systemu, because we want to see the output
212
- # immediately, but still - maybe we can make systemu do that.
213
186
  def vagrant(args)
214
187
  Dir.chdir(@vagrant_path) do
215
188
  system("vagrant #{args}")
@@ -217,5 +190,13 @@ module RSpecSystem
217
190
  nil
218
191
  end
219
192
 
193
+ # Returns a list of options that apply to all types of vagrant providers
194
+ #
195
+ # @return [Array<String>] Array of options
196
+ # @api private
197
+ def global_vagrant_options
198
+ ['ip']
199
+ end
200
+
220
201
  end
221
202
  end
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+ require 'systemu'
3
+ require 'net/ssh'
4
+ require 'net/scp'
5
+ require 'rspec-system/node_set/vagrant_base'
6
+
7
+ module RSpecSystem
8
+ # A NodeSet implementation for Vagrant.
9
+ class NodeSet::VagrantVirtualbox < NodeSet::VagrantBase
10
+ PROVIDER_TYPE = 'vagrant_virtualbox'
11
+
12
+ # Name of provider
13
+ #
14
+ # @return [String] name of the provider as used by `vagrant --provider`
15
+ def vagrant_provider_name
16
+ 'virtualbox'
17
+ end
18
+
19
+ # Adds virtualbox customization to the Vagrantfile
20
+ #
21
+ # @api private
22
+ # @param name [String] name of the node
23
+ # @param options [Hash] customization options
24
+ # @return [String] a series of vbox.customize lines
25
+ def customize_provider(name,options)
26
+ custom_config = ""
27
+ options.each_pair do |key,value|
28
+ next if global_vagrant_options.include?(key)
29
+ case key
30
+ when 'cpus','memory'
31
+ custom_config << " prov.customize ['modifyvm', :id, '--#{key}','#{value}']\n"
32
+ when 'mac'
33
+ custom_config << " prov.customize ['modifyvm', :id, '--macaddress1','#{value}']\n"
34
+ else
35
+ log.warn("Skipped invalid custom option for node #{name}: #{key}=#{value}")
36
+ end
37
+ end
38
+ custom_config
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ require 'fileutils'
2
+ require 'systemu'
3
+ require 'net/ssh'
4
+ require 'net/scp'
5
+ require 'rspec-system/node_set/vagrant_base'
6
+
7
+ module RSpecSystem
8
+ # A NodeSet implementation for Vagrant using the vmware_fusion provider
9
+ class NodeSet::VagrantVmwareFusion < NodeSet::VagrantBase
10
+ PROVIDER_TYPE = 'vagrant_vmware_fusion'
11
+
12
+ # Name of provider
13
+ #
14
+ # @return [String] name of the provider as used by `vagrant --provider`
15
+ def vagrant_provider_name
16
+ 'vmware_fusion'
17
+ end
18
+
19
+ # Adds virtualbox customization to the Vagrantfile
20
+ #
21
+ # @api private
22
+ # @param name [String] name of the node
23
+ # @param options [Hash] customization options
24
+ # @return [String] a series of vbox.customize lines
25
+ def customize_provider(name,options)
26
+ custom_config = ""
27
+ options.each_pair do |key,value|
28
+ next if global_vagrant_options.include?(key)
29
+ case key
30
+ when 'cpus'
31
+ custom_config << " prov.vmx['numvcpus'] = '#{value}'\n"
32
+ when 'memory'
33
+ custom_config << " prov.vmx['memsize'] = '#{value}'\n"
34
+ when 'mac'
35
+ custom_config << " prov.vmx['ethernet0.generatedAddress'] = '#{value}'\n"
36
+ else
37
+ log.warn("Skipped invalid custom option for node #{name}: #{key}=#{value}")
38
+ end
39
+ end
40
+ custom_config
41
+ end
42
+ end
43
+ end
@@ -10,7 +10,7 @@ module RSpecSystem
10
10
  class NodeSet::Vsphere < RSpecSystem::NodeSet::Base
11
11
  include RSpecSystem::Log
12
12
 
13
- ENV_TYPE = 'vsphere'
13
+ PROVIDER_TYPE = 'vsphere'
14
14
 
15
15
  attr_reader :vmconf
16
16
 
@@ -26,8 +26,7 @@ module RSpecSystem
26
26
  # Valid supported ENV variables
27
27
  options = [:host, :user, :pass, :dest_dir, :template_dir, :rpool,
28
28
  :cluster, :ssh_keys, :datacenter, :node_timeout, :node_tries,
29
- :node_sleep, :ssh_timeout, :ssh_tries, :ssh_sleep, :connect_timeout,
30
- :connect_tries]
29
+ :node_sleep, :connect_timeout, :connect_tries]
31
30
 
32
31
  # Devise defaults, use fog configuration from file system if it exists
33
32
  defaults = load_fog_config()
@@ -35,9 +34,6 @@ module RSpecSystem
35
34
  :node_timeout => 1200,
36
35
  :node_tries => 10,
37
36
  :node_sleep => 30 + rand(60),
38
- :ssh_timeout => 60,
39
- :ssh_tries => 10,
40
- :ssh_sleep => 4,
41
37
  :connect_timeout => 60,
42
38
  :connect_tries => 10,
43
39
  })
@@ -45,8 +41,8 @@ module RSpecSystem
45
41
  # Traverse the ENV variables and load them into our config automatically
46
42
  @vmconf = defaults
47
43
  ENV.each do |k,v|
48
- next unless k =~/^RSPEC_VSPHERE_/
49
- var = k.sub(/^RSPEC_VSPHERE_/, '').downcase.to_sym
44
+ next unless k =~/^RS(PEC)?_VSPHERE_/
45
+ var = k.sub(/^RS(PEC)?_VSPHERE_/, '').downcase.to_sym
50
46
  unless options.include?(var)
51
47
  log.info("Ignoring unknown environment variable #{k}")
52
48
  next
@@ -55,7 +51,7 @@ module RSpecSystem
55
51
  end
56
52
 
57
53
  # Initialize node storage if not already
58
- RSpec.configuration.rspec_storage[:nodes] ||= {}
54
+ RSpec.configuration.rs_storage[:nodes] ||= {}
59
55
  end
60
56
 
61
57
  # Retrieves fog configuration if it exists
@@ -82,7 +78,7 @@ module RSpecSystem
82
78
  # The connection handling automatically retries upon failure.
83
79
  #
84
80
  # @api private
85
- def with_connection(&block)
81
+ def with_vsphere_connection(&block)
86
82
  vim = nil
87
83
  dc = nil
88
84
 
@@ -122,11 +118,11 @@ module RSpecSystem
122
118
 
123
119
  # @!group NodeSet Methods
124
120
 
125
- # Setup the NodeSet by starting all nodes.
121
+ # Launch the nodes
126
122
  #
127
123
  # @return [void]
128
- def setup
129
- with_connection do |dc|
124
+ def launch
125
+ with_vsphere_connection do |dc|
130
126
  # Traverse folders to find target folder for new vm's and template
131
127
  # folders. Automatically create the destination folder if it doesn't
132
128
  # exist.
@@ -148,10 +144,7 @@ module RSpecSystem
148
144
 
149
145
  log.info "Launching VSphere instances one by one"
150
146
  nodes.each do |k,v|
151
- #####################
152
- # Node launching step
153
- #####################
154
- RSpec.configuration.rspec_storage[:nodes][k] ||= {}
147
+ RSpec.configuration.rs_storage[:nodes][k] ||= {}
155
148
 
156
149
  # Obtain the template name to use
157
150
  ps = v.provider_specifics['vsphere']
@@ -165,7 +158,7 @@ module RSpecSystem
165
158
 
166
159
  # Create a random name for the new VM
167
160
  vm_name = "rspec-system-#{k}-#{random_string(10)}"
168
- RSpec.configuration.rspec_storage[:nodes][k][:vm] = vm_name
161
+ RSpec.configuration.rs_storage[:nodes][k][:vm] = vm_name
169
162
 
170
163
  log.info "Launching VSphere instance #{k} with template #{vmconf[:template_dir]}/#{template} as #{vmconf[:dest_dir]}/#{vm_name}"
171
164
 
@@ -201,7 +194,7 @@ module RSpecSystem
201
194
  time3 = Time.now
202
195
  log.info "#{k}> Time in seconds waiting for IP: #{time3 - time2}"
203
196
  end
204
- RSpec.configuration.rspec_storage[:nodes][k][:ipaddress] = ipaddress
197
+ RSpec.configuration.rs_storage[:nodes][k][:ipaddress] = ipaddress
205
198
  rescue Timeout::Error, SystemCallError => e
206
199
  tries += 1
207
200
  log.error("VM launch attempt #{tries} failed with: " + e.message)
@@ -227,47 +220,31 @@ module RSpecSystem
227
220
  raise e
228
221
  end
229
222
  end
223
+
230
224
  time2 = Time.now
231
225
  log.info "#{k}> Took #{time2 - start_time} seconds to boot instance"
226
+ end
227
+ end
232
228
 
233
- #####################
234
- # SSH Step
235
- #####################
236
- tries = 0
237
- begin
238
- timeout(vmconf[:ssh_timeout]) do
239
- output << bold(color("localhost$", :green)) << " ssh #{k}\n"
240
- chan = Net::SSH.start(ipaddress, 'root', {
241
- :keys => vmconf[:ssh_keys].split(":"),
242
- })
229
+ nil
230
+ end
243
231
 
244
- RSpec.configuration.rspec_storage[:nodes][k][:ssh] = chan
245
- end
246
- rescue Timeout::Error, SystemCallError => e
247
- tries += 1
248
- output << e.message << "\n"
249
- if tries < vmconf[:ssh_tries]
250
- log.info("Sleeping for #{vmconf[:ssh_sleep]} seconds then trying again ...")
251
- sleep vmconf[:ssh_sleep]
252
- retry
253
- else
254
- log.error("Inability to connect to host, already tried #{tries} times, throwing exception")
255
- raise e
256
- end
257
- end
258
- time3 = Time.now
259
- log.info "#{k}> Took #{time3 - start_time} seconds for instance to be ready"
260
-
261
- ######################
262
- # Do initial box setup
263
- ######################
264
- hosts = <<-EOS
265
- 127.0.0.1 localhost localhost.localdomain
266
- #{ipaddress} #{k}
267
- EOS
268
- shell(:n => k, :c => "echo '#{hosts}' > /etc/hosts")
269
- shell(:n => k, :c => "hostname #{k}")
270
- end
232
+ # Connect to the nodes
233
+ #
234
+ # @return [void]
235
+ def connect
236
+ nodes.each do |k,v|
237
+ rs_storage = RSpec.configuration.rs_storage[:nodes][k]
238
+ raise RuntimeError, "No internal storage for node #{k}" if rs_storage.nil?
239
+
240
+ ipaddress = rs_storage[:ipaddress]
241
+ raise RuntimeError, "No ipaddress provided from launch phase for node #{k}" if ipaddress.nil?
242
+
243
+ chan = ssh_connect(:host => k, :user => 'root', :net_ssh_options => {
244
+ :keys => vmconf[:ssh_keys].split(":"),
245
+ :host_name => ipaddress,
246
+ })
247
+ RSpec.configuration.rs_storage[:nodes][k][:ssh] = chan
271
248
  end
272
249
 
273
250
  nil
@@ -277,9 +254,9 @@ module RSpecSystem
277
254
  #
278
255
  # @return [void]
279
256
  def teardown
280
- with_connection do |dc|
257
+ with_vsphere_connection do |dc|
281
258
  nodes.each do |k,v|
282
- storage = RSpec.configuration.rspec_storage[:nodes][k]
259
+ storage = RSpec.configuration.rs_storage[:nodes][k]
283
260
 
284
261
  if storage.nil?
285
262
  log.info "No entry for node #{k}, no teardown necessary"
@@ -325,42 +302,5 @@ module RSpecSystem
325
302
  nil
326
303
  end
327
304
 
328
- # Run a command on a host in the NodeSet.
329
- #
330
- # @param opts [Hash] options
331
- # @return [Hash] a hash containing :exit_code, :stdout and :stderr
332
- def run(opts)
333
- dest = opts[:n].name
334
- cmd = opts[:c]
335
-
336
- ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh]
337
- ssh_exec!(ssh, cmd)
338
- end
339
-
340
- # Transfer files to a host in the NodeSet.
341
- #
342
- # @param opts [Hash] options
343
- # @return [Boolean] returns true if command succeeded, false otherwise
344
- # @todo This is damn ugly, because we ssh in as vagrant, we copy to a temp
345
- # path then move it later. Its slow and brittle and we need a better
346
- # solution. Its also very Linux-centrix in its use of temp dirs.
347
- def rcp(opts)
348
- dest = opts[:d].name
349
- source = opts[:sp]
350
- dest_path = opts[:dp]
351
-
352
- # Do the copy and print out results for debugging
353
- ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh]
354
-
355
- begin
356
- ssh.scp.upload! source.to_s, dest_path.to_s, :recursive => true
357
- rescue => e
358
- log.error("Error with scp of file #{source} to #{dest}:#{dest_path}")
359
- raise e
360
- end
361
-
362
- true
363
- end
364
-
365
305
  end
366
306
  end