beaker 4.24.0 → 4.28.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,7 @@ module Unix::Pkg
5
5
  # unix-specific package management setup
6
6
  def pkg_initialize
7
7
  @apt_needs_update = true
8
+ @pacman_needs_update = true
8
9
  end
9
10
 
10
11
  def check_for_command(name)
@@ -71,6 +72,17 @@ module Unix::Pkg
71
72
  end
72
73
  end
73
74
 
75
+ # Arch Linux is a rolling release distribution. We need to ensure that it is up2date
76
+ # Except for the kernel. An upgrade will purge the modules for the currently running kernel
77
+ def update_pacman_if_needed
78
+ if self['platform'] =~ /archlinux/
79
+ if @pacman_needs_update
80
+ execute("pacman --sync --noconfirm --noprogressbar --refresh --sysupgrade --ignore linux --ignore linux-docs --ignore linux-headers")
81
+ @pacman_needs_update = false
82
+ end
83
+ end
84
+ end
85
+
74
86
  def install_package(name, cmdline_args = '', version = nil, opts = {})
75
87
  case self['platform']
76
88
  when /sles-/
@@ -138,6 +150,7 @@ module Unix::Pkg
138
150
  retry
139
151
  end
140
152
  when /archlinux/
153
+ update_pacman_if_needed
141
154
  execute("pacman -S --noconfirm #{cmdline_args} #{name}", opts)
142
155
  else
143
156
  raise "Package #{name} cannot be installed on #{self}"
@@ -0,0 +1,86 @@
1
+ require 'open3'
2
+
3
+ module Beaker
4
+ class LocalConnection
5
+
6
+ attr_accessor :logger, :hostname, :ip
7
+
8
+ def initialize options = {}
9
+ @logger = options[:logger]
10
+ @ssh_env_file = File.expand_path(options[:ssh_env_file])
11
+ @hostname = 'localhost'
12
+ @ip = '127.0.0.1'
13
+ @options = options
14
+ end
15
+
16
+ def self.connect options = {}
17
+ connection = new options
18
+ connection.connect
19
+ connection
20
+ end
21
+
22
+ def connect options = {}
23
+ @logger.debug "Local connection, no connection to start"
24
+ end
25
+
26
+ def close
27
+ @logger.debug "Local connection, no connection to close"
28
+ end
29
+
30
+ def with_env(env)
31
+ backup = ENV.to_hash
32
+ ENV.replace(env)
33
+ yield
34
+ ensure
35
+ ENV.replace(backup)
36
+ end
37
+
38
+ def execute command, options = {}, stdout_callback = nil, stderr_callback = stdout_callback
39
+ result = Result.new(@hostname, command)
40
+ envs = {}
41
+ if File.readable?(@ssh_env_file)
42
+ File.foreach(@ssh_env_file) do |line|
43
+ key, value = line.split('=')
44
+ envs[key] = value
45
+ end
46
+ end
47
+
48
+ begin
49
+ clean_env = ENV.reject{ |k| k =~ /^BUNDLE|^RUBY|^GEM/ }
50
+
51
+ with_env(clean_env) do
52
+ std_out, std_err, status = Open3.capture3(envs, command)
53
+ result.stdout << std_out
54
+ result.stderr << std_err
55
+ result.exit_code = status.exitstatus
56
+ end
57
+ rescue => e
58
+ result.stderr << e.inspect
59
+ result.exit_code = 1
60
+ end
61
+
62
+ result.finalize!
63
+ @logger.last_result = result
64
+ result
65
+ end
66
+
67
+ def scp_to(source, target, _options = {})
68
+
69
+ result = Result.new(@hostname, [source, target])
70
+ begin
71
+ FileUtils.cp_r source, target
72
+ rescue Errno::ENOENT => e
73
+ @logger.warn "#{e.class} error in cp'ing. Forcing the connection to close, which should " \
74
+ "raise an error."
75
+ end
76
+
77
+ result.stdout << " CP'ed file #{source} to #{target}"
78
+ result.exit_code = 0
79
+ result
80
+ end
81
+
82
+ def scp_from(source, target, options = {})
83
+ scp_to(target, source, options)
84
+ end
85
+ end
86
+ end
@@ -69,8 +69,13 @@ module Beaker
69
69
  begin
70
70
  @logger.debug "Attempting ssh connection to #{host}, user: #{user}, opts: #{ssh_opts}"
71
71
 
72
+ # Work around net-ssh 6+ incompatibilities
72
73
  if ssh_opts.include?(:strict_host_key_checking) && (Net::SSH::Version::CURRENT.major > 5)
73
- ssh_opts[:paranoid] = ssh_opts.delete(:strict_host_key_checking)
74
+ strict_host_key_checking = ssh_opts.delete(:strict_host_key_checking)
75
+
76
+ unless ssh_opts[:verify_host_key].is_a?(Symbol)
77
+ ssh_opts[:verify_host_key] ||= strict_host_key_checking ? :always : :never
78
+ end
74
79
  end
75
80
 
76
81
  Net::SSH.start(host, user, ssh_opts)
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '4.24.0'
3
+ STRING = '4.28.0'
4
4
  end
5
5
  end
@@ -29,8 +29,8 @@ module Beaker
29
29
  it "deletes" do
30
30
  path = '/path/to/delete'
31
31
  corrected_path = '\\path\\to\\delete'
32
- expect( instance ).to receive(:execute).with("del /s /q #{corrected_path}").and_return(0)
33
- expect( instance.rm_rf(path) ).to be === 0
32
+ expect(instance).to receive(:execute).with(%(del /s /q "#{corrected_path}")).and_return(0)
33
+ expect(instance.rm_rf(path)).to eq(0)
34
34
  end
35
35
  end
36
36
 
@@ -39,10 +39,9 @@ module Beaker
39
39
  let(:destination) { '/destination/path/of/content' }
40
40
 
41
41
  it 'rm first' do
42
- expect( instance ).to receive(:execute).with("del /s /q #{destination.gsub(/\//, '\\')}").and_return(0)
43
- expect( instance ).to receive(:execute).with("move /y #{origin.gsub(/\//, '\\')} #{destination.gsub(/\//, '\\')}").and_return(0)
44
- expect( instance.mv(origin, destination) ).to be === 0
45
-
42
+ expect(instance).to receive(:execute).with("del /s /q \"\\destination\\path\\of\\content\"").and_return(0)
43
+ expect(instance).to receive(:execute).with("move /y #{origin.gsub(/\//, '\\')} #{destination.gsub(/\//, '\\')}").and_return(0)
44
+ expect(instance.mv(origin, destination)).to eq(0)
46
45
  end
47
46
 
48
47
  it 'does not rm' do
@@ -169,6 +169,64 @@ module Beaker
169
169
  end
170
170
 
171
171
  describe '#reboot' do
172
+ check_cmd_output = {
173
+ :centos6 => {
174
+ :who => {
175
+ :initial => ' system boot 2020-05-13 03:51',
176
+ :success => ' system boot 2020-05-13 03:52',
177
+ },
178
+ :last => {
179
+ :initial => <<~LAST_F,
180
+ reboot system boot 2.6.32-754.29.1. Tue May 5 17:34:52 2020 - Tue May 5 17:52:48 2020 (00:17)
181
+ reboot system boot 2.6.32-754.29.1. Mon May 4 18:45:43 2020 - Mon May 5 05:35:44 2020 (4+01:50)
182
+ LAST_F
183
+ :success => <<~LAST_F,
184
+ reboot system boot 2.6.32-754.29.1. Tue May 5 17:52:48 2020 - Tue May 5 17:52:49 2020 (00:17)
185
+ reboot system boot 2.6.32-754.29.1. Mon May 4 18:45:43 2020 - Mon May 5 05:35:44 2020 (4+01:50)
186
+ LAST_F
187
+ },
188
+ },
189
+ :centos7 => {
190
+ :who => {
191
+ :initial => ' system boot 2020-05-13 03:51',
192
+ :success => ' system boot 2020-05-13 03:52',
193
+ },
194
+ :last => {
195
+ :initial => <<~LAST_F,
196
+ reboot system boot 3.10.0-1127.el7. Tue May 5 17:34:52 2020 - Tue May 5 17:52:48 2020 (00:17)
197
+ reboot system boot 3.10.0-1127.el7. Mon May 4 18:45:43 2020 - Mon May 5 05:35:44 2020 (4+01:50)
198
+ LAST_F
199
+ :success => <<~LAST_F,
200
+ reboot system boot 3.10.0-1127.el7. Tue May 5 17:52:48 2020 - Tue May 5 17:52:49 2020 (00:17)
201
+ reboot system boot 3.10.0-1127.el7. Mon May 4 18:45:43 2020 - Mon May 5 05:35:44 2020 (4+01:50)
202
+ LAST_F
203
+ },
204
+ },
205
+ :centos8 => {
206
+ :who => {
207
+ :initial => ' system boot 2020-05-13 03:51',
208
+ :success => ' system boot 2020-05-13 03:52',
209
+ },
210
+ :last => {
211
+ :initial => <<~LAST_F,
212
+ reboot system boot 4.18.0-147.8.1.e Tue May 5 17:34:52 2020 still running
213
+ reboot system boot 4.18.0-147.8.1.e Mon May 4 17:41:27 2020 - Tue May 5 17:00:00 2020 (5+00:11)
214
+ LAST_F
215
+ :success => <<~LAST_F,
216
+ reboot system boot 4.18.0-147.8.1.e Tue May 5 17:34:53 2020 still running
217
+ reboot system boot 4.18.0-147.8.1.e Mon May 4 17:41:27 2020 - Tue May 5 17:00:00 2020 (5+00:11)
218
+ LAST_F
219
+ },
220
+ },
221
+ :freebsd => {
222
+ # last -F doesn't work on freebsd so no output will be returned
223
+ :who => {
224
+ :initial => ' system boot May 13 03:51',
225
+ :success => ' system boot May 13 03:52',
226
+ }
227
+ },
228
+ }
229
+
172
230
  # no-op response
173
231
  let (:response) { double( 'response' ) }
174
232
  let (:boot_time_initial_response) { double( 'response' ) }
@@ -179,7 +237,7 @@ module Beaker
179
237
  before :each do
180
238
  # stubs enough to survive the first boot_time call & output parsing
181
239
  # note: just stubs input-chain between calls, parsing methods still run
182
- allow(Beaker::Command).to receive(:new).with('who -b').and_return(:boot_time_command_stub)
240
+ allow(Beaker::Command).to receive(:new).with('last -F reboot || who -b').and_return(:boot_time_command_stub)
183
241
 
184
242
  allow(boot_time_initial_response).to receive(:stdout).and_return(boot_time_initial_stdout)
185
243
  allow(boot_time_success_response).to receive(:stdout).and_return(boot_time_success_stdout)
@@ -190,116 +248,128 @@ module Beaker
190
248
  end
191
249
 
192
250
  context 'new boot time greater than old boot time' do
193
- let (:boot_time_initial_stdout) { ' system boot 2020-05-13 03:51' }
194
- let (:boot_time_success_stdout) { ' system boot 2020-05-13 03:52' }
195
-
196
- it 'passes with defaults' do
197
- expect(instance).to receive(:sleep).with(sleep_time)
198
- # bypass shutdown command itself
199
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response)
200
- # allow the second boot_time and the hash arguments in exec
201
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
202
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
203
-
204
- expect(instance.reboot).to be(nil)
205
- end
251
+ check_cmd_output.each do |check_os, cmd_opts|
252
+ cmd_opts.each do |cmd_name, cmd_outputs|
253
+ context "on '#{check_os}' with the '#{cmd_name}' command" do
254
+ let (:boot_time_initial_stdout) { cmd_outputs[:initial] }
255
+ let (:boot_time_success_stdout) { cmd_outputs[:success] }
256
+
257
+ it 'passes with defaults' do
258
+ expect(instance).to receive(:sleep).with(sleep_time)
259
+ # bypass shutdown command itself
260
+ expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response)
261
+ # allow the second boot_time and the hash arguments in exec
262
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
263
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
206
264
 
207
- it 'passes with wait_time_parameter' do
208
- expect(instance).to receive(:sleep).with(10)
209
- # bypass shutdown command itself
210
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
211
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
212
- # allow the second boot_time and the hash arguments in exec
213
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
265
+ expect(instance.reboot).to be(nil)
266
+ end
214
267
 
215
- expect(instance.reboot(10)).to be(nil)
216
- end
268
+ it 'passes with wait_time_parameter' do
269
+ expect(instance).to receive(:sleep).with(10)
270
+ # bypass shutdown command itself
271
+ expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
272
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
273
+ # allow the second boot_time and the hash arguments in exec
274
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
217
275
 
218
- it 'passes with max_connection_tries parameter' do
219
- expect(instance).to receive(:sleep).with(sleep_time)
220
- # bypass shutdown command itself
221
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
222
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
223
- # allow the second boot_time and the hash arguments in exec
224
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, hash_including(:max_connection_tries => 20)).and_return(boot_time_success_response).once
276
+ expect(instance.reboot(10)).to be(nil)
277
+ end
225
278
 
226
- expect(instance.reboot(sleep_time, 20)).to be(nil)
227
- end
279
+ it 'passes with max_connection_tries parameter' do
280
+ expect(instance).to receive(:sleep).with(sleep_time)
281
+ # bypass shutdown command itself
282
+ expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
283
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
284
+ # allow the second boot_time and the hash arguments in exec
285
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, hash_including(:max_connection_tries => 20)).and_return(boot_time_success_response).once
228
286
 
229
- context 'command errors' do
230
- before :each do
231
- allow(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
232
- end
287
+ expect(instance.reboot(sleep_time, 20)).to be(nil)
288
+ end
233
289
 
234
- it 'raises a reboot failure when command fails' do
235
- expect(instance).not_to receive(:sleep)
236
- expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Host::CommandFailure).once
290
+ context 'command errors' do
291
+ before :each do
292
+ allow(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).at_least(:once)
293
+ end
237
294
 
238
- expect{ instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Command failed when attempting to reboot: .*/)
239
- end
295
+ it 'raises a reboot failure when command fails' do
296
+ expect(instance).to receive(:sleep).at_least(:once)
297
+ expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Host::CommandFailure).at_least(:once)
240
298
 
241
- it 'raises a reboot failure when we receive an unexpected error' do
242
- expect(instance).not_to receive(:sleep)
243
- expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Net::SSH::HostKeyError).once
299
+ expect{ instance.reboot }.to raise_error(Beaker::Host::CommandFailure)
300
+ end
244
301
 
245
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unexpected exception in reboot: .*/)
246
- end
302
+ it 'raises a reboot failure when we receive an unexpected error' do
303
+ expect(instance).to receive(:sleep).at_least(:once)
304
+ expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Net::SSH::HostKeyError).at_least(:once)
247
305
 
248
- context 'incorrect time string' do
249
- context 'original time' do
250
- let (:boot_time_initial_stdout) { 'boot bad' }
306
+ expect { instance.reboot }.to raise_error(Net::SSH::HostKeyError)
307
+ end
251
308
 
252
- it 'raises a reboot failure' do
253
- expect(instance).not_to receive(:sleep)
309
+ context 'incorrect time string' do
310
+ context 'original time' do
311
+ let (:boot_time_initial_stdout) { 'boot bad' }
254
312
 
255
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unable to parse time: .*/)
256
- end
257
- end
313
+ it 'raises a reboot failure' do
314
+ # Handle the 'retry'
315
+ allow(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).at_least(:once)
258
316
 
259
- context 'current time' do
260
- let (:boot_time_success_stdout) { 'boot bad' }
317
+ expect(instance).not_to receive(:sleep)
261
318
 
262
- it 'raises a reboot failure' do
263
- expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_return(response).once
264
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
265
- # allow the second boot_time and the hash arguments in exec, repeated 10 times by default
266
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
319
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootWarning, /Found no valid times in .*/)
320
+ end
321
+ end
322
+
323
+ context 'current time' do
324
+ let (:boot_time_success_stdout) { 'boot bad' }
267
325
 
268
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unable to parse time: .*/)
326
+ it 'raises a reboot failure' do
327
+ expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_return(response).once
328
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
329
+ # allow the second boot_time and the hash arguments in exec, repeated 10 times by default
330
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).at_least(:once)
331
+
332
+ expect { instance.reboot(10,9,1) }.to raise_error(Beaker::Host::RebootWarning, /Found no valid times in .*/)
333
+ end
334
+ end
335
+ end
269
336
  end
270
337
  end
271
338
  end
272
339
  end
273
340
  end
274
341
 
275
- context 'new boot time less than old boot time' do
276
- let (:boot_time_initial_stdout) { ' system boot 2020-05-13 03:51' }
277
- let (:boot_time_success_stdout) { ' system boot 2020-05-13 03:50' }
342
+ context 'system did not reboot' do
343
+ check_cmd_output.each do |check_os, cmd_opts|
344
+ cmd_opts.each do |cmd_name, cmd_outputs|
345
+ context "on '#{check_os}' with the '#{cmd_name}' command" do
346
+ let (:boot_time_initial_stdout) { cmd_outputs[:initial] }
347
+ let (:boot_time_success_stdout) { cmd_outputs[:initial] }
278
348
 
279
- it 'raises RebootFailure' do
280
- expect(instance).to receive(:sleep).with(sleep_time)
281
- # bypass shutdown command itself
282
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
349
+ it 'raises RebootFailure' do
350
+ expect(instance).to receive(:sleep).with(sleep_time)
351
+ # bypass shutdown command itself
352
+ expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
283
353
 
284
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
285
- # allow the second boot_time and the hash arguments in exec, repeated 18 times by default in order to replicate the previous behavior of the ping based Host.down?
286
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).exactly(18).times
354
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
355
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
287
356
 
288
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Boot time did not reset/)
289
- end
357
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Boot time did not reset/)
358
+ end
290
359
 
291
- it 'raises RebootFailure if the number of retries is changed' do
292
- expect(instance).to receive(:sleep).with(sleep_time)
293
- # bypass shutdown command itself
294
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
295
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
296
- # allow the second boot_time and the hash arguments in exec, repeated 10 times by default
297
- expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).exactly(10).times
360
+ it 'raises RebootFailure if the number of retries is changed' do
361
+ expect(instance).to receive(:sleep).with(sleep_time)
362
+ # bypass shutdown command itself
363
+ expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
364
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_initial_response).once
365
+ expect(instance).to receive( :exec ).with(:boot_time_command_stub, anything).and_return(boot_time_success_response).once
298
366
 
299
- expect { instance.reboot(wait_time=sleep_time, max_connection_tries=9, boot_time_retries=10) }.to raise_error(Beaker::Host::RebootFailure, /Boot time did not reset/)
367
+ expect { instance.reboot(wait_time=sleep_time, max_connection_tries=9, boot_time_retries=10) }.to raise_error(Beaker::Host::RebootFailure, /Boot time did not reset/)
368
+ end
369
+ end
370
+ end
300
371
  end
301
372
  end
302
-
303
373
  end
304
374
 
305
375
  describe '#enable_remote_rsyslog' do